1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  * Alias|Wavefront pix/matte image reading and writing code
4  * Copyright (C) 1997 Mike Taylor
5  * (email: mtaylor@aw.sgi.com, WWW: http://reality.sgi.com/mtaylor)
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21 
22 /* This plug-in was written using the online documentation from
23  * Alias|Wavefront Inc's PowerAnimator product.
24  *
25  * Bug reports or suggestions should be e-mailed to mtaylor@aw.sgi.com
26  */
27 
28 /* Event history:
29  * V 1.0, MT, 02-Jul-97: initial version of plug-in
30  * V 1.1, MT, 04-Dec-97: added .als file extension
31  */
32 
33 /* Features
34  *  - loads and exports
35  *    - 24-bit (.pix)
36  *    - 8-bit (.matte, .alpha, or .mask) images
37  *
38  * NOTE: pix and matte files do not support alpha channels or indexed
39  *       color, so neither does this plug-in
40  */
41 
42 #include "config.h"
43 
44 #include <errno.h>
45 #include <string.h>
46 
47 #include <glib/gstdio.h>
48 
49 #include <libgimp/gimp.h>
50 #include <libgimp/gimpui.h>
51 
52 #include "libgimp/stdplugins-intl.h"
53 
54 
55 #define LOAD_PROC      "file-pix-load"
56 #define SAVE_PROC      "file-pix-save"
57 #define PLUG_IN_BINARY "file-pix"
58 #define PLUG_IN_ROLE   "gimp-file-pix"
59 
60 
61 /* #define PIX_DEBUG */
62 
63 #ifdef PIX_DEBUG
64 #    define PIX_DEBUG_PRINT(a,b) g_printerr (a,b)
65 #else
66 #    define PIX_DEBUG_PRINT(a,b)
67 #endif
68 
69 
70 /**************
71  * Prototypes *
72  **************/
73 
74 static void      query      (void);
75 static void      run        (const gchar      *name,
76                              gint              nparams,
77                              const GimpParam  *param,
78                              gint             *nreturn_vals,
79                              GimpParam       **return_vals);
80 
81 static gint32    load_image (GFile           *file,
82                              GError         **error);
83 static gboolean  save_image (GFile           *file,
84                              gint32           image_ID,
85                              gint32           drawable_ID,
86                              GError         **error);
87 
88 static gboolean  get_short  (GInputStream    *input,
89                              guint16         *value,
90                              GError         **error);
91 static gboolean  put_short  (GOutputStream   *output,
92                              guint16          value,
93                              GError         **error);
94 
95 
96 /******************
97  * Implementation *
98  ******************/
99 
100 const GimpPlugInInfo PLUG_IN_INFO =
101 {
102   NULL,  /* init_proc  */
103   NULL,  /* quit_proc  */
104   query, /* query_proc */
105   run,   /* run_proc   */
106 };
107 
MAIN()108 MAIN ()
109 
110 static void
111 query (void)
112 {
113   /*
114    * Description:
115    *     Register the services provided by this plug-in
116    */
117   static const GimpParamDef load_args[] =
118   {
119     { GIMP_PDB_INT32,  "run-mode",      "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
120     { GIMP_PDB_STRING, "filename",      "The name of the file to load" },
121     { GIMP_PDB_STRING, "raw-filename",   "The name entered"            }
122   };
123   static const GimpParamDef load_return_vals[] =
124   {
125     { GIMP_PDB_IMAGE, "image", "Output image" }
126   };
127 
128   static const GimpParamDef save_args[] =
129   {
130     { GIMP_PDB_INT32,    "run-mode",     "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
131     { GIMP_PDB_IMAGE,    "image",        "Input image"                  },
132     { GIMP_PDB_DRAWABLE, "drawable",     "Drawable to export"           },
133     { GIMP_PDB_STRING,   "filename",     "The name of the file to export the image in" },
134     { GIMP_PDB_STRING,   "raw-filename", "The name of the file to export the image in" }
135   };
136 
137   gimp_install_procedure (LOAD_PROC,
138                           "loads files of the Alias|Wavefront Pix file format",
139                           "loads files of the Alias|Wavefront Pix file format",
140                           "Michael Taylor",
141                           "Michael Taylor",
142                           "1997",
143                           N_("Alias Pix image"),
144                           NULL,
145                           GIMP_PLUGIN,
146                           G_N_ELEMENTS (load_args),
147                           G_N_ELEMENTS (load_return_vals),
148                           load_args, load_return_vals);
149 
150   gimp_register_file_handler_uri (LOAD_PROC);
151   gimp_register_load_handler (LOAD_PROC, "pix,matte,mask,alpha,als", "");
152 
153   gimp_install_procedure (SAVE_PROC,
154                           "export file in the Alias|Wavefront pix/matte file format",
155                           "export file in the Alias|Wavefront pix/matte file format",
156                           "Michael Taylor",
157                           "Michael Taylor",
158                           "1997",
159                           N_("Alias Pix image"),
160                           "RGB*, GRAY*, INDEXED*",
161                           GIMP_PLUGIN,
162                           G_N_ELEMENTS (save_args), 0,
163                           save_args, NULL);
164 
165   gimp_register_file_handler_uri (SAVE_PROC);
166   gimp_register_save_handler (SAVE_PROC, "pix,matte,mask,alpha,als", "");
167 }
168 
169 /*
170  *  Description:
171  *      perform registered plug-in function
172  *
173  *  Arguments:
174  *      name         - name of the function to perform
175  *      nparams      - number of parameters passed to the function
176  *      param        - parameters passed to the function
177  *      nreturn_vals - number of parameters returned by the function
178  *      return_vals  - parameters returned by the function
179  */
180 
181 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)182 run (const gchar      *name,
183      gint              nparams,
184      const GimpParam  *param,
185      gint             *nreturn_vals,
186      GimpParam       **return_vals)
187 {
188   static GimpParam  values[2];
189   GimpRunMode       run_mode;
190   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
191   gint32            image_ID;
192   gint32            drawable_ID;
193   GimpExportReturn  export = GIMP_EXPORT_CANCEL;
194   GError           *error  = NULL;
195 
196   INIT_I18N ();
197   gegl_init (NULL, NULL);
198 
199   run_mode = param[0].data.d_int32;
200 
201   *nreturn_vals = 1;
202   *return_vals  = values;
203 
204   values[0].type          = GIMP_PDB_STATUS;
205   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
206 
207   if (strcmp (name, LOAD_PROC) == 0)
208     {
209       /* Perform the image load */
210       image_ID = load_image (g_file_new_for_uri (param[1].data.d_string),
211                              &error);
212 
213       if (image_ID != -1)
214         {
215           /* The image load was successful */
216           *nreturn_vals = 2;
217           values[1].type         = GIMP_PDB_IMAGE;
218           values[1].data.d_image = image_ID;
219         }
220       else
221         {
222           /* The image load failed */
223           status = GIMP_PDB_EXECUTION_ERROR;
224         }
225     }
226   else if (strcmp (name, SAVE_PROC) == 0)
227     {
228       image_ID    = param[1].data.d_int32;
229       drawable_ID = param[2].data.d_int32;
230 
231       /*  eventually export the image */
232       switch (run_mode)
233         {
234         case GIMP_RUN_INTERACTIVE:
235         case GIMP_RUN_WITH_LAST_VALS:
236           gimp_ui_init (PLUG_IN_BINARY, FALSE);
237 
238           export = gimp_export_image (&image_ID, &drawable_ID, "PIX",
239                                       GIMP_EXPORT_CAN_HANDLE_RGB  |
240                                       GIMP_EXPORT_CAN_HANDLE_GRAY |
241                                       GIMP_EXPORT_CAN_HANDLE_INDEXED);
242 
243           if (export == GIMP_EXPORT_CANCEL)
244             {
245               values[0].data.d_status = GIMP_PDB_CANCEL;
246               return;
247             }
248           break;
249         default:
250           break;
251         }
252 
253       if (status == GIMP_PDB_SUCCESS)
254         {
255           if (! save_image (g_file_new_for_uri (param[3].data.d_string),
256                             image_ID, drawable_ID,
257                             &error))
258             {
259               status = GIMP_PDB_EXECUTION_ERROR;
260             }
261         }
262 
263       if (export == GIMP_EXPORT_EXPORT)
264         gimp_image_delete (image_ID);
265     }
266   else
267     {
268       status = GIMP_PDB_CALLING_ERROR;
269     }
270 
271   if (status != GIMP_PDB_SUCCESS && error)
272     {
273       *nreturn_vals = 2;
274       values[1].type          = GIMP_PDB_STRING;
275       values[1].data.d_string = error->message;
276     }
277 
278   values[0].data.d_status = status;
279 }
280 
281 
282 /*
283  * Description:
284  *     Reads a 16-bit integer from a file in such a way that the machine's
285  *     byte order should not matter.
286  */
287 
288 static gboolean
get_short(GInputStream * input,guint16 * value,GError ** error)289 get_short (GInputStream  *input,
290            guint16       *value,
291            GError       **error)
292 {
293   guchar buf[2];
294   gsize  bytes_read;
295 
296   if (! g_input_stream_read_all (input, buf, 2,
297                                  &bytes_read, NULL, error) ||
298       bytes_read != 2)
299     {
300       return FALSE;
301     }
302 
303   if (value)
304     *value = (buf[0] << 8) + buf[1];
305 
306   return TRUE;
307 }
308 
309 /*
310  * Description:
311  *     Writes a 16-bit integer to a file in such a way that the machine's
312  *     byte order should not matter.
313  */
314 
315 static gboolean
put_short(GOutputStream * output,guint16 value,GError ** error)316 put_short (GOutputStream  *output,
317            guint16         value,
318            GError        **error)
319 {
320   guchar buf[2];
321 
322   buf[0] = (value >> 8) & 0xFF;
323   buf[1] = value & 0xFF;
324 
325   return g_output_stream_write_all (output, buf, 2, NULL, NULL, error);
326 }
327 
328 /*
329  *  Description:
330  *      load the given image into gimp
331  *
332  *  Arguments:
333  *      filename      - name on the file to read
334  *
335  *  Return Value:
336  *      Image id for the loaded image
337  *
338  */
339 
340 static gint32
load_image(GFile * file,GError ** error)341 load_image (GFile   *file,
342             GError **error)
343 {
344   GInputStream      *input;
345   GeglBuffer        *buffer;
346   GimpImageBaseType  imgtype;
347   GimpImageType      gdtype;
348   guchar            *dest;
349   guchar            *dest_base;
350   gint32             image_ID;
351   gint32             layer_ID;
352   gushort            width, height, depth;
353   gint               i, j, tile_height, row;
354 
355   PIX_DEBUG_PRINT ("Opening file: %s\n", filename);
356 
357   gimp_progress_init_printf (_("Opening '%s'"),
358                              g_file_get_parse_name (file));
359 
360   input = G_INPUT_STREAM (g_file_read (file, NULL, error));
361   if (! input)
362     return -1;
363 
364   /* Read header information */
365   if (! get_short (input, &width,  error) ||
366       ! get_short (input, &height, error) ||
367       ! get_short (input, NULL,    error) || /* Discard obsolete field */
368       ! get_short (input, NULL,    error) || /* Discard obsolete field */
369       ! get_short (input, &depth,  error))
370     {
371       g_object_unref (input);
372       return -1;
373     }
374 
375   PIX_DEBUG_PRINT ("Width %hu\n",  width);
376   PIX_DEBUG_PRINT ("Height %hu\n", height);
377 
378   if (depth == 8)
379     {
380       /* Loading a matte file */
381       imgtype = GIMP_GRAY;
382       gdtype  = GIMP_GRAY_IMAGE;
383     }
384   else if (depth == 24)
385     {
386       /* Loading an RGB file */
387       imgtype = GIMP_RGB;
388       gdtype  = GIMP_RGB_IMAGE;
389     }
390   else
391     {
392       /* Header is invalid */
393       g_object_unref (input);
394       return -1;
395     }
396 
397   image_ID = gimp_image_new (width, height, imgtype);
398   gimp_image_set_filename (image_ID, g_file_get_uri (file));
399 
400   layer_ID = gimp_layer_new (image_ID, _("Background"),
401                              width, height,
402                              gdtype,
403                              100,
404                              gimp_image_get_default_new_layer_mode (image_ID));
405   gimp_image_insert_layer (image_ID, layer_ID, -1, 0);
406 
407   buffer = gimp_drawable_get_buffer (layer_ID);
408 
409   tile_height = gimp_tile_height ();
410 
411   if (depth == 24)
412     {
413       /* Read a 24-bit Pix image */
414 
415       dest_base = dest = g_new (guchar, 3 * width * tile_height);
416 
417       for (i = 0; i < height;)
418         {
419           for (dest = dest_base, row = 0;
420                row < tile_height && i < height;
421                i++, row++)
422             {
423               guchar record[4];
424               gsize  bytes_read;
425               guchar count;
426 
427               /* Read a row of the image */
428               j = 0;
429               while (j < width)
430                 {
431                   if (! g_input_stream_read_all (input, record, 4,
432                                                  &bytes_read, NULL, error) ||
433                       bytes_read != 4)
434                     break;
435 
436                   for (count = 0; count < record[0]; ++count)
437                     {
438                       dest[0]   = record[3];
439                       dest[1]   = record[2];
440                       dest[2]   = record[1];
441                       dest += 3;
442                       j++;
443                       if (j >= width)
444                         break;
445                     }
446                 }
447             }
448 
449           gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - row, width, row), 0,
450                            NULL, dest_base, GEGL_AUTO_ROWSTRIDE);
451 
452           gimp_progress_update ((double) i / (double) height);
453         }
454 
455       g_free (dest_base);
456     }
457   else
458     {
459       /* Read an 8-bit Matte image */
460 
461       dest_base = dest = g_new (guchar, width * tile_height);
462 
463       for (i = 0; i < height;)
464         {
465           for (dest = dest_base, row = 0;
466                row < tile_height && i < height;
467                i++, row++)
468             {
469               guchar record[2];
470               gsize  bytes_read;
471               guchar count;
472 
473               /* Read a row of the image */
474               j = 0;
475               while (j < width)
476                 {
477                   if (! g_input_stream_read_all (input, record, 2,
478                                                  &bytes_read, NULL, error) ||
479                       bytes_read != 2)
480                     break;
481 
482                   for (count = 0; count < record[0]; ++count)
483                     {
484                       dest[j]   = record[1];
485                       j++;
486                       if (j >= width)
487                         break;
488                     }
489                 }
490 
491               dest += width;
492             }
493 
494           gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - row, width, row), 0,
495                            NULL, dest_base, GEGL_AUTO_ROWSTRIDE);
496 
497           gimp_progress_update ((double) i / (double) height);
498         }
499 
500       g_free (dest_base);
501     }
502 
503   g_object_unref (buffer);
504   g_object_unref (input);
505 
506   gimp_progress_update (1.0);
507 
508   return image_ID;
509 }
510 
511 /*
512  *  Description:
513  *      save the given file out as an alias pix or matte file
514  *
515  *  Arguments:
516  *      filename    - name of file to save to
517  *      image_ID    - ID of image to save
518  *      drawable_ID - current drawable
519  */
520 
521 static gboolean
save_image(GFile * file,gint32 image_ID,gint32 drawable_ID,GError ** error)522 save_image (GFile   *file,
523             gint32   image_ID,
524             gint32   drawable_ID,
525             GError **error)
526 {
527   GOutputStream *output;
528   GeglBuffer    *buffer;
529   const Babl    *format;
530   GCancellable  *cancellable;
531   gint           width;
532   gint           height;
533   gint           depth, i, j, row, tile_height, rectHeight;
534   gboolean       savingColor = TRUE;
535   guchar        *src;
536   guchar        *src_base;
537 
538   gimp_progress_init_printf (_("Exporting '%s'"),
539                              g_file_get_parse_name (file));
540 
541   output = G_OUTPUT_STREAM (g_file_replace (file,
542                                             NULL, FALSE, G_FILE_CREATE_NONE,
543                                             NULL, error));
544   if (! output)
545     return FALSE;
546 
547   /* Get info about image */
548   buffer = gimp_drawable_get_buffer (drawable_ID);
549 
550   width  = gegl_buffer_get_width  (buffer);
551   height = gegl_buffer_get_height (buffer);
552 
553   savingColor = ! gimp_drawable_is_gray (drawable_ID);
554 
555   if (savingColor)
556     format = babl_format ("R'G'B' u8");
557   else
558     format = babl_format ("Y' u8");
559 
560   depth = babl_format_get_bytes_per_pixel (format);
561 
562   /* Write the image header */
563   PIX_DEBUG_PRINT ("Width %hu\n",  width);
564   PIX_DEBUG_PRINT ("Height %hu\n", height);
565 
566   if (! put_short (output, width,  error) ||
567       ! put_short (output, height, error) ||
568       ! put_short (output, 0,      error) ||
569       ! put_short (output, 0,      error))
570     {
571       cancellable = g_cancellable_new ();
572       g_cancellable_cancel (cancellable);
573       g_output_stream_close (output, cancellable, NULL);
574       g_object_unref (cancellable);
575 
576       g_object_unref (output);
577       g_object_unref (buffer);
578       return FALSE;
579     }
580 
581   tile_height = gimp_tile_height ();
582   src_base    = g_new (guchar, tile_height * width * depth);
583 
584   if (savingColor)
585     {
586       /* Writing a 24-bit Pix image */
587 
588       if (! put_short (output, 24, error))
589         goto fail;
590 
591       for (i = 0; i < height;)
592         {
593           rectHeight = (tile_height < (height - i)) ?
594                         tile_height : (height - i);
595 
596           gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, rectHeight), 1.0,
597                            format, src_base,
598                            GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
599 
600           for (src = src_base, row = 0;
601                row < tile_height && i < height;
602                i += 1, row += 1)
603             {
604               /* Write a row of the image */
605 
606               guchar record[4];
607 
608               record[0] = 1;
609               record[3] = src[0];
610               record[2] = src[1];
611               record[1] = src[2];
612               src += depth;
613               for (j = 1; j < width; ++j)
614                 {
615                   if ((record[3] != src[0]) ||
616                       (record[2] != src[1]) ||
617                       (record[1] != src[2]) ||
618                       (record[0] == 255))
619                     {
620                       /* Write current RLE record and start a new one */
621 
622                       if (! g_output_stream_write_all (output, record, 4,
623                                                        NULL, NULL, error))
624                         {
625                           goto fail;
626                         }
627 
628                       record[0] = 1;
629                       record[3] = src[0];
630                       record[2] = src[1];
631                       record[1] = src[2];
632                     }
633                   else
634                     {
635                       /* increment run length in current record */
636                       record[0]++;
637                     }
638                   src += depth;
639                 }
640 
641               /* Write last record in row */
642 
643               if (! g_output_stream_write_all (output, record, 4,
644                                                NULL, NULL, error))
645                 {
646                   goto fail;
647                 }
648             }
649 
650           gimp_progress_update ((double) i / (double) height);
651         }
652     }
653   else
654     {
655       /* Writing a 8-bit Matte (Mask) image */
656 
657       if (! put_short (output, 8, error))
658         goto fail;
659 
660       for (i = 0; i < height;)
661         {
662           rectHeight = (tile_height < (height - i)) ?
663                         tile_height : (height - i);
664 
665           gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, rectHeight), 1.0,
666                            format, src_base,
667                            GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
668 
669           for (src = src_base, row = 0;
670                row < tile_height && i < height;
671                i += 1, row += 1)
672             {
673               /* Write a row of the image */
674 
675               guchar record[2];
676 
677               record[0] = 1;
678               record[1] = src[0];
679               src += depth;
680               for (j = 1; j < width; ++j)
681                 {
682                   if ((record[1] != src[0]) || (record[0] == 255))
683                     {
684                       /* Write current RLE record and start a new one */
685 
686                       if (! g_output_stream_write_all (output, record, 2,
687                                                        NULL, NULL, error))
688                         {
689                           goto fail;
690                         }
691 
692                       record[0] = 1;
693                       record[1] = src[0];
694                     }
695                   else
696                     {
697                       /* increment run length in current record */
698                       record[0] ++;
699                     }
700                   src += depth;
701                 }
702 
703               /* Write last record in row */
704 
705               if (! g_output_stream_write_all (output, record, 2,
706                                                NULL, NULL, error))
707                 {
708                   goto fail;
709                 }
710             }
711 
712           gimp_progress_update ((double) i / (double) height);
713         }
714     }
715 
716   g_free (src_base);
717   g_object_unref (output);
718   g_object_unref (buffer);
719 
720   gimp_progress_update (1.0);
721 
722   return TRUE;
723 
724  fail:
725 
726   cancellable = g_cancellable_new ();
727   g_cancellable_cancel (cancellable);
728   g_output_stream_close (output, cancellable, NULL);
729   g_object_unref (cancellable);
730 
731   g_free (src_base);
732   g_object_unref (output);
733   g_object_unref (buffer);
734 
735   return FALSE;
736 }
737