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