1
/* GIMP - The GNU Image Manipulation Program
2
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
4
* Multiple-image Network Graphics (MNG) plug-in
6
* Copyright (C) 2002 S. Mukund <muks@mukund.org>
7
* Portions are copyright of the authors of the file-gif-save, file-png-save
8
* and file-jpeg-save plug-ins' code. The exact ownership of these code
9
* fragments has not been determined.
11
* This work was sponsored by Xinit Systems Limited, UK.
12
* http://www.xinitsystems.com/
14
* THIS SOURCE CODE DOES NOT INCLUDE ANY FUNCTIONALITY FOR READING
15
* OR WRITING CONTENT IN THE GIF IMAGE FORMAT.
17
* This program is free software; you can redistribute it and/or modify
18
* it under the terms of the GNU General Public License as published by
19
* the Free Software Foundation; either version 2 of the License, or
20
* (at your option) any later version.
22
* This program is distributed in the hope that it will be useful,
23
* but WITHOUT ANY WARRANTY; without even the implied warranty of
24
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
* GNU General Public License for more details.
27
* You should have received a copy of the GNU General Public License
28
* along with this program; if not, write to the Free Software
29
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32
* For now, this MNG plug-in can only save images. It cannot load images.
33
* Save your working copy as .xcf and use this for "exporting" your images
34
* to MNG. Supports animation the same way as animated GIFs. Supports alpha
35
* transparency. Uses the libmng library (http://www.libmng.com/).
36
* The MIME content-type for MNG images is video/x-mng for now. Make sure
37
* your web-server is configured appropriately if you want to serve MNG
40
* Since libmng cannot write PNG, JNG and delta PNG chunks at this time
41
* (when this text was written), this plug-in uses libpng and jpeglib to
42
* create the data for the chunks.
53
#include <glib/gstdio.h>
60
/* libpng and jpeglib are currently used in this plug-in. */
66
/* Grrr. The grrr is because the following have to be defined
67
* by the application as well for some reason, although they
68
* were enabled when libmng was compiled. The authors of libmng
69
* must look into this. */
71
#if !defined(MNG_SUPPORT_FULL)
72
#define MNG_SUPPORT_FULL 1
75
#if !defined(MNG_SUPPORT_READ)
76
#define MNG_SUPPORT_READ 1
79
#if !defined(MNG_SUPPORT_WRITE)
80
#define MNG_SUPPORT_WRITE 1
83
#if !defined(MNG_SUPPORT_DISPLAY)
84
#define MNG_SUPPORT_DISPLAY 1
87
#if !defined(MNG_ACCESS_CHUNKS)
88
#define MNG_ACCESS_CHUNKS 1
93
#include "libgimp/gimp.h"
94
#include "libgimp/gimpui.h"
96
#include "libgimp/stdplugins-intl.h"
99
#define SAVE_PROC "file-mng-save"
100
#define PLUG_IN_BINARY "mng"
101
#define SCALE_WIDTH 125
118
/* The contents of this struct remain static among multiple
119
* invocations of the plug-in. */
121
/* TODO: describe the members of the struct */
130
gint32 default_chunks;
135
gint32 compression_level;
138
gint32 default_delay;
139
gint32 default_dispose;
143
/* Values of the instance of the above struct when the plug-in is
146
struct mng_data_t mng_data =
148
FALSE, /* interlaced */
153
CHUNKS_PNG_D, /* default_chunks */
158
9, /* compression_level */
161
100, /* default_delay */
162
DISPOSE_COMBINE /* default_dispose */
166
/* The output FILE pointer which is used by libmng;
167
* passed around as user data. */
168
struct mnglib_userdata_t
175
* Function prototypes
178
static mng_ptr myalloc (mng_size_t size);
179
static void myfree (mng_ptr ptr,
181
static mng_bool myopenstream (mng_handle handle);
182
static mng_bool myclosestream (mng_handle handle);
183
static mng_bool mywritedata (mng_handle handle,
186
mng_uint32 *written_size);
188
static gint32 parse_chunks_type_from_layer_name (const gchar *str);
189
static gint32 parse_disposal_type_from_layer_name (const gchar *str);
190
static gint32 parse_ms_tag_from_layer_name (const gchar *str);
192
static gint find_unused_ia_colour (guchar *pixels,
195
static gboolean ia_has_transparent_pixels (guchar *pixels,
197
static gboolean respin_cmap (png_structp png_ptr,
198
png_infop png_info_ptr,
201
GimpDrawable *drawable);
203
static gint mng_save_image (const gchar *filename,
206
gint32 original_image_id);
207
static gint mng_save_dialog (gint32 image_id);
209
static void query (void);
210
static void run (const gchar *name,
212
const GimpParam *param,
214
GimpParam **return_vals);
217
* Callbacks for libmng
221
myalloc (mng_size_t size)
224
ptr = g_try_malloc ((gulong) size);
227
memset (ptr, 0, size);
229
return ((mng_ptr) ptr);
240
myopenstream (mng_handle handle)
246
myclosestream (mng_handle handle)
252
mywritedata (mng_handle handle,
255
mng_uint32 *written_size)
257
struct mnglib_userdata_t *userdata =
258
(struct mnglib_userdata_t *) mng_get_userdata (handle);
261
*written_size = (mng_uint32) fwrite ((void *) buf, 1,
262
(size_t) size, userdata->fp);
268
/* Parses which output chunk type to use for this layer
269
* from the layer name. */
272
parse_chunks_type_from_layer_name (const gchar *str)
276
for (i = 0; (i + 5) <= strlen (str); i++)
278
if (g_ascii_strncasecmp (str + i, "(png)", 5) == 0)
280
else if (g_ascii_strncasecmp (str + i, "(jng)", 5) == 0)
284
return mng_data.default_chunks;
288
/* Parses which disposal type to use for this layer
289
* from the layer name. */
292
parse_disposal_type_from_layer_name (const gchar *str)
297
for (i = 0; (i + 9) <= strlen (str); i++)
299
if (g_ascii_strncasecmp (str + i, "(combine)", 9) == 0)
300
return DISPOSE_COMBINE;
301
else if (g_ascii_strncasecmp (str + i, "(replace)", 9) == 0)
302
return DISPOSE_REPLACE;
305
return mng_data.default_dispose;
309
/* Parses the millisecond delay to use for this layer
310
* from the layer name. */
313
parse_ms_tag_from_layer_name (const gchar *str)
317
guint length = strlen (str);
323
while ((offset < length) && (str[offset] != '('))
326
if (offset >= length)
327
return mng_data.default_delay;
331
if (g_ascii_isdigit (str[offset]))
338
sum += str[offset] - '0';
341
while ((offset < length) && (g_ascii_isdigit (str[offset])));
343
if ((length - offset) <= 2)
344
return mng_data.default_delay;
346
if ((g_ascii_toupper (str[offset]) != 'M') ||
347
(g_ascii_toupper (str[offset + 1]) != 'S'))
348
return mng_data.default_delay;
354
/* Try to find a colour in the palette which isn't actually
355
* used in the image, so that we can use it as the transparency
356
* index. Taken from png.c */
358
find_unused_ia_colour (guchar *pixels,
363
gboolean ix_used[256];
364
gboolean trans_used = FALSE;
366
for (i = 0; i < *colors; i++)
371
for (i = 0; i < numpixels; i++)
373
/* If alpha is over a threshold, the colour index in the
374
* palette is taken. Otherwise, this pixel is transparent. */
375
if (pixels[i * 2 + 1] > 127)
376
ix_used[pixels[i * 2]] = TRUE;
381
/* If there is no transparency, ignore alpha. */
382
if (trans_used == FALSE)
385
for (i = 0; i < *colors; i++)
387
if (ix_used[i] == FALSE)
393
/* Couldn't find an unused colour index within the number of
394
bits per pixel we wanted. Will have to increment the number
395
of colours in the image and assign a transparent pixel there. */
399
return ((*colors) - 1);
407
ia_has_transparent_pixels (guchar *pixels,
412
if (pixels [1] <= 127)
420
/* Spins the color map (palette) putting the transparent color at
421
* index 0 if there is space. If there isn't any space, warn the user
422
* and forget about transparency. Returns TRUE if the colormap has
423
* been changed and FALSE otherwise.
427
respin_cmap (png_structp png_ptr,
428
png_infop png_info_ptr,
431
GimpDrawable *drawable)
433
static guchar trans[] = { 0 };
440
GimpPixelRgn pixel_rgn;
442
before = gimp_image_get_colormap (image_id, &colors);
445
* Make sure there is something in the colormap.
449
before = g_new0 (guchar, 3);
453
cols = drawable->width;
454
rows = drawable->height;
455
numpixels = cols * rows;
457
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
458
drawable->width, drawable->height, FALSE, FALSE);
460
pixels = (guchar *) g_malloc (numpixels * 2);
462
gimp_pixel_rgn_get_rect (&pixel_rgn, pixels, 0, 0,
463
drawable->width, drawable->height);
465
if (ia_has_transparent_pixels (pixels, numpixels))
467
transparent = find_unused_ia_colour (pixels, numpixels, &colors);
469
if (transparent != -1)
471
png_color palette[256];
474
png_set_tRNS (png_ptr, png_info_ptr, (png_bytep) trans, 1, NULL);
476
/* Transform all pixels with a value = transparent to
477
* 0 and vice versa to compensate for re-ordering in palette
478
* due to png_set_tRNS() */
480
remap[0] = transparent;
481
remap[transparent] = 0;
483
/* Copy from index 0 to index transparent - 1 to index 1 to
484
* transparent of after, then from transparent+1 to colors-1
485
* unchanged, and finally from index transparent to index 0. */
487
for (i = 0; i < colors; i++)
489
palette[i].red = before[3 * remap[i]];
490
palette[i].green = before[3 * remap[i] + 1];
491
palette[i].blue = before[3 * remap[i] + 2];
494
png_set_PLTE (png_ptr, png_info_ptr, (png_colorp) palette, colors);
499
g_message (_("Couldn't losslessly save transparency, "
500
"saving opacity instead."));
503
png_set_PLTE (png_ptr, png_info_ptr, (png_colorp) before, colors);
510
mng_save_image (const gchar *filename,
513
gint32 original_image_id)
523
struct mnglib_userdata_t *userdata;
526
guint32 mng_ticks_per_second;
527
guint32 mng_simplicity_profile;
529
layers = gimp_image_get_layers (image_id, &num_layers);
535
mng_ticks_per_second = 1000;
537
mng_ticks_per_second = 0;
539
rows = gimp_image_height (image_id);
540
cols = gimp_image_width (image_id);
542
mng_simplicity_profile =
543
(MNG_SIMPLICITY_VALID | MNG_SIMPLICITY_SIMPLEFEATURES |
544
MNG_SIMPLICITY_COMPLEXFEATURES);
545
mng_simplicity_profile |= (MNG_SIMPLICITY_JNG | MNG_SIMPLICITY_DELTAPNG); /* JNG and delta-PNG chunks exist */
547
for (i = 0; i < num_layers; i++)
548
if (gimp_drawable_has_alpha (layers[i]))
550
mng_simplicity_profile |= MNG_SIMPLICITY_TRANSPARENCY; /* internal transparency exists */
551
mng_simplicity_profile |= 0x00000040; /* validity of following */
552
mng_simplicity_profile |= 0x00000100; /* semi-transparency exists */
553
mng_simplicity_profile |= 0x00000080; /* background transparency should happen */
557
userdata = g_new0 (struct mnglib_userdata_t, 1);
559
if ((userdata->fp = g_fopen (filename, "wb")) == NULL)
561
g_message (_("Could not open '%s' for writing: %s"),
562
gimp_filename_to_utf8 (filename), g_strerror (errno));
568
mng_initialize ((mng_ptr) userdata, myalloc, myfree,
571
g_warning ("Unable to mng_initialize() in mng_save_image()");
572
fclose (userdata->fp);
577
if (((ret = mng_setcb_openstream (handle, myopenstream)) != MNG_NOERROR) ||
578
((ret = mng_setcb_closestream (handle, myclosestream)) != MNG_NOERROR)
579
|| ((ret = mng_setcb_writedata (handle, mywritedata)) != MNG_NOERROR))
581
g_warning ("Unable to setup callbacks in mng_save_image()");
582
mng_cleanup (&handle);
583
fclose (userdata->fp);
588
if ((ret = mng_create (handle)) != MNG_NOERROR)
590
g_warning ("Unable to mng_create() image in mng_save_image()");
591
mng_cleanup (&handle);
592
fclose (userdata->fp);
598
mng_putchunk_mhdr (handle, cols, rows, mng_ticks_per_second, 1,
599
num_layers, mng_data.default_delay,
600
mng_simplicity_profile)) != MNG_NOERROR)
602
g_warning ("Unable to mng_putchunk_mhdr() in mng_save_image()");
603
mng_cleanup (&handle);
604
fclose (userdata->fp);
609
if ((num_layers > 1) && (mng_data.loop))
612
mng_putchunk_term (handle, MNG_TERMACTION_REPEAT,
613
MNG_ITERACTION_LASTFRAME,
614
parse_ms_tag_from_layer_name
615
(gimp_drawable_get_name (layers[0])),
616
0x7fffffff)) != MNG_NOERROR)
618
g_warning ("Unable to mng_putchunk_term() in mng_save_image()");
619
mng_cleanup (&handle);
620
fclose (userdata->fp);
628
mng_putchunk_term (handle, MNG_TERMACTION_LASTFRAME,
629
MNG_ITERACTION_LASTFRAME,
630
parse_ms_tag_from_layer_name
631
(gimp_drawable_get_name (layers[0])),
632
0x7fffffff)) != MNG_NOERROR)
634
g_warning ("Unable to mng_putchunk_term() in mng_save_image()");
635
mng_cleanup (&handle);
636
fclose (userdata->fp);
643
/* For now, we hardwire a comment */
646
mng_putchunk_text (handle, strlen (MNG_TEXT_TITLE), MNG_TEXT_TITLE, 22,
647
"Created using GIMP")) != MNG_NOERROR)
649
g_warning ("Unable to mng_putchunk_text() in mng_save_image()");
650
mng_cleanup (&handle);
651
fclose (userdata->fp);
657
/* how do we get this to work? */
662
guchar red, green, blue;
664
gimp_context_get_background(&bgcolor);
665
gimp_rgb_get_uchar(&bgcolor, &red, &green, &blue);
667
if ((ret = mng_putchunk_back(handle, red, green, blue,
668
MNG_BACKGROUNDCOLOR_MANDATORY,
669
0, MNG_BACKGROUNDIMAGE_NOTILE)) != MNG_NOERROR)
671
g_warning("Unable to mng_putchunk_back() in mng_save_image()");
672
mng_cleanup(&handle);
673
fclose(userdata->fp);
678
if ((ret = mng_putchunk_bkgd(handle, MNG_FALSE, 2, 0,
679
gimp_rgb_luminance_uchar(&bgcolor),
680
red, green, blue)) != MNG_NOERROR)
682
g_warning("Unable to mng_putchunk_bkgd() in mng_save_image()");
683
mng_cleanup(&handle);
684
fclose(userdata->fp);
694
mng_putchunk_gama (handle, MNG_FALSE,
695
(1.0 / (gimp_gamma ()) * 100000))) != MNG_NOERROR)
697
g_warning ("Unable to mng_putchunk_gama() in mng_save_image()");
698
mng_cleanup (&handle);
699
fclose (userdata->fp);
706
/* how do we get this to work? */
710
gimp_image_get_resolution(original_image_id, &xres, &yres);
712
if ((ret = mng_putchunk_phyg(handle, MNG_FALSE, (mng_uint32) (xres * 39.37), (mng_uint32) (yres * 39.37), 1)) != MNG_NOERROR)
714
g_warning("Unable to mng_putchunk_phyg() in mng_save_image()");
715
mng_cleanup(&handle);
716
fclose(userdata->fp);
721
if ((ret = mng_putchunk_phys(handle, MNG_FALSE, (mng_uint32) (xres * 39.37), (mng_uint32) (yres * 39.37), 1)) != MNG_NOERROR)
723
g_warning("Unable to mng_putchunk_phys() in mng_save_image()");
724
mng_cleanup(&handle);
725
fclose(userdata->fp);
738
mng_putchunk_time (handle, gmt->tm_year + 1900, gmt->tm_mon + 1,
739
gmt->tm_mday, gmt->tm_hour, gmt->tm_min,
740
gmt->tm_sec)) != MNG_NOERROR)
742
g_warning ("Unable to mng_putchunk_time() in mng_save_image()");
743
mng_cleanup (&handle);
744
fclose (userdata->fp);
750
if (gimp_image_base_type (image_id) == GIMP_INDEXED)
755
palette = gimp_image_get_colormap (image_id, &numcolors);
757
if (numcolors != 0 &&
758
(ret = mng_putchunk_plte (handle, numcolors, (mng_palette8e *) palette))
761
g_warning ("Unable to mng_putchunk_plte() in mng_save_image()");
762
mng_cleanup (&handle);
763
fclose (userdata->fp);
769
for (i = (num_layers - 1); i >= 0; i--)
772
GimpImageType layer_drawable_type;
773
GimpDrawable *layer_drawable;
774
gint layer_offset_x, layer_offset_y;
775
gint layer_rows, layer_cols;
777
gint layer_chunks_type;
778
volatile gint layer_bpp;
779
GimpPixelRgn layer_pixel_rgn;
781
guint8 layer_mng_colortype;
782
guint8 layer_mng_compression_type;
783
guint8 layer_mng_interlace_type;
784
gboolean layer_has_unique_palette;
788
gchar *temp_file_name;
790
png_infop png_info_ptr;
791
FILE *infile, *outfile;
794
guchar **layer_pixels, *layer_pixel;
795
int pass, j, k, begin, end, num;
797
guchar layer_remap[256];
800
layer_name = gimp_drawable_get_name (layers[i]);
801
layer_chunks_type = parse_chunks_type_from_layer_name (layer_name);
802
layer_drawable_type = gimp_drawable_type (layers[i]);
804
layer_drawable = gimp_drawable_get (layers[i]);
805
layer_rows = layer_drawable->height;
806
layer_cols = layer_drawable->width;
807
gimp_drawable_offsets (layers[i], &layer_offset_x, &layer_offset_y);
809
layer_has_unique_palette = TRUE;
811
for (j = 0; j < 256; j++)
814
switch (layer_drawable_type)
818
layer_mng_colortype = MNG_COLORTYPE_RGB;
820
case GIMP_RGBA_IMAGE:
822
layer_mng_colortype = MNG_COLORTYPE_RGBA;
824
case GIMP_GRAY_IMAGE:
826
layer_mng_colortype = MNG_COLORTYPE_GRAY;
828
case GIMP_GRAYA_IMAGE:
830
layer_mng_colortype = MNG_COLORTYPE_GRAYA;
832
case GIMP_INDEXED_IMAGE:
834
layer_mng_colortype = MNG_COLORTYPE_INDEXED;
836
case GIMP_INDEXEDA_IMAGE:
838
layer_mng_colortype = MNG_COLORTYPE_INDEXED | MNG_COLORTYPE_GRAYA;
841
g_warning ("Unsupported GimpImageType in mng_save_image()");
842
mng_cleanup (&handle);
843
fclose (userdata->fp);
848
/* Delta PNG chunks are not yet supported */
850
/* if (i == (num_layers - 1)) */
852
if (layer_chunks_type == CHUNKS_JNG_D)
853
layer_chunks_type = CHUNKS_JNG;
854
else if (layer_chunks_type == CHUNKS_PNG_D)
855
layer_chunks_type = CHUNKS_PNG;
858
switch (layer_chunks_type)
861
layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
862
if (mng_data.interlaced != 0)
863
layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
865
layer_mng_interlace_type = MNG_INTERLACE_NONE;
868
layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
869
if (mng_data.interlaced != 0)
870
layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
872
layer_mng_interlace_type = MNG_INTERLACE_NONE;
875
layer_mng_compression_type = MNG_COMPRESSION_DEFLATE;
876
if (mng_data.interlaced != 0)
877
layer_mng_interlace_type = MNG_INTERLACE_ADAM7;
879
layer_mng_interlace_type = MNG_INTERLACE_NONE;
882
layer_mng_compression_type = MNG_COMPRESSION_BASELINEJPEG;
883
if (mng_data.interlaced != 0)
884
layer_mng_interlace_type = MNG_INTERLACE_PROGRESSIVE;
886
layer_mng_interlace_type = MNG_INTERLACE_SEQUENTIAL;
890
("Huh? Programmer stupidity error with 'layer_chunks_type'");
891
mng_cleanup (&handle);
892
fclose (userdata->fp);
897
if ((i == (num_layers - 1))
898
|| (parse_disposal_type_from_layer_name (layer_name) !=
900
frame_mode = MNG_FRAMINGMODE_3;
902
frame_mode = MNG_FRAMINGMODE_1;
904
frame_delay = parse_ms_tag_from_layer_name (layer_name);
906
if ((ret = mng_putchunk_fram (handle, MNG_FALSE, frame_mode, 0, NULL,
907
MNG_CHANGEDELAY_DEFAULT,
909
MNG_CHANGECLIPPING_DEFAULT,
913
layer_offset_x + layer_cols,
915
layer_offset_y + layer_rows,
916
0, 0)) != MNG_NOERROR)
918
g_warning ("Unable to mng_putchunk_fram() in mng_save_image()");
919
mng_cleanup (&handle);
920
fclose (userdata->fp);
925
if ((layer_offset_x != 0) || (layer_offset_y != 0))
927
mng_putchunk_defi (handle, 0, 0, 1, 1, layer_offset_x,
928
layer_offset_y, 1, layer_offset_x,
929
layer_offset_x + layer_cols, layer_offset_y,
930
layer_offset_y + layer_rows)) != MNG_NOERROR)
932
g_warning ("Unable to mng_putchunk_defi() in mng_save_image()");
933
mng_cleanup (&handle);
934
fclose (userdata->fp);
939
if ((temp_file_name = gimp_temp_name ("mng")) == NULL)
941
g_warning ("gimp_temp_name() failed in mng_save_image(");
942
mng_cleanup (&handle);
943
fclose (userdata->fp);
948
if ((outfile = g_fopen (temp_file_name, "wb")) == NULL)
950
g_message (_("Could not open '%s' for writing: %s"),
951
gimp_filename_to_utf8 (temp_file_name),
953
g_unlink (temp_file_name);
954
mng_cleanup (&handle);
955
fclose (userdata->fp);
960
if ((png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
962
(png_error_ptr) NULL,
963
(png_error_ptr) NULL)) == NULL)
966
("Unable to png_create_write_struct() in mng_save_image()");
968
g_unlink (temp_file_name);
969
mng_cleanup (&handle);
970
fclose (userdata->fp);
975
if ((png_info_ptr = png_create_info_struct (png_ptr)) == NULL)
978
("Unable to png_create_info_struct() in mng_save_image()");
979
png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
981
g_unlink (temp_file_name);
982
mng_cleanup (&handle);
983
fclose (userdata->fp);
988
if (setjmp (png_ptr->jmpbuf) != 0)
990
g_warning ("HRM saving PNG in mng_save_image()");
991
png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
993
g_unlink (temp_file_name);
994
mng_cleanup (&handle);
995
fclose (userdata->fp);
1000
png_init_io (png_ptr, outfile);
1001
png_set_compression_level (png_ptr, mng_data.compression_level);
1003
png_info_ptr->width = layer_cols;
1004
png_info_ptr->height = layer_rows;
1005
png_info_ptr->interlace_type = ((mng_data.interlaced == 0) ? 0 : 1);
1006
png_info_ptr->bit_depth = 8;
1008
switch (layer_drawable_type)
1010
case GIMP_RGB_IMAGE:
1011
png_info_ptr->color_type = PNG_COLOR_TYPE_RGB;
1013
case GIMP_RGBA_IMAGE:
1014
png_info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
1016
case GIMP_GRAY_IMAGE:
1017
png_info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
1019
case GIMP_GRAYA_IMAGE:
1020
png_info_ptr->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
1022
case GIMP_INDEXED_IMAGE:
1023
png_info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
1024
png_info_ptr->valid |= PNG_INFO_PLTE;
1025
png_info_ptr->palette =
1026
(png_colorp) gimp_image_get_colormap (image_id, &num_colors);
1027
png_info_ptr->num_palette = num_colors;
1029
case GIMP_INDEXEDA_IMAGE:
1030
png_info_ptr->color_type = PNG_COLOR_TYPE_PALETTE;
1031
layer_has_unique_palette =
1032
respin_cmap (png_ptr, png_info_ptr, layer_remap,
1033
image_id, layer_drawable);
1036
g_warning ("This can't be!\n");
1040
if ((png_info_ptr->valid & PNG_INFO_PLTE) == PNG_INFO_PLTE)
1042
if (png_info_ptr->num_palette <= 2)
1043
png_info_ptr->bit_depth = 1;
1044
else if (png_info_ptr->num_palette <= 4)
1045
png_info_ptr->bit_depth = 2;
1046
else if (png_info_ptr->num_palette <= 16)
1047
png_info_ptr->bit_depth = 4;
1050
png_write_info (png_ptr, png_info_ptr);
1052
if (mng_data.interlaced != 0)
1053
num_passes = png_set_interlace_handling (png_ptr);
1057
if ((png_info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
1058
&& (png_info_ptr->bit_depth < 8))
1059
png_set_packing (png_ptr);
1061
tile_height = gimp_tile_height ();
1062
layer_pixel = g_new (guchar, tile_height * layer_cols * layer_bpp);
1063
layer_pixels = g_new (guchar *, tile_height);
1065
for (j = 0; j < tile_height; j++)
1066
layer_pixels[j] = layer_pixel + (layer_cols * layer_bpp * j);
1068
gimp_pixel_rgn_init (&layer_pixel_rgn, layer_drawable, 0, 0, layer_cols,
1069
layer_rows, FALSE, FALSE);
1071
for (pass = 0; pass < num_passes; pass++)
1073
for (begin = 0, end = tile_height; begin < layer_rows;
1074
begin += tile_height, end += tile_height)
1076
if (end > layer_rows)
1080
gimp_pixel_rgn_get_rect (&layer_pixel_rgn, layer_pixel, 0,
1081
begin, layer_cols, num);
1083
if ((png_info_ptr->valid & PNG_INFO_tRNS) == PNG_INFO_tRNS)
1085
for (j = 0; j < num; j++)
1087
fixed = layer_pixels[j];
1089
for (k = 0; k < layer_cols; k++)
1091
((fixed[k * 2 + 1] > 127) ? layer_remap[fixed[k * 2]] : 0);
1095
if (((png_info_ptr->valid & PNG_INFO_PLTE) == PNG_INFO_PLTE)
1096
&& (layer_bpp == 2))
1098
for (j = 0; j < num; j++)
1100
fixed = layer_pixels[j];
1102
for (k = 0; k < layer_cols; k++)
1103
fixed[k] = fixed[k * 2];
1107
png_write_rows (png_ptr, layer_pixels, num);
1111
png_write_end (png_ptr, png_info_ptr);
1112
png_destroy_write_struct (&png_ptr, &png_info_ptr);
1114
g_free (layer_pixels);
1115
g_free (layer_pixel);
1119
if ((infile = g_fopen (temp_file_name, "rb")) == NULL)
1121
g_message (_("Could not open '%s' for reading: %s"),
1122
gimp_filename_to_utf8 (temp_file_name),
1123
g_strerror (errno));
1124
g_unlink (temp_file_name);
1125
mng_cleanup (&handle);
1126
fclose (userdata->fp);
1131
fseek (infile, 8L, SEEK_SET);
1133
while (!feof (infile))
1135
guchar chunksize_chars[4];
1138
guchar *chunkbuffer;
1141
gchar chunkbitdepth;
1142
gchar chunkcolortype;
1143
gchar chunkcompression;
1145
gchar chunkinterlaced;
1148
if (fread (chunksize_chars, 1, 4, infile) != 4)
1151
if (fread (chunkname, 1, 4, infile) != 4)
1156
chunksize = (chunksize_chars[0] << 24) | (chunksize_chars[1] << 16)
1157
| (chunksize_chars[2] << 8) | chunksize_chars[3];
1163
chunkbuffer = g_new (guchar, chunksize);
1165
if (fread (chunkbuffer, 1, chunksize, infile) != chunksize)
1169
if (strncmp (chunkname, "IHDR", 4) == 0)
1171
chunkwidth = (chunkbuffer[0] << 24) | (chunkbuffer[1] << 16)
1172
| (chunkbuffer[2] << 8) | chunkbuffer[3];
1174
chunkheight = (chunkbuffer[4] << 24) | (chunkbuffer[5] << 16)
1175
| (chunkbuffer[6] << 8) | chunkbuffer[7];
1177
chunkbitdepth = chunkbuffer[8];
1178
chunkcolortype = chunkbuffer[9];
1179
chunkcompression = chunkbuffer[10];
1180
chunkfilter = chunkbuffer[11];
1181
chunkinterlaced = chunkbuffer[12];
1184
mng_putchunk_ihdr (handle, chunkwidth, chunkheight,
1185
chunkbitdepth, chunkcolortype,
1186
chunkcompression, chunkfilter,
1187
chunkinterlaced)) != MNG_NOERROR)
1190
("Unable to mng_putchunk_ihdr() in mng_save_image()");
1191
mng_cleanup (&handle);
1192
fclose (userdata->fp);
1197
else if (strncmp (chunkname, "IDAT", 4) == 0)
1200
mng_putchunk_idat (handle, chunksize,
1201
chunkbuffer)) != MNG_NOERROR)
1204
("Unable to mng_putchunk_idat() in mng_save_image()");
1205
mng_cleanup (&handle);
1206
fclose (userdata->fp);
1211
else if (strncmp (chunkname, "IEND", 4) == 0)
1213
if ((ret = mng_putchunk_iend (handle)) != MNG_NOERROR)
1216
("Unable to mng_putchunk_iend() in mng_save_image()");
1217
mng_cleanup (&handle);
1218
fclose (userdata->fp);
1223
else if (strncmp (chunkname, "PLTE", 4) == 0)
1225
/* if this frame's palette is the same as the global palette,
1226
write a 0-color palette chunk */
1228
mng_putchunk_plte (handle,
1229
layer_has_unique_palette ? (chunksize / 3) : 0,
1230
(mng_palette8e *) chunkbuffer))
1234
("Unable to mng_putchunk_plte() in mng_save_image()");
1235
mng_cleanup (&handle);
1236
fclose (userdata->fp);
1241
else if (strncmp (chunkname, "tRNS", 4) == 0)
1244
mng_putchunk_trns (handle, 0, 0, 3, chunksize,
1245
(mng_uint8 *) chunkbuffer, 0, 0, 0, 0,
1247
(mng_uint8 *) chunkbuffer)) !=
1251
("Unable to mng_putchunk_trns() in mng_save_image()");
1252
mng_cleanup (&handle);
1253
fclose (userdata->fp);
1260
g_free (chunkbuffer);
1262
/* read 4 bytes after the chunk */
1264
fread (chunkname, 1, 4, infile);
1268
g_unlink (temp_file_name);
1271
if ((ret = mng_putchunk_mend (handle)) != MNG_NOERROR)
1273
g_warning ("Unable to mng_putchunk_mend() in mng_save_image()");
1274
mng_cleanup (&handle);
1275
fclose (userdata->fp);
1280
if ((ret = mng_write (handle)) != MNG_NOERROR)
1282
g_warning ("Unable to mng_write() the image in mng_save_image()");
1283
mng_cleanup (&handle);
1284
fclose (userdata->fp);
1289
mng_cleanup (&handle);
1290
fclose (userdata->fp);
1297
/* The interactive dialog. */
1300
mng_save_dialog (gint32 image_id)
1303
GtkWidget *main_vbox;
1312
GtkObject *scale_adj;
1313
GtkWidget *spinbutton;
1314
GtkObject *spinbutton_adj;
1319
dialog = gimp_dialog_new (_("Save as MNG"), PLUG_IN_BINARY,
1321
gimp_standard_help_func, SAVE_PROC,
1323
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1324
GTK_STOCK_SAVE, GTK_RESPONSE_OK,
1328
gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1330
GTK_RESPONSE_CANCEL,
1333
gimp_window_set_transient (GTK_WINDOW (dialog));
1335
main_vbox = gtk_vbox_new (FALSE, 12);
1336
gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 12);
1337
gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
1339
frame = gimp_frame_new (_("MNG Options"));
1340
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
1342
vbox = gtk_vbox_new (FALSE, 6);
1343
gtk_container_add (GTK_CONTAINER (frame), vbox);
1345
toggle = gtk_check_button_new_with_label (_("Interlace"));
1346
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1348
g_signal_connect (toggle, "toggled",
1349
G_CALLBACK (gimp_toggle_button_update),
1350
&mng_data.interlaced);
1352
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
1353
mng_data.interlaced);
1355
gtk_widget_show (toggle);
1357
toggle = gtk_check_button_new_with_label (_("Save background color"));
1358
gtk_widget_set_sensitive (toggle, FALSE);
1359
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1360
g_signal_connect (toggle, "toggled",
1361
G_CALLBACK (gimp_toggle_button_update),
1364
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.bkgd);
1366
gtk_widget_show (toggle);
1368
toggle = gtk_check_button_new_with_label (_("Save gamma"));
1369
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1370
g_signal_connect (toggle, "toggled",
1371
G_CALLBACK (gimp_toggle_button_update),
1374
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.gama);
1376
gtk_widget_show (toggle);
1378
toggle = gtk_check_button_new_with_label (_("Save resolution"));
1379
gtk_widget_set_sensitive (toggle, FALSE);
1380
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1381
g_signal_connect (toggle, "toggled",
1382
G_CALLBACK (gimp_toggle_button_update),
1385
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.phys);
1387
gtk_widget_show (toggle);
1389
toggle = gtk_check_button_new_with_label (_("Save creation time"));
1390
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1391
g_signal_connect (toggle, "toggled",
1392
G_CALLBACK (gimp_toggle_button_update),
1395
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), mng_data.time);
1397
gtk_widget_show (toggle);
1399
table = gtk_table_new (2, 4, FALSE);
1400
gtk_table_set_col_spacings (GTK_TABLE (table), 6);
1401
gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1402
gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
1403
gtk_widget_show (table);
1405
gimp_image_get_layers (image_id, &num_layers);
1407
if (num_layers == 1)
1408
combo = gimp_int_combo_box_new (_("PNG"), CHUNKS_PNG_D,
1409
_("JNG"), CHUNKS_JNG_D,
1412
combo = gimp_int_combo_box_new (_("PNG + delta PNG"), CHUNKS_PNG_D,
1413
_("JNG + delta PNG"), CHUNKS_JNG_D,
1414
_("All PNG"), CHUNKS_PNG,
1415
_("All JNG"), CHUNKS_JNG,
1418
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
1419
mng_data.default_chunks);
1421
g_signal_connect (combo, "changed",
1422
G_CALLBACK (gimp_int_combo_box_get_active),
1423
&mng_data.default_chunks);
1425
gtk_widget_set_sensitive (combo, FALSE);
1426
gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
1427
_("Default chunks type:"), 0.0, 0.5,
1430
combo = gimp_int_combo_box_new (_("Combine"), DISPOSE_COMBINE,
1431
_("Replace"), DISPOSE_REPLACE,
1434
gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
1435
mng_data.default_dispose);
1437
g_signal_connect (combo, "changed",
1438
G_CALLBACK (gimp_int_combo_box_get_active),
1439
&mng_data.default_dispose);
1441
gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
1442
_("Default frame disposal:"), 0.0, 0.5,
1445
scale_adj = gtk_adjustment_new (mng_data.compression_level,
1446
0.0, 9.0, 1.0, 1.0, 0.0);
1448
scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_adj));
1449
gtk_widget_set_size_request (scale, SCALE_WIDTH, -1);
1450
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
1451
gtk_scale_set_digits (GTK_SCALE (scale), 0);
1452
gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
1453
gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
1454
_("PNG compression level:"), 0.0, 0.9,
1457
g_signal_connect (scale_adj, "value-changed",
1458
G_CALLBACK (gimp_int_adjustment_update),
1459
&mng_data.compression_level);
1461
gimp_help_set_help_data (scale,
1462
_("Choose a high compression level "
1463
"for small file size"),
1466
scale_adj = gtk_adjustment_new (mng_data.quality,
1467
0.0, 1.0, 0.01, 0.01, 0.0);
1469
scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_adj));
1470
gtk_widget_set_size_request (scale, SCALE_WIDTH, -1);
1471
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
1472
gtk_scale_set_digits (GTK_SCALE (scale), 2);
1473
gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
1474
gtk_widget_set_sensitive (scale, FALSE);
1475
gimp_table_attach_aligned (GTK_TABLE (table), 0, 3,
1476
_("JPEG compression quality:"), 0.0, 0.9,
1479
g_signal_connect (scale_adj, "value-changed",
1480
G_CALLBACK (gimp_int_adjustment_update),
1483
scale_adj = gtk_adjustment_new (mng_data.smoothing,
1484
0.0, 1.0, 0.01, 0.01, 0.0);
1486
scale = gtk_hscale_new (GTK_ADJUSTMENT (scale_adj));
1487
gtk_widget_set_size_request (scale, SCALE_WIDTH, -1);
1488
gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
1489
gtk_scale_set_digits (GTK_SCALE (scale), 2);
1490
gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
1491
gtk_widget_set_sensitive (scale, FALSE);
1492
gimp_table_attach_aligned (GTK_TABLE (table), 0, 4,
1493
_("JPEG smoothing factor:"), 0.0, 0.9,
1496
g_signal_connect (scale_adj, "value-changed",
1497
G_CALLBACK (gimp_int_adjustment_update),
1498
&mng_data.smoothing);
1500
gtk_widget_show (vbox);
1501
gtk_widget_show (frame);
1503
frame = gimp_frame_new (_("Animated MNG Options"));
1504
gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
1506
vbox = gtk_vbox_new (FALSE, 6);
1507
gtk_container_add (GTK_CONTAINER (frame), vbox);
1509
toggle = gtk_check_button_new_with_label (_("Loop"));
1510
gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1511
g_signal_connect (toggle, "toggled",
1512
G_CALLBACK (gimp_toggle_button_update),
1515
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
1518
gtk_widget_show (toggle);
1520
hbox = gtk_hbox_new (FALSE, 4);
1521
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1523
label = gtk_label_new (_("Default frame delay:"));
1524
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1525
gtk_widget_show (label);
1527
spinbutton = gimp_spin_button_new (&spinbutton_adj,
1528
mng_data.default_delay,
1529
0, 65000, 10, 100, 0, 1, 0);
1531
g_signal_connect (spinbutton_adj, "value-changed",
1532
G_CALLBACK (gimp_int_adjustment_update),
1533
&mng_data.default_delay);
1535
gtk_box_pack_start (GTK_BOX (hbox), spinbutton, FALSE, FALSE, 0);
1537
gtk_widget_show (spinbutton);
1539
label = gtk_label_new (_("milliseconds"));
1540
gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1541
gtk_widget_show (label);
1543
gtk_widget_show (hbox);
1545
gtk_widget_show (vbox);
1546
gtk_widget_show (frame);
1548
gtk_widget_set_sensitive (frame, num_layers > 1);
1550
gtk_widget_show (main_vbox);
1551
gtk_widget_show (dialog);
1553
run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
1555
gtk_widget_destroy (dialog);
1561
/* GIMP calls these methods. */
1566
static const GimpParamDef save_args[] =
1568
{ GIMP_PDB_INT32, "run-mode", "Interactive, non-interactive" },
1569
{ GIMP_PDB_IMAGE, "image", "Input image" },
1570
{ GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
1571
{ GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
1572
{ GIMP_PDB_STRING, "raw-filename", "The name of the file to save the image in" },
1574
{ GIMP_PDB_INT32, "interlace", "Use interlacing" },
1575
{ GIMP_PDB_INT32, "compression", "PNG deflate compression level (0 - 9)" },
1576
{ GIMP_PDB_FLOAT, "quality", "JPEG quality factor (0.00 - 1.00)" },
1577
{ GIMP_PDB_FLOAT, "smoothing", "JPEG smoothing factor (0.00 - 1.00)" },
1578
{ GIMP_PDB_INT32, "loop", "(ANIMATED MNG) Loop infinitely" },
1579
{ GIMP_PDB_INT32, "default-delay", "(ANIMATED MNG) Default delay between frames in milliseconds" },
1580
{ GIMP_PDB_INT32, "default-chunks", "(ANIMATED MNG) Default chunks type (0 = PNG + Delta PNG; 1 = JNG + Delta PNG; 2 = All PNG; 3 = All JNG)" },
1581
{ GIMP_PDB_INT32, "default-dispose", "(ANIMATED MNG) Default dispose type (0 = combine; 1 = replace)" },
1582
{ GIMP_PDB_INT32, "bkgd", "Write bKGD (background color) chunk" },
1583
{ GIMP_PDB_INT32, "gama", "Write gAMA (gamma) chunk"},
1584
{ GIMP_PDB_INT32, "phys", "Write pHYs (image resolution) chunk" },
1585
{ GIMP_PDB_INT32, "time", "Write tIME (creation time) chunk" }
1588
gimp_install_procedure (SAVE_PROC,
1589
"Saves images in the MNG file format",
1590
"This plug-in saves images in the Multiple-image "
1591
"Network Graphics (MNG) format which can be used as "
1592
"a replacement for animated GIFs, and more.",
1593
"S. Mukund <muks@mukund.org>",
1594
"S. Mukund <muks@mukund.org>",
1595
"November 19, 2002",
1596
N_("MNG animation"),
1597
"RGB*,GRAY*,INDEXED*",
1599
G_N_ELEMENTS (save_args), 0,
1602
gimp_register_file_handler_mime (SAVE_PROC, "image/x-mng");
1603
gimp_register_save_handler (SAVE_PROC, "mng", "");
1607
run (const gchar *name,
1609
const GimpParam *param,
1611
GimpParam **return_vals)
1613
static GimpParam values[1];
1618
*return_vals = values;
1619
values[0].type = GIMP_PDB_STATUS;
1620
values[0].data.d_status = GIMP_PDB_SUCCESS;
1622
if (strcmp (name, SAVE_PROC) == 0)
1624
GimpRunMode run_mode;
1625
gint32 image_id, original_image_id;
1627
GimpExportReturn export = GIMP_EXPORT_IGNORE;
1629
run_mode = param[0].data.d_int32;
1630
image_id = original_image_id = param[1].data.d_int32;
1631
drawable_id = param[2].data.d_int32;
1633
if ((run_mode == GIMP_RUN_INTERACTIVE)
1634
|| (run_mode == GIMP_RUN_WITH_LAST_VALS))
1636
gimp_procedural_db_get_data (SAVE_PROC, &mng_data);
1638
gimp_ui_init (PLUG_IN_BINARY, FALSE);
1639
export = gimp_export_image (&image_id, &drawable_id, "MNG",
1640
(GIMP_EXPORT_CAN_HANDLE_RGB |
1641
GIMP_EXPORT_CAN_HANDLE_GRAY |
1642
GIMP_EXPORT_CAN_HANDLE_INDEXED |
1643
GIMP_EXPORT_CAN_HANDLE_ALPHA |
1644
GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION));
1647
if (export == GIMP_EXPORT_CANCEL)
1648
values[0].data.d_status = GIMP_PDB_CANCEL;
1649
else if ((export == GIMP_EXPORT_IGNORE)
1650
|| (export == GIMP_EXPORT_EXPORT))
1652
if (run_mode == GIMP_RUN_INTERACTIVE)
1654
if (mng_save_dialog (image_id) == 0)
1655
values[0].data.d_status = GIMP_PDB_CANCEL;
1657
else if (run_mode == GIMP_RUN_NONINTERACTIVE)
1661
g_message ("Incorrect number of parameters "
1662
"passed to file-mng-save()");
1663
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
1667
mng_data.interlaced = param[5].data.d_int32;
1668
mng_data.compression_level = param[6].data.d_int32;
1669
mng_data.quality = param[7].data.d_float;
1670
mng_data.smoothing = param[8].data.d_float;
1671
mng_data.loop = param[9].data.d_int32;
1672
mng_data.default_delay = param[10].data.d_int32;
1673
mng_data.default_chunks = param[11].data.d_int32;
1674
mng_data.default_dispose = param[12].data.d_int32;
1675
mng_data.bkgd = param[13].data.d_int32;
1676
mng_data.gama = param[14].data.d_int32;
1677
mng_data.phys = param[15].data.d_int32;
1678
mng_data.time = param[16].data.d_int32;
1680
if ((mng_data.compression_level < 0)
1681
|| (mng_data.compression_level > 9))
1683
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.");
1684
mng_data.compression_level = 6;
1687
if ((mng_data.quality < ((float) 0))
1688
|| (mng_data.quality > ((float) 1)))
1690
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.");
1691
mng_data.quality = 0.75;
1694
if ((mng_data.smoothing < ((float) 0))
1695
|| (mng_data.smoothing > ((float) 1)))
1697
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.");
1698
mng_data.smoothing = 0.0;
1701
if ((mng_data.default_chunks < 0)
1702
|| (mng_data.default_chunks > 3))
1704
g_warning ("Parameter 'default_chunks' passed to file-mng-save() must be in the range 0 - 2.");
1705
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
1708
if ((mng_data.default_dispose < 0)
1709
|| (mng_data.default_dispose > 1))
1711
g_warning ("Parameter 'default_dispose' passed to file-mng-save() must be in the range 0 - 1.");
1712
values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
1717
if (values[0].data.d_status == GIMP_PDB_SUCCESS)
1720
(param[3].data.d_string, image_id, drawable_id,
1721
original_image_id) != 0)
1722
gimp_set_data (SAVE_PROC, &mng_data, sizeof (mng_data));
1724
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
1727
if (export == GIMP_EXPORT_EXPORT)
1728
gimp_image_delete (image_id);
1733
values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
1737
/* Only query and run are implemented by this plug-in. */
1739
const GimpPlugInInfo PLUG_IN_INFO =