1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * Portable Network Graphics (PNG) plug-in
5 *
6 * Copyright 1997-1998 Michael Sweet (mike@easysw.com) and
7 * Daniel Skarda (0rfelyus@atrey.karlin.mff.cuni.cz).
8 * and 1999-2000 Nick Lamb (njl195@zepler.org.uk)
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 *
23 * Contents:
24 *
25 * main() - Main entry - just call gimp_main()...
26 * query() - Respond to a plug-in query...
27 * run() - Run the plug-in...
28 * load_image() - Load a PNG image into a new image window.
29 * offsets_dialog() - Asks the user about offsets when loading.
30 * respin_cmap() - Re-order a Gimp colormap for PNG tRNS
31 * save_image() - Export the specified image to a PNG file.
32 * save_compression_callback() - Update the image compression level.
33 * save_interlace_update() - Update the interlacing option.
34 * save_dialog() - Pop up the export dialog.
35 *
36 * Revision History:
37 *
38 * see ChangeLog
39 */
40
41 #include "config.h"
42
43 #include <stdlib.h>
44 #include <errno.h>
45
46 #include <glib/gstdio.h>
47
48 #include <libgimp/gimp.h>
49 #include <libgimp/gimpui.h>
50
51 #include <png.h> /* PNG library definitions */
52
53 #include "libgimp/stdplugins-intl.h"
54
55
56 /*
57 * Constants...
58 */
59
60 #define LOAD_PROC "file-png-load"
61 #define SAVE_PROC "file-png-save"
62 #define SAVE2_PROC "file-png-save2"
63 #define SAVE_DEFAULTS_PROC "file-png-save-defaults"
64 #define GET_DEFAULTS_PROC "file-png-get-defaults"
65 #define SET_DEFAULTS_PROC "file-png-set-defaults"
66 #define PLUG_IN_BINARY "file-png"
67 #define PLUG_IN_ROLE "gimp-file-png"
68
69 #define PLUG_IN_VERSION "1.3.4 - 03 September 2002"
70 #define SCALE_WIDTH 125
71
72 #define DEFAULT_GAMMA 2.20
73
74 #define PNG_DEFAULTS_PARASITE "png-save-defaults"
75
76 /*
77 * Structures...
78 */
79
80 typedef enum _PngExportformat {
81 PNG_FORMAT_AUTO = 0,
82 PNG_FORMAT_RGB8,
83 PNG_FORMAT_GRAY8,
84 PNG_FORMAT_RGBA8,
85 PNG_FORMAT_GRAYA8,
86 PNG_FORMAT_RGB16,
87 PNG_FORMAT_GRAY16,
88 PNG_FORMAT_RGBA16,
89 PNG_FORMAT_GRAYA16
90 } PngExportFormat;
91
92 typedef struct
93 {
94 gboolean interlaced;
95 gboolean bkgd;
96 gboolean gama;
97 gboolean offs;
98 gboolean phys;
99 gboolean time;
100 gboolean comment;
101 gboolean save_transp_pixels;
102 gint compression_level;
103 gboolean save_exif;
104 gboolean save_xmp;
105 gboolean save_iptc;
106 gboolean save_thumbnail;
107 gboolean save_profile;
108 PngExportFormat export_format;
109 }
110 PngSaveVals;
111
112 typedef struct
113 {
114 gboolean run;
115
116 GtkWidget *interlaced;
117 GtkWidget *bkgd;
118 GtkWidget *gama;
119 GtkWidget *offs;
120 GtkWidget *phys;
121 GtkWidget *time;
122 GtkWidget *comment;
123 GtkWidget *pixelformat;
124 GtkWidget *save_transp_pixels;
125 GtkAdjustment *compression_level;
126 GtkWidget *save_exif;
127 GtkWidget *save_xmp;
128 GtkWidget *save_iptc;
129 GtkWidget *save_thumbnail;
130 GtkWidget *save_profile;
131 }
132 PngSaveGui;
133
134 /* These are not saved or restored. */
135 typedef struct
136 {
137 gboolean has_trns;
138 png_bytep trans;
139 int num_trans;
140 gboolean has_plte;
141 png_colorp palette;
142 int num_palette;
143 }
144 PngGlobals;
145
146
147 /*
148 * Local functions...
149 */
150
151 static void query (void);
152 static void run (const gchar *name,
153 gint nparams,
154 const GimpParam *param,
155 gint *nreturn_vals,
156 GimpParam **return_vals);
157
158 static gint32 load_image (const gchar *filename,
159 gboolean interactive,
160 gboolean *resolution_loaded,
161 gboolean *profile_loaded,
162 GError **error);
163 static gboolean save_image (const gchar *filename,
164 gint32 image_ID,
165 gint32 drawable_ID,
166 gint32 orig_image_ID,
167 GError **error);
168
169 static int respin_cmap (png_structp pp,
170 png_infop info,
171 guchar *remap,
172 gint32 image_ID,
173 gint32 drawable_ID);
174
175 static gboolean save_dialog (gint32 image_ID,
176 gboolean alpha);
177
178 static void save_dialog_response (GtkWidget *widget,
179 gint response_id,
180 gpointer data);
181
182 static gboolean offsets_dialog (gint offset_x,
183 gint offset_y);
184
185 static gboolean ia_has_transparent_pixels (GeglBuffer *buffer);
186
187 static gint find_unused_ia_color (GeglBuffer *buffer,
188 gint *colors);
189
190 static void load_parasite (void);
191 static void save_parasite (void);
192 static void load_gui_defaults (PngSaveGui *pg);
193
194
195 /*
196 * Globals...
197 */
198
199 const GimpPlugInInfo PLUG_IN_INFO =
200 {
201 NULL,
202 NULL,
203 query,
204 run
205 };
206
207 static const PngSaveVals defaults =
208 {
209 FALSE,
210 TRUE,
211 FALSE,
212 FALSE,
213 TRUE,
214 TRUE,
215 TRUE,
216 FALSE,
217 9,
218 FALSE, /* save exif */
219 FALSE, /* save xmp */
220 FALSE, /* save iptc */
221 TRUE, /* save thumbnail */
222 PNG_FORMAT_AUTO
223 };
224
225 static PngSaveVals pngvals;
226 static PngGlobals pngg;
227
228
229 /*
230 * 'main()' - Main entry - just call gimp_main()...
231 */
232
MAIN()233 MAIN ()
234
235
236 /*
237 * 'query()' - Respond to a plug-in query...
238 */
239
240 static void
241 query (void)
242 {
243 static const GimpParamDef load_args[] =
244 {
245 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
246 { GIMP_PDB_STRING, "filename", "The name of the file to load" },
247 { GIMP_PDB_STRING, "raw-filename", "The name of the file to load" }
248 };
249 static const GimpParamDef load_return_vals[] =
250 {
251 { GIMP_PDB_IMAGE, "image", "Output image" }
252 };
253
254 #define COMMON_SAVE_ARGS \
255 { GIMP_PDB_INT32, "run-mode", "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" }, \
256 { GIMP_PDB_IMAGE, "image", "Input image" }, \
257 { GIMP_PDB_DRAWABLE, "drawable", "Drawable to export" }, \
258 { GIMP_PDB_STRING, "filename", "The name of the file to export the image in"}, \
259 { GIMP_PDB_STRING, "raw-filename", "The name of the file to export the image in"}
260
261 #define OLD_CONFIG_ARGS \
262 { GIMP_PDB_INT32, "interlace", "Use Adam7 interlacing?" }, \
263 { GIMP_PDB_INT32, "compression", "Deflate Compression factor (0--9)" }, \
264 { GIMP_PDB_INT32, "bkgd", "Write bKGD chunk?" }, \
265 { GIMP_PDB_INT32, "gama", "Write gAMA chunk?" }, \
266 { GIMP_PDB_INT32, "offs", "Write oFFs chunk?" }, \
267 { GIMP_PDB_INT32, "phys", "Write pHYs chunk?" }, \
268 { GIMP_PDB_INT32, "time", "Write tIME chunk?" }
269
270 #define FULL_CONFIG_ARGS \
271 OLD_CONFIG_ARGS, \
272 { GIMP_PDB_INT32, "comment", "Write comment?" }, \
273 { GIMP_PDB_INT32, "svtrans", "Preserve color of transparent pixels?" }
274
275 static const GimpParamDef save_args[] =
276 {
277 COMMON_SAVE_ARGS,
278 OLD_CONFIG_ARGS
279 };
280
281 static const GimpParamDef save_args2[] =
282 {
283 COMMON_SAVE_ARGS,
284 FULL_CONFIG_ARGS
285 };
286
287 static const GimpParamDef save_args_defaults[] =
288 {
289 COMMON_SAVE_ARGS
290 };
291
292 static const GimpParamDef save_get_defaults_return_vals[] =
293 {
294 FULL_CONFIG_ARGS
295 };
296
297 static const GimpParamDef save_args_set_defaults[] =
298 {
299 FULL_CONFIG_ARGS
300 };
301
302 gimp_install_procedure (LOAD_PROC,
303 "Loads files in PNG file format",
304 "This plug-in loads Portable Network Graphics "
305 "(PNG) files.",
306 "Michael Sweet <mike@easysw.com>, "
307 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
308 "Michael Sweet <mike@easysw.com>, "
309 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
310 "Nick Lamb <njl195@zepler.org.uk>",
311 PLUG_IN_VERSION,
312 N_("PNG image"),
313 NULL,
314 GIMP_PLUGIN,
315 G_N_ELEMENTS (load_args),
316 G_N_ELEMENTS (load_return_vals),
317 load_args, load_return_vals);
318
319 gimp_register_file_handler_mime (LOAD_PROC, "image/png");
320 gimp_register_magic_load_handler (LOAD_PROC,
321 "png", "", "0,string,\211PNG\r\n\032\n");
322
323 gimp_install_procedure (SAVE_PROC,
324 "Exports files in PNG file format",
325 "This plug-in exports Portable Network Graphics "
326 "(PNG) files.",
327 "Michael Sweet <mike@easysw.com>, "
328 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
329 "Michael Sweet <mike@easysw.com>, "
330 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
331 "Nick Lamb <njl195@zepler.org.uk>",
332 PLUG_IN_VERSION,
333 N_("PNG image"),
334 "RGB*,GRAY*,INDEXED*",
335 GIMP_PLUGIN,
336 G_N_ELEMENTS (save_args), 0,
337 save_args, NULL);
338
339 gimp_install_procedure (SAVE2_PROC,
340 "Exports files in PNG file format",
341 "This plug-in exports Portable Network Graphics "
342 "(PNG) files. "
343 "This procedure adds 2 extra parameters to "
344 "file-png-save that control whether "
345 "image comments are saved and whether transparent "
346 "pixels are saved or nullified.",
347 "Michael Sweet <mike@easysw.com>, "
348 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
349 "Michael Sweet <mike@easysw.com>, "
350 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
351 "Nick Lamb <njl195@zepler.org.uk>",
352 PLUG_IN_VERSION,
353 N_("PNG image"),
354 "RGB*,GRAY*,INDEXED*",
355 GIMP_PLUGIN,
356 G_N_ELEMENTS (save_args2), 0,
357 save_args2, NULL);
358
359 gimp_install_procedure (SAVE_DEFAULTS_PROC,
360 "Exports files in PNG file format",
361 "This plug-in exports Portable Network Graphics (PNG) "
362 "files, using the default settings stored as "
363 "a parasite.",
364 "Michael Sweet <mike@easysw.com>, "
365 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
366 "Michael Sweet <mike@easysw.com>, "
367 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
368 "Nick Lamb <njl195@zepler.org.uk>",
369 PLUG_IN_VERSION,
370 N_("PNG image"),
371 "RGB*,GRAY*,INDEXED*",
372 GIMP_PLUGIN,
373 G_N_ELEMENTS (save_args_defaults), 0,
374 save_args_defaults, NULL);
375
376 gimp_register_file_handler_mime (SAVE_DEFAULTS_PROC, "image/png");
377 gimp_register_save_handler (SAVE_DEFAULTS_PROC, "png", "");
378
379 gimp_install_procedure (GET_DEFAULTS_PROC,
380 "Get the current set of defaults used by the "
381 "PNG file export plug-in",
382 "This procedure returns the current set of "
383 "defaults stored as a parasite for the PNG "
384 "export plug-in. "
385 "These defaults are used to seed the UI, by the "
386 "file_png_save_defaults procedure, and by "
387 "gimp_file_save when it detects to use PNG.",
388 "Michael Sweet <mike@easysw.com>, "
389 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
390 "Michael Sweet <mike@easysw.com>, "
391 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
392 "Nick Lamb <njl195@zepler.org.uk>",
393 PLUG_IN_VERSION,
394 NULL,
395 NULL,
396 GIMP_PLUGIN,
397 0, G_N_ELEMENTS (save_get_defaults_return_vals),
398 NULL, save_get_defaults_return_vals);
399
400 gimp_install_procedure (SET_DEFAULTS_PROC,
401 "Set the current set of defaults used by the "
402 "PNG file export plug-in",
403 "This procedure set the current set of defaults "
404 "stored as a parasite for the PNG export plug-in. "
405 "These defaults are used to seed the UI, by the "
406 "file_png_save_defaults procedure, and by "
407 "gimp_file_save when it detects to use PNG.",
408 "Michael Sweet <mike@easysw.com>, "
409 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
410 "Michael Sweet <mike@easysw.com>, "
411 "Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, "
412 "Nick Lamb <njl195@zepler.org.uk>",
413 PLUG_IN_VERSION,
414 NULL,
415 NULL,
416 GIMP_PLUGIN,
417 G_N_ELEMENTS (save_args_set_defaults), 0,
418 save_args_set_defaults, NULL);
419 }
420
421
422 /*
423 * 'run()' - Run the plug-in...
424 */
425
426 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)427 run (const gchar *name,
428 gint nparams,
429 const GimpParam *param,
430 gint *nreturn_vals,
431 GimpParam **return_vals)
432 {
433 static GimpParam values[10];
434 GimpRunMode run_mode = GIMP_RUN_NONINTERACTIVE;
435 GimpPDBStatusType status = GIMP_PDB_SUCCESS;
436 gint32 image_ID;
437 gint32 drawable_ID;
438 GError *error = NULL;
439
440 if (nparams)
441 run_mode = param[0].data.d_int32;
442
443 INIT_I18N ();
444 gegl_init (NULL, NULL);
445
446 *nreturn_vals = 1;
447 *return_vals = values;
448
449 values[0].type = GIMP_PDB_STATUS;
450 values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
451
452 if (strcmp (name, LOAD_PROC) == 0)
453 {
454 gboolean interactive;
455 gboolean resolution_loaded = FALSE;
456 gboolean profile_loaded = FALSE;
457
458 switch (run_mode)
459 {
460 case GIMP_RUN_INTERACTIVE:
461 case GIMP_RUN_WITH_LAST_VALS:
462 gimp_ui_init (PLUG_IN_BINARY, FALSE);
463 interactive = TRUE;
464 break;
465 default:
466 interactive = FALSE;
467 break;
468 }
469
470 image_ID = load_image (param[1].data.d_string,
471 interactive,
472 &resolution_loaded,
473 &profile_loaded,
474 &error);
475
476 if (image_ID != -1)
477 {
478 GFile *file = g_file_new_for_path (param[1].data.d_string);
479 GimpMetadata *metadata;
480
481 metadata = gimp_image_metadata_load_prepare (image_ID, "image/png",
482 file, NULL);
483
484 if (metadata)
485 {
486 GimpMetadataLoadFlags flags = GIMP_METADATA_LOAD_ALL;
487
488 if (resolution_loaded)
489 flags &= ~GIMP_METADATA_LOAD_RESOLUTION;
490
491 if (profile_loaded)
492 flags &= ~GIMP_METADATA_LOAD_COLORSPACE;
493
494 gimp_image_metadata_load_finish (image_ID, "image/png",
495 metadata, flags,
496 interactive);
497
498 g_object_unref (metadata);
499 }
500
501 g_object_unref (file);
502
503 *nreturn_vals = 2;
504 values[1].type = GIMP_PDB_IMAGE;
505 values[1].data.d_image = image_ID;
506 }
507 else
508 {
509 status = GIMP_PDB_EXECUTION_ERROR;
510 }
511 }
512 else if (strcmp (name, SAVE_PROC) == 0 ||
513 strcmp (name, SAVE2_PROC) == 0 ||
514 strcmp (name, SAVE_DEFAULTS_PROC) == 0)
515 {
516 GimpMetadata *metadata;
517 GimpMetadataSaveFlags metadata_flags;
518 gint32 orig_image_ID;
519 GimpExportReturn export = GIMP_EXPORT_CANCEL;
520 gboolean alpha;
521
522 image_ID = param[1].data.d_int32;
523 drawable_ID = param[2].data.d_int32;
524
525 orig_image_ID = image_ID;
526
527 switch (run_mode)
528 {
529 case GIMP_RUN_INTERACTIVE:
530 case GIMP_RUN_WITH_LAST_VALS:
531 gimp_ui_init (PLUG_IN_BINARY, FALSE);
532
533 export = gimp_export_image (&image_ID, &drawable_ID, "PNG",
534 GIMP_EXPORT_CAN_HANDLE_RGB |
535 GIMP_EXPORT_CAN_HANDLE_GRAY |
536 GIMP_EXPORT_CAN_HANDLE_INDEXED |
537 GIMP_EXPORT_CAN_HANDLE_ALPHA);
538
539 if (export == GIMP_EXPORT_CANCEL)
540 {
541 *nreturn_vals = 1;
542 values[0].data.d_status = GIMP_PDB_CANCEL;
543 return;
544 }
545 break;
546
547 default:
548 break;
549 }
550
551 /* Initialize with hardcoded defaults */
552 pngvals = defaults;
553
554 /* Override the defaults with preferences. */
555 metadata = gimp_image_metadata_save_prepare (orig_image_ID,
556 "image/png",
557 &metadata_flags);
558 pngvals.save_exif = (metadata_flags & GIMP_METADATA_SAVE_EXIF) != 0;
559 pngvals.save_xmp = (metadata_flags & GIMP_METADATA_SAVE_XMP) != 0;
560 pngvals.save_iptc = (metadata_flags & GIMP_METADATA_SAVE_IPTC) != 0;
561 pngvals.save_thumbnail = (metadata_flags & GIMP_METADATA_SAVE_THUMBNAIL) != 0;
562 pngvals.save_profile = (metadata_flags & GIMP_METADATA_SAVE_COLOR_PROFILE) != 0;
563
564 /* Override preferences from PNG export defaults (if saved). */
565 load_parasite ();
566
567 switch (run_mode)
568 {
569 case GIMP_RUN_INTERACTIVE:
570 /* Finally possibly retrieve data from previous run. */
571 gimp_get_data (SAVE_PROC, &pngvals);
572
573 alpha = gimp_drawable_has_alpha (drawable_ID);
574
575 /* If the image has no transparency, then there is usually
576 * no need to save a bKGD chunk. For more information, see:
577 * http://bugzilla.gnome.org/show_bug.cgi?id=92395
578 */
579 if (! alpha)
580 pngvals.bkgd = FALSE;
581
582 /* Then acquire information with a dialog...
583 */
584 if (! save_dialog (orig_image_ID, alpha))
585 status = GIMP_PDB_CANCEL;
586 break;
587
588 case GIMP_RUN_NONINTERACTIVE:
589 /*
590 * Make sure all the arguments are there!
591 */
592 if (nparams != 5)
593 {
594 if (nparams != 12 && nparams != 14)
595 {
596 status = GIMP_PDB_CALLING_ERROR;
597 }
598 else
599 {
600 pngvals.interlaced = param[5].data.d_int32;
601 pngvals.compression_level = param[6].data.d_int32;
602 pngvals.bkgd = param[7].data.d_int32;
603 pngvals.gama = param[8].data.d_int32;
604 pngvals.offs = param[9].data.d_int32;
605 pngvals.phys = param[10].data.d_int32;
606 pngvals.time = param[11].data.d_int32;
607
608 if (nparams == 14)
609 {
610 pngvals.comment = param[12].data.d_int32;
611 pngvals.save_transp_pixels = param[13].data.d_int32;
612 }
613 else
614 {
615 pngvals.comment = TRUE;
616 pngvals.save_transp_pixels = TRUE;
617 }
618
619 if (pngvals.compression_level < 0 ||
620 pngvals.compression_level > 9)
621 {
622 status = GIMP_PDB_CALLING_ERROR;
623 }
624 }
625 }
626 break;
627
628 case GIMP_RUN_WITH_LAST_VALS:
629 /* possibly retrieve data */
630 gimp_get_data (SAVE_PROC, &pngvals);
631 break;
632
633 default:
634 break;
635 }
636
637 if (status == GIMP_PDB_SUCCESS)
638 {
639 if (save_image (param[3].data.d_string,
640 image_ID, drawable_ID, orig_image_ID, &error))
641 {
642 if (metadata)
643 {
644 GFile *file;
645
646 gimp_metadata_set_bits_per_sample (metadata, 8);
647
648 if (pngvals.save_exif)
649 metadata_flags |= GIMP_METADATA_SAVE_EXIF;
650 else
651 metadata_flags &= ~GIMP_METADATA_SAVE_EXIF;
652
653 if (pngvals.save_xmp)
654 metadata_flags |= GIMP_METADATA_SAVE_XMP;
655 else
656 metadata_flags &= ~GIMP_METADATA_SAVE_XMP;
657
658 if (pngvals.save_iptc)
659 metadata_flags |= GIMP_METADATA_SAVE_IPTC;
660 else
661 metadata_flags &= ~GIMP_METADATA_SAVE_IPTC;
662
663 if (pngvals.save_thumbnail)
664 metadata_flags |= GIMP_METADATA_SAVE_THUMBNAIL;
665 else
666 metadata_flags &= ~GIMP_METADATA_SAVE_THUMBNAIL;
667
668 if (pngvals.save_profile)
669 metadata_flags |= GIMP_METADATA_SAVE_COLOR_PROFILE;
670 else
671 metadata_flags &= ~GIMP_METADATA_SAVE_COLOR_PROFILE;
672
673 file = g_file_new_for_path (param[3].data.d_string);
674 gimp_image_metadata_save_finish (orig_image_ID,
675 "image/png",
676 metadata, metadata_flags,
677 file, NULL);
678 g_object_unref (file);
679 }
680
681 gimp_set_data (SAVE_PROC, &pngvals, sizeof (pngvals));
682 }
683 else
684 {
685 status = GIMP_PDB_EXECUTION_ERROR;
686 }
687 }
688
689 if (export == GIMP_EXPORT_EXPORT)
690 gimp_image_delete (image_ID);
691
692 if (metadata)
693 g_object_unref (metadata);
694 }
695 else if (strcmp (name, GET_DEFAULTS_PROC) == 0)
696 {
697 pngvals = defaults;
698 load_parasite ();
699
700 *nreturn_vals = 10;
701
702 #define SET_VALUE(index, field) G_STMT_START { \
703 values[(index)].type = GIMP_PDB_INT32; \
704 values[(index)].data.d_int32 = pngvals.field; \
705 } G_STMT_END
706
707 SET_VALUE (1, interlaced);
708 SET_VALUE (2, compression_level);
709 SET_VALUE (3, bkgd);
710 SET_VALUE (4, gama);
711 SET_VALUE (5, offs);
712 SET_VALUE (6, phys);
713 SET_VALUE (7, time);
714 SET_VALUE (8, comment);
715 SET_VALUE (9, save_transp_pixels);
716
717 #undef SET_VALUE
718 }
719 else if (strcmp (name, SET_DEFAULTS_PROC) == 0)
720 {
721 if (nparams == 9)
722 {
723 pngvals = defaults;
724 load_parasite ();
725
726 pngvals.interlaced = param[0].data.d_int32;
727 pngvals.compression_level = param[1].data.d_int32;
728 pngvals.bkgd = param[2].data.d_int32;
729 pngvals.gama = param[3].data.d_int32;
730 pngvals.offs = param[4].data.d_int32;
731 pngvals.phys = param[5].data.d_int32;
732 pngvals.time = param[6].data.d_int32;
733 pngvals.comment = param[7].data.d_int32;
734 pngvals.save_transp_pixels = param[8].data.d_int32;
735
736 save_parasite ();
737 }
738 else
739 {
740 status = GIMP_PDB_CALLING_ERROR;
741 }
742 }
743 else
744 {
745 status = GIMP_PDB_CALLING_ERROR;
746 }
747
748 if (status != GIMP_PDB_SUCCESS && error)
749 {
750 *nreturn_vals = 2;
751 values[1].type = GIMP_PDB_STRING;
752 values[1].data.d_string = error->message;
753 }
754
755 values[0].data.d_status = status;
756 }
757
758
759 struct read_error_data
760 {
761 guchar *pixel; /* Pixel data */
762 GeglBuffer *buffer; /* GEGL buffer for layer */
763 const Babl *file_format;
764 guint32 width; /* png_infop->width */
765 guint32 height; /* png_infop->height */
766 gint bpp; /* Bytes per pixel */
767 gint tile_height; /* Height of tile in GIMP */
768 gint begin; /* Beginning tile row */
769 gint end; /* Ending tile row */
770 gint num; /* Number of rows to load */
771 };
772
773 static void
on_read_error(png_structp png_ptr,png_const_charp error_msg)774 on_read_error (png_structp png_ptr,
775 png_const_charp error_msg)
776 {
777 struct read_error_data *error_data = png_get_error_ptr (png_ptr);
778 gint begin;
779 gint end;
780 gint num;
781
782 g_printerr (_("Error loading PNG file: %s\n"), error_msg);
783
784 /* Flush the current half-read row of tiles */
785
786 gegl_buffer_set (error_data->buffer,
787 GEGL_RECTANGLE (0, error_data->begin,
788 error_data->width,
789 error_data->num),
790 0,
791 error_data->file_format,
792 error_data->pixel,
793 GEGL_AUTO_ROWSTRIDE);
794
795 begin = error_data->begin + error_data->tile_height;
796
797 if (begin < error_data->height)
798 {
799 end = MIN (error_data->end + error_data->tile_height, error_data->height);
800 num = end - begin;
801
802 gegl_buffer_clear (error_data->buffer,
803 GEGL_RECTANGLE (0, begin, error_data->width, num));
804 }
805
806 g_object_unref (error_data->buffer);
807 longjmp (png_jmpbuf (png_ptr), 1);
808 }
809
810 static int
get_bit_depth_for_palette(int num_palette)811 get_bit_depth_for_palette (int num_palette)
812 {
813 if (num_palette <= 2)
814 return 1;
815 else if (num_palette <= 4)
816 return 2;
817 else if (num_palette <= 16)
818 return 4;
819 else
820 return 8;
821 }
822
823 static GimpColorProfile *
load_color_profile(png_structp pp,png_infop info,gchar ** profile_name)824 load_color_profile (png_structp pp,
825 png_infop info,
826 gchar **profile_name)
827 {
828 GimpColorProfile *profile = NULL;
829
830 #if defined(PNG_iCCP_SUPPORTED)
831 png_uint_32 proflen;
832 png_charp profname;
833 png_bytep prof;
834 int profcomp;
835
836 if (png_get_iCCP (pp, info, &profname, &profcomp, &prof, &proflen))
837 {
838 profile = gimp_color_profile_new_from_icc_profile ((guint8 *) prof,
839 proflen, NULL);
840 if (profile && profname)
841 {
842 *profile_name = g_convert (profname, strlen (profname),
843 "ISO-8859-1", "UTF-8", NULL, NULL, NULL);
844 }
845 }
846 #endif
847
848 return profile;
849 }
850
851 /*
852 * 'load_image()' - Load a PNG image into a new image window.
853 */
854 static gint32
load_image(const gchar * filename,gboolean interactive,gboolean * resolution_loaded,gboolean * profile_loaded,GError ** error)855 load_image (const gchar *filename,
856 gboolean interactive,
857 gboolean *resolution_loaded,
858 gboolean *profile_loaded,
859 GError **error)
860 {
861 gint i; /* Looping var */
862 gint trns; /* Transparency present */
863 gint bpp; /* Bytes per pixel */
864 gint width; /* image width */
865 gint height; /* image height */
866 gint num_passes; /* Number of interlace passes in file */
867 gint pass; /* Current pass in file */
868 gint tile_height; /* Height of tile in GIMP */
869 gint begin; /* Beginning tile row */
870 gint end; /* Ending tile row */
871 gint num; /* Number of rows to load */
872 GimpImageBaseType image_type; /* Type of image */
873 GimpPrecision image_precision; /* Precision of image */
874 GimpImageType layer_type; /* Type of drawable/layer */
875 GimpColorProfile *profile = NULL; /* Color profile */
876 gchar *profile_name = NULL; /* Profile's name */
877 gboolean linear = FALSE; /* Linear RGB */
878 FILE *fp; /* File pointer */
879 volatile gint32 image = -1; /* Image -- protected for setjmp() */
880 gint32 layer; /* Layer */
881 GeglBuffer *buffer; /* GEGL buffer for layer */
882 const Babl *file_format; /* BABL format for layer */
883 png_structp pp; /* PNG read pointer */
884 png_infop info; /* PNG info pointers */
885 guchar **pixels; /* Pixel rows */
886 guchar *pixel; /* Pixel data */
887 guchar alpha[256]; /* Index -> Alpha */
888 png_textp text;
889 gint num_texts;
890 struct read_error_data error_data;
891
892 pp = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
893 if (! pp)
894 {
895 /* this could happen if the compile time and run-time libpng
896 versions do not match. */
897
898 g_set_error (error, 0, 0,
899 _("Error creating PNG read struct while loading '%s'."),
900 gimp_filename_to_utf8 (filename));
901 return -1;
902 }
903
904 info = png_create_info_struct (pp);
905 if (! info)
906 {
907 g_set_error (error, 0, 0,
908 _("Error while reading '%s'. Could not create PNG header info structure."),
909 gimp_filename_to_utf8 (filename));
910 return -1;
911 }
912
913 if (setjmp (png_jmpbuf (pp)))
914 {
915 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
916 _("Error while reading '%s'. File corrupted?"),
917 gimp_filename_to_utf8 (filename));
918 return image;
919 }
920
921 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
922 /* Change some libpng errors to warnings (e.g. bug 721135) */
923 png_set_benign_errors (pp, TRUE);
924
925 /* bug 765850 */
926 png_set_option (pp, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
927 #endif
928
929 /*
930 * Open the file and initialize the PNG read "engine"...
931 */
932
933 gimp_progress_init_printf (_("Opening '%s'"),
934 gimp_filename_to_utf8 (filename));
935
936 fp = g_fopen (filename, "rb");
937
938 if (fp == NULL)
939 {
940 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
941 _("Could not open '%s' for reading: %s"),
942 gimp_filename_to_utf8 (filename), g_strerror (errno));
943 return -1;
944 }
945
946 png_init_io (pp, fp);
947 png_set_compression_buffer_size (pp, 512);
948
949 /*
950 * Get the image info
951 */
952
953 png_read_info (pp, info);
954
955 if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
956 png_set_swap (pp);
957
958 /*
959 * Get the iCCP (color profile) chunk, if any, and figure if it's
960 * a linear RGB profile
961 */
962 profile = load_color_profile (pp, info, &profile_name);
963
964 if (profile)
965 {
966 *profile_loaded = TRUE;
967
968 linear = gimp_color_profile_is_linear (profile);
969 }
970
971 /*
972 * Get image precision and color model
973 */
974
975 if (png_get_bit_depth (pp, info) == 16)
976 {
977 if (linear)
978 image_precision = GIMP_PRECISION_U16_LINEAR;
979 else
980 image_precision = GIMP_PRECISION_U16_GAMMA;
981 }
982 else
983 {
984 if (linear)
985 image_precision = GIMP_PRECISION_U8_LINEAR;
986 else
987 image_precision = GIMP_PRECISION_U8_GAMMA;
988 }
989
990 if (png_get_bit_depth (pp, info) < 8)
991 {
992 if (png_get_color_type (pp, info) == PNG_COLOR_TYPE_GRAY)
993 png_set_expand (pp);
994
995 if (png_get_color_type (pp, info) == PNG_COLOR_TYPE_PALETTE)
996 png_set_packing (pp);
997 }
998
999 /*
1000 * Expand G+tRNS to GA, RGB+tRNS to RGBA
1001 */
1002
1003 if (png_get_color_type (pp, info) != PNG_COLOR_TYPE_PALETTE &&
1004 png_get_valid (pp, info, PNG_INFO_tRNS))
1005 png_set_expand (pp);
1006
1007 /*
1008 * Turn on interlace handling... libpng returns just 1 (ie single pass)
1009 * if the image is not interlaced
1010 */
1011
1012 num_passes = png_set_interlace_handling (pp);
1013
1014 /*
1015 * Special handling for INDEXED + tRNS (transparency palette)
1016 */
1017
1018 if (png_get_valid (pp, info, PNG_INFO_tRNS) &&
1019 png_get_color_type (pp, info) == PNG_COLOR_TYPE_PALETTE)
1020 {
1021 guchar *alpha_ptr;
1022
1023 png_get_tRNS (pp, info, &alpha_ptr, &num, NULL);
1024
1025 /* Copy the existing alpha values from the tRNS chunk */
1026 for (i = 0; i < num; ++i)
1027 alpha[i] = alpha_ptr[i];
1028
1029 /* And set any others to fully opaque (255) */
1030 for (i = num; i < 256; ++i)
1031 alpha[i] = 255;
1032
1033 trns = 1;
1034 }
1035 else
1036 {
1037 trns = 0;
1038 }
1039
1040 /*
1041 * Update the info structures after the transformations take effect
1042 */
1043
1044 png_read_update_info (pp, info);
1045
1046 switch (png_get_color_type (pp, info))
1047 {
1048 case PNG_COLOR_TYPE_RGB:
1049 image_type = GIMP_RGB;
1050 layer_type = GIMP_RGB_IMAGE;
1051 break;
1052
1053 case PNG_COLOR_TYPE_RGB_ALPHA:
1054 image_type = GIMP_RGB;
1055 layer_type = GIMP_RGBA_IMAGE;
1056 break;
1057
1058 case PNG_COLOR_TYPE_GRAY:
1059 image_type = GIMP_GRAY;
1060 layer_type = GIMP_GRAY_IMAGE;
1061 break;
1062
1063 case PNG_COLOR_TYPE_GRAY_ALPHA:
1064 image_type = GIMP_GRAY;
1065 layer_type = GIMP_GRAYA_IMAGE;
1066 break;
1067
1068 case PNG_COLOR_TYPE_PALETTE:
1069 image_type = GIMP_INDEXED;
1070 layer_type = GIMP_INDEXED_IMAGE;
1071 break;
1072
1073 default:
1074 g_set_error (error, 0, 0,
1075 _("Unknown color model in PNG file '%s'."),
1076 gimp_filename_to_utf8 (filename));
1077 return -1;
1078 }
1079
1080 width = png_get_image_width (pp, info);
1081 height = png_get_image_height (pp, info);
1082
1083 image = gimp_image_new_with_precision (width, height,
1084 image_type, image_precision);
1085 if (image == -1)
1086 {
1087 g_set_error (error, 0, 0,
1088 _("Could not create new image for '%s': %s"),
1089 gimp_filename_to_utf8 (filename), gimp_get_pdb_error ());
1090 return -1;
1091 }
1092
1093 /*
1094 * Create the "background" layer to hold the image...
1095 */
1096
1097 layer = gimp_layer_new (image, _("Background"), width, height,
1098 layer_type,
1099 100,
1100 gimp_image_get_default_new_layer_mode (image));
1101 gimp_image_insert_layer (image, layer, -1, 0);
1102
1103 file_format = gimp_drawable_get_format (layer);
1104
1105 /*
1106 * Find out everything we can about the image resolution
1107 * This is only practical with the new 1.0 APIs, I'm afraid
1108 * due to a bug in libpng-1.0.6, see png-implement for details
1109 */
1110
1111 if (png_get_valid (pp, info, PNG_INFO_gAMA))
1112 {
1113 GimpParasite *parasite;
1114 gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
1115 gdouble gamma;
1116
1117 png_get_gAMA (pp, info, &gamma);
1118
1119 g_ascii_dtostr (buf, sizeof (buf), gamma);
1120
1121 parasite = gimp_parasite_new ("gamma",
1122 GIMP_PARASITE_PERSISTENT,
1123 strlen (buf) + 1, buf);
1124 gimp_image_attach_parasite (image, parasite);
1125 gimp_parasite_free (parasite);
1126 }
1127
1128 if (png_get_valid (pp, info, PNG_INFO_oFFs))
1129 {
1130 gint offset_x = png_get_x_offset_pixels (pp, info);
1131 gint offset_y = png_get_y_offset_pixels (pp, info);
1132
1133 if (offset_x != 0 ||
1134 offset_y != 0)
1135 {
1136 if (! interactive)
1137 {
1138 gimp_layer_set_offsets (layer, offset_x, offset_y);
1139 }
1140 else if (offsets_dialog (offset_x, offset_y))
1141 {
1142 gimp_layer_set_offsets (layer, offset_x, offset_y);
1143
1144 if (abs (offset_x) > width ||
1145 abs (offset_y) > height)
1146 {
1147 g_message (_("The PNG file specifies an offset that caused "
1148 "the layer to be positioned outside the image."));
1149 }
1150 }
1151 }
1152 }
1153
1154 if (png_get_valid (pp, info, PNG_INFO_pHYs))
1155 {
1156 png_uint_32 xres;
1157 png_uint_32 yres;
1158 gint unit_type;
1159
1160 if (png_get_pHYs (pp, info,
1161 &xres, &yres, &unit_type) && xres > 0 && yres > 0)
1162 {
1163 switch (unit_type)
1164 {
1165 case PNG_RESOLUTION_UNKNOWN:
1166 {
1167 gdouble image_xres, image_yres;
1168
1169 gimp_image_get_resolution (image, &image_xres, &image_yres);
1170
1171 if (xres > yres)
1172 image_xres = image_yres * (gdouble) xres / (gdouble) yres;
1173 else
1174 image_yres = image_xres * (gdouble) yres / (gdouble) xres;
1175
1176 gimp_image_set_resolution (image, image_xres, image_yres);
1177
1178 *resolution_loaded = TRUE;
1179 }
1180 break;
1181
1182 case PNG_RESOLUTION_METER:
1183 gimp_image_set_resolution (image,
1184 (gdouble) xres * 0.0254,
1185 (gdouble) yres * 0.0254);
1186 gimp_image_set_unit (image, GIMP_UNIT_MM);
1187
1188 *resolution_loaded = TRUE;
1189 break;
1190
1191 default:
1192 break;
1193 }
1194 }
1195
1196 }
1197
1198 gimp_image_set_filename (image, filename);
1199
1200 /*
1201 * Load the colormap as necessary...
1202 */
1203
1204 if (png_get_color_type (pp, info) & PNG_COLOR_MASK_PALETTE)
1205 {
1206 png_colorp palette;
1207 int num_palette;
1208
1209 png_get_PLTE (pp, info, &palette, &num_palette);
1210 gimp_image_set_colormap (image, (guchar *) palette,
1211 num_palette);
1212 }
1213
1214 bpp = babl_format_get_bytes_per_pixel (file_format);
1215
1216 buffer = gimp_drawable_get_buffer (layer);
1217
1218 /*
1219 * Temporary buffer...
1220 */
1221
1222 tile_height = gimp_tile_height ();
1223 pixel = g_new0 (guchar, tile_height * width * bpp);
1224 pixels = g_new (guchar *, tile_height);
1225
1226 for (i = 0; i < tile_height; i++)
1227 pixels[i] = pixel + width * bpp * i;
1228
1229 /* Install our own error handler to handle incomplete PNG files better */
1230 error_data.buffer = buffer;
1231 error_data.pixel = pixel;
1232 error_data.file_format = file_format;
1233 error_data.tile_height = tile_height;
1234 error_data.width = width;
1235 error_data.height = height;
1236 error_data.bpp = bpp;
1237
1238 png_set_error_fn (pp, &error_data, on_read_error, NULL);
1239
1240 for (pass = 0; pass < num_passes; pass++)
1241 {
1242 /*
1243 * This works if you are only reading one row at a time...
1244 */
1245
1246 for (begin = 0; begin < height; begin += tile_height)
1247 {
1248 end = MIN (begin + tile_height, height);
1249 num = end - begin;
1250
1251 if (pass != 0) /* to handle interlaced PiNGs */
1252 gegl_buffer_get (buffer,
1253 GEGL_RECTANGLE (0, begin, width, num),
1254 1.0,
1255 file_format,
1256 pixel,
1257 GEGL_AUTO_ROWSTRIDE,
1258 GEGL_ABYSS_NONE);
1259
1260 error_data.begin = begin;
1261 error_data.end = end;
1262 error_data.num = num;
1263
1264 png_read_rows (pp, pixels, NULL, num);
1265
1266 gegl_buffer_set (buffer,
1267 GEGL_RECTANGLE (0, begin, width, num),
1268 0,
1269 file_format,
1270 pixel,
1271 GEGL_AUTO_ROWSTRIDE);
1272
1273 gimp_progress_update
1274 (((gdouble) pass +
1275 (gdouble) end / (gdouble) height) /
1276 (gdouble) num_passes);
1277 }
1278 }
1279
1280 png_read_end (pp, info);
1281
1282 /* Switch back to default error handler */
1283 png_set_error_fn (pp, NULL, NULL, NULL);
1284
1285 if (png_get_text (pp, info, &text, &num_texts))
1286 {
1287 gchar *comment = NULL;
1288
1289 for (i = 0; i < num_texts && !comment; i++, text++)
1290 {
1291 if (text->key == NULL || strcmp (text->key, "Comment"))
1292 continue;
1293
1294 if (text->text_length > 0) /* tEXt */
1295 {
1296 comment = g_convert (text->text, -1,
1297 "UTF-8", "ISO-8859-1",
1298 NULL, NULL, NULL);
1299 }
1300 else if (g_utf8_validate (text->text, -1, NULL))
1301 { /* iTXt */
1302 comment = g_strdup (text->text);
1303 }
1304 }
1305
1306 if (comment && *comment)
1307 {
1308 GimpParasite *parasite;
1309
1310 parasite = gimp_parasite_new ("gimp-comment",
1311 GIMP_PARASITE_PERSISTENT,
1312 strlen (comment) + 1, comment);
1313 gimp_image_attach_parasite (image, parasite);
1314 gimp_parasite_free (parasite);
1315 }
1316
1317 g_free (comment);
1318 }
1319
1320 /*
1321 * Attach the color profile, if any
1322 */
1323
1324 if (profile)
1325 {
1326 gimp_image_set_color_profile (image, profile);
1327 g_object_unref (profile);
1328
1329 if (profile_name)
1330 {
1331 GimpParasite *parasite;
1332
1333 parasite = gimp_parasite_new ("icc-profile-name",
1334 GIMP_PARASITE_PERSISTENT |
1335 GIMP_PARASITE_UNDOABLE,
1336 strlen (profile_name),
1337 profile_name);
1338 gimp_image_attach_parasite (image, parasite);
1339 gimp_parasite_free (parasite);
1340
1341 g_free (profile_name);
1342 }
1343 }
1344
1345 /*
1346 * Done with the file...
1347 */
1348
1349 png_destroy_read_struct (&pp, &info, NULL);
1350
1351 g_free (pixel);
1352 g_free (pixels);
1353 g_object_unref (buffer);
1354 free (pp);
1355 free (info);
1356
1357 fclose (fp);
1358
1359 if (trns)
1360 {
1361 GeglBufferIterator *iter;
1362 gint n_components;
1363
1364 gimp_layer_add_alpha (layer);
1365 buffer = gimp_drawable_get_buffer (layer);
1366 file_format = gegl_buffer_get_format (buffer);
1367
1368 iter = gegl_buffer_iterator_new (buffer, NULL, 0, file_format,
1369 GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
1370 n_components = babl_format_get_n_components (file_format);
1371 g_warn_if_fail (n_components == 2);
1372
1373 while (gegl_buffer_iterator_next (iter))
1374 {
1375 guchar *data = iter->items[0].data;
1376 gint length = iter->length;
1377
1378 while (length--)
1379 {
1380 data[1] = alpha[data[0]];
1381
1382 data += n_components;
1383 }
1384 }
1385
1386 g_object_unref (buffer);
1387 }
1388
1389 return image;
1390 }
1391
1392 /*
1393 * 'offsets_dialog ()' - Asks the user about offsets when loading.
1394 */
1395 static gboolean
offsets_dialog(gint offset_x,gint offset_y)1396 offsets_dialog (gint offset_x,
1397 gint offset_y)
1398 {
1399 GtkWidget *dialog;
1400 GtkWidget *hbox;
1401 GtkWidget *image;
1402 GtkWidget *label;
1403 gchar *message;
1404 gboolean run;
1405
1406 gimp_ui_init (PLUG_IN_BINARY, FALSE);
1407
1408 dialog = gimp_dialog_new (_("Apply PNG Offset"), PLUG_IN_ROLE,
1409 NULL, 0,
1410 gimp_standard_help_func, LOAD_PROC,
1411
1412 _("Ignore PNG offset"), GTK_RESPONSE_NO,
1413 _("Apply PNG offset to layer"), GTK_RESPONSE_YES,
1414
1415 NULL);
1416
1417 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1418 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1419 GTK_RESPONSE_YES,
1420 GTK_RESPONSE_NO,
1421 -1);
1422
1423 gimp_window_set_transient (GTK_WINDOW (dialog));
1424 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
1425
1426 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
1427 gtk_container_set_border_width (GTK_CONTAINER (hbox), 12);
1428 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
1429 hbox, FALSE, FALSE, 0);
1430 gtk_widget_show (hbox);
1431
1432 image = gtk_image_new_from_icon_name (GIMP_ICON_DIALOG_QUESTION,
1433 GTK_ICON_SIZE_DIALOG);
1434 gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
1435 gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
1436 gtk_widget_show (image);
1437
1438 message = g_strdup_printf (_("The PNG image you are importing specifies an "
1439 "offset of %d, %d. Do you want to apply "
1440 "this offset to the layer?"),
1441 offset_x, offset_y);
1442 label = gtk_label_new (message);
1443 gtk_label_set_yalign (GTK_LABEL (label), 0.0);
1444 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
1445 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
1446 gtk_widget_show (label);
1447
1448 gtk_widget_show (dialog);
1449
1450 run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_YES);
1451
1452 gtk_widget_destroy (dialog);
1453
1454 return run;
1455 }
1456
1457 /*
1458 * 'save_image ()' - Export the specified image to a PNG file.
1459 */
1460
1461 static gboolean
save_image(const gchar * filename,gint32 image_ID,gint32 drawable_ID,gint32 orig_image_ID,GError ** error)1462 save_image (const gchar *filename,
1463 gint32 image_ID,
1464 gint32 drawable_ID,
1465 gint32 orig_image_ID,
1466 GError **error)
1467 {
1468 gint i, k; /* Looping vars */
1469 gint bpp = 0; /* Bytes per pixel */
1470 gint type; /* Type of drawable/layer */
1471 gint num_passes; /* Number of interlace passes in file */
1472 gint pass; /* Current pass in file */
1473 gint tile_height; /* Height of tile in GIMP */
1474 gint width; /* image width */
1475 gint height; /* image height */
1476 gint begin; /* Beginning tile row */
1477 gint end; /* Ending tile row */
1478 gint num; /* Number of rows to load */
1479 FILE *fp; /* File pointer */
1480 GimpColorProfile *profile = NULL; /* Color profile */
1481 gboolean out_linear; /* Save linear RGB */
1482 GeglBuffer *buffer; /* GEGL buffer for layer */
1483 const Babl *file_format; /* BABL format of file */
1484 png_structp pp; /* PNG read pointer */
1485 png_infop info; /* PNG info pointer */
1486 gint offx, offy; /* Drawable offsets from origin */
1487 guchar **pixels; /* Pixel rows */
1488 guchar *fixed; /* Fixed-up pixel data */
1489 guchar *pixel; /* Pixel data */
1490 gdouble xres, yres; /* GIMP resolution (dpi) */
1491 png_color_16 background; /* Background color */
1492 png_time mod_time; /* Modification time (ie NOW) */
1493 time_t cutime; /* Time since epoch */
1494 struct tm *gmt; /* GMT broken down */
1495 gint color_type; /* PNG color type */
1496 gint bit_depth; /* Default to bit depth 16 */
1497
1498 guchar remap[256]; /* Re-mapping for the palette */
1499
1500 png_textp text = NULL;
1501
1502 out_linear = FALSE;
1503 #if defined(PNG_iCCP_SUPPORTED)
1504 /* If no profile is written: export as sRGB.
1505 * If manually assigned profile written: follow its TRC.
1506 * If default profile written:
1507 * - when export as auto or 16-bit: follow the storage TRC.
1508 * - when export from 8-bit storage: follow the storage TRC.
1509 * - when converting high bit depth to 8-bit: export as sRGB.
1510 */
1511 if (pngvals.save_profile)
1512 {
1513 profile = gimp_image_get_color_profile (orig_image_ID);
1514
1515 if (profile ||
1516 pngvals.export_format == PNG_FORMAT_AUTO ||
1517 pngvals.export_format == PNG_FORMAT_RGB16 ||
1518 pngvals.export_format == PNG_FORMAT_RGBA16 ||
1519 pngvals.export_format == PNG_FORMAT_GRAY16 ||
1520 pngvals.export_format == PNG_FORMAT_GRAYA16 ||
1521 gimp_image_get_precision (image_ID) == GIMP_PRECISION_U8_LINEAR ||
1522 gimp_image_get_precision (image_ID) == GIMP_PRECISION_U8_GAMMA)
1523 {
1524 if (! profile)
1525 profile = gimp_image_get_effective_color_profile (orig_image_ID);
1526 out_linear = (gimp_color_profile_is_linear (profile));
1527 }
1528 else
1529 {
1530 /* When converting higher bit depth work image into 8-bit,
1531 * with no manually assigned profile, make sure the result is
1532 * sRGB.
1533 */
1534 profile = gimp_image_get_effective_color_profile (orig_image_ID);
1535
1536 if (gimp_color_profile_is_linear (profile))
1537 {
1538 GimpColorProfile *saved_profile;
1539
1540 saved_profile = gimp_color_profile_new_srgb_trc_from_color_profile (profile);
1541 g_object_unref (profile);
1542 profile = saved_profile;
1543 }
1544 }
1545 }
1546 #endif
1547
1548 /* We save as 8-bit PNG only if:
1549 * (1) Work image is 8-bit linear with linear profile to be saved.
1550 * (2) Work image is 8-bit non-linear or perceptual with or without
1551 * profile.
1552 */
1553 bit_depth = 16;
1554 switch (gimp_image_get_precision (image_ID))
1555 {
1556 case GIMP_PRECISION_U8_LINEAR:
1557 if (out_linear)
1558 bit_depth = 8;
1559 break;
1560
1561 case GIMP_PRECISION_U8_GAMMA:
1562 if (! out_linear)
1563 bit_depth = 8;
1564 break;
1565
1566 default:
1567 break;
1568 }
1569
1570 pp = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
1571 if (!pp)
1572 {
1573 /* this could happen if the compile time and run-time libpng
1574 * versions do not match.
1575 */
1576 g_set_error (error, 0, 0,
1577 _("Error creating PNG write struct while exporting '%s'."),
1578 gimp_filename_to_utf8 (filename));
1579 return FALSE;
1580 }
1581
1582 info = png_create_info_struct (pp);
1583 if (! info)
1584 {
1585 g_set_error (error, 0, 0,
1586 _("Error while exporting '%s'. Could not create PNG header info structure."),
1587 gimp_filename_to_utf8 (filename));
1588 return FALSE;
1589 }
1590
1591 if (setjmp (png_jmpbuf (pp)))
1592 {
1593 g_set_error (error, 0, 0,
1594 _("Error while exporting '%s'. Could not export image."),
1595 gimp_filename_to_utf8 (filename));
1596 return FALSE;
1597 }
1598
1599 #ifdef PNG_BENIGN_ERRORS_SUPPORTED
1600 /* Change some libpng errors to warnings (e.g. bug 721135) */
1601 png_set_benign_errors (pp, TRUE);
1602
1603 /* bug 765850 */
1604 png_set_option (pp, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
1605 #endif
1606
1607 /*
1608 * Open the file and initialize the PNG write "engine"...
1609 */
1610
1611 gimp_progress_init_printf (_("Exporting '%s'"),
1612 gimp_filename_to_utf8 (filename));
1613
1614 fp = g_fopen (filename, "wb");
1615 if (fp == NULL)
1616 {
1617 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
1618 _("Could not open '%s' for writing: %s"),
1619 gimp_filename_to_utf8 (filename), g_strerror (errno));
1620 return FALSE;
1621 }
1622
1623 png_init_io (pp, fp);
1624
1625 /*
1626 * Get the buffer for the current image...
1627 */
1628
1629 buffer = gimp_drawable_get_buffer (drawable_ID);
1630 width = gegl_buffer_get_width (buffer);
1631 height = gegl_buffer_get_height (buffer);
1632 type = gimp_drawable_type (drawable_ID);
1633
1634 /*
1635 * Initialise remap[]
1636 */
1637 for (i = 0; i < 256; i++)
1638 remap[i] = i;
1639
1640 if (pngvals.export_format == PNG_FORMAT_AUTO)
1641 {
1642 /*
1643 * Set color type and remember bytes per pixel count
1644 */
1645
1646 switch (type)
1647 {
1648 case GIMP_RGB_IMAGE:
1649 color_type = PNG_COLOR_TYPE_RGB;
1650 if (bit_depth == 8)
1651 {
1652 if (out_linear)
1653 file_format = babl_format ("RGB u8");
1654 else
1655 file_format = babl_format ("R'G'B' u8");
1656 }
1657 else
1658 {
1659 if (out_linear)
1660 file_format = babl_format ("RGB u16");
1661 else
1662 file_format = babl_format ("R'G'B' u16");
1663 }
1664 break;
1665
1666 case GIMP_RGBA_IMAGE:
1667 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
1668 if (bit_depth == 8)
1669 {
1670 if (out_linear)
1671 file_format = babl_format ("RGBA u8");
1672 else
1673 file_format = babl_format ("R'G'B'A u8");
1674 }
1675 else
1676 {
1677 if (out_linear)
1678 file_format = babl_format ("RGBA u16");
1679 else
1680 file_format = babl_format ("R'G'B'A u16");
1681 }
1682 break;
1683
1684 case GIMP_GRAY_IMAGE:
1685 color_type = PNG_COLOR_TYPE_GRAY;
1686 if (bit_depth == 8)
1687 {
1688 if (out_linear)
1689 file_format = babl_format ("Y u8");
1690 else
1691 file_format = babl_format ("Y' u8");
1692 }
1693 else
1694 {
1695 if (out_linear)
1696 file_format = babl_format ("Y u16");
1697 else
1698 file_format = babl_format ("Y' u16");
1699 }
1700 break;
1701
1702 case GIMP_GRAYA_IMAGE:
1703 color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
1704 if (bit_depth == 8)
1705 {
1706 if (out_linear)
1707 file_format = babl_format ("YA u8");
1708 else
1709 file_format = babl_format ("Y'A u8");
1710 }
1711 else
1712 {
1713 if (out_linear)
1714 file_format = babl_format ("YA u16");
1715 else
1716 file_format = babl_format ("Y'A u16");
1717 }
1718 break;
1719
1720 case GIMP_INDEXED_IMAGE:
1721 color_type = PNG_COLOR_TYPE_PALETTE;
1722 file_format = gimp_drawable_get_format (drawable_ID);
1723 pngg.has_plte = TRUE;
1724 pngg.palette = (png_colorp) gimp_image_get_colormap (image_ID,
1725 &pngg.num_palette);
1726 bit_depth = get_bit_depth_for_palette (pngg.num_palette);
1727 break;
1728
1729 case GIMP_INDEXEDA_IMAGE:
1730 color_type = PNG_COLOR_TYPE_PALETTE;
1731 file_format = gimp_drawable_get_format (drawable_ID);
1732 /* fix up transparency */
1733 bit_depth = respin_cmap (pp, info, remap, image_ID, drawable_ID);
1734 break;
1735
1736 default:
1737 g_set_error (error, 0, 0, "Image type can't be exported as PNG");
1738 return FALSE;
1739 }
1740 }
1741 else
1742 {
1743 switch (pngvals.export_format)
1744 {
1745 case PNG_FORMAT_RGB8:
1746 color_type = PNG_COLOR_TYPE_RGB;
1747 if (out_linear)
1748 file_format = babl_format ("RGB u8");
1749 else
1750 file_format = babl_format ("R'G'B' u8");
1751 bit_depth = 8;
1752 break;
1753 case PNG_FORMAT_GRAY8:
1754 color_type = PNG_COLOR_TYPE_GRAY;
1755 if (out_linear)
1756 file_format = babl_format ("Y u8");
1757 else
1758 file_format = babl_format ("Y' u8");
1759 bit_depth = 8;
1760 break;
1761 case PNG_FORMAT_RGBA8:
1762 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
1763 if (out_linear)
1764 file_format = babl_format ("RGBA u8");
1765 else
1766 file_format = babl_format ("R'G'B'A u8");
1767 bit_depth = 8;
1768 break;
1769 case PNG_FORMAT_GRAYA8:
1770 color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
1771 if (out_linear)
1772 file_format = babl_format ("YA u8");
1773 else
1774 file_format = babl_format ("Y'A u8");
1775 bit_depth = 8;
1776 break;
1777 case PNG_FORMAT_RGB16:
1778 color_type = PNG_COLOR_TYPE_RGB;
1779 if (out_linear)
1780 file_format = babl_format ("RGB u16");
1781 else
1782 file_format = babl_format ("R'G'B' u16");
1783 bit_depth = 16;
1784 break;
1785 case PNG_FORMAT_GRAY16:
1786 color_type = PNG_COLOR_TYPE_GRAY;
1787 if (out_linear)
1788 file_format = babl_format ("Y u16");
1789 else
1790 file_format = babl_format ("Y' u16");
1791 bit_depth = 16;
1792 break;
1793 case PNG_FORMAT_RGBA16:
1794 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
1795 if (out_linear)
1796 file_format = babl_format ("RGBA u16");
1797 else
1798 file_format = babl_format ("R'G'B'A u16");
1799 bit_depth = 16;
1800 break;
1801 case PNG_FORMAT_GRAYA16:
1802 color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
1803 if (out_linear)
1804 file_format = babl_format ("YA u16");
1805 else
1806 file_format = babl_format ("Y'A u16");
1807 bit_depth = 16;
1808 break;
1809 case PNG_FORMAT_AUTO:
1810 g_return_val_if_reached (FALSE);
1811 }
1812 }
1813
1814 bpp = babl_format_get_bytes_per_pixel (file_format);
1815
1816 /* Note: png_set_IHDR() must be called before any other png_set_*()
1817 functions. */
1818 png_set_IHDR (pp, info, width, height, bit_depth, color_type,
1819 pngvals.interlaced ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
1820 PNG_COMPRESSION_TYPE_BASE,
1821 PNG_FILTER_TYPE_BASE);
1822
1823 if (pngg.has_trns)
1824 png_set_tRNS (pp, info, pngg.trans, pngg.num_trans, NULL);
1825
1826 if (pngg.has_plte)
1827 png_set_PLTE (pp, info, pngg.palette, pngg.num_palette);
1828
1829 /* Set the compression level */
1830
1831 png_set_compression_level (pp, pngvals.compression_level);
1832
1833 /* All this stuff is optional extras, if the user is aiming for smallest
1834 possible file size she can turn them all off */
1835
1836 if (pngvals.bkgd)
1837 {
1838 GimpRGB color;
1839 guchar red, green, blue;
1840
1841 gimp_context_get_background (&color);
1842 gimp_rgb_get_uchar (&color, &red, &green, &blue);
1843
1844 background.index = 0;
1845 background.red = red;
1846 background.green = green;
1847 background.blue = blue;
1848 background.gray = gimp_rgb_luminance_uchar (&color);
1849 png_set_bKGD (pp, info, &background);
1850 }
1851
1852 if (pngvals.gama)
1853 {
1854 GimpParasite *parasite;
1855 gdouble gamma = 1.0 / DEFAULT_GAMMA;
1856
1857 parasite = gimp_image_get_parasite (orig_image_ID, "gamma");
1858 if (parasite)
1859 {
1860 gamma = g_ascii_strtod (gimp_parasite_data (parasite), NULL);
1861 gimp_parasite_free (parasite);
1862 }
1863
1864 png_set_gAMA (pp, info, gamma);
1865 }
1866
1867 if (pngvals.offs)
1868 {
1869 gimp_drawable_offsets (drawable_ID, &offx, &offy);
1870 if (offx != 0 || offy != 0)
1871 png_set_oFFs (pp, info, offx, offy, PNG_OFFSET_PIXEL);
1872 }
1873
1874 if (pngvals.phys)
1875 {
1876 gimp_image_get_resolution (orig_image_ID, &xres, &yres);
1877 png_set_pHYs (pp, info, RINT (xres / 0.0254), RINT (yres / 0.0254),
1878 PNG_RESOLUTION_METER);
1879 }
1880
1881 if (pngvals.time)
1882 {
1883 cutime = time (NULL); /* time right NOW */
1884 gmt = gmtime (&cutime);
1885
1886 mod_time.year = gmt->tm_year + 1900;
1887 mod_time.month = gmt->tm_mon + 1;
1888 mod_time.day = gmt->tm_mday;
1889 mod_time.hour = gmt->tm_hour;
1890 mod_time.minute = gmt->tm_min;
1891 mod_time.second = gmt->tm_sec;
1892 png_set_tIME (pp, info, &mod_time);
1893 }
1894
1895 #if defined(PNG_iCCP_SUPPORTED)
1896 if (pngvals.save_profile)
1897 {
1898 GimpParasite *parasite;
1899 gchar *profile_name = NULL;
1900 const guint8 *icc_data;
1901 gsize icc_length;
1902
1903 icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length);
1904
1905 parasite = gimp_image_get_parasite (orig_image_ID,
1906 "icc-profile-name");
1907 if (parasite)
1908 profile_name = g_convert (gimp_parasite_data (parasite),
1909 gimp_parasite_data_size (parasite),
1910 "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
1911
1912 png_set_iCCP (pp,
1913 info,
1914 profile_name ? profile_name : "ICC profile",
1915 0,
1916 icc_data,
1917 icc_length);
1918
1919 g_free (profile_name);
1920 g_object_unref (profile);
1921 }
1922 #endif
1923
1924 #ifdef PNG_zTXt_SUPPORTED
1925 /* Small texts are not worth compressing and will be even bigger if compressed.
1926 Empirical length limit of a text being worth compressing. */
1927 #define COMPRESSION_WORTHY_LENGTH 200
1928 #endif
1929
1930 if (pngvals.comment)
1931 {
1932 GimpParasite *parasite;
1933 gsize text_length = 0;
1934
1935 parasite = gimp_image_get_parasite (orig_image_ID, "gimp-comment");
1936 if (parasite)
1937 {
1938 gchar *comment = g_strndup (gimp_parasite_data (parasite),
1939 gimp_parasite_data_size (parasite));
1940
1941 gimp_parasite_free (parasite);
1942
1943 if (comment && strlen (comment) > 0)
1944 {
1945 text = g_new0 (png_text, 1);
1946
1947 text[0].key = "Comment";
1948
1949 #ifdef PNG_iTXt_SUPPORTED
1950
1951 text[0].text = g_convert (comment, -1,
1952 "ISO-8859-1",
1953 "UTF-8",
1954 NULL,
1955 &text_length,
1956 NULL);
1957
1958 if (text[0].text == NULL || strlen (text[0].text) == 0)
1959 {
1960 /* We can't convert to ISO-8859-1 without loss.
1961 Save the comment as iTXt (UTF-8). */
1962 g_free (text[0].text);
1963
1964 text[0].text = g_strdup (comment);
1965 text[0].itxt_length = strlen (text[0].text);
1966
1967 #ifdef PNG_zTXt_SUPPORTED
1968 text[0].compression = strlen (text[0].text) > COMPRESSION_WORTHY_LENGTH ?
1969 PNG_ITXT_COMPRESSION_zTXt : PNG_ITXT_COMPRESSION_NONE;
1970 #else
1971 text[0].compression = PNG_ITXT_COMPRESSION_NONE;
1972 #endif /* PNG_zTXt_SUPPORTED */
1973 }
1974 else
1975 /* The comment is ISO-8859-1 compatible, so we use tEXt
1976 even if there is iTXt support for compatibility to more
1977 png reading programs. */
1978 #endif /* PNG_iTXt_SUPPORTED */
1979 {
1980 #ifndef PNG_iTXt_SUPPORTED
1981 /* No iTXt support, so we are forced to use tEXt (ISO-8859-1).
1982 A broken comment is better than no comment at all, so the
1983 conversion does not fail on unknown character.
1984 They are simply ignored. */
1985 text[0].text = g_convert_with_fallback (comment, -1,
1986 "ISO-8859-1",
1987 "UTF-8",
1988 "",
1989 NULL,
1990 &text_length,
1991 NULL);
1992 #endif
1993
1994 #ifdef PNG_zTXt_SUPPORTED
1995 text[0].compression = strlen (text[0].text) > COMPRESSION_WORTHY_LENGTH ?
1996 PNG_TEXT_COMPRESSION_zTXt : PNG_TEXT_COMPRESSION_NONE;
1997 #else
1998 text[0].compression = PNG_TEXT_COMPRESSION_NONE;
1999 #endif /* PNG_zTXt_SUPPORTED */
2000
2001 text[0].text_length = text_length;
2002 }
2003
2004 if (! text[0].text || strlen (text[0].text) == 0)
2005 {
2006 g_free (text[0].text);
2007 g_free (text);
2008 text = NULL;
2009 }
2010
2011 g_free (comment);
2012 }
2013 }
2014 }
2015
2016 #ifdef PNG_zTXt_SUPPORTED
2017 #undef COMPRESSION_WORTHY_LENGTH
2018 #endif
2019
2020 if (text)
2021 png_set_text (pp, info, text, 1);
2022
2023 png_write_info (pp, info);
2024 if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
2025 png_set_swap (pp);
2026
2027 /*
2028 * Turn on interlace handling...
2029 */
2030
2031 if (pngvals.interlaced)
2032 num_passes = png_set_interlace_handling (pp);
2033 else
2034 num_passes = 1;
2035
2036 /*
2037 * Convert unpacked pixels to packed if necessary
2038 */
2039
2040 if (color_type == PNG_COLOR_TYPE_PALETTE &&
2041 bit_depth < 8)
2042 png_set_packing (pp);
2043
2044 /*
2045 * Allocate memory for "tile_height" rows and export the image...
2046 */
2047
2048 tile_height = gimp_tile_height ();
2049 pixel = g_new (guchar, tile_height * width * bpp);
2050 pixels = g_new (guchar *, tile_height);
2051
2052 for (i = 0; i < tile_height; i++)
2053 pixels[i] = pixel + width * bpp * i;
2054
2055 for (pass = 0; pass < num_passes; pass++)
2056 {
2057 /* This works if you are only writing one row at a time... */
2058 for (begin = 0, end = tile_height;
2059 begin < height; begin += tile_height, end += tile_height)
2060 {
2061 if (end > height)
2062 end = height;
2063
2064 num = end - begin;
2065
2066 gegl_buffer_get (buffer,
2067 GEGL_RECTANGLE (0, begin, width, num),
2068 1.0,
2069 file_format,
2070 pixel,
2071 GEGL_AUTO_ROWSTRIDE,
2072 GEGL_ABYSS_NONE);
2073
2074 /* If we are with a RGBA image and have to pre-multiply the
2075 alpha channel */
2076 if (bpp == 4 && ! pngvals.save_transp_pixels)
2077 {
2078 for (i = 0; i < num; ++i)
2079 {
2080 fixed = pixels[i];
2081 for (k = 0; k < width; ++k)
2082 {
2083 if (!fixed[3])
2084 fixed[0] = fixed[1] = fixed[2] = 0;
2085 fixed += bpp;
2086 }
2087 }
2088 }
2089
2090 if (bpp == 8 && ! pngvals.save_transp_pixels)
2091 {
2092 for (i = 0; i < num; ++i)
2093 {
2094 fixed = pixels[i];
2095 for (k = 0; k < width; ++k)
2096 {
2097 if (!fixed[6] && !fixed[7])
2098 fixed[0] = fixed[1] = fixed[2] =
2099 fixed[3] = fixed[4] = fixed[5] = 0;
2100 fixed += bpp;
2101 }
2102 }
2103 }
2104
2105 /* If we're dealing with a paletted image with
2106 * transparency set, write out the remapped palette */
2107 if (png_get_valid (pp, info, PNG_INFO_tRNS))
2108 {
2109 guchar inverse_remap[256];
2110
2111 for (i = 0; i < 256; i++)
2112 inverse_remap[ remap[i] ] = i;
2113
2114 for (i = 0; i < num; ++i)
2115 {
2116 fixed = pixels[i];
2117 for (k = 0; k < width; ++k)
2118 {
2119 fixed[k] = (fixed[k*2+1] > 127) ?
2120 inverse_remap[ fixed[k*2] ] :
2121 0;
2122 }
2123 }
2124 }
2125
2126 /* Otherwise if we have a paletted image and transparency
2127 * couldn't be set, we ignore the alpha channel */
2128 else if (png_get_valid (pp, info, PNG_INFO_PLTE) &&
2129 bpp == 2)
2130 {
2131 for (i = 0; i < num; ++i)
2132 {
2133 fixed = pixels[i];
2134 for (k = 0; k < width; ++k)
2135 {
2136 fixed[k] = fixed[k * 2];
2137 }
2138 }
2139 }
2140
2141 png_write_rows (pp, pixels, num);
2142
2143 gimp_progress_update (((double) pass + (double) end /
2144 (double) height) /
2145 (double) num_passes);
2146 }
2147 }
2148
2149 gimp_progress_update (1.0);
2150
2151 png_write_end (pp, info);
2152 png_destroy_write_struct (&pp, &info);
2153
2154 g_free (pixel);
2155 g_free (pixels);
2156
2157 /*
2158 * Done with the file...
2159 */
2160
2161 if (text)
2162 {
2163 g_free (text[0].text);
2164 g_free (text);
2165 }
2166
2167 free (pp);
2168 free (info);
2169
2170 fclose (fp);
2171
2172 return TRUE;
2173 }
2174
2175 static gboolean
ia_has_transparent_pixels(GeglBuffer * buffer)2176 ia_has_transparent_pixels (GeglBuffer *buffer)
2177 {
2178 GeglBufferIterator *iter;
2179 const Babl *format;
2180 gint n_components;
2181
2182 format = gegl_buffer_get_format (buffer);
2183 iter = gegl_buffer_iterator_new (buffer, NULL, 0, format,
2184 GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
2185 n_components = babl_format_get_n_components (format);
2186 g_return_val_if_fail (n_components == 2, FALSE);
2187
2188 while (gegl_buffer_iterator_next (iter))
2189 {
2190 const guchar *data = iter->items[0].data;
2191 gint length = iter->length;
2192
2193 while (length--)
2194 {
2195 if (data[1] <= 127)
2196 {
2197 gegl_buffer_iterator_stop (iter);
2198 return TRUE;
2199 }
2200
2201 data += n_components;
2202 }
2203 }
2204
2205 return FALSE;
2206 }
2207
2208 /* Try to find a color in the palette which isn't actually
2209 * used in the image, so that we can use it as the transparency
2210 * index. Taken from gif.c */
2211 static gint
find_unused_ia_color(GeglBuffer * buffer,gint * colors)2212 find_unused_ia_color (GeglBuffer *buffer,
2213 gint *colors)
2214 {
2215 GeglBufferIterator *iter;
2216 const Babl *format;
2217 gint n_components;
2218 gboolean ix_used[256];
2219 gboolean trans_used = FALSE;
2220 gint i;
2221
2222 for (i = 0; i < *colors; i++)
2223 ix_used[i] = FALSE;
2224
2225 format = gegl_buffer_get_format (buffer);
2226 iter = gegl_buffer_iterator_new (buffer, NULL, 0, format,
2227 GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
2228 n_components = babl_format_get_n_components (format);
2229 g_return_val_if_fail (n_components == 2, FALSE);
2230
2231 while (gegl_buffer_iterator_next (iter))
2232 {
2233 const guchar *data = iter->items[0].data;
2234 gint length = iter->length;
2235
2236 while (length--)
2237 {
2238 if (data[1] > 127)
2239 ix_used[data[0]] = TRUE;
2240 else
2241 trans_used = TRUE;
2242
2243 data += n_components;
2244 }
2245 }
2246
2247 /* If there is no transparency, ignore alpha. */
2248 if (trans_used == FALSE)
2249 return -1;
2250
2251 /* If there is still some room at the end of the palette, increment
2252 * the number of colors in the image and assign a transparent pixel
2253 * there. */
2254 if ((*colors) < 256)
2255 {
2256 (*colors)++;
2257
2258 return (*colors) - 1;
2259 }
2260
2261 for (i = 0; i < *colors; i++)
2262 {
2263 if (ix_used[i] == FALSE)
2264 return i;
2265 }
2266
2267 return -1;
2268 }
2269
2270
2271 static int
respin_cmap(png_structp pp,png_infop info,guchar * remap,gint32 image_ID,gint32 drawable_ID)2272 respin_cmap (png_structp pp,
2273 png_infop info,
2274 guchar *remap,
2275 gint32 image_ID,
2276 gint32 drawable_ID)
2277 {
2278 static guchar trans[] = { 0 };
2279 GeglBuffer *buffer;
2280
2281 gint colors;
2282 guchar *before;
2283
2284 before = gimp_image_get_colormap (image_ID, &colors);
2285 buffer = gimp_drawable_get_buffer (drawable_ID);
2286
2287 /*
2288 * Make sure there is something in the colormap.
2289 */
2290 if (colors == 0)
2291 {
2292 before = g_newa (guchar, 3);
2293 memset (before, 0, sizeof (guchar) * 3);
2294
2295 colors = 1;
2296 }
2297
2298 /* Try to find an entry which isn't actually used in the
2299 image, for a transparency index. */
2300
2301 if (ia_has_transparent_pixels (buffer))
2302 {
2303 gint transparent = find_unused_ia_color (buffer, &colors);
2304
2305 if (transparent != -1) /* we have a winner for a transparent
2306 * index - do like gif2png and swap
2307 * index 0 and index transparent */
2308 {
2309 static png_color palette[256];
2310 gint i;
2311
2312 /* Set tRNS chunk values for writing later. */
2313 pngg.has_trns = TRUE;
2314 pngg.trans = trans;
2315 pngg.num_trans = 1;
2316
2317 /* Transform all pixels with a value = transparent to
2318 * 0 and vice versa to compensate for re-ordering in palette
2319 * due to png_set_tRNS() */
2320
2321 remap[0] = transparent;
2322 for (i = 1; i <= transparent; i++)
2323 remap[i] = i - 1;
2324
2325 /* Copy from index 0 to index transparent - 1 to index 1 to
2326 * transparent of after, then from transparent+1 to colors-1
2327 * unchanged, and finally from index transparent to index 0. */
2328
2329 for (i = 0; i < colors; i++)
2330 {
2331 palette[i].red = before[3 * remap[i]];
2332 palette[i].green = before[3 * remap[i] + 1];
2333 palette[i].blue = before[3 * remap[i] + 2];
2334 }
2335
2336 /* Set PLTE chunk values for writing later. */
2337 pngg.has_plte = TRUE;
2338 pngg.palette = palette;
2339 pngg.num_palette = colors;
2340 }
2341 else
2342 {
2343 /* Inform the user that we couldn't losslessly save the
2344 * transparency & just use the full palette */
2345 g_message (_("Couldn't losslessly save transparency, "
2346 "saving opacity instead."));
2347
2348 /* Set PLTE chunk values for writing later. */
2349 pngg.has_plte = TRUE;
2350 pngg.palette = (png_colorp) before;
2351 pngg.num_palette = colors;
2352 }
2353 }
2354 else
2355 {
2356 /* Set PLTE chunk values for writing later. */
2357 pngg.has_plte = TRUE;
2358 pngg.palette = (png_colorp) before;
2359 pngg.num_palette = colors;
2360 }
2361
2362 g_object_unref (buffer);
2363
2364 return get_bit_depth_for_palette (colors);
2365 }
2366
2367 static GtkWidget *
toggle_button_init(GtkBuilder * builder,const gchar * name,gboolean initial_value,gboolean * value_pointer)2368 toggle_button_init (GtkBuilder *builder,
2369 const gchar *name,
2370 gboolean initial_value,
2371 gboolean *value_pointer)
2372 {
2373 GtkWidget *toggle = NULL;
2374
2375 toggle = GTK_WIDGET (gtk_builder_get_object (builder, name));
2376 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), initial_value);
2377 g_signal_connect (toggle, "toggled",
2378 G_CALLBACK (gimp_toggle_button_update),
2379 value_pointer);
2380
2381 return toggle;
2382 }
2383
pixformat_changed(GtkWidget * widget,void * foo)2384 static void pixformat_changed (GtkWidget *widget,
2385 void *foo)
2386 {
2387 PngExportFormat *ep = foo;
2388 *ep = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
2389 }
2390
2391 static gboolean
save_dialog(gint32 image_ID,gboolean alpha)2392 save_dialog (gint32 image_ID,
2393 gboolean alpha)
2394 {
2395 PngSaveGui pg;
2396 GtkWidget *dialog;
2397 GtkBuilder *builder;
2398 gchar *ui_file;
2399 GimpParasite *parasite;
2400 GError *error = NULL;
2401
2402 /* Dialog init */
2403 dialog = gimp_export_dialog_new (_("PNG"), PLUG_IN_BINARY, SAVE_PROC);
2404 g_signal_connect (dialog, "response",
2405 G_CALLBACK (save_dialog_response),
2406 &pg);
2407 g_signal_connect (dialog, "destroy",
2408 G_CALLBACK (gtk_main_quit),
2409 NULL);
2410
2411 /* GtkBuilder init */
2412 builder = gtk_builder_new ();
2413 ui_file = g_build_filename (gimp_data_directory (),
2414 "ui/plug-ins/plug-in-file-png.ui",
2415 NULL);
2416 if (! gtk_builder_add_from_file (builder, ui_file, &error))
2417 {
2418 gchar *display_name = g_filename_display_name (ui_file);
2419
2420 g_printerr (_("Error loading UI file '%s': %s"),
2421 display_name, error ? error->message : _("Unknown error"));
2422
2423 g_free (display_name);
2424 }
2425
2426 g_free (ui_file);
2427
2428 /* Table */
2429 gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
2430 GTK_WIDGET (gtk_builder_get_object (builder, "table")),
2431 FALSE, FALSE, 0);
2432
2433 /* Toggles */
2434 pg.interlaced = toggle_button_init (builder, "interlace",
2435 pngvals.interlaced,
2436 &pngvals.interlaced);
2437 pg.bkgd = toggle_button_init (builder, "save-background-color",
2438 pngvals.bkgd,
2439 &pngvals.bkgd);
2440 pg.gama = toggle_button_init (builder, "save-gamma",
2441 pngvals.gama,
2442 &pngvals.gama);
2443 pg.offs = toggle_button_init (builder, "save-layer-offset",
2444 pngvals.offs,
2445 &pngvals.offs);
2446 pg.phys = toggle_button_init (builder, "save-resolution",
2447 pngvals.phys,
2448 &pngvals.phys);
2449 pg.time = toggle_button_init (builder, "save-creation-time",
2450 pngvals.time,
2451 &pngvals.time);
2452 pg.save_exif = toggle_button_init (builder, "save-exif",
2453 pngvals.save_exif,
2454 &pngvals.save_exif);
2455 pg.save_xmp = toggle_button_init (builder, "save-xmp",
2456 pngvals.save_xmp,
2457 &pngvals.save_xmp);
2458 pg.save_iptc = toggle_button_init (builder, "save-iptc",
2459 pngvals.save_iptc,
2460 &pngvals.save_iptc);
2461 pg.save_thumbnail = toggle_button_init (builder, "save-thumbnail",
2462 pngvals.save_thumbnail,
2463 &pngvals.save_thumbnail);
2464 pg.save_profile = toggle_button_init (builder, "save-color-profile",
2465 pngvals.save_profile,
2466 &pngvals.save_profile);
2467
2468 #if !defined(PNG_iCCP_SUPPORTED)
2469 gtk_widget_hide (pg.save_profile);
2470 #endif
2471
2472 /* Comment toggle */
2473 parasite = gimp_image_get_parasite (image_ID, "gimp-comment");
2474 pg.comment =
2475 toggle_button_init (builder, "save-comment",
2476 pngvals.comment && parasite != NULL,
2477 &pngvals.comment);
2478 gtk_widget_set_sensitive (pg.comment, parasite != NULL);
2479 gimp_parasite_free (parasite);
2480
2481 /* Transparent pixels toggle */
2482 pg.save_transp_pixels =
2483 toggle_button_init (builder,
2484 "save-transparent-pixels",
2485 alpha && pngvals.save_transp_pixels,
2486 &pngvals.save_transp_pixels);
2487 gtk_widget_set_sensitive (pg.save_transp_pixels, alpha);
2488
2489 /* Compression level scale */
2490 pg.compression_level =
2491 GTK_ADJUSTMENT (gtk_builder_get_object (builder, "compression-level"));
2492 gtk_adjustment_set_value (pg.compression_level, pngvals.compression_level);
2493 g_signal_connect (pg.compression_level, "value-changed",
2494 G_CALLBACK (gimp_int_adjustment_update),
2495 &pngvals.compression_level);
2496
2497 /* Compression level scale */
2498 pg.pixelformat =
2499 GTK_WIDGET (gtk_builder_get_object (builder, "pixelformat-combo"));
2500 gtk_combo_box_set_active (GTK_COMBO_BOX (pg.pixelformat), pngvals.export_format);
2501 g_signal_connect (pg.pixelformat, "changed",
2502 G_CALLBACK (pixformat_changed),
2503 &pngvals.export_format);
2504
2505 #if 0
2506 gtk_adjustment_set_value (pg.compression_level, pngvals.compression_level);
2507 g_signal_connect (pg.compression_level, "value-changed",
2508 G_CALLBACK (gimp_int_adjustment_update),
2509 &pngvals.compression_level);
2510 #endif
2511
2512 /* Load/save defaults buttons */
2513 g_signal_connect_swapped (gtk_builder_get_object (builder, "load-defaults"),
2514 "clicked",
2515 G_CALLBACK (load_gui_defaults),
2516 &pg);
2517
2518 g_signal_connect_swapped (gtk_builder_get_object (builder, "save-defaults"),
2519 "clicked",
2520 G_CALLBACK (save_parasite),
2521 &pg);
2522
2523 /* Show dialog and run */
2524 gtk_widget_show (dialog);
2525
2526 pg.run = FALSE;
2527
2528 gtk_main ();
2529
2530 return pg.run;
2531 }
2532
2533 static void
save_dialog_response(GtkWidget * widget,gint response_id,gpointer data)2534 save_dialog_response (GtkWidget *widget,
2535 gint response_id,
2536 gpointer data)
2537 {
2538 PngSaveGui *pg = data;
2539
2540 switch (response_id)
2541 {
2542 case GTK_RESPONSE_OK:
2543 pg->run = TRUE;
2544
2545 default:
2546 gtk_widget_destroy (widget);
2547 break;
2548 }
2549 }
2550
2551 static void
load_parasite(void)2552 load_parasite (void)
2553 {
2554 GimpParasite *parasite;
2555
2556 parasite = gimp_get_parasite (PNG_DEFAULTS_PARASITE);
2557
2558 if (parasite)
2559 {
2560 gchar *def_str;
2561 PngSaveVals tmpvals = defaults;
2562 gint num_fields;
2563
2564 def_str = g_strndup (gimp_parasite_data (parasite),
2565 gimp_parasite_data_size (parasite));
2566
2567 gimp_parasite_free (parasite);
2568
2569 num_fields = sscanf (def_str, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d",
2570 &tmpvals.interlaced,
2571 &tmpvals.bkgd,
2572 &tmpvals.gama,
2573 &tmpvals.offs,
2574 &tmpvals.phys,
2575 &tmpvals.time,
2576 &tmpvals.comment,
2577 &tmpvals.save_transp_pixels,
2578 &tmpvals.compression_level,
2579 &tmpvals.save_exif,
2580 &tmpvals.save_xmp,
2581 &tmpvals.save_iptc,
2582 &tmpvals.save_thumbnail,
2583 &tmpvals.save_profile);
2584
2585 g_free (def_str);
2586
2587 if (num_fields == 9 || num_fields == 13 || num_fields == 14)
2588 pngvals = tmpvals;
2589 }
2590 }
2591
2592 static void
save_parasite(void)2593 save_parasite (void)
2594 {
2595 GimpParasite *parasite;
2596 gchar *def_str;
2597
2598 def_str = g_strdup_printf ("%d %d %d %d %d %d %d %d %d %d %d %d %d %d",
2599 pngvals.interlaced,
2600 pngvals.bkgd,
2601 pngvals.gama,
2602 pngvals.offs,
2603 pngvals.phys,
2604 pngvals.time,
2605 pngvals.comment,
2606 pngvals.save_transp_pixels,
2607 pngvals.compression_level,
2608 pngvals.save_exif,
2609 pngvals.save_xmp,
2610 pngvals.save_iptc,
2611 pngvals.save_thumbnail,
2612 pngvals.save_profile);
2613
2614 parasite = gimp_parasite_new (PNG_DEFAULTS_PARASITE,
2615 GIMP_PARASITE_PERSISTENT,
2616 strlen (def_str), def_str);
2617
2618 gimp_attach_parasite (parasite);
2619
2620 gimp_parasite_free (parasite);
2621 g_free (def_str);
2622 }
2623
2624 static void
load_gui_defaults(PngSaveGui * pg)2625 load_gui_defaults (PngSaveGui *pg)
2626 {
2627 /* initialize with hardcoded defaults */
2628 pngvals = defaults;
2629 /* Override with parasite. */
2630 load_parasite ();
2631
2632 #define SET_ACTIVE(field) \
2633 if (gtk_widget_is_sensitive (pg->field)) \
2634 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pg->field), pngvals.field)
2635
2636 SET_ACTIVE (interlaced);
2637 SET_ACTIVE (bkgd);
2638 SET_ACTIVE (gama);
2639 SET_ACTIVE (offs);
2640 SET_ACTIVE (phys);
2641 SET_ACTIVE (time);
2642 SET_ACTIVE (comment);
2643 SET_ACTIVE (save_transp_pixels);
2644 SET_ACTIVE (save_exif);
2645 SET_ACTIVE (save_xmp);
2646 SET_ACTIVE (save_iptc);
2647 SET_ACTIVE (save_thumbnail);
2648 SET_ACTIVE (save_profile);
2649
2650 #undef SET_ACTIVE
2651
2652 gtk_adjustment_set_value (pg->compression_level,
2653 pngvals.compression_level);
2654 }
2655