png.c 31.3 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1
2
3
4
5
6
/*
 * "$Id$"
 *
 *   Portable Network Graphics (PNG) plug-in for The GIMP -- an image
 *   manipulation program
 *
Manish Singh's avatar
Manish Singh committed
7
 *   Copyright 1997-1998 Michael Sweet (mike@easysw.com) and
Elliot Lee's avatar
Elliot Lee committed
8
 *   Daniel Skarda (0rfelyus@atrey.karlin.mff.cuni.cz).
9
 *   and 1999-2000 Nick Lamb (njl195@zepler.org.uk)
Elliot Lee's avatar
Elliot Lee committed
10
11
12
13
14
15
16
17
18
19
20
21
22
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
23
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
24
25
26
27
28
29
30
 *
 * Contents:
 *
 *   main()                      - Main entry - just call gimp_main()...
 *   query()                     - Respond to a plug-in query...
 *   run()                       - Run the plug-in...
 *   load_image()                - Load a PNG image into a new image window.
31
 *   respin_cmap()               - Re-order a Gimp colormap for PNG tRNS
Elliot Lee's avatar
Elliot Lee committed
32
33
34
35
36
37
38
39
 *   save_image()                - Save the specified image to a PNG file.
 *   save_ok_callback()          - Destroy the save dialog and save the image.
 *   save_compression_callback() - Update the image compression level.
 *   save_interlace_update()     - Update the interlacing option.
 *   save_dialog()               - Pop up the save dialog.
 *
 * Revision History:
 *
Manish Singh's avatar
Manish Singh committed
40
 *   see ChangeLog
Elliot Lee's avatar
Elliot Lee committed
41
42
 */

43
44
#include "config.h"

Elliot Lee's avatar
Elliot Lee committed
45
46
#include <stdio.h>
#include <stdlib.h>
47
#include <time.h>
Elliot Lee's avatar
Elliot Lee committed
48
49

#include <gtk/gtk.h>
50

Elliot Lee's avatar
Elliot Lee committed
51
#include <libgimp/gimp.h>
Sven Neumann's avatar
Sven Neumann committed
52
#include <libgimp/gimpui.h>
Elliot Lee's avatar
Elliot Lee committed
53

54
55
#include <png.h>		/* PNG library definitions */

56
#include "libgimp/stdplugins-intl.h"
Elliot Lee's avatar
Elliot Lee committed
57

58

Elliot Lee's avatar
Elliot Lee committed
59
60
61
62
/*
 * Constants...
 */

63
#define PLUG_IN_VERSION  "1.3.3 - 30 June 2000"
64
#define SCALE_WIDTH      125
Elliot Lee's avatar
Elliot Lee committed
65

66
#define DEFAULT_GAMMA    2.20
Elliot Lee's avatar
Elliot Lee committed
67
68
69
70
71
72
73
74
75

/*
 * Structures...
 */

typedef struct
{
  gint	interlaced;
  gint	compression_level;
76
77
78
79
80
  gint  bkgd;
  gint  gama;
  gint  offs;
  gint  phys;
  gint  time;
Elliot Lee's avatar
Elliot Lee committed
81
82
83
84
85
86
87
} PngSaveVals;


/*
 * Local functions...
 */

Sven Neumann's avatar
Sven Neumann committed
88
static void	query                     (void);
Michael Natterer's avatar
Michael Natterer committed
89
90
static void	run                       (gchar   *name,
					   gint     nparams,
Sven Neumann's avatar
Sven Neumann committed
91
					   GimpParam  *param,
Michael Natterer's avatar
Michael Natterer committed
92
					   gint    *nreturn_vals,
Sven Neumann's avatar
Sven Neumann committed
93
					   GimpParam **return_vals);
Michael Natterer's avatar
Michael Natterer committed
94
95
96
97
98
99
100

static gint32	load_image                (gchar   *filename);
static gint	save_image                (gchar   *filename,
					   gint32   image_ID,
					   gint32   drawable_ID,
					   gint32   orig_image_ID);

101
102
103
static void	respin_cmap		  (png_structp pp,
					   png_infop info,
					   gint32   image_ID);
104

Sven Neumann's avatar
Sven Neumann committed
105
static gint	save_dialog               (void);
Michael Natterer's avatar
Michael Natterer committed
106
107
static void	save_ok_callback          (GtkWidget     *widget,
					   gpointer       data);
Elliot Lee's avatar
Elliot Lee committed
108
109
110
111
112
113


/*
 * Globals...
 */

Sven Neumann's avatar
Sven Neumann committed
114
GimpPlugInInfo PLUG_IN_INFO =
Elliot Lee's avatar
Elliot Lee committed
115
{
116
117
118
119
  NULL,  /* init_proc  */
  NULL,  /* quit_proc  */
  query, /* query_proc */
  run,   /* run_proc   */
Elliot Lee's avatar
Elliot Lee committed
120
121
};

122
PngSaveVals pngvals = 
Elliot Lee's avatar
Elliot Lee committed
123
{
124
  FALSE,
125
126
  6,
  TRUE, TRUE, TRUE, TRUE, TRUE
Elliot Lee's avatar
Elliot Lee committed
127
128
};

129
static gboolean runme = FALSE;
Elliot Lee's avatar
Elliot Lee committed
130
131
132
133
134

/*
 * 'main()' - Main entry - just call gimp_main()...
 */

Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
135
MAIN()
Elliot Lee's avatar
Elliot Lee committed
136
137
138
139
140
141

/*
 * 'query()' - Respond to a plug-in query...
 */

static void
Sven Neumann's avatar
Sven Neumann committed
142
query (void)
Elliot Lee's avatar
Elliot Lee committed
143
{
Sven Neumann's avatar
Sven Neumann committed
144
  static GimpParamDef load_args[] =
Elliot Lee's avatar
Elliot Lee committed
145
  {
Sven Neumann's avatar
Sven Neumann committed
146
147
148
    { GIMP_PDB_INT32,      "run_mode",     "Interactive, non-interactive" },
    { GIMP_PDB_STRING,     "filename",     "The name of the file to load" },
    { GIMP_PDB_STRING,     "raw_filename", "The name of the file to load" }
Elliot Lee's avatar
Elliot Lee committed
149
  };
Sven Neumann's avatar
Sven Neumann committed
150
  static GimpParamDef load_return_vals[] =
Elliot Lee's avatar
Elliot Lee committed
151
  {
Sven Neumann's avatar
Sven Neumann committed
152
    { GIMP_PDB_IMAGE,      "image",        "Output image" }
Elliot Lee's avatar
Elliot Lee committed
153
  };
154
155
156
157
  static gint nload_args = sizeof (load_args) / sizeof (load_args[0]);
  static gint nload_return_vals = (sizeof (load_return_vals) /
				   sizeof (load_return_vals[0]));

Sven Neumann's avatar
Sven Neumann committed
158
  static GimpParamDef	save_args[] =
Elliot Lee's avatar
Elliot Lee committed
159
  {
Sven Neumann's avatar
Sven Neumann committed
160
161
162
163
164
165
166
167
168
169
170
171
    { GIMP_PDB_INT32,	"run_mode",	"Interactive, non-interactive" },
    { GIMP_PDB_IMAGE,	"image",	"Input image" },
    { GIMP_PDB_DRAWABLE,	"drawable",	"Drawable to save" },
    { GIMP_PDB_STRING,	"filename",	"The name of the file to save the image in" },
    { GIMP_PDB_STRING,	"raw_filename",	"The name of the file to save the image in" },
    { GIMP_PDB_INT32,	"interlace",	"Use Adam7 interlacing?" },
    { GIMP_PDB_INT32,	"compression",	"Deflate Compression factor (0--9)" },
    { GIMP_PDB_INT32,	"bkgd",		"Write bKGD chunk?" },
    { GIMP_PDB_INT32,	"gama",		"Write gAMA chunk?" },
    { GIMP_PDB_INT32,	"offs",		"Write oFFs chunk?" },
    { GIMP_PDB_INT32,	"phys",		"Write tIME chunk?" },
    { GIMP_PDB_INT32,	"time",		"Write pHYs chunk?" }
Elliot Lee's avatar
Elliot Lee committed
172
  };
173
  static gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
Elliot Lee's avatar
Elliot Lee committed
174

Sven Neumann's avatar
Sven Neumann committed
175
  gimp_install_procedure ("file_png_load",
Marc Lehmann's avatar
Marc Lehmann committed
176
177
			  "Loads files in PNG file format",
			  "This plug-in loads Portable Network Graphics (PNG) files.",
Michael Natterer's avatar
Michael Natterer committed
178
179
180
181
182
			  "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
			  "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, Nick Lamb <njl195@zepler.org.uk>",
			  PLUG_IN_VERSION,
			  "<Load>/PNG",
			  NULL,
Sven Neumann's avatar
Sven Neumann committed
183
			  GIMP_PLUGIN,
Michael Natterer's avatar
Michael Natterer committed
184
185
186
187
188
189
190
191
192
193
			  nload_args, nload_return_vals,
			  load_args, load_return_vals);

  gimp_install_procedure  ("file_png_save",
			   "Saves files in PNG file format",
			   "This plug-in saves Portable Network Graphics (PNG) files.",
			   "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
			   "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, Nick Lamb <njl195@zepler.org.uk>",
			   PLUG_IN_VERSION,
			   "<Save>/PNG",
194
			   "RGB*,GRAY*,INDEXED*",
Sven Neumann's avatar
Sven Neumann committed
195
			   GIMP_PLUGIN,
Michael Natterer's avatar
Michael Natterer committed
196
197
198
			   nsave_args, 0,
			   save_args, NULL);

199
200
201
  gimp_register_magic_load_handler ("file_png_load",
				    "png",
				    "",
Michael Natterer's avatar
Michael Natterer committed
202
				    "0,string,\211PNG\r\n\032\n");
203
204
205
  gimp_register_save_handler       ("file_png_save",
				    "png",
				    "");
Elliot Lee's avatar
Elliot Lee committed
206
207
208
209
210
211
212
213
}


/*
 * 'run()' - Run the plug-in...
 */

static void
214
215
run (gchar   *name,
     gint     nparams,
Sven Neumann's avatar
Sven Neumann committed
216
     GimpParam  *param,
217
     gint    *nreturn_vals,
Sven Neumann's avatar
Sven Neumann committed
218
     GimpParam **return_vals)
Elliot Lee's avatar
Elliot Lee committed
219
{
Sven Neumann's avatar
Sven Neumann committed
220
221
222
  static GimpParam values[2];
  GimpRunModeType  run_mode;
  GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
223
224
225
  gint32        image_ID;
  gint32        drawable_ID;
  gint32        orig_image_ID;
226
  GimpExportReturnType export = GIMP_EXPORT_CANCEL;
Elliot Lee's avatar
Elliot Lee committed
227

228
  *nreturn_vals = 1;
Elliot Lee's avatar
Elliot Lee committed
229
  *return_vals  = values;
Sven Neumann's avatar
Sven Neumann committed
230
231
  values[0].type          = GIMP_PDB_STATUS;
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
Elliot Lee's avatar
Elliot Lee committed
232

Sven Neumann's avatar
Sven Neumann committed
233
  if (strcmp (name, "file_png_load") == 0)
Michael Natterer's avatar
Michael Natterer committed
234
235
236
    {
      INIT_I18N ();
      image_ID = load_image (param[1].data.d_string);
Elliot Lee's avatar
Elliot Lee committed
237

Michael Natterer's avatar
Michael Natterer committed
238
239
      if (image_ID != -1)
	{
240
	  *nreturn_vals = 2;
Sven Neumann's avatar
Sven Neumann committed
241
	  values[1].type         = GIMP_PDB_IMAGE;
Michael Natterer's avatar
Michael Natterer committed
242
243
244
	  values[1].data.d_image = image_ID;
	}
      else
245
	{
Sven Neumann's avatar
Sven Neumann committed
246
	  status = GIMP_PDB_EXECUTION_ERROR;
247
	}
Elliot Lee's avatar
Elliot Lee committed
248
249
250
    }
  else if (strcmp (name, "file_png_save") == 0)
    {
Michael Natterer's avatar
Michael Natterer committed
251
      INIT_I18N_UI();
Elliot Lee's avatar
Elliot Lee committed
252

Michael Natterer's avatar
Michael Natterer committed
253
254
255
256
257
258
259
      run_mode = param[0].data.d_int32;
      image_ID = orig_image_ID = param[1].data.d_int32;
      drawable_ID = param[2].data.d_int32;
    
      /*  eventually export the image */ 
      switch (run_mode)
	{
Sven Neumann's avatar
Sven Neumann committed
260
261
	case GIMP_RUN_INTERACTIVE:
	case GIMP_RUN_WITH_LAST_VALS:
262
	  gimp_ui_init ("png", FALSE);
Michael Natterer's avatar
Michael Natterer committed
263
	  export = gimp_export_image (&image_ID, &drawable_ID, "PNG", 
264
265
266
267
268
				      (GIMP_EXPORT_CAN_HANDLE_RGB |
				       GIMP_EXPORT_CAN_HANDLE_GRAY |
				       GIMP_EXPORT_CAN_HANDLE_INDEXED |
				       GIMP_EXPORT_CAN_HANDLE_ALPHA ));
	  if (export == GIMP_EXPORT_CANCEL)
Michael Natterer's avatar
Michael Natterer committed
269
270
	    {
	      *nreturn_vals = 1;
Sven Neumann's avatar
Sven Neumann committed
271
	      values[0].data.d_status = GIMP_PDB_CANCEL;
Michael Natterer's avatar
Michael Natterer committed
272
273
274
275
276
277
278
279
280
	      return;
	    }
	  break;
	default:
	  break;
	}

      switch (run_mode)
	{
Sven Neumann's avatar
Sven Neumann committed
281
	case GIMP_RUN_INTERACTIVE:
Michael Natterer's avatar
Michael Natterer committed
282
283
284
	  /*
	   * Possibly retrieve data...
	   */
Sven Neumann's avatar
Sven Neumann committed
285
          gimp_get_data ("file_png_save", &pngvals);
Elliot Lee's avatar
Elliot Lee committed
286

Michael Natterer's avatar
Michael Natterer committed
287
288
289
	  /*
	   * Then acquire information with a dialog...
	   */
Elliot Lee's avatar
Elliot Lee committed
290
          if (!save_dialog())
Sven Neumann's avatar
Sven Neumann committed
291
            status = GIMP_PDB_CANCEL;
Elliot Lee's avatar
Elliot Lee committed
292
293
          break;

Sven Neumann's avatar
Sven Neumann committed
294
	case GIMP_RUN_NONINTERACTIVE:
Michael Natterer's avatar
Michael Natterer committed
295
296
297
	  /*
	   * Make sure all the arguments are there!
	   */
Marc Lehmann's avatar
Marc Lehmann committed
298
          if (nparams != 12)
299
	    {
Sven Neumann's avatar
Sven Neumann committed
300
	      status = GIMP_PDB_CALLING_ERROR;
301
	    }
Elliot Lee's avatar
Elliot Lee committed
302
          else
Michael Natterer's avatar
Michael Natterer committed
303
304
	    {
	      pngvals.interlaced        = param[5].data.d_int32;
305
306
307
308
309
310
	      pngvals.compression_level = param[6].data.d_int32;
	      pngvals.bkgd              = param[7].data.d_int32;
	      pngvals.gama              = param[8].data.d_int32;
	      pngvals.phys              = param[9].data.d_int32;
	      pngvals.offs              = param[10].data.d_int32;
	      pngvals.time              = param[11].data.d_int32;
Michael Natterer's avatar
Michael Natterer committed
311
312
313

	      if (pngvals.compression_level < 0 ||
		  pngvals.compression_level > 9)
Sven Neumann's avatar
Sven Neumann committed
314
		status = GIMP_PDB_CALLING_ERROR;
Michael Natterer's avatar
Michael Natterer committed
315
	    };
Elliot Lee's avatar
Elliot Lee committed
316
317
          break;

Sven Neumann's avatar
Sven Neumann committed
318
	case GIMP_RUN_WITH_LAST_VALS:
Michael Natterer's avatar
Michael Natterer committed
319
320
321
	  /*
	   * Possibly retrieve data...
	   */
Sven Neumann's avatar
Sven Neumann committed
322
          gimp_get_data ("file_png_save", &pngvals);
Elliot Lee's avatar
Elliot Lee committed
323
324
          break;

Michael Natterer's avatar
Michael Natterer committed
325
	default:
Elliot Lee's avatar
Elliot Lee committed
326
          break;
Michael Natterer's avatar
Michael Natterer committed
327
328
	};

Sven Neumann's avatar
Sven Neumann committed
329
      if (status == GIMP_PDB_SUCCESS)
Michael Natterer's avatar
Michael Natterer committed
330
331
332
	{
	  if (save_image (param[3].data.d_string,
			  image_ID, drawable_ID, orig_image_ID))
333
334
335
	    {
	      gimp_set_data ("file_png_save", &pngvals, sizeof (pngvals));
	    }
Michael Natterer's avatar
Michael Natterer committed
336
	  else
337
	    {
Sven Neumann's avatar
Sven Neumann committed
338
	      status = GIMP_PDB_EXECUTION_ERROR;
339
340
	    }
	}
341

342
      if (export == GIMP_EXPORT_EXPORT)
Michael Natterer's avatar
Michael Natterer committed
343
344
	gimp_image_delete (image_ID);
    }
Elliot Lee's avatar
Elliot Lee committed
345
  else
346
    {
Sven Neumann's avatar
Sven Neumann committed
347
      status = GIMP_PDB_EXECUTION_ERROR;
348
349
350
    }

  values[0].data.d_status = status;
Elliot Lee's avatar
Elliot Lee committed
351
352
353
354
355
356
357
358
}


/*
 * 'load_image()' - Load a PNG image into a new image window.
 */

static gint32
Michael Natterer's avatar
Michael Natterer committed
359
load_image (gchar *filename)	/* I - File to load */
Elliot Lee's avatar
Elliot Lee committed
360
361
{
  int		i,		/* Looping var */
362
363
364
365
366
                trns,		/* Transparency present */
		bpp,		/* Bytes per pixel */
		image_type,	/* Type of image */
		layer_type,	/* Type of drawable/layer */
		empty,		/* Number of fully transparent indices */
Elliot Lee's avatar
Elliot Lee committed
367
368
369
370
371
372
373
		num_passes,	/* Number of interlace passes in file */
		pass,		/* Current pass in file */
		tile_height,	/* Height of tile in GIMP */
		begin,		/* Beginning tile row */
		end,		/* Ending tile row */
		num;		/* Number of rows to load */
  FILE		*fp;		/* File pointer */
374
  volatile gint32 image;	/* Image -- preserved against setjmp() */
375
  gint32	layer;		/* Layer */
Sven Neumann's avatar
Sven Neumann committed
376
377
  GimpDrawable	*drawable;	/* Drawable for layer */
  GimpPixelRgn	pixel_rgn;	/* Pixel region for layer */
Elliot Lee's avatar
Elliot Lee committed
378
379
380
381
  png_structp	pp;		/* PNG read pointer */
  png_infop	info;		/* PNG info pointers */
  guchar	**pixels,	/* Pixel rows */
		*pixel;		/* Pixel data */
382
  gchar		*progress;	/* Title for progress display... */
383
384
  guchar	alpha[256],	/* Index -> Alpha */
  		*alpha_ptr;	/* Temporary pointer */
Elliot Lee's avatar
Elliot Lee committed
385
386

 /*
387
388
  * PNG 0.89 and newer have a sane, forwards compatible constructor.
  * Some SGI IRIX users will not have a new enough version though
Elliot Lee's avatar
Elliot Lee committed
389
390
391
392
393
394
395
396
397
398
399
400
  */

#if PNG_LIBPNG_VER > 88
  pp   = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  info = png_create_info_struct(pp);
#else
  pp = (png_structp)calloc(sizeof(png_struct), 1);
  png_read_init(pp);

  info = (png_infop)calloc(sizeof(png_info), 1);
#endif /* PNG_LIBPNG_VER > 88 */

Sven Neumann's avatar
Sven Neumann committed
401
  if (setjmp (pp->jmpbuf))
402
  {
403
    g_message (_("%s\nPNG error. File corrupted?"), filename);
Nick Lamb /GIMP's avatar
Nick Lamb /GIMP committed
404
    return image;
405
406
  }

407
  /* initialise image here, thus avoiding compiler warnings */
Nick Lamb /GIMP's avatar
Nick Lamb /GIMP committed
408
409
410

  image= -1;

Elliot Lee's avatar
Elliot Lee committed
411
412
413
414
 /*
  * Open the file and initialize the PNG read "engine"...
  */

415
  fp = fopen(filename, "rb");
416

Sven Neumann's avatar
Sven Neumann committed
417
418
419
420
  if (fp == NULL) 
    {
      g_message ("%s\nis not present or is unreadable", filename);
      gimp_quit ();
421
  }
Elliot Lee's avatar
Elliot Lee committed
422
423
424
425

  png_init_io(pp, fp);

  if (strrchr(filename, '/') != NULL)
426
    progress = g_strdup_printf (_("Loading %s:"), strrchr(filename, '/') + 1);
Elliot Lee's avatar
Elliot Lee committed
427
  else
428
    progress = g_strdup_printf (_("Loading %s:"), filename);
Elliot Lee's avatar
Elliot Lee committed
429
430

  gimp_progress_init(progress);
431
  g_free (progress);
Elliot Lee's avatar
Elliot Lee committed
432
433
434
435
436
437
438

 /*
  * Get the image dimensions and create the image...
  */

  png_read_info(pp, info);

439
 /*
440
  * Latest attempt, this should be my best yet :)
441
442
  */

443
444
445
446
  if (info->bit_depth == 16) {
    png_set_strip_16(pp);
  }

447
448
449
450
451
  if (info->color_type == PNG_COLOR_TYPE_GRAY && info->bit_depth < 8) {
    png_set_expand(pp);
  }

  if (info->color_type == PNG_COLOR_TYPE_PALETTE && info->bit_depth < 8) {
Elliot Lee's avatar
Elliot Lee committed
452
453
    png_set_packing(pp);
  }
454

455
456
457
458
459
460
461
 /*
  * Expand G+tRNS to GA, RGB+tRNS to RGBA
  */

  if (info->color_type != PNG_COLOR_TYPE_PALETTE &&
                       (info->valid & PNG_INFO_tRNS)) {
    png_set_expand(pp);
462
  }
Elliot Lee's avatar
Elliot Lee committed
463
464

 /*
465
466
  * Turn on interlace handling... libpng returns just 1 (ie single pass)
  * if the image is not interlaced
Elliot Lee's avatar
Elliot Lee committed
467
468
  */

469
  num_passes = png_set_interlace_handling(pp);
470

471
472
473
474
 /*
  * Special handling for INDEXED + tRNS (transparency palette)
  */

475
476
#if PNG_LIBPNG_VER > 99
  if (png_get_valid(pp, info, PNG_INFO_tRNS) &&
477
478
479
480
481
482
483
484
485
486
      info->color_type == PNG_COLOR_TYPE_PALETTE)
  {
    png_get_tRNS(pp, info, &alpha_ptr, &num, NULL);
    /* Copy the existing alpha values from the tRNS chunk */
    for (i= 0; i < num; ++i)
      alpha[i]= alpha_ptr[i];
    /* And set any others to fully opaque (255)  */
    for (i= num; i < 256; ++i)
      alpha[i]= 255;
    trns= 1;
487
488
  } else {
    trns= 0;
489
  }
490
491
492
#else
    trns= 0;
#endif /* PNG_LIBPNG_VER > 99 */
493

494
495
496
497
498
 /*
  * Update the info structures after the transformations take effect
  */

  png_read_update_info(pp, info);
Elliot Lee's avatar
Elliot Lee committed
499
500
501
502
503
  
  switch (info->color_type)
  {
    case PNG_COLOR_TYPE_RGB :		/* RGB */
        bpp        = 3;
Sven Neumann's avatar
Sven Neumann committed
504
505
        image_type = GIMP_RGB;
        layer_type = GIMP_RGB_IMAGE;
Elliot Lee's avatar
Elliot Lee committed
506
507
508
509
        break;

    case PNG_COLOR_TYPE_RGB_ALPHA :	/* RGBA */
        bpp        = 4;
Sven Neumann's avatar
Sven Neumann committed
510
511
        image_type = GIMP_RGB;
        layer_type = GIMP_RGBA_IMAGE;
Elliot Lee's avatar
Elliot Lee committed
512
513
514
515
        break;

    case PNG_COLOR_TYPE_GRAY :		/* Grayscale */
        bpp        = 1;
Sven Neumann's avatar
Sven Neumann committed
516
517
        image_type = GIMP_GRAY;
        layer_type = GIMP_GRAY_IMAGE;
Elliot Lee's avatar
Elliot Lee committed
518
519
520
521
        break;

    case PNG_COLOR_TYPE_GRAY_ALPHA :	/* Grayscale + alpha */
        bpp        = 2;
Sven Neumann's avatar
Sven Neumann committed
522
523
        image_type = GIMP_GRAY;
        layer_type = GIMP_GRAYA_IMAGE;
Elliot Lee's avatar
Elliot Lee committed
524
525
526
527
        break;

    case PNG_COLOR_TYPE_PALETTE :	/* Indexed */
        bpp        = 1;
Sven Neumann's avatar
Sven Neumann committed
528
529
        image_type = GIMP_INDEXED;
        layer_type = GIMP_INDEXED_IMAGE;
Elliot Lee's avatar
Elliot Lee committed
530
        break;
531
532
533
    default:				/* Aie! Unknown type */
        g_message (_("%s\nPNG unknown color model"), filename);
        return -1;
Elliot Lee's avatar
Elliot Lee committed
534
535
536
537
538
  };

  image = gimp_image_new(info->width, info->height, image_type);
  if (image == -1)
  {
539
    g_message("Can't allocate new image\n%s", filename);
Elliot Lee's avatar
Elliot Lee committed
540
541
542
    gimp_quit();
  };

543
544
545
546
547
 /*
  * Create the "background" layer to hold the image...
  */

  layer = gimp_layer_new(image, _("Background"), info->width, info->height,
Sven Neumann's avatar
Sven Neumann committed
548
                         layer_type, 100, GIMP_NORMAL_MODE);
549
550
  gimp_image_add_layer(image, layer, 0);

551
552
  /*
   * Find out everything we can about the image resolution
553
554
   * This is only practical with the new 1.0 APIs, I'm afraid
   * due to a bug in libpng-1.0.6, see png-implement for details
555
556
   */

557
#if PNG_LIBPNG_VER > 99
558
559
560
561
562
563
564
565
  if (png_get_valid(pp, info, PNG_INFO_gAMA)) {
    /* I sure would like to handle this, but there's no mechanism to
       do so in Gimp :( */
  }
  if (png_get_valid(pp, info, PNG_INFO_oFFs)) {
    gimp_layer_set_offsets (layer, png_get_x_offset_pixels(pp, info),
                                   png_get_y_offset_pixels(pp, info));
  }
566
567
568
569
570
571
  if (png_get_valid(pp, info, PNG_INFO_pHYs)) {
    gimp_image_set_resolution(image,
         ((double) png_get_x_pixels_per_meter(pp, info)) * 0.0254,
         ((double) png_get_y_pixels_per_meter(pp, info)) * 0.0254);
  }
#endif /* PNG_LIBPNG_VER > 99 */
572

Elliot Lee's avatar
Elliot Lee committed
573
574
575
576
577
578
  gimp_image_set_filename(image, filename);

 /*
  * Load the colormap as necessary...
  */

579
580
  empty= 0; /* by default assume no full transparent palette entries */

581
582
  if (info->color_type & PNG_COLOR_MASK_PALETTE) {

583
584
#if PNG_LIBPNG_VER > 99
    if (png_get_valid(pp, info, PNG_INFO_tRNS)) {
585
586
587
588
589
590
591
592
      for (empty= 0; empty < 256 && alpha[empty] == 0; ++empty);
        /* Calculates number of fully transparent "empty" entries */

      gimp_image_set_cmap(image, (guchar *) (info->palette + empty),
                          info->num_palette - empty);
    } else {
      gimp_image_set_cmap(image, (guchar *)info->palette, info->num_palette);
    }
593
594
595
596
#else
    gimp_image_set_cmap(image, (guchar *)info->palette, info->num_palette);
#endif /* PNG_LIBPNG_VER > 99 */

597
  }
Elliot Lee's avatar
Elliot Lee committed
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634

 /*
  * Get the drawable and set the pixel region for our load...
  */

  drawable = gimp_drawable_get(layer);

  gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width,
                      drawable->height, TRUE, FALSE);

 /*
  * Temporary buffer...
  */

  tile_height = gimp_tile_height ();
  pixel       = g_new(guchar, tile_height * info->width * bpp);
  pixels      = g_new(guchar *, tile_height);

  for (i = 0; i < tile_height; i ++)
    pixels[i] = pixel + info->width * info->channels * i;

  for (pass = 0; pass < num_passes; pass ++)
  {
   /*
    * This works if you are only reading one row at a time...
    */

    for (begin = 0, end = tile_height;
         begin < info->height;
         begin += tile_height, end += tile_height)
    {
      if (end > info->height)
        end = info->height;

      num = end - begin;
	
      if (pass != 0) /* to handle interlaced PiNGs */
635
636
        gimp_pixel_rgn_get_rect(&pixel_rgn, pixel, 0, begin,
                                drawable->width, num);
Elliot Lee's avatar
Elliot Lee committed
637
638
639

      png_read_rows(pp, pixels, NULL, num);

640
641
      gimp_pixel_rgn_set_rect(&pixel_rgn, pixel, 0, begin,
                              drawable->width, num);
Elliot Lee's avatar
Elliot Lee committed
642
643
644
645
646
647
648
649
650
651
652
653
654

      gimp_progress_update(((double)pass + (double)end / (double)info->height) /
                           (double)num_passes);
    };
  };

 /*
  * Done with the file...
  */

  png_read_end(pp, info);
  png_read_destroy(pp, info, NULL);

655
656
  g_free(pixel);
  g_free(pixels);
Elliot Lee's avatar
Elliot Lee committed
657
658
659
660
661
  free(pp);
  free(info);

  fclose(fp);

662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
  if (trns) {
    gimp_layer_add_alpha(layer);
    drawable = gimp_drawable_get(layer);
    gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width,
                        drawable->height, TRUE, FALSE);

    pixel  = g_new(guchar, tile_height * drawable->width * 2); /* bpp == 1 */

    for (begin = 0, end = tile_height;
         begin < drawable->height;
         begin += tile_height, end += tile_height)
    {
      if (end > drawable->height) end = drawable->height;
      num= end - begin;

      gimp_pixel_rgn_get_rect(&pixel_rgn, pixel, 0, begin,
                                drawable->width, num);

      for (i= 0; i < tile_height * drawable->width; ++i) {
        pixel[i*2+1]= alpha [ pixel[i*2] ];
682
        pixel[i*2]-= empty;
683
684
685
686
687
688
689
690
      }

      gimp_pixel_rgn_set_rect(&pixel_rgn, pixel, 0, begin,
                              drawable->width, num);
    }
    g_free(pixel);
  }

Elliot Lee's avatar
Elliot Lee committed
691
692
693
694
695
696
697
698
699
700
701
702
 /*
  * Update the display...
  */

  gimp_drawable_flush(drawable);
  gimp_drawable_detach(drawable);

  return (image);
}


/*
Sven Neumann's avatar
Sven Neumann committed
703
 * 'save_image ()' - Save the specified image to a PNG file.
Elliot Lee's avatar
Elliot Lee committed
704
705
706
 */

static gint
Michael Natterer's avatar
Michael Natterer committed
707
save_image (gchar  *filename,	        /* I - File to save to */
Sven Neumann's avatar
Sven Neumann committed
708
709
710
	    gint32  image_ID,	        /* I - Image to save */
	    gint32  drawable_ID,	/* I - Current drawable */
	    gint32  orig_image_ID)      /* I - Original image before export */
Elliot Lee's avatar
Elliot Lee committed
711
{
712
  int		i, k,		/* Looping vars */
713
		bpp = 0,	/* Bytes per pixel */
Elliot Lee's avatar
Elliot Lee committed
714
715
716
717
718
719
720
721
		type,		/* Type of drawable/layer */
		num_passes,	/* Number of interlace passes in file */
		pass,		/* Current pass in file */
		tile_height,	/* Height of tile in GIMP */
		begin,		/* Beginning tile row */
		end,		/* Ending tile row */
		num;		/* Number of rows to load */
  FILE		*fp;		/* File pointer */
Sven Neumann's avatar
Sven Neumann committed
722
723
  GimpDrawable	*drawable;	/* Drawable for layer */
  GimpPixelRgn	pixel_rgn;	/* Pixel region for layer */
Elliot Lee's avatar
Elliot Lee committed
724
725
  png_structp	pp;		/* PNG read pointer */
  png_infop	info;		/* PNG info pointer */
726
  gint		num_colors;	/* Number of colors in colormap */
727
  gint		offx, offy;	/* Drawable offsets from origin */
Elliot Lee's avatar
Elliot Lee committed
728
  guchar	**pixels,	/* Pixel rows */
729
		*fixed,		/* Fixed-up pixel data */
Elliot Lee's avatar
Elliot Lee committed
730
		*pixel;		/* Pixel data */
731
  gchar		*progress;	/* Title for progress display... */
732
  gdouble       xres, yres;	/* GIMP resolution (dpi) */
733
734
735
736
737
  gdouble	gamma;          /* GIMP gamma e.g. 2.20 */
  png_color_16  background;     /* Background color */
  png_time      mod_time;       /* Modification time (ie NOW) */
  guchar	red, green,
                blue;           /* Used for palette background */
738
739
  time_t	cutime;         /* Time since epoch */
  struct tm	*gmt;		/* GMT broken down */
Elliot Lee's avatar
Elliot Lee committed
740
741

 /*
742
743
  * PNG 0.89 and newer have a sane, forwards compatible constructor.
  * Some SGI IRIX users will not have a new enough version though
Elliot Lee's avatar
Elliot Lee committed
744
745
746
  */

#if PNG_LIBPNG_VER > 88
747
  pp   = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
748
  info = png_create_info_struct(pp);
Elliot Lee's avatar
Elliot Lee committed
749
750
#else
  pp = (png_structp)calloc(sizeof(png_struct), 1);
751
  png_write_init(pp);
Elliot Lee's avatar
Elliot Lee committed
752
753
754
755

  info = (png_infop)calloc(sizeof(png_info), 1);
#endif /* PNG_LIBPNG_VER > 88 */

756
757
  if (setjmp (pp->jmpbuf))
  {
758
    g_message (_("%s\nPNG error. Couldn't save image"), filename);
759
760
761
    return 0;
  }

Elliot Lee's avatar
Elliot Lee committed
762
 /*
763
  * Open the file and initialize the PNG write "engine"...
Elliot Lee's avatar
Elliot Lee committed
764
765
  */

766
  fp = fopen(filename, "wb");
767
768
769
770
  if (fp == NULL) {
    g_message (_("%s\nCouldn't create file"), filename);
    return 0;
  }
Elliot Lee's avatar
Elliot Lee committed
771
772
773
774

  png_init_io(pp, fp);

  if (strrchr(filename, '/') != NULL)
775
    progress = g_strdup_printf (_("Saving %s:"), strrchr(filename, '/') + 1);
Elliot Lee's avatar
Elliot Lee committed
776
  else
777
    progress = g_strdup_printf (_("Saving %s:"), filename);
Elliot Lee's avatar
Elliot Lee committed
778
779

  gimp_progress_init(progress);
780
  g_free (progress);
Elliot Lee's avatar
Elliot Lee committed
781
782
783
784
785

 /*
  * Get the drawable for the current image...
  */

Sven Neumann's avatar
Sven Neumann committed
786
787
  drawable = gimp_drawable_get (drawable_ID);
  type     = gimp_drawable_type (drawable_ID);
Elliot Lee's avatar
Elliot Lee committed
788
789

 /*
790
  * Set the image dimensions, bit depth, interlacing and compression
Elliot Lee's avatar
Elliot Lee committed
791
792
  */

Sven Neumann's avatar
Sven Neumann committed
793
  png_set_compression_level (pp, pngvals.compression_level);
Elliot Lee's avatar
Elliot Lee committed
794
795
796
797
798

  info->width          = drawable->width;
  info->height         = drawable->height;
  info->bit_depth      = 8;
  info->interlace_type = pngvals.interlaced;
799

800
801
802
 /*
  * Set color type and remember bytes per pixel count 
  */
803

Elliot Lee's avatar
Elliot Lee committed
804
805
  switch (type)
  {
Sven Neumann's avatar
Sven Neumann committed
806
    case GIMP_RGB_IMAGE :
Elliot Lee's avatar
Elliot Lee committed
807
808
809
        info->color_type = PNG_COLOR_TYPE_RGB;
        bpp              = 3;
        break;
Sven Neumann's avatar
Sven Neumann committed
810
    case GIMP_RGBA_IMAGE :
Elliot Lee's avatar
Elliot Lee committed
811
812
813
        info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
        bpp              = 4;
        break;
Sven Neumann's avatar
Sven Neumann committed
814
    case GIMP_GRAY_IMAGE :
Elliot Lee's avatar
Elliot Lee committed
815
816
817
        info->color_type = PNG_COLOR_TYPE_GRAY;
        bpp              = 1;
        break;
Sven Neumann's avatar
Sven Neumann committed
818
    case GIMP_GRAYA_IMAGE :
Elliot Lee's avatar
Elliot Lee committed
819
820
821
        info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
        bpp              = 2;
        break;
Sven Neumann's avatar
Sven Neumann committed
822
    case GIMP_INDEXED_IMAGE :
823
	bpp		 = 1;
Elliot Lee's avatar
Elliot Lee committed
824
        info->color_type = PNG_COLOR_TYPE_PALETTE;
825
826
	info->valid      |= PNG_INFO_PLTE;
        info->palette= (png_colorp) gimp_image_get_cmap(image_ID, &num_colors);
827
        info->num_palette= num_colors;
Elliot Lee's avatar
Elliot Lee committed
828
        break;
Sven Neumann's avatar
Sven Neumann committed
829
    case GIMP_INDEXEDA_IMAGE :
830
831
	bpp		 = 2;
	info->color_type = PNG_COLOR_TYPE_PALETTE;
832
	respin_cmap (pp, info, image_ID); /* fix up transparency */
833
	break;
Marc Lehmann's avatar
Marc Lehmann committed
834
    default:
835
836
        g_message ("%s\nImage type can't be saved as PNG", filename);
        return 0;
Elliot Lee's avatar
Elliot Lee committed
837
838
  };

839
840
841
 /*
  * Fix bit depths for (possibly) smaller colormap images
  */
842
843
844
845
846
847
848
849
850
851
  
  if (info->valid & PNG_INFO_PLTE) {
    if (info->num_palette <= 2)
      info->bit_depth= 1;
    else if (info->num_palette <= 4)
      info->bit_depth= 2;
    else if (info->num_palette <= 16)
      info->bit_depth= 4;
    /* otherwise the default is fine */
  }
852

853
854
855
856
  /* All this stuff is optional extras, if the user is aiming for smallest
     possible file size she can turn them all off */

#if PNG_LIBPNG_VER > 99
857
858
859
860
861
862
863
864
865
866
  if (pngvals.bkgd) {
    gimp_palette_get_background(&red, &green, &blue);
      
    background.index = 0;
    background.red = red;
    background.green = green;
    background.blue = blue;
    background.gray = (red + green + blue) / 3;
    png_set_bKGD(pp, info, &background);
  }
867

868
  if (pngvals.gama) {
869
870
    gamma = gimp_gamma();
    png_set_gAMA(pp, info, 1.0 / (gamma != 1.00 ? gamma : DEFAULT_GAMMA));
871
  }
872

873
  if (pngvals.offs) {
874
875
876
877
    gimp_drawable_offsets(drawable_ID, &offx, &offy);
    if (offx != 0 || offy != 0) {
      png_set_oFFs(pp, info, offx, offy, PNG_OFFSET_PIXEL);
    }
878
  }
879

880
881
882
883
884
885
  if (pngvals.phys) {
    gimp_image_get_resolution (orig_image_ID, &xres, &yres);
    png_set_pHYs(pp, info, xres * 39.37, yres * 39.37, PNG_RESOLUTION_METER);
  }

  if (pngvals.time) {
886
887
888
889
890
891
892
893
894
895
    cutime= time(NULL); /* time right NOW */
    gmt = gmtime(&cutime);

    mod_time.year = gmt->tm_year + 1900;
    mod_time.month = gmt->tm_mon + 1;
    mod_time.day = gmt->tm_mday;
    mod_time.hour = gmt->tm_hour;
    mod_time.minute = gmt->tm_min;
    mod_time.second = gmt->tm_sec;
    png_set_tIME(pp, info, &mod_time);
896
  }
897

898
#endif /* PNG_LIBPNG_VER > 99 */
899

Sven Neumann's avatar
Sven Neumann committed
900
  png_write_info (pp, info);
Elliot Lee's avatar
Elliot Lee committed
901
902
903
904
905
906
907
908

 /*
  * Turn on interlace handling...
  */

  if (pngvals.interlaced)
    num_passes = png_set_interlace_handling(pp);
  else
909
910
911
912
913
914
915
916
    num_passes = 1;

 /*
  * Convert unpacked pixels to packed if necessary
  */

  if (info->color_type == PNG_COLOR_TYPE_PALETTE && info->bit_depth < 8)
    png_set_packing(pp);
Elliot Lee's avatar
Elliot Lee committed
917
918
919
920
921
922
923
924
925
926
927
928

 /*
  * Allocate memory for "tile_height" rows and save the image...
  */

  tile_height = gimp_tile_height();
  pixel       = g_new(guchar, tile_height * drawable->width * bpp);
  pixels      = g_new(guchar *, tile_height);

  for (i = 0; i < tile_height; i ++)
    pixels[i]= pixel + drawable->width * bpp * i;

929
930
931
  gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width,
                      drawable->height, FALSE, FALSE);

Elliot Lee's avatar
Elliot Lee committed
932
933
  for (pass = 0; pass < num_passes; pass ++)
  {
934
      /* This works if you are only writing one row at a time... */
Elliot Lee's avatar
Elliot Lee committed
935
936
937
    for (begin = 0, end = tile_height;
         begin < drawable->height;
         begin += tile_height, end += tile_height)
Sven Neumann's avatar
Sven Neumann committed
938
939
940
      {
	if (end > drawable->height)
	  end = drawable->height;
Elliot Lee's avatar
Elliot Lee committed
941

Sven Neumann's avatar
Sven Neumann committed
942
943
944
	num = end - begin;
	
	gimp_pixel_rgn_get_rect (&pixel_rgn, pixel, 0, begin, drawable->width, num);
945
        if (info->valid & PNG_INFO_tRNS) {
946
947
948
949
950
951
          for (i = 0; i < num; ++i) {
	    fixed= pixels[i];
            for (k = 0; k < drawable->width; ++k) {
              fixed[k] = (fixed[k*2+1] > 127) ? fixed[k*2] + 1 : 0;
            }
          }
952
953
954
955
956
957
958
959
       /* Forgot this case before, what if there are too many colors? */
        } else if (info->valid & PNG_INFO_PLTE && bpp == 2) {
          for (i = 0; i < num; ++i) {
	    fixed= pixels[i];
            for (k = 0; k < drawable->width; ++k) {
              fixed[k] = fixed[k*2];
            }
          }
960
        }
Sven Neumann's avatar
Sven Neumann committed
961
962
963
	
	png_write_rows (pp, pixels, num);
	
964
965
	gimp_progress_update (((double)pass + (double)end /
                    (double)info->height) / (double)num_passes);
Sven Neumann's avatar
Sven Neumann committed
966
      };
Elliot Lee's avatar
Elliot Lee committed
967
968
  };

Sven Neumann's avatar
Sven Neumann committed
969
970
  png_write_end (pp, info);
  png_write_destroy (pp);
Elliot Lee's avatar
Elliot Lee committed
971

Sven Neumann's avatar
Sven Neumann committed
972
973
  g_free (pixel);
  g_free (pixels);
Elliot Lee's avatar
Elliot Lee committed
974
975
976
977
978

 /*
  * Done with the file...
  */

Sven Neumann's avatar
Sven Neumann committed
979
980
  free (pp);
  free (info);
Elliot Lee's avatar
Elliot Lee committed
981

Sven Neumann's avatar
Sven Neumann committed
982
  fclose (fp);
Elliot Lee's avatar
Elliot Lee committed
983
984
985
986
987

  return (1);
}

static void
Michael Natterer's avatar
Michael Natterer committed
988
989
save_ok_callback (GtkWidget *widget,
		  gpointer  data)
Elliot Lee's avatar
Elliot Lee committed
990
991
992
{
  runme = TRUE;

Michael Natterer's avatar
Michael Natterer committed
993
  gtk_widget_destroy (GTK_WIDGET (data));
Elliot Lee's avatar
Elliot Lee committed
994
995
}

996
997
998
999
/* respin_cmap actually reverse fills the colormap, so that empty entries
   are left at the FRONT, this is only needed to reduce the size of the
   tRNS chunk when saving GIF-like transparent images with colormaps */

1000
static void respin_cmap (png_structp pp, png_infop info, gint32 image_ID) {
Nick Lamb /GIMP's avatar
Nick Lamb /GIMP committed
1001
  static const guchar trans[] = { 0 };
1002
  static guchar after[3 * 256];
1003
  gint colors;
1004
1005
  guchar *before;

1006
1007
1008
1009
1010
  before= gimp_image_get_cmap(image_ID, &colors);

#if PNG_LIBPNG_VER > 99
  if (colors < 256) { /* spare space in palette :) */
    memcpy(after + 3, before, colors * 3);
1011

1012
1013
1014
    /* Apps with no natural background will use this instead, see
       elsewhere for the bKGD chunk being written to use index 0 */
    gimp_palette_get_background(after+0, after+1, after+2);
1015
1016
1017
1018

    /* One transparent palette entry, alpha == 0 */
    png_set_tRNS(pp, info, (png_bytep) trans, 1, NULL);
    png_set_PLTE(pp, info, (png_colorp) after, colors + 1);
1019
  } else {
1020
    png_set_PLTE(pp, info, (png_colorp) before, colors);
1021
  }
1022
1023
1024
1025
1026
#else
  info->valid	  |= PNG_INFO_PLTE;
  info->palette=     (png_colorp) before;
  info->num_palette= colors;
#endif /* PNG_LIBPNG_VER > 99 */
1027
1028
1029

}

Elliot Lee's avatar
Elliot Lee committed
1030
static gint
Sven Neumann's avatar
Sven Neumann committed
1031
save_dialog (void)
Elliot Lee's avatar
Elliot Lee committed
1032
{
Michael Natterer's avatar
Michael Natterer committed
1033
1034
1035
1036
1037
1038
  GtkWidget *dlg;
  GtkWidget *frame;
  GtkWidget *table;
  GtkWidget *toggle;
  GtkWidget *scale;
  GtkObject *scale_data;
Elliot Lee's avatar
Elliot Lee committed
1039

1040
  dlg = gimp_dialog_new (_("Save as PNG"), "png",
1041
			 gimp_standard_help_func, "filters/png.html",
1042
1043
1044
1045
1046
1047
1048
1049
1050
			 GTK_WIN_POS_MOUSE,
			 FALSE, TRUE, FALSE,

			 _("OK"), save_ok_callback,
			 NULL, NULL, NULL, TRUE, FALSE,
			 _("Cancel"), gtk_widget_destroy,
			 NULL, 1, NULL, FALSE, TRUE,

			 NULL);
1051

1052
1053
1054
  gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
		      GTK_SIGNAL_FUNC (gtk_main_quit),
		      NULL);
Elliot Lee's avatar
Elliot Lee committed
1055

1056
1057
  frame = gtk_frame_new (_("Parameter Settings"));
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
1058
  gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
1059
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
Michael Natterer's avatar
Michael Natterer committed
1060
  gtk_widget_show (frame);
Elliot Lee's avatar
Elliot Lee committed
1061

1062
  table = gtk_table_new (2, 7, FALSE);
1063
1064
  gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
Michael Natterer's avatar
Michael Natterer committed
1065
1066
1067
1068
1069
1070
1071
1072
  gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  gtk_container_add (GTK_CONTAINER (frame), table);
  gtk_widget_show (table);

  toggle = gtk_check_button_new_with_label (_("Interlacing (Adam7)"));
  gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 0, 1,
		    GTK_FILL, 0, 0, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
1073
1074
		      GTK_SIGNAL_FUNC (gimp_toggle_button_update),
		      &pngvals.interlaced);
Michael Natterer's avatar
Michael Natterer committed
1075
1076
1077
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.interlaced);
  gtk_widget_show (toggle);

1078
  toggle = gtk_check_button_new_with_label (_("Save background color"));
Michael Natterer's avatar
Michael Natterer committed
1079
1080
1081
  gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 1, 2,
		    GTK_FILL, 0, 0, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
1082
		      GTK_SIGNAL_FUNC (gimp_toggle_button_update),
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
		      &pngvals.bkgd);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.bkgd);
  gtk_widget_show (toggle);

  toggle = gtk_check_button_new_with_label (_("Save gamma"));
  gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 2, 3,
		    GTK_FILL, 0, 0, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      GTK_SIGNAL_FUNC (gimp_toggle_button_update),
		      &pngvals.gama);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.gama);
  gtk_widget_show (toggle);

  toggle = gtk_check_button_new_with_label (_("Save layer offset"));
  gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 3, 4,
		    GTK_FILL, 0, 0, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      GTK_SIGNAL_FUNC (gimp_toggle_button_update),
		      &pngvals.offs);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.offs);
  gtk_widget_show (toggle);

  toggle = gtk_check_button_new_with_label (_("Save resolution"));
  gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 4, 5,
		    GTK_FILL, 0, 0, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      GTK_SIGNAL_FUNC (gimp_toggle_button_update),
		      &pngvals.phys);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.phys);
  gtk_widget_show (toggle);

  toggle = gtk_check_button_new_with_label (_("Save creation time"));
  gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 5, 6,
		    GTK_FILL, 0, 0, 0);
  gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
		      GTK_SIGNAL_FUNC (gimp_toggle_button_update),
		      &pngvals.time);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.time);
Michael Natterer's avatar
Michael Natterer committed
1121
  gtk_widget_show (toggle);
1122

Michael Natterer's avatar
Michael Natterer committed
1123
1124
1125
1126
  scale_data = gtk_adjustment_new (pngvals.compression_level,
				   1.0, 9.0, 1.0, 1.0, 0.0);
  scale      = gtk_hscale_new (GTK_ADJUSTMENT (scale_data));
  gtk_widget_set_usize (scale, SCALE_WIDTH, 0);
Elliot Lee's avatar
Elliot Lee committed
1127
  gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
Michael Natterer's avatar
Michael Natterer committed
1128
1129
  gtk_scale_set_digits (GTK_SCALE (scale), 0);
  gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
1130
  gimp_table_attach_aligned (GTK_TABLE (table), 0, 6,
1131
			     _("Compression Level:"), 1.0, 1.0,
1132
			     scale, 1, FALSE);
Michael Natterer's avatar
Michael Natterer committed
1133
  gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
1134
1135
		      GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
		      &pngvals.compression_level);
Michael Natterer's avatar
Michael Natterer committed
1136
1137
1138
1139
1140
1141
1142
1143
  gtk_widget_show (scale);

  gtk_widget_show (dlg);

  gtk_main ();
  gdk_flush ();

  return runme;
Elliot Lee's avatar