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