2
* Multiple-image Network Graphics (MNG) plug-in for The GIMP -- an image
5
* Copyright (C) 2002 S. Mukund <muks@mukund.org>
6
* Portions are copyright of the authors of the file-gif-save, file-png-save
7
* and file-jpeg-save plug-ins' code. The exact ownership of these code
8
* fragments has not been determined.
10
* This work was sponsored by Xinit Systems Limited, UK.
11
* http://www.xinitsystems.com/
13
* THIS SOURCE CODE DOES NOT INCLUDE ANY FUNCTIONALITY FOR READING
14
* OR WRITING CONTENT IN THE GIF IMAGE FORMAT.
16
* This program is free software; you can redistribute it and/or modify
17
* it under the terms of the GNU General Public License as published by
18
* the Free Software Foundation; either version 2 of the License, or
19
* (at your option) any later version.
21
* This program is distributed in the hope that it will be useful,
22
* but WITHOUT ANY WARRANTY; without even the implied warranty of
23
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
* GNU General Public License for more details.
26
* You should have received a copy of the GNU General Public License
27
* along with this program; if not, write to the Free Software
28
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31
* For now, this MNG plug-in can only save images. It cannot load images.
32
* Save your working copy as .xcf and use this for "exporting" your images
33
* to MNG. Supports animation the same way as animated GIFs. Supports alpha
34
* transparency. Uses the libmng library (http://www.libmng.com/).
35
* The MIME content-type for MNG images is video/x-mng for now. Make sure
36
* your web-server is configured appropriately if you want to serve MNG
39
* Since libmng cannot write PNG, JNG and delta PNG chunks at this time
40
* (when this text was written), this plug-in uses libpng and jpeglib to
41
* create the data for the chunks.
58
/* libpng and jpeglib are currently used in this plug-in. */
64
/* Grrr. The grrr is because the following have to be defined
65
* by the application as well for some reason, although they
66
* were enabled when libmng was compiled. The authors of libmng
67
* must look into this. */
69
#if !defined(MNG_SUPPORT_FULL)
70
#define MNG_SUPPORT_FULL 1
73
#if !defined(MNG_SUPPORT_READ)
74
#define MNG_SUPPORT_READ 1
77
#if !defined(MNG_SUPPORT_WRITE)
78
#define MNG_SUPPORT_WRITE 1
81
#if !defined(MNG_SUPPORT_DISPLAY)
82
#define MNG_SUPPORT_DISPLAY 1
85
#if !defined(MNG_ACCESS_CHUNKS)
86
#define MNG_ACCESS_CHUNKS 1
91
#include "libgimp/gimp.h"
92
#include "libgimp/gimpui.h"
94
#include "libgimp/stdplugins-intl.h"
97
#define SCALE_WIDTH 125
114
/* The contents of this struct remain static among multiple
115
* invocations of the plug-in. */
117
/* TODO: describe the members of the struct */
126
gint32 default_chunks;
131
gint32 compression_level;
134
gint32 default_delay;
135
gint32 default_dispose;
139
/* Values of the instance of the above struct when the plug-in is
142
struct mng_data_t mng_data =
144
FALSE, /* interlaced */
149
CHUNKS_PNG_D, /* default_chunks */
154
9, /* compression_level */
157
100, /* default_delay */
158
DISPOSE_COMBINE /* default_dispose */
162
/* The output FILE pointer which is used by libmng;
163
* passed around as user data. */
164
struct mnglib_userdata_t
171
* Function prototypes
174
static mng_ptr myalloc (mng_size_t size);
175
static void myfree (mng_ptr ptr,
177
static mng_bool myopenstream (mng_handle handle);
178
static mng_bool myclosestream (mng_handle handle);
179
static mng_bool mywritedata (mng_handle handle,
182
mng_uint32 *written_size);
184
static gint32 parse_chunks_type_from_layer_name (const gchar *str);
185
static gint32 parse_disposal_type_from_layer_name (const gchar *str);
186
static gint32 parse_ms_tag_from_layer_name (const gchar *str);
188
static gint find_unused_ia_colour (guchar *pixels,
191
static gboolean ia_has_transparent_pixels (guchar *pixels,
193
static gboolean respin_cmap (png_structp png_ptr,
194
png_infop png_info_ptr,
197
GimpDrawable *drawable);
199
static gint mng_save_image (const gchar *filename,
202
gint32 original_image_id);
203
static gint mng_save_dialog (gint32 image_id);
205
static void query (void);
206
static void run (const gchar *name,
208
const GimpParam *param,
210
GimpParam **return_vals);
213
* Callbacks for libmng
217
myalloc (mng_size_t size)
220
ptr = g_try_malloc ((gulong) size);
223
memset (ptr, 0, size);
225
return ((mng_ptr) ptr);
236
myopenstream (mng_handle handle)
242
myclosestream (mng_handle handle)
248
mywritedata (mng_handle handle,
251
mng_uint32 *written_size)
253
struct mnglib_userdata_t *userdata =
254
(struct mnglib_userdata_t *) mng_get_userdata (handle);
257
*written_size = (mng_uint32) fwrite ((void *) buf, 1,
258
(size_t) size, userdata->fp);
264
/* Parses which output chunk type to use for this layer
265
* from the layer name. */
268
parse_chunks_type_from_layer_name (const gchar *str)
272
for (i = 0; (i + 5) <= strlen (str); i++)
274
if (g_ascii_strncasecmp (str + i, "(png)", 5) == 0)
276
else if (g_ascii_strncasecmp (str + i, "(jng)", 5) == 0)
280
return mng_data.default_chunks;
284
/* Parses which disposal type to use for this layer
285
* from the layer name. */
288
parse_disposal_type_from_layer_name (const gchar *str)
293
for (i = 0; (i + 9) <= strlen (str); i++)
295
if (g_ascii_strncasecmp (str + i, "(combine)", 9) == 0)
296
return DISPOSE_COMBINE;
297
else if (g_ascii_strncasecmp (str + i, "(replace)", 9) == 0)
298
return DISPOSE_REPLACE;
301
return mng_data.default_dispose;
305
/* Parses the millisecond delay to use for this layer
306
* from the layer name. */
309
parse_ms_tag_from_layer_name (const gchar *str)
313
guint length = strlen (str);
319
while ((offset < length) && (str[offset] != '('))
322
if (offset >= length)
323
return mng_data.default_delay;
327
if (g_ascii_isdigit (str[offset]))
334
sum += str[offset] - '0';
337
while ((offset < length) && (g_ascii_isdigit (str[offset])));
339
if ((length - offset) <= 2)
340
return mng_data.default_delay;
342
if ((g_ascii_toupper (str[offset]) != 'M') ||
343
(g_ascii_toupper (str[offset + 1]) != 'S'))
344
return mng_data.default_delay;
350
/* Try to find a colour in the palette which isn't actually
351
* used in the image, so that we can use it as the transparency
352
* index. Taken from png.c */
354
find_unused_ia_colour (guchar *pixels,
359
gboolean ix_used[256];
360
gboolean trans_used = FALSE;
362
for (i = 0; i < *colors; i++)
367
for (i = 0; i < numpixels; i++)
369
/* If alpha is over a threshold, the colour index in the
370
* palette is taken. Otherwise, this pixel is transparent. */
371
if (pixels[i * 2 + 1] > 127)
372
ix_used[pixels[i * 2]] = TRUE;
377
/* If there is no transparency, ignore alpha. */
378
if (trans_used == FALSE)
381
for (i = 0; i < *colors; i++)
383
if (ix_used[i] == FALSE)
389
/* Couldn't find an unused colour index within the number of
390
bits per pixel we wanted. Will have to increment the number
391
of colours in the image and assign a transparent pixel there. */
395
return ((*colors) - 1);
403
ia_has_transparent_pixels (guchar *pixels,
408
if (pixels [1] <= 127)
416
/* Spins the color map (palette) putting the transparent color at
417
* index 0 if there is space. If there isn't any space, warn the user
418
* and forget about transparency. Returns TRUE if the colormap has
419
* been changed and FALSE otherwise.
423
respin_cmap (png_structp png_ptr,
424
png_infop png_info_ptr,
427
GimpDrawable *drawable)
429
static guchar trans[] = { 0 };
436
GimpPixelRgn pixel_rgn;
438
before = gimp_image_get_colormap (image_id, &colors);
441
* Make sure there is something in the colormap.
445
before = g_new0 (guchar, 3);
449
cols = drawable->width;
450
rows = drawable->height;
451
numpixels = cols * rows;
453
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
454
drawable->width, drawable->height, FALSE, FALSE);
456
pixels = (guchar *) g_malloc (numpixels * 2);
458
gimp_pixel_rgn_get_rect (&pixel_rgn, pixels, 0, 0,
459
drawable->width, drawable->height);
461
if (ia_has_transparent_pixels (pixels, numpixels))
463
transparent = find_unused_ia_colour (pixels, numpixels, &colors);
465
if (transparent != -1)
467
png_color palette[256];
470
png_set_tRNS (png_ptr, png_info_ptr, (png_bytep) trans, 1, NULL);
472
/* Transform all pixels with a value = transparent to
473
* 0 and vice versa to compensate for re-ordering in palette
474
* due to png_set_tRNS() */
476
remap[0] = transparent;
477
remap[transparent] = 0;
479
/* Copy from index 0 to index transparent - 1 to index 1 to
480
* transparent of after, then from transparent+1 to colors-1
481
* unchanged, and finally from index transparent to index 0. */
483
for (i = 0; i < colors; i++)
485
palette[i].red = before[3 * remap[i]];
486
palette[i].green = before[3 * remap[i] + 1];
487
palette[i].blue = before[3 * remap[i] + 2];
490
png_set_PLTE (png_ptr, png_info_ptr, (png_colorp) palette, colors);
495
g_message (_("Couldn't losslessly save transparency, "
496
"saving opacity instead."));
499
png_set_PLTE (png_ptr, png_info_ptr, (png_colorp) before, colors);
506
mng_save_image (const gchar *filename,
509
gint32 original_image_id)
520
struct mnglib_userdata_t *userdata;
523
guint32 mng_ticks_per_second;
524
guint32 mng_simplicity_profile;
526
layers = gimp_image_get_layers (image_id, &num_layers);
532
mng_ticks_per_second = 1000;
534
mng_ticks_per_second = 0;
536
rows = gimp_image_height (image_id);
537
cols = gimp_image_width (image_id);
539
mng_simplicity_profile =
540
(MNG_SIMPLICITY_VALID | MNG_SIMPLICITY_SIMPLEFEATURES |
541
MNG_SIMPLICITY_COMPLEXFEATURES);
542
mng_simplicity_profile |= (MNG_SIMPLICITY_JNG | MNG_SIMPLICITY_DELTAPNG); /* JNG and delta-PNG chunks exist */
544
for (i = 0; i < num_layers; i++)
545
if (gimp_drawable_has_alpha (layers[i]))
547
mng_simplicity_profile |= MNG_SIMPLICITY_TRANSPARENCY; /* internal transparency exists */
548
mng_simplicity_profile |= 0x00000040; /* validity of following */
549
mng_simplicity_profile |= 0x00000100; /* semi-transparency exists */
550
mng_simplicity_profile |= 0x00000080; /* background transparency should happen */
554
userdata = g_new0 (struct mnglib_userdata_t, 1);
556
if ((userdata->fp = fopen (filename, "wb")) == NULL)
558
g_message (_("Could not open '%s' for writing: %s"),
559
gimp_filename_to_utf8 (filename), g_strerror (errno));
565
mng_initialize ((mng_ptr) userdata, myalloc, myfree,
568
g_warning ("Unable to mng_initialize() in mng_save_image()");
569
fclose (userdata->fp);
574
if (((ret = mng_setcb_openstream (handle, myopenstream)) != MNG_NOERROR) ||
575
((ret = mng_setcb_closestream (handle, myclosestream)) != MNG_NOERROR)
576
|| ((ret = mng_setcb_writedata (handle, mywritedata)) != MNG_NOERROR))
578
g_warning ("Unable to setup callbacks in mng_save_image()");
579
mng_cleanup (&handle);
580
fclose (userdata->fp);
585
if ((ret = mng_create (handle)) != MNG_NOERROR)
587
g_warning ("Unable to mng_create() image in mng_save_image()");
588
mng_cleanup (&handle);
589
fclose (userdata->fp);
595
mng_putchunk_mhdr (handle, cols, rows, mng_ticks_per_second, 1,
596
num_layers, mng_data.default_delay,
597
mng_simplicity_profile)) != MNG_NOERROR)
599
g_warning ("Unable to mng_putchunk_mhdr() in mng_save_image()");
600
mng_cleanup (&handle);
601
fclose (userdata->fp);
606
if ((num_layers > 1) && (mng_data.loop))
609
mng_putchunk_term (handle, MNG_TERMACTION_REPEAT,
610
MNG_ITERACTION_LASTFRAME,
611
parse_ms_tag_from_layer_name
612
(gimp_drawable_get_name (layers[0])),
613
0x7fffffff)) != MNG_NOERROR)
615
g_warning ("Unable to mng_putchunk_term() in mng_save_image()");
616
mng_cleanup (&handle);
617
fclose (userdata->fp);
625
mng_putchunk_term (handle, MNG_TERMACTION_LASTFRAME,
626
MNG_ITERACTION_LASTFRAME,
627
parse_ms_tag_from_layer_name
628
(gimp_drawable_get_name (layers[0])),
629
0x7fffffff)) != MNG_NOERROR)
631
g_warning ("Unable to mng_putchunk_term() in mng_save_image()");
632
mng_cleanup (&handle);
633
fclose (userdata->fp);
640
/* For now, we hardwire a comment */
643
mng_putchunk_text (handle, strlen (MNG_TEXT_TITLE), MNG_TEXT_TITLE, 22,
644
"Created using The GIMP")) != MNG_NOERROR)
646
g_warning ("Unable to mng_putchunk_text() in mng_save_image()");
647
mng_cleanup (&handle);
648
fclose (userdata->fp);
654
/* how do we get this to work? */
659
guchar red, green, blue;
661
gimp_context_get_background(&bgcolor);
662
gimp_rgb_get_uchar(&bgcolor, &red, &green, &blue);
664
if ((ret = mng_putchunk_back(handle, red, green, blue, MNG_BACKGROUNDCOLOR_MANDATORY, 0, MNG_BACKGROUNDIMAGE_NOTILE)) != MNG_NOERROR)
666
g_warning("Unable to mng_putchunk_back() in mng_save_image()");
667
mng_cleanup(&handle);
668
fclose(userdata->fp);
673
if ((ret = mng_putchunk_bkgd(handle, MNG_FALSE, 2, 0, gimp_rgb_intensity_uchar(&bgcolor), red, green, blue)) != MNG_NOERROR)
675
g_warning("Unable to mng_putchunk_bkgd() in mng_save_image()");
676
mng_cleanup(&handle);
677
fclose(userdata->fp);
686
gamma = gimp_gamma ();
689
mng_putchunk_gama (handle, MNG_FALSE,
690
(1.0 / ((gamma != 1.0) ? gamma : 2.2)) *
691
100000)) != MNG_NOERROR)
693
g_warning ("Unable to mng_putchunk_gama() in mng_save_image()");
694
mng_cleanup (&handle);
695
fclose (userdata->fp);
702
/* how do we get this to work? */
706
gimp_image_get_resolution(original_image_id, &xres, &yres);
708
if ((ret = mng_putchunk_phyg(handle, MNG_FALSE, (mng_uint32) (xres * 39.37), (mng_uint32) (yres * 39.37), 1)) != MNG_NOERROR)
710
g_warning("Unable to mng_putchunk_phyg() in mng_save_image()");
711
mng_cleanup(&handle);
712
fclose(userdata->fp);
717
if ((ret = mng_putchunk_phys(handle, MNG_FALSE, (mng_uint32) (xres * 39.37), (mng_uint32) (yres * 39.37), 1)) != MNG_NOERROR)
719
g_warning("Unable to mng_putchunk_phys() in mng_save_image()");
720
mng_cleanup(&handle);
721
fclose(userdata->fp);
734
mng_putchunk_time (handle, gmt->tm_year + 1900, gmt->tm_mon + 1,
735
gmt->tm_mday, gmt->tm_hour, gmt->tm_min,
736
gmt->tm_sec)) != MNG_NOERROR)
738
g_warning ("Unable to mng_putchunk_time() in mng_save_image()");
739
mng_cleanup (&handle);
740
fclose (userdata->fp);
746
if (gimp_image_base_type (image_id) == GIMP_INDEXED)
751
palette = gimp_image_get_colormap (image_id, &numcolors);
753
if (numcolors != 0 &&
754
(ret = mng_putchunk_plte (handle, numcolors, (mng_palette8e *) palette))
757
g_warning ("Unable to mng_putchunk_plte() in mng_save_image()");
758
mng_cleanup (&handle);
759
fclose (userdata->fp);
765
for (i = (num_layers - 1); i >= 0; i--)
768
GimpImageType layer_drawable_type;
769
GimpDrawable *layer_drawable;
770
gint layer_offset_x, layer_offset_y;
771
gint layer_rows, layer_cols;
773
gint layer_chunks_type;
774
volatile gint layer_bpp;
775
GimpPixelRgn layer_pixel_rgn;
777
guint8 layer_mng_colortype;
778
guint8 layer_mng_compression_type;
779
guint8 layer_mng_interlace_type;
780
gboolean layer_has_unique_palette;
784
gchar *temp_file_name;
786
png_infop png_info_ptr;
787
FILE *infile, *outfile;
790
guchar **layer_pixels, *layer_pixel;
791
int pass, j, k, begin, end, num;
793
guchar layer_remap[256];
796
layer_name = gimp_drawable_get_name (layers[i]);
797
layer_chunks_type = parse_chunks_type_from_layer_name (layer_name);
798
layer_drawable_type = gimp_drawable_type (layers[i]);
800
layer_drawable = gimp_drawable_get (layers[i]);
801
layer_rows = layer_drawable->height;
802
layer_cols = layer_drawable->width;
803
gimp_drawable_offsets (layers[i], &layer_offset_x, &layer_offset_y);
805
layer_has_unique_palette = TRUE;
807
for (j = 0; j < 256; j++)
810
switch (layer_drawable_type)
814
layer_mng_colortype = MNG_COLORTYPE_RGB;
816
case GIMP_RGBA_IMAGE:
818
layer_mng_colortype = MNG_COLORTYPE_RGBA;
820
case GIMP_GRAY_IMAGE:
822
layer_mng_colortype = MNG_COLORTYPE_GRAY;
824
case GIMP_GRAYA_IMAGE:
826
layer_mng_colortype = MNG_COLORTYPE_GRAYA;
828
case GIMP_INDEXED_IMAGE:
830
layer_mng_colortype = MNG_COLORTYPE_INDEXED;
832
case GIMP_INDEXEDA_IMAGE:
834
layer_mng_colortype = MNG_COLORTYPE_INDEXED | MNG_COLORTYPE_GRAYA;
837
g_warning ("Unsupported GimpImageType in mng_save_image()");
838
mng_cleanup (&handle);
839
fclose (userdata->fp);
844
/* Delta PNG chunks are not yet supported */
846
/* if (i == (num_layers - 1)) */
848
if (layer_chunks_type == CHUNKS_JNG_D)
849
layer_chunks_type = CHUNKS_JNG;
850
else if (layer_chunks_type == CHUNKS_PNG_D)
851
layer_chunks_type = CHUNKS_PNG;
854
switch (layer_chunks_type)
857
layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
858
if (mng_data.interlaced != 0)
859
layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
861
layer_mng_interlace_type = MNG_INTERLACE_NONE;
864
layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
865
if (mng_data.interlaced != 0)
866
layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
868
layer_mng_interlace_type = MNG_INTERLACE_NONE;
871
layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
872
if (mng_data.interlaced != 0)
873
layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
875
layer_mng_interlace_type = MNG_INTERLACE_NONE;
878
layer_mng_compression_type = MNG_COMPRESSION_BASELINEJPEG;
879
if (mng_data.interlaced != 0)
880
layer_mng_interlace_type = MNG_INTERLACE_PROGRESSIVE;
882
layer_mng_interlace_type = MNG_INTERLACE_SEQUENTIAL;
886
("Huh? Programmer stupidity error with 'layer_chunks_type'");
887
mng_cleanup (&handle);
888
fclose (userdata->fp);
893
if ((i == (num_layers - 1))
894
|| (parse_disposal_type_from_layer_name (layer_name) !=
896
frame_mode = MNG_FRAMINGMODE_3;
898
frame_mode = MNG_FRAMINGMODE_1;
900
frame_delay = parse_ms_tag_from_layer_name (layer_name);
902
if ((ret = mng_putchunk_fram (handle, MNG_FALSE, frame_mode, 0, NULL,
903
MNG_CHANGEDELAY_DEFAULT,
905
MNG_CHANGECLIPPING_DEFAULT,
909
layer_offset_x + layer_cols,
911
layer_offset_y + layer_rows,
912
0, 0)) != MNG_NOERROR)
914
g_warning ("Unable to mng_putchunk_fram() in mng_save_image()");
915
mng_cleanup (&handle);
916
fclose (userdata->fp);
921
if ((layer_offset_x != 0) || (layer_offset_y != 0))
923
mng_putchunk_defi (handle, 0, 0, 1, 1, layer_offset_x,
924
layer_offset_y, 1, layer_offset_x,
925
layer_offset_x + layer_cols, layer_offset_y,
926
layer_offset_y + layer_rows)) != MNG_NOERROR)
928
g_warning ("Unable to mng_putchunk_defi() in mng_save_image()");
929
mng_cleanup (&handle);
930
fclose (userdata->fp);
935
if ((temp_file_name = gimp_temp_name ("mng")) == NULL)
937
g_warning ("gimp_temp_name() failed in mng_save_image(");
938
mng_cleanup (&handle);
939
fclose (userdata->fp);
944
if ((outfile = fopen (temp_file_name, "wb")) == NULL)
946
g_message (_("Could not open '%s' for writing: %s"),
947
gimp_filename_to_utf8 (temp_file_name),
949
unlink (temp_file_name);
950
mng_cleanup (&handle);
951
fclose (userdata->fp);
956
if ((png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
958
(png_error_ptr) NULL,
959
(png_error_ptr) NULL)) == NULL)
962
("Unable to png_create_write_struct() in mng_save_image()");
964
unlink (temp_file_name);
965
mng_cleanup (&handle);
966
fclose (userdata->fp);
971
if ((png_info_ptr = png_create_info_struct (png_ptr)) == NULL)
974
("Unable to png_create_info_struct() in mng_save_image()");
975
png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
977
unlink (temp_file_name);
978
mng_cleanup (&handle);
979
fclose (userdata->fp);
984
if (setjmp (png_ptr->jmpbuf) != 0)
986
g_warning ("HRM saving PNG in mng_save_image()");
987
png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
989
unlink (temp_file_name);
990
mng_cleanup (&handle);
991
fclose (userdata->fp);
996
png_init_io (png_ptr, outfile);
997
png_set_compression_level (png_ptr, mng_data.compression_level);
999
png_info_ptr->width = layer_cols;
1000
png_info_ptr->height = layer_rows;
1001
png_info_ptr->interlace_type = ((mng_data.interlaced == 0) ? 0 : 1);
1002
png_info_ptr->bit_depth = 8;
1004
switch (layer_drawable_type)
1006
case GIMP_RGB_IMAGE:
1007
png_info_ptr->color_type = PNG_COLOR_TYPE_RGB;
1009
case GIMP_RGBA_IMAGE:
1010
png_info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
1012
case GIMP_GRAY_IMAGE:
1013
png_info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
1015
case GIMP_GRAYA_IMAGE:
1016
png_info_ptr->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
1018
case GIMP_INDEXED_IMAGE:
1019
png_info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
1020
png_info_ptr->valid |= PNG_INFO_PLTE;
1021
png_info_ptr->palette =
1022
(png_colorp) gimp_image_get_colormap (image_id, &num_colors);
1023
png_info_ptr->num_palette = num_colors;
1025
case GIMP_INDEXEDA_IMAGE:
1026
png_info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
1027
layer_has_unique_palette =
1028
respin_cmap (png_ptr, png_info_ptr, layer_remap,
1029
image_id, layer_drawable);
1032
g_warning ("This can't be!\n");
1036
if ((png_info_ptr->valid & PNG_INFO_PLTE) == PNG_INFO_PLTE)
1038
if (png_info_ptr->num_palette <= 2)
1039
png_info_ptr->bit_depth = 1;
1040
else if (png_info_ptr->num_palette <= 4)
1041
png_info_ptr->bit_depth = 2;
1042
else if (png_info_ptr->num_palette <= 16)
1043
png_info_ptr->bit_depth = 4;
1046
png_write_info (png_ptr, png_info_ptr);
1048
if (mng_data.interlaced != 0)
1049
num_passes = png_set_interlace_handling (png_ptr);
1053
if ((png_info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
1054
&& (png_info_ptr->bit_depth < 8))
1055
png_set_packing (png_ptr);
1057
tile_height = gimp_tile_height ();
1058
layer_pixel = g_new (guchar, tile_height * layer_cols * layer_bpp);
1059
layer_pixels = g_new (guchar *, tile_height);
1061
for (j = 0; j < tile_height; j++)
1062
layer_pixels[j] = layer_pixel + (layer_cols * layer_bpp * j);
1064
gimp_pixel_rgn_init (&layer_pixel_rgn, layer_drawable, 0, 0, layer_cols,
1065
layer_rows, FALSE, FALSE);
1067
for (pass = 0; pass < num_passes; pass++)
1069
for (begin = 0, end = tile_height; begin < layer_rows;
1070
begin += tile_height, end += tile_height)
1072
if (end > layer_rows)
1076
gimp_pixel_rgn_get_rect (&layer_pixel_rgn, layer_pixel, 0,
1077
begin, layer_cols, num);
1079
if ((png_info_ptr->valid & PNG_INFO_tRNS) == PNG_INFO_tRNS)
1081
for (j = 0; j < num; j++)
1083
fixed = layer_pixels[j];
1085
for (k = 0; k < layer_cols; k++)
1087
((fixed[k * 2 + 1] > 127) ? layer_remap[fixed[k * 2]] : 0);
1091
if (((png_info_ptr->valid & PNG_INFO_PLTE) == PNG_INFO_PLTE)
1092
&& (layer_bpp == 2))
1094
for (j = 0; j < num; j++)
1096
fixed = layer_pixels[j];
1098
for (k = 0; k < layer_cols; k++)
1099
fixed[k] = fixed[k * 2];
1103
png_write_rows (png_ptr, layer_pixels, num);
1107
png_write_end (png_ptr, png_info_ptr);
1108
png_destroy_write_struct (&png_ptr, &png_info_ptr);
1110
g_free (layer_pixels);
1111
g_free (layer_pixel);
1115
if ((infile = fopen (temp_file_name, "rb")) == NULL)
1117
g_message (_("Could not open '%s' for reading: %s"),
1118
gimp_filename_to_utf8 (temp_file_name),
1119
g_strerror (errno));
1120
unlink (temp_file_name);
1121
mng_cleanup (&handle);
1122
fclose (userdata->fp);
1127
fseek (infile, 8L, SEEK_SET);
1129
while (!feof (infile))
1131
unsigned char chunksize_chars[4];
1132
unsigned long chunksize;
1133
unsigned char chunkname[5];
1134
guchar *chunkbuffer;
1139
char chunkcolortype;
1140
char chunkcompression;
1142
char chunkinterlaced;
1145
if (fread (chunksize_chars, 1, 4, infile) != 4)
1147
if (fread (chunkname, 1, 4, infile) != 4)
1151
chunksize = (chunksize_chars[0] << 24) | (chunksize_chars[1] << 16)
1152
| (chunksize_chars[2] << 8) | chunksize_chars[3];
1158
chunkbuffer = g_new (guchar, chunksize);
1160
if (fread (chunkbuffer, 1, chunksize, infile) != chunksize)
1164
if (strncmp (chunkname, "IHDR", 4) == 0)
1166
chunkwidth = (chunkbuffer[0] << 24) | (chunkbuffer[1] << 16)
1167
| (chunkbuffer[2] << 8) | chunkbuffer[3];
1169
chunkheight = (chunkbuffer[4] << 24) | (chunkbuffer[5] << 16)
1170
| (chunkbuffer[6] << 8) | chunkbuffer[7];
1172
chunkbitdepth = chunkbuffer[8];
1173
chunkcolortype = chunkbuffer[9];
1174
chunkcompression = chunkbuffer[10];
1175
chunkfilter = chunkbuffer[11];
1176
chunkinterlaced = chunkbuffer[12];
1179
mng_putchunk_ihdr (handle, chunkwidth, chunkheight,
1180
chunkbitdepth, chunkcolortype,
1181
chunkcompression, chunkfilter,
1182
chunkinterlaced)) != MNG_NOERROR)
1185
("Unable to mng_putchunk_ihdr() in mng_save_image()");
1186
mng_cleanup (&handle);
1187
fclose (userdata->fp);
1192
else if (strncmp (chunkname, "IDAT", 4) == 0)
1195
mng_putchunk_idat (handle, chunksize,
1196
chunkbuffer)) != MNG_NOERROR)
1199
("Unable to mng_putchunk_idat() in mng_save_image()");
1200
mng_cleanup (&handle);
1201
fclose (userdata->fp);
1206
else if (strncmp (chunkname, "IEND", 4) == 0)
1208
if ((ret = mng_putchunk_iend (handle)) != MNG_NOERROR)
1211
("Unable to mng_putchunk_iend() in mng_save_image()");
1212
mng_cleanup (&handle);
1213
fclose (userdata->fp);
1218
else if (strncmp (chunkname, "PLTE", 4) == 0)
1220
/* if this frame's palette is the same as the global palette,
1221
write a 0-color palette chunk */
1223
mng_putchunk_plte (handle,
1224
layer_has_unique_palette ? (chunksize / 3) : 0,
1225
(mng_palette8e *) chunkbuffer))
1229
("Unable to mng_putchunk_plte() in mng_save_image()");
1230
mng_cleanup (&handle);
1231
fclose (userdata->fp);
1236
else if (strncmp (chunkname, "tRNS", 4) == 0)
1239
mng_putchunk_trns (handle, 0, 0, 3, chunksize,
1240
(mng_uint8 *) chunkbuffer, 0, 0, 0, 0,
1242
(mng_uint8 *) chunkbuffer)) !=
1246
("Unable to mng_putchunk_trns() in mng_save_image()");
1247
mng_cleanup (&handle);
1248
fclose (userdata->fp);
1255
g_free (chunkbuffer);
1257
/* read 4 bytes after the chunk */
1259
fread (chunkname, 1, 4, infile);
1263
unlink (temp_file_name);
1266
if ((ret = mng_putchunk_mend (handle)) != MNG_NOERROR)
1268
g_warning ("Unable to mng_putchunk_mend() in mng_save_image()");
1269
mng_cleanup (&handle);
1270
fclose (userdata->fp);
1275
if ((ret = mng_write (handle)) != MNG_NOERROR)
1277
g_warning ("Unable to mng_write() the image in mng_save_image()");
1278
mng_cleanup (&handle);
1279
fclose (userdata->fp);
1284
mng_cleanup (&handle);
1285
fclose (userdata->fp);
1292
/* The interactive dialog. */
1295
mng_save_dialog (gint32 image_id)
1298
GtkWidget *main_vbox;
1307
GtkObject *scale_adj;
1308
GtkWidget *spinbutton;
1309
GtkObject *spinbutton_adj;
1314
dlg = gimp_dialog_new (_("Save as MNG"), "mng",
1316
gimp_standard_help_func, "file-mng-save",
1318
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1319
GTK_STOCK_OK, GTK_RESPONSE_OK,
1323
main_vbox = gtk_vbox_new (FALSE, 12);
1324
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
1325
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dlg)->vbox), main_vbox);
1327
frame = gimp_frame_new (_("MNG Options"));
1328
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
1330
vbox = gtk_vbox_new (FALSE, 6);
1331
gtk_container_add (GTK_CONTAINER (frame), vbox);
1333
toggle = gtk_check_button_new_with_label (_("Interlace"));
1334
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1336
g_signal_connect (toggle, "toggled",
1337
G_CALLBACK (gimp_toggle_button_update),
1338
&mng_data.interlaced);
1340
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
1341
mng_data.interlaced);
1343
gtk_widget_show (toggle);
1345
toggle = gtk_check_button_new_with_label (_("Save background color"));
1346
gtk_widget_set_sensitive (toggle, FALSE);
1347
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1348
g_signal_connect (toggle, "toggled",
1349
G_CALLBACK (gimp_toggle_button_update),
1352
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.bkgd);
1354
gtk_widget_show (toggle);
1356
toggle = gtk_check_button_new_with_label (_("Save gamma"));
1357
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1358
g_signal_connect (toggle, "toggled",
1359
G_CALLBACK (gimp_toggle_button_update),
1362
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.gama);
1364
gtk_widget_show (toggle);
1366
toggle = gtk_check_button_new_with_label (_("Save resolution"));
1367
gtk_widget_set_sensitive (toggle, FALSE);
1368
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1369
g_signal_connect (toggle, "toggled",
1370
G_CALLBACK (gimp_toggle_button_update),
1373
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.phys);
1375
gtk_widget_show (toggle);
1377
toggle = gtk_check_button_new_with_label (_("Save creation time"));
1378
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1379
g_signal_connect (toggle, "toggled",
1380
G_CALLBACK (gimp_toggle_button_update),
1383
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.time);
1385
gtk_widget_show (toggle);
1387
table = gtk_table_new (2, 4, FALSE);
1388
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
1389
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1390
gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
1391
gtk_widget_show (table);
1393
gimp_image_get_layers (image_id, &num_layers);
1395
if (num_layers == 1)
1396
combo = gimp_int_combo_box_new (_("PNG"), CHUNKS_PNG_D,
1397
_("JNG"), CHUNKS_JNG_D,
1400
combo = gimp_int_combo_box_new (_("PNG + delta PNG"), CHUNKS_PNG_D,
1401
_("JNG + delta PNG"), CHUNKS_JNG_D,
1402
_("All PNG"), CHUNKS_PNG,
1403
_("All JNG"), CHUNKS_JNG,
1406
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
1407
mng_data.default_chunks);
1409
g_signal_connect (combo, "changed",
1410
G_CALLBACK (gimp_int_combo_box_get_active),
1411
&mng_data.default_chunks);
1413
gtk_widget_set_sensitive (combo, FALSE);
1414
gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
1415
_("Default chunks type:"), 0.0, 0.5,
1418
combo = gimp_int_combo_box_new (_("Combine"), DISPOSE_COMBINE,
1419
_("Replace"), DISPOSE_REPLACE,
1422
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
1423
mng_data.default_dispose);
1425
g_signal_connect (combo, "changed",
1426
G_CALLBACK (gimp_int_combo_box_get_active),
1427
&mng_data.default_dispose);
1429
gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
1430
_("Default frame disposal:"), 0.0, 0.5,
1433
scale_adj = gtk_adjustment_new (mng_data.compression_level,
1434
0.0, 9.0, 1.0, 1.0, 0.0);
1436
scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_adj));
1437
gtk_widget_set_size_request (scale, SCALE_WIDTH, -1);
1438
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
1439
gtk_scale_set_digits (GTK_SCALE (scale), 0);
1440
gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
1441
gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
1442
_("PNG compression level:"), 0.0, 0.9,
1445
g_signal_connect (scale_adj, "value_changed",
1446
G_CALLBACK (gimp_int_adjustment_update),
1447
&mng_data.compression_level);
1449
gimp_help_set_help_data (scale,
1450
_("Choose a high compression level "
1451
"for small file size"),
1454
scale_adj = gtk_adjustment_new (mng_data.quality,
1455
0.0, 1.0, 0.01, 0.01, 0.0);
1457
scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_adj));
1458
gtk_widget_set_size_request (scale, SCALE_WIDTH, -1);
1459
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
1460
gtk_scale_set_digits (GTK_SCALE (scale), 2);
1461
gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
1462
gtk_widget_set_sensitive (scale, FALSE);
1463
gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
1464
_("JPEG compression quality:"), 0.0, 0.9,
1467
g_signal_connect (scale_adj, "value_changed",
1468
G_CALLBACK (gimp_int_adjustment_update),
1471
scale_adj = gtk_adjustment_new (mng_data.smoothing,
1472
0.0, 1.0, 0.01, 0.01, 0.0);
1474
scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_adj));
1475
gtk_widget_set_size_request (scale, SCALE_WIDTH, -1);
1476
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
1477
gtk_scale_set_digits (GTK_SCALE (scale), 2);
1478
gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
1479
gtk_widget_set_sensitive (scale, FALSE);
1480
gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
1481
_("JPEG smoothing factor:"), 0.0, 0.9,
1484
g_signal_connect (scale_adj, "value_changed",
1485
G_CALLBACK (gimp_int_adjustment_update),
1486
&mng_data.smoothing);
1488
gtk_widget_show (vbox);
1489
gtk_widget_show (frame);
1491
frame = gimp_frame_new (_("Animated MNG options"));
1492
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
1494
vbox = gtk_vbox_new (FALSE, 6);
1495
gtk_container_add (GTK_CONTAINER (frame), vbox);
1497
toggle = gtk_check_button_new_with_label (_("Loop"));
1498
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1499
g_signal_connect (toggle, "toggled",
1500
G_CALLBACK (gimp_toggle_button_update),
1503
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
1506
gtk_widget_show (toggle);
1508
hbox = gtk_hbox_new (FALSE, 4);
1509
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1511
label = gtk_label_new (_("Default frame delay:"));
1512
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1513
gtk_widget_show (label);
1515
spinbutton = gimp_spin_button_new (&spinbutton_adj,
1516
mng_data.default_delay,
1517
0, 65000, 10, 100, 0, 1, 0);
1519
g_signal_connect (spinbutton_adj, "value_changed",
1520
G_CALLBACK (gimp_int_adjustment_update),
1521
&mng_data.default_delay);
1523
gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
1525
gtk_widget_show (spinbutton);
1527
label = gtk_label_new (_("milliseconds"));
1528
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1529
gtk_widget_show (label);
1531
gtk_widget_show (hbox);
1533
gtk_widget_show (vbox);
1534
gtk_widget_show (frame);
1536
gtk_widget_set_sensitive (frame, num_layers > 1);
1538
gtk_widget_show (main_vbox);
1539
gtk_widget_show (dlg);
1541
run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
1543
gtk_widget_destroy (dlg);
1549
/* GIMP calls these methods. */
1554
static GimpParamDef save_args[] =
1556
{ GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
1557
{ GIMP_PDB_IMAGE, "image", "Input image" },
1558
{ GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
1559
{ GIMP_PDB_STRING, "filename",
1560
"The name of the file to save the image in" },
1561
{ GIMP_PDB_STRING, "raw_filename",
1562
"The name of the file to save the image in" },
1564
{ GIMP_PDB_INT32, "interlace", "Use interlacing" },
1565
{ GIMP_PDB_INT32, "compression", "PNG deflate compression level (0 - 9)" },
1566
{ GIMP_PDB_FLOAT, "quality", "JPEG quality factor (0.00 - 1.00)" },
1567
{ GIMP_PDB_FLOAT, "smoothing", "JPEG smoothing factor (0.00 - 1.00)" },
1568
{ GIMP_PDB_INT32, "loop", "(ANIMATED MNG) Loop infinitely" },
1569
{ GIMP_PDB_INT32, "default_delay",
1570
"(ANIMATED MNG) Default delay between frames in milliseconds" },
1571
{ GIMP_PDB_INT32, "default_chunks",
1572
"(ANIMATED MNG) Default chunks type (0 = PNG + Delta PNG; 1 = JNG + Delta PNG; 2 = All PNG; 3 = All JNG)" },
1573
{ GIMP_PDB_INT32, "default_dispose",
1574
"(ANIMATED MNG) Default dispose type (0 = combine; 1 = replace)" },
1575
{ GIMP_PDB_INT32, "bkgd", "Write bKGD (background color) chunk" },
1576
{ GIMP_PDB_INT32, "gama", "Write gAMA (gamma) chunk"},
1577
{ GIMP_PDB_INT32, "phys", "Write pHYs (image resolution) chunk" },
1578
{ GIMP_PDB_INT32, "time", "Write tIME (creation time) chunk" }
1581
gimp_install_procedure ("file_mng_save",
1582
"Saves images in the MNG file format",
1583
"This plug-in saves images in the Multiple-image "
1584
"Network Graphics (MNG) format which can be used as "
1585
"a replacement for animated GIFs, and more.",
1586
"S. Mukund <muks@mukund.org>",
1587
"S. Mukund <muks@mukund.org>",
1588
"November 19, 2002",
1589
N_("MNG animation"),
1590
"RGB*,GRAY*,INDEXED*",
1592
G_N_ELEMENTS (save_args), 0,
1595
gimp_register_file_handler_mime ("file_mng_save", "image/x-mng");
1596
gimp_register_save_handler ("file_mng_save", "mng", "");
1600
run (const gchar *name,
1602
const GimpParam *param,
1604
GimpParam **return_vals)
1606
static GimpParam values[1];
1611
*return_vals = values;
1612
values[0].type = GIMP_PDB_STATUS;
1613
values[0].data.d_status = GIMP_PDB_SUCCESS;
1615
if (strcmp (name, "file_mng_save") == 0)
1617
GimpRunMode run_mode;
1618
gint32 image_id, original_image_id;
1620
GimpExportReturn export = GIMP_EXPORT_IGNORE;
1622
run_mode = param[0].data.d_int32;
1623
image_id = original_image_id = param[1].data.d_int32;
1624
drawable_id = param[2].data.d_int32;
1626
if ((run_mode == GIMP_RUN_INTERACTIVE)
1627
|| (run_mode == GIMP_RUN_WITH_LAST_VALS))
1629
gimp_procedural_db_get_data ("file_mng_save", &mng_data);
1631
gimp_ui_init ("mng", FALSE);
1632
export = gimp_export_image (&image_id, &drawable_id, "MNG",
1633
(GIMP_EXPORT_CAN_HANDLE_RGB |
1634
GIMP_EXPORT_CAN_HANDLE_GRAY |
1635
GIMP_EXPORT_CAN_HANDLE_INDEXED |
1636
GIMP_EXPORT_CAN_HANDLE_ALPHA |
1637
GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION));
1640
if (export == GIMP_EXPORT_CANCEL)
1641
values[0].data.d_status = GIMP_PDB_CANCEL;
1642
else if ((export == GIMP_EXPORT_IGNORE)
1643
|| (export == GIMP_EXPORT_EXPORT))
1645
if (run_mode == GIMP_RUN_INTERACTIVE)
1647
if (mng_save_dialog (image_id) == 0)
1648
values[0].data.d_status = GIMP_PDB_CANCEL;
1650
else if (run_mode == GIMP_RUN_NONINTERACTIVE)
1654
g_message ("Incorrect number of parameters "
1655
"passed to file-mng-save()");
1656
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
1660
mng_data.interlaced = param[5].data.d_int32;
1661
mng_data.compression_level = param[6].data.d_int32;
1662
mng_data.quality = param[7].data.d_float;
1663
mng_data.smoothing = param[8].data.d_float;
1664
mng_data.loop = param[9].data.d_int32;
1665
mng_data.default_delay = param[10].data.d_int32;
1666
mng_data.default_chunks = param[11].data.d_int32;
1667
mng_data.default_dispose = param[12].data.d_int32;
1668
mng_data.bkgd = param[13].data.d_int32;
1669
mng_data.gama = param[14].data.d_int32;
1670
mng_data.phys = param[15].data.d_int32;
1671
mng_data.time = param[16].data.d_int32;
1673
if ((mng_data.compression_level < 0)
1674
|| (mng_data.compression_level > 9))
1676
g_warning ("Parameter 'compression_level' passed to file-mng-save() must be in the range 0 - 9; Clamping it to the default value of 6.");
1677
mng_data.compression_level = 6;
1680
if ((mng_data.quality < ((float) 0))
1681
|| (mng_data.quality > ((float) 1)))
1683
g_warning ("Parameter 'quality' passed to file-mng-save() must be in the range 0.00 - 1.00; Clamping it to the default value of 0.75.");
1684
mng_data.quality = 0.75;
1687
if ((mng_data.smoothing < ((float) 0))
1688
|| (mng_data.smoothing > ((float) 1)))
1690
g_warning ("Parameter 'smoothing' passed to file-mng-save() must be in the range 0.00 - 1.00; Clamping it to the default value of 0.00.");
1691
mng_data.smoothing = 0.0;
1694
if ((mng_data.default_chunks < 0)
1695
|| (mng_data.default_chunks > 3))
1697
g_warning ("Parameter 'default_chunks' passed to file-mng-save() must be in the range 0 - 2.");
1698
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
1701
if ((mng_data.default_dispose < 0)
1702
|| (mng_data.default_dispose > 1))
1704
g_warning ("Parameter 'default_dispose' passed to file-mng-save() must be in the range 0 - 1.");
1705
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
1710
if (values[0].data.d_status == GIMP_PDB_SUCCESS)
1713
(param[3].data.d_string, image_id, drawable_id,
1714
original_image_id) != 0)
1715
gimp_set_data ("file_mng_save", &mng_data, sizeof (mng_data));
1717
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
1720
if (export == GIMP_EXPORT_EXPORT)
1721
gimp_image_delete (image_id);
1726
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
1730
/* Only query and run are implemented by this plug-in. */
1732
GimpPlugInInfo PLUG_IN_INFO =