home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / common / png.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-09-18  |  32.5 KB  |  1,145 lines

  1. /*
  2.  * "$Id: png.c,v 1.61 2000/09/15 00:50:12 nicklamb Exp $"
  3.  *
  4.  *   Portable Network Graphics (PNG) plug-in for The GIMP -- an image
  5.  *   manipulation program
  6.  *
  7.  *   Copyright 1997-1998 Michael Sweet (mike@easysw.com) and
  8.  *   Daniel Skarda (0rfelyus@atrey.karlin.mff.cuni.cz).
  9.  *   and 1999-2000 Nick Lamb (njl195@zepler.org.uk)
  10.  *
  11.  *   This program is free software; you can redistribute it and/or modify
  12.  *   it under the terms of the GNU General Public License as published by
  13.  *   the Free Software Foundation; either version 2 of the License, or
  14.  *   (at your option) any later version.
  15.  *
  16.  *   This program is distributed in the hope that it will be useful,
  17.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  *   GNU General Public License for more details.
  20.  *
  21.  *   You should have received a copy of the GNU General Public License
  22.  *   along with this program; if not, write to the Free Software
  23.  *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  24.  *
  25.  * Contents:
  26.  *
  27.  *   main()                      - Main entry - just call gimp_main()...
  28.  *   query()                     - Respond to a plug-in query...
  29.  *   run()                       - Run the plug-in...
  30.  *   load_image()                - Load a PNG image into a new image window.
  31.  *   respin_cmap()               - Re-order a Gimp colormap for PNG tRNS
  32.  *   save_image()                - Save the specified image to a PNG file.
  33.  *   save_ok_callback()          - Destroy the save dialog and save the image.
  34.  *   save_compression_callback() - Update the image compression level.
  35.  *   save_interlace_update()     - Update the interlacing option.
  36.  *   save_dialog()               - Pop up the save dialog.
  37.  *
  38.  * Revision History:
  39.  *
  40.  *   see ChangeLog
  41.  */
  42.  
  43. #include "config.h"
  44.  
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <time.h>
  48.  
  49. #include <gtk/gtk.h>
  50.  
  51. #include <libgimp/gimp.h>
  52. #include <libgimp/gimpui.h>
  53.  
  54. #include <png.h>        /* PNG library definitions */
  55.  
  56. #include "libgimp/stdplugins-intl.h"
  57.  
  58.  
  59. /*
  60.  * Constants...
  61.  */
  62.  
  63. #define PLUG_IN_VERSION  "1.3.3 - 30 June 2000"
  64. #define SCALE_WIDTH      125
  65.  
  66. #define DEFAULT_GAMMA    2.20
  67.  
  68. /*
  69.  * Structures...
  70.  */
  71.  
  72. typedef struct
  73. {
  74.   gint    interlaced;
  75.   gint    compression_level;
  76.   gint  bkgd;
  77.   gint  gama;
  78.   gint  offs;
  79.   gint  phys;
  80.   gint  time;
  81. } PngSaveVals;
  82.  
  83.  
  84. /*
  85.  * Local functions...
  86.  */
  87.  
  88. static void    query                     (void);
  89. static void    run                       (gchar   *name,
  90.                        gint     nparams,
  91.                        GimpParam  *param,
  92.                        gint    *nreturn_vals,
  93.                        GimpParam **return_vals);
  94.  
  95. static gint32    load_image                (gchar   *filename);
  96. static gint    save_image                (gchar   *filename,
  97.                        gint32   image_ID,
  98.                        gint32   drawable_ID,
  99.                        gint32   orig_image_ID);
  100.  
  101. static void    respin_cmap          (png_structp pp,
  102.                        png_infop info,
  103.                        gint32   image_ID);
  104.  
  105. static gint    save_dialog               (void);
  106. static void    save_ok_callback          (GtkWidget     *widget,
  107.                        gpointer       data);
  108.  
  109.  
  110. /*
  111.  * Globals...
  112.  */
  113.  
  114. GimpPlugInInfo PLUG_IN_INFO =
  115. {
  116.   NULL,  /* init_proc  */
  117.   NULL,  /* quit_proc  */
  118.   query, /* query_proc */
  119.   run,   /* run_proc   */
  120. };
  121.  
  122. PngSaveVals pngvals = 
  123. {
  124.   FALSE,
  125.   6,
  126.   TRUE, TRUE, TRUE, TRUE, TRUE
  127. };
  128.  
  129. static gboolean runme = FALSE;
  130.  
  131. /*
  132.  * 'main()' - Main entry - just call gimp_main()...
  133.  */
  134.  
  135. MAIN()
  136.  
  137. /*
  138.  * 'query()' - Respond to a plug-in query...
  139.  */
  140.  
  141. static void
  142. query (void)
  143. {
  144.   static GimpParamDef load_args[] =
  145.   {
  146.     { GIMP_PDB_INT32,      "run_mode",     "Interactive, non-interactive" },
  147.     { GIMP_PDB_STRING,     "filename",     "The name of the file to load" },
  148.     { GIMP_PDB_STRING,     "raw_filename", "The name of the file to load" }
  149.   };
  150.   static GimpParamDef load_return_vals[] =
  151.   {
  152.     { GIMP_PDB_IMAGE,      "image",        "Output image" }
  153.   };
  154.   static gint nload_args = sizeof (load_args) / sizeof (load_args[0]);
  155.   static gint nload_return_vals = (sizeof (load_return_vals) /
  156.                    sizeof (load_return_vals[0]));
  157.  
  158.   static GimpParamDef    save_args[] =
  159.   {
  160.     { GIMP_PDB_INT32,    "run_mode",    "Interactive, non-interactive" },
  161.     { GIMP_PDB_IMAGE,    "image",    "Input image" },
  162.     { GIMP_PDB_DRAWABLE,    "drawable",    "Drawable to save" },
  163.     { GIMP_PDB_STRING,    "filename",    "The name of the file to save the image in" },
  164.     { GIMP_PDB_STRING,    "raw_filename",    "The name of the file to save the image in" },
  165.     { GIMP_PDB_INT32,    "interlace",    "Use Adam7 interlacing?" },
  166.     { GIMP_PDB_INT32,    "compression",    "Deflate Compression factor (0--9)" },
  167.     { GIMP_PDB_INT32,    "bkgd",        "Write bKGD chunk?" },
  168.     { GIMP_PDB_INT32,    "gama",        "Write gAMA chunk?" },
  169.     { GIMP_PDB_INT32,    "offs",        "Write oFFs chunk?" },
  170.     { GIMP_PDB_INT32,    "phys",        "Write tIME chunk?" },
  171.     { GIMP_PDB_INT32,    "time",        "Write pHYs chunk?" }
  172.   };
  173.   static gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
  174.  
  175.   gimp_install_procedure ("file_png_load",
  176.               "Loads files in PNG file format",
  177.               "This plug-in loads Portable Network Graphics (PNG) files.",
  178.               "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
  179.               "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, Nick Lamb <njl195@zepler.org.uk>",
  180.               PLUG_IN_VERSION,
  181.               "<Load>/PNG",
  182.               NULL,
  183.               GIMP_PLUGIN,
  184.               nload_args, nload_return_vals,
  185.               load_args, load_return_vals);
  186.  
  187.   gimp_install_procedure  ("file_png_save",
  188.                "Saves files in PNG file format",
  189.                "This plug-in saves Portable Network Graphics (PNG) files.",
  190.                "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
  191.                "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, Nick Lamb <njl195@zepler.org.uk>",
  192.                PLUG_IN_VERSION,
  193.                "<Save>/PNG",
  194.                "RGB*,GRAY*,INDEXED*",
  195.                GIMP_PLUGIN,
  196.                nsave_args, 0,
  197.                save_args, NULL);
  198.  
  199.   gimp_register_magic_load_handler ("file_png_load",
  200.                     "png",
  201.                     "",
  202.                     "0,string,\211PNG\r\n\032\n");
  203.   gimp_register_save_handler       ("file_png_save",
  204.                     "png",
  205.                     "");
  206. }
  207.  
  208.  
  209. /*
  210.  * 'run()' - Run the plug-in...
  211.  */
  212.  
  213. static void
  214. run (gchar   *name,
  215.      gint     nparams,
  216.      GimpParam  *param,
  217.      gint    *nreturn_vals,
  218.      GimpParam **return_vals)
  219. {
  220.   static GimpParam values[2];
  221.   GimpRunModeType  run_mode;
  222.   GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
  223.   gint32        image_ID;
  224.   gint32        drawable_ID;
  225.   gint32        orig_image_ID;
  226.   GimpExportReturnType export = GIMP_EXPORT_CANCEL;
  227.  
  228.   *nreturn_vals = 1;
  229.   *return_vals  = values;
  230.   values[0].type          = GIMP_PDB_STATUS;
  231.   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  232.  
  233.   if (strcmp (name, "file_png_load") == 0)
  234.     {
  235.       INIT_I18N ();
  236.       image_ID = load_image (param[1].data.d_string);
  237.  
  238.       if (image_ID != -1)
  239.     {
  240.       *nreturn_vals = 2;
  241.       values[1].type         = GIMP_PDB_IMAGE;
  242.       values[1].data.d_image = image_ID;
  243.     }
  244.       else
  245.     {
  246.       status = GIMP_PDB_EXECUTION_ERROR;
  247.     }
  248.     }
  249.   else if (strcmp (name, "file_png_save") == 0)
  250.     {
  251.       INIT_I18N_UI();
  252.  
  253.       run_mode = param[0].data.d_int32;
  254.       image_ID = orig_image_ID = param[1].data.d_int32;
  255.       drawable_ID = param[2].data.d_int32;
  256.     
  257.       /*  eventually export the image */ 
  258.       switch (run_mode)
  259.     {
  260.     case GIMP_RUN_INTERACTIVE:
  261.     case GIMP_RUN_WITH_LAST_VALS:
  262.       gimp_ui_init ("png", FALSE);
  263.       export = gimp_export_image (&image_ID, &drawable_ID, "PNG", 
  264.                       (GIMP_EXPORT_CAN_HANDLE_RGB |
  265.                        GIMP_EXPORT_CAN_HANDLE_GRAY |
  266.                        GIMP_EXPORT_CAN_HANDLE_INDEXED |
  267.                        GIMP_EXPORT_CAN_HANDLE_ALPHA ));
  268.       if (export == GIMP_EXPORT_CANCEL)
  269.         {
  270.           *nreturn_vals = 1;
  271.           values[0].data.d_status = GIMP_PDB_CANCEL;
  272.           return;
  273.         }
  274.       break;
  275.     default:
  276.       break;
  277.     }
  278.  
  279.       switch (run_mode)
  280.     {
  281.     case GIMP_RUN_INTERACTIVE:
  282.       /*
  283.        * Possibly retrieve data...
  284.        */
  285.           gimp_get_data ("file_png_save", &pngvals);
  286.  
  287.       /*
  288.        * Then acquire information with a dialog...
  289.        */
  290.           if (!save_dialog())
  291.             status = GIMP_PDB_CANCEL;
  292.           break;
  293.  
  294.     case GIMP_RUN_NONINTERACTIVE:
  295.       /*
  296.        * Make sure all the arguments are there!
  297.        */
  298.           if (nparams != 12)
  299.         {
  300.           status = GIMP_PDB_CALLING_ERROR;
  301.         }
  302.           else
  303.         {
  304.           pngvals.interlaced        = param[5].data.d_int32;
  305.           pngvals.compression_level = param[6].data.d_int32;
  306.           pngvals.bkgd              = param[7].data.d_int32;
  307.           pngvals.gama              = param[8].data.d_int32;
  308.           pngvals.phys              = param[9].data.d_int32;
  309.           pngvals.offs              = param[10].data.d_int32;
  310.           pngvals.time              = param[11].data.d_int32;
  311.  
  312.           if (pngvals.compression_level < 0 ||
  313.           pngvals.compression_level > 9)
  314.         status = GIMP_PDB_CALLING_ERROR;
  315.         };
  316.           break;
  317.  
  318.     case GIMP_RUN_WITH_LAST_VALS:
  319.       /*
  320.        * Possibly retrieve data...
  321.        */
  322.           gimp_get_data ("file_png_save", &pngvals);
  323.           break;
  324.  
  325.     default:
  326.           break;
  327.     };
  328.  
  329.       if (status == GIMP_PDB_SUCCESS)
  330.     {
  331.       if (save_image (param[3].data.d_string,
  332.               image_ID, drawable_ID, orig_image_ID))
  333.         {
  334.           gimp_set_data ("file_png_save", &pngvals, sizeof (pngvals));
  335.         }
  336.       else
  337.         {
  338.           status = GIMP_PDB_EXECUTION_ERROR;
  339.         }
  340.     }
  341.  
  342.       if (export == GIMP_EXPORT_EXPORT)
  343.     gimp_image_delete (image_ID);
  344.     }
  345.   else
  346.     {
  347.       status = GIMP_PDB_EXECUTION_ERROR;
  348.     }
  349.  
  350.   values[0].data.d_status = status;
  351. }
  352.  
  353.  
  354. /*
  355.  * 'load_image()' - Load a PNG image into a new image window.
  356.  */
  357.  
  358. static gint32
  359. load_image (gchar *filename)    /* I - File to load */
  360. {
  361.   int        i,        /* Looping var */
  362.                 trns,        /* Transparency present */
  363.         bpp,        /* Bytes per pixel */
  364.         image_type,    /* Type of image */
  365.         layer_type,    /* Type of drawable/layer */
  366.         empty,        /* Number of fully transparent indices */
  367.         num_passes,    /* Number of interlace passes in file */
  368.         pass,        /* Current pass in file */
  369.         tile_height,    /* Height of tile in GIMP */
  370.         begin,        /* Beginning tile row */
  371.         end,        /* Ending tile row */
  372.         num;        /* Number of rows to load */
  373.   FILE        *fp;        /* File pointer */
  374.   volatile gint32 image;    /* Image -- preserved against setjmp() */
  375.   gint32    layer;        /* Layer */
  376.   GimpDrawable    *drawable;    /* Drawable for layer */
  377.   GimpPixelRgn    pixel_rgn;    /* Pixel region for layer */
  378.   png_structp    pp;        /* PNG read pointer */
  379.   png_infop    info;        /* PNG info pointers */
  380.   guchar    **pixels,    /* Pixel rows */
  381.         *pixel;        /* Pixel data */
  382.   gchar        *progress;    /* Title for progress display... */
  383.   guchar    alpha[256],    /* Index -> Alpha */
  384.           *alpha_ptr;    /* Temporary pointer */
  385.  
  386.  /*
  387.   * PNG 0.89 and newer have a sane, forwards compatible constructor.
  388.   * Some SGI IRIX users will not have a new enough version though
  389.   */
  390.  
  391. #if PNG_LIBPNG_VER > 88
  392.   pp   = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  393.   info = png_create_info_struct(pp);
  394. #else
  395.   pp = (png_structp)calloc(sizeof(png_struct), 1);
  396.   png_read_init(pp);
  397.  
  398.   info = (png_infop)calloc(sizeof(png_info), 1);
  399. #endif /* PNG_LIBPNG_VER > 88 */
  400.  
  401.   if (setjmp (pp->jmpbuf))
  402.   {
  403.     g_message (_("%s\nPNG error. File corrupted?"), filename);
  404.     return image;
  405.   }
  406.  
  407.   /* initialise image here, thus avoiding compiler warnings */
  408.  
  409.   image= -1;
  410.  
  411.  /*
  412.   * Open the file and initialize the PNG read "engine"...
  413.   */
  414.  
  415.   fp = fopen(filename, "rb");
  416.  
  417.   if (fp == NULL) 
  418.     {
  419.       g_message ("%s\nis not present or is unreadable", filename);
  420.       gimp_quit ();
  421.   }
  422.  
  423.   png_init_io(pp, fp);
  424.  
  425.   if (strrchr(filename, '/') != NULL)
  426.     progress = g_strdup_printf (_("Loading %s:"), strrchr(filename, '/') + 1);
  427.   else
  428.     progress = g_strdup_printf (_("Loading %s:"), filename);
  429.  
  430.   gimp_progress_init(progress);
  431.   g_free (progress);
  432.  
  433.  /*
  434.   * Get the image dimensions and create the image...
  435.   */
  436.  
  437.   png_read_info(pp, info);
  438.  
  439.  /*
  440.   * Latest attempt, this should be my best yet :)
  441.   */
  442.  
  443.   if (info->bit_depth == 16) {
  444.     png_set_strip_16(pp);
  445.   }
  446.  
  447.   if (info->color_type == PNG_COLOR_TYPE_GRAY && info->bit_depth < 8) {
  448.     png_set_expand(pp);
  449.   }
  450.  
  451.   if (info->color_type == PNG_COLOR_TYPE_PALETTE && info->bit_depth < 8) {
  452.     png_set_packing(pp);
  453.   }
  454.  
  455.  /*
  456.   * Expand G+tRNS to GA, RGB+tRNS to RGBA
  457.   */
  458.  
  459.   if (info->color_type != PNG_COLOR_TYPE_PALETTE &&
  460.                        (info->valid & PNG_INFO_tRNS)) {
  461.     png_set_expand(pp);
  462.   }
  463.  
  464.  /*
  465.   * Turn on interlace handling... libpng returns just 1 (ie single pass)
  466.   * if the image is not interlaced
  467.   */
  468.  
  469.   num_passes = png_set_interlace_handling(pp);
  470.  
  471.  /*
  472.   * Special handling for INDEXED + tRNS (transparency palette)
  473.   */
  474.  
  475. #if PNG_LIBPNG_VER > 99
  476.   if (png_get_valid(pp, info, PNG_INFO_tRNS) &&
  477.       info->color_type == PNG_COLOR_TYPE_PALETTE)
  478.   {
  479.     png_get_tRNS(pp, info, &alpha_ptr, &num, NULL);
  480.     /* Copy the existing alpha values from the tRNS chunk */
  481.     for (i= 0; i < num; ++i)
  482.       alpha[i]= alpha_ptr[i];
  483.     /* And set any others to fully opaque (255)  */
  484.     for (i= num; i < 256; ++i)
  485.       alpha[i]= 255;
  486.     trns= 1;
  487.   } else {
  488.     trns= 0;
  489.   }
  490. #else
  491.     trns= 0;
  492. #endif /* PNG_LIBPNG_VER > 99 */
  493.  
  494.  /*
  495.   * Update the info structures after the transformations take effect
  496.   */
  497.  
  498.   png_read_update_info(pp, info);
  499.   
  500.   switch (info->color_type)
  501.   {
  502.     case PNG_COLOR_TYPE_RGB :        /* RGB */
  503.         bpp        = 3;
  504.         image_type = GIMP_RGB;
  505.         layer_type = GIMP_RGB_IMAGE;
  506.         break;
  507.  
  508.     case PNG_COLOR_TYPE_RGB_ALPHA :    /* RGBA */
  509.         bpp        = 4;
  510.         image_type = GIMP_RGB;
  511.         layer_type = GIMP_RGBA_IMAGE;
  512.         break;
  513.  
  514.     case PNG_COLOR_TYPE_GRAY :        /* Grayscale */
  515.         bpp        = 1;
  516.         image_type = GIMP_GRAY;
  517.         layer_type = GIMP_GRAY_IMAGE;
  518.         break;
  519.  
  520.     case PNG_COLOR_TYPE_GRAY_ALPHA :    /* Grayscale + alpha */
  521.         bpp        = 2;
  522.         image_type = GIMP_GRAY;
  523.         layer_type = GIMP_GRAYA_IMAGE;
  524.         break;
  525.  
  526.     case PNG_COLOR_TYPE_PALETTE :    /* Indexed */
  527.         bpp        = 1;
  528.         image_type = GIMP_INDEXED;
  529.         layer_type = GIMP_INDEXED_IMAGE;
  530.         break;
  531.     default:                /* Aie! Unknown type */
  532.         g_message (_("%s\nPNG unknown color model"), filename);
  533.         return -1;
  534.   };
  535.  
  536.   image = gimp_image_new(info->width, info->height, image_type);
  537.   if (image == -1)
  538.   {
  539.     g_message("Can't allocate new image\n%s", filename);
  540.     gimp_quit();
  541.   };
  542.  
  543.  /*
  544.   * Create the "background" layer to hold the image...
  545.   */
  546.  
  547.   layer = gimp_layer_new(image, _("Background"), info->width, info->height,
  548.                          layer_type, 100, GIMP_NORMAL_MODE);
  549.   gimp_image_add_layer(image, layer, 0);
  550.  
  551.   /*
  552.    * Find out everything we can about the image resolution
  553.    * This is only practical with the new 1.0 APIs, I'm afraid
  554.    * due to a bug in libpng-1.0.6, see png-implement for details
  555.    */
  556.  
  557. #if PNG_LIBPNG_VER > 99
  558.   if (png_get_valid(pp, info, PNG_INFO_gAMA)) {
  559.     /* I sure would like to handle this, but there's no mechanism to
  560.        do so in Gimp :( */
  561.   }
  562.   if (png_get_valid(pp, info, PNG_INFO_oFFs)) {
  563.     gimp_layer_set_offsets (layer, png_get_x_offset_pixels(pp, info),
  564.                                    png_get_y_offset_pixels(pp, info));
  565.   }
  566.   if (png_get_valid(pp, info, PNG_INFO_pHYs)) {
  567.     gimp_image_set_resolution(image,
  568.          ((double) png_get_x_pixels_per_meter(pp, info)) * 0.0254,
  569.          ((double) png_get_y_pixels_per_meter(pp, info)) * 0.0254);
  570.   }
  571. #endif /* PNG_LIBPNG_VER > 99 */
  572.  
  573.   gimp_image_set_filename(image, filename);
  574.  
  575.  /*
  576.   * Load the colormap as necessary...
  577.   */
  578.  
  579.   empty= 0; /* by default assume no full transparent palette entries */
  580.  
  581.   if (info->color_type & PNG_COLOR_MASK_PALETTE) {
  582.  
  583. #if PNG_LIBPNG_VER > 99
  584.     if (png_get_valid(pp, info, PNG_INFO_tRNS)) {
  585.       for (empty= 0; empty < 256 && alpha[empty] == 0; ++empty);
  586.         /* Calculates number of fully transparent "empty" entries */
  587.  
  588.       gimp_image_set_cmap(image, (guchar *) (info->palette + empty),
  589.                           info->num_palette - empty);
  590.     } else {
  591.       gimp_image_set_cmap(image, (guchar *)info->palette, info->num_palette);
  592.     }
  593. #else
  594.     gimp_image_set_cmap(image, (guchar *)info->palette, info->num_palette);
  595. #endif /* PNG_LIBPNG_VER > 99 */
  596.  
  597.   }
  598.  
  599.  /*
  600.   * Get the drawable and set the pixel region for our load...
  601.   */
  602.  
  603.   drawable = gimp_drawable_get(layer);
  604.  
  605.   gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width,
  606.                       drawable->height, TRUE, FALSE);
  607.  
  608.  /*
  609.   * Temporary buffer...
  610.   */
  611.  
  612.   tile_height = gimp_tile_height ();
  613.   pixel       = g_new(guchar, tile_height * info->width * bpp);
  614.   pixels      = g_new(guchar *, tile_height);
  615.  
  616.   for (i = 0; i < tile_height; i ++)
  617.     pixels[i] = pixel + info->width * info->channels * i;
  618.  
  619.   for (pass = 0; pass < num_passes; pass ++)
  620.   {
  621.    /*
  622.     * This works if you are only reading one row at a time...
  623.     */
  624.  
  625.     for (begin = 0, end = tile_height;
  626.          begin < info->height;
  627.          begin += tile_height, end += tile_height)
  628.     {
  629.       if (end > info->height)
  630.         end = info->height;
  631.  
  632.       num = end - begin;
  633.     
  634.       if (pass != 0) /* to handle interlaced PiNGs */
  635.         gimp_pixel_rgn_get_rect(&pixel_rgn, pixel, 0, begin,
  636.                                 drawable->width, num);
  637.  
  638.       png_read_rows(pp, pixels, NULL, num);
  639.  
  640.       gimp_pixel_rgn_set_rect(&pixel_rgn, pixel, 0, begin,
  641.                               drawable->width, num);
  642.  
  643.       gimp_progress_update(((double)pass + (double)end / (double)info->height) /
  644.                            (double)num_passes);
  645.     };
  646.   };
  647.  
  648.  /*
  649.   * Done with the file...
  650.   */
  651.  
  652.   png_read_end(pp, info);
  653.   png_read_destroy(pp, info, NULL);
  654.  
  655.   g_free(pixel);
  656.   g_free(pixels);
  657.   free(pp);
  658.   free(info);
  659.  
  660.   fclose(fp);
  661.  
  662.   if (trns) {
  663.     gimp_layer_add_alpha(layer);
  664.     drawable = gimp_drawable_get(layer);
  665.     gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width,
  666.                         drawable->height, TRUE, FALSE);
  667.  
  668.     pixel  = g_new(guchar, tile_height * drawable->width * 2); /* bpp == 1 */
  669.  
  670.     for (begin = 0, end = tile_height;
  671.          begin < drawable->height;
  672.          begin += tile_height, end += tile_height)
  673.     {
  674.       if (end > drawable->height) end = drawable->height;
  675.       num= end - begin;
  676.  
  677.       gimp_pixel_rgn_get_rect(&pixel_rgn, pixel, 0, begin,
  678.                                 drawable->width, num);
  679.  
  680.       for (i= 0; i < tile_height * drawable->width; ++i) {
  681.         pixel[i*2+1]= alpha [ pixel[i*2] ];
  682.         pixel[i*2]-= empty;
  683.       }
  684.  
  685.       gimp_pixel_rgn_set_rect(&pixel_rgn, pixel, 0, begin,
  686.                               drawable->width, num);
  687.     }
  688.     g_free(pixel);
  689.   }
  690.  
  691.  /*
  692.   * Update the display...
  693.   */
  694.  
  695.   gimp_drawable_flush(drawable);
  696.   gimp_drawable_detach(drawable);
  697.  
  698.   return (image);
  699. }
  700.  
  701.  
  702. /*
  703.  * 'save_image ()' - Save the specified image to a PNG file.
  704.  */
  705.  
  706. static gint
  707. save_image (gchar  *filename,            /* I - File to save to */
  708.         gint32  image_ID,            /* I - Image to save */
  709.         gint32  drawable_ID,    /* I - Current drawable */
  710.         gint32  orig_image_ID)      /* I - Original image before export */
  711. {
  712.   int        i, k,        /* Looping vars */
  713.         bpp = 0,    /* Bytes per pixel */
  714.         type,        /* Type of drawable/layer */
  715.         num_passes,    /* Number of interlace passes in file */
  716.         pass,        /* Current pass in file */
  717.         tile_height,    /* Height of tile in GIMP */
  718.         begin,        /* Beginning tile row */
  719.         end,        /* Ending tile row */
  720.         num;        /* Number of rows to load */
  721.   FILE        *fp;        /* File pointer */
  722.   GimpDrawable    *drawable;    /* Drawable for layer */
  723.   GimpPixelRgn    pixel_rgn;    /* Pixel region for layer */
  724.   png_structp    pp;        /* PNG read pointer */
  725.   png_infop    info;        /* PNG info pointer */
  726.   gint        num_colors;    /* Number of colors in colormap */
  727.   gint        offx, offy;    /* Drawable offsets from origin */
  728.   guchar    **pixels,    /* Pixel rows */
  729.         *fixed,        /* Fixed-up pixel data */
  730.         *pixel;        /* Pixel data */
  731.   gchar        *progress;    /* Title for progress display... */
  732.   gdouble       xres, yres;    /* GIMP resolution (dpi) */
  733.   gdouble    gamma;          /* GIMP gamma e.g. 2.20 */
  734.   png_color_16  background;     /* Background color */
  735.   png_time      mod_time;       /* Modification time (ie NOW) */
  736.   guchar    red, green,
  737.                 blue;           /* Used for palette background */
  738.   time_t    cutime;         /* Time since epoch */
  739.   struct tm    *gmt;        /* GMT broken down */
  740.  
  741.  /*
  742.   * PNG 0.89 and newer have a sane, forwards compatible constructor.
  743.   * Some SGI IRIX users will not have a new enough version though
  744.   */
  745.  
  746. #if PNG_LIBPNG_VER > 88
  747.   pp   = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  748.   info = png_create_info_struct(pp);
  749. #else
  750.   pp = (png_structp)calloc(sizeof(png_struct), 1);
  751.   png_write_init(pp);
  752.  
  753.   info = (png_infop)calloc(sizeof(png_info), 1);
  754. #endif /* PNG_LIBPNG_VER > 88 */
  755.  
  756.   if (setjmp (pp->jmpbuf))
  757.   {
  758.     g_message (_("%s\nPNG error. Couldn't save image"), filename);
  759.     return 0;
  760.   }
  761.  
  762.  /*
  763.   * Open the file and initialize the PNG write "engine"...
  764.   */
  765.  
  766.   fp = fopen(filename, "wb");
  767.   if (fp == NULL) {
  768.     g_message (_("%s\nCouldn't create file"), filename);
  769.     return 0;
  770.   }
  771.  
  772.   png_init_io(pp, fp);
  773.  
  774.   if (strrchr(filename, '/') != NULL)
  775.     progress = g_strdup_printf (_("Saving %s:"), strrchr(filename, '/') + 1);
  776.   else
  777.     progress = g_strdup_printf (_("Saving %s:"), filename);
  778.  
  779.   gimp_progress_init(progress);
  780.   g_free (progress);
  781.  
  782.  /*
  783.   * Get the drawable for the current image...
  784.   */
  785.  
  786.   drawable = gimp_drawable_get (drawable_ID);
  787.   type     = gimp_drawable_type (drawable_ID);
  788.  
  789.  /*
  790.   * Set the image dimensions, bit depth, interlacing and compression
  791.   */
  792.  
  793.   png_set_compression_level (pp, pngvals.compression_level);
  794.  
  795.   info->width          = drawable->width;
  796.   info->height         = drawable->height;
  797.   info->bit_depth      = 8;
  798.   info->interlace_type = pngvals.interlaced;
  799.  
  800.  /*
  801.   * Set color type and remember bytes per pixel count 
  802.   */
  803.  
  804.   switch (type)
  805.   {
  806.     case GIMP_RGB_IMAGE :
  807.         info->color_type = PNG_COLOR_TYPE_RGB;
  808.         bpp              = 3;
  809.         break;
  810.     case GIMP_RGBA_IMAGE :
  811.         info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
  812.         bpp              = 4;
  813.         break;
  814.     case GIMP_GRAY_IMAGE :
  815.         info->color_type = PNG_COLOR_TYPE_GRAY;
  816.         bpp              = 1;
  817.         break;
  818.     case GIMP_GRAYA_IMAGE :
  819.         info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
  820.         bpp              = 2;
  821.         break;
  822.     case GIMP_INDEXED_IMAGE :
  823.     bpp         = 1;
  824.         info->color_type = PNG_COLOR_TYPE_PALETTE;
  825.     info->valid      |= PNG_INFO_PLTE;
  826.         info->palette= (png_colorp) gimp_image_get_cmap(image_ID, &num_colors);
  827.         info->num_palette= num_colors;
  828.         break;
  829.     case GIMP_INDEXEDA_IMAGE :
  830.     bpp         = 2;
  831.     info->color_type = PNG_COLOR_TYPE_PALETTE;
  832.     respin_cmap (pp, info, image_ID); /* fix up transparency */
  833.     break;
  834.     default:
  835.         g_message ("%s\nImage type can't be saved as PNG", filename);
  836.         return 0;
  837.   };
  838.  
  839.  /*
  840.   * Fix bit depths for (possibly) smaller colormap images
  841.   */
  842.   
  843.   if (info->valid & PNG_INFO_PLTE) {
  844.     if (info->num_palette <= 2)
  845.       info->bit_depth= 1;
  846.     else if (info->num_palette <= 4)
  847.       info->bit_depth= 2;
  848.     else if (info->num_palette <= 16)
  849.       info->bit_depth= 4;
  850.     /* otherwise the default is fine */
  851.   }
  852.  
  853.   /* All this stuff is optional extras, if the user is aiming for smallest
  854.      possible file size she can turn them all off */
  855.  
  856. #if PNG_LIBPNG_VER > 99
  857.   if (pngvals.bkgd) {
  858.     gimp_palette_get_background(&red, &green, &blue);
  859.       
  860.     background.index = 0;
  861.     background.red = red;
  862.     background.green = green;
  863.     background.blue = blue;
  864.     background.gray = (red + green + blue) / 3;
  865.     png_set_bKGD(pp, info, &background);
  866.   }
  867.  
  868.   if (pngvals.gama) {
  869.     gamma = gimp_gamma();
  870.     png_set_gAMA(pp, info, 1.0 / (gamma != 1.00 ? gamma : DEFAULT_GAMMA));
  871.   }
  872.  
  873.   if (pngvals.offs) {
  874.     gimp_drawable_offsets(drawable_ID, &offx, &offy);
  875.     if (offx != 0 || offy != 0) {
  876.       png_set_oFFs(pp, info, offx, offy, PNG_OFFSET_PIXEL);
  877.     }
  878.   }
  879.  
  880.   if (pngvals.phys) {
  881.     gimp_image_get_resolution (orig_image_ID, &xres, &yres);
  882.     png_set_pHYs(pp, info, xres * 39.37, yres * 39.37, PNG_RESOLUTION_METER);
  883.   }
  884.  
  885.   if (pngvals.time) {
  886.     cutime= time(NULL); /* time right NOW */
  887.     gmt = gmtime(&cutime);
  888.  
  889.     mod_time.year = gmt->tm_year + 1900;
  890.     mod_time.month = gmt->tm_mon + 1;
  891.     mod_time.day = gmt->tm_mday;
  892.     mod_time.hour = gmt->tm_hour;
  893.     mod_time.minute = gmt->tm_min;
  894.     mod_time.second = gmt->tm_sec;
  895.     png_set_tIME(pp, info, &mod_time);
  896.   }
  897.  
  898. #endif /* PNG_LIBPNG_VER > 99 */
  899.  
  900.   png_write_info (pp, info);
  901.  
  902.  /*
  903.   * Turn on interlace handling...
  904.   */
  905.  
  906.   if (pngvals.interlaced)
  907.     num_passes = png_set_interlace_handling(pp);
  908.   else
  909.     num_passes = 1;
  910.  
  911.  /*
  912.   * Convert unpacked pixels to packed if necessary
  913.   */
  914.  
  915.   if (info->color_type == PNG_COLOR_TYPE_PALETTE && info->bit_depth < 8)
  916.     png_set_packing(pp);
  917.  
  918.  /*
  919.   * Allocate memory for "tile_height" rows and save the image...
  920.   */
  921.  
  922.   tile_height = gimp_tile_height();
  923.   pixel       = g_new(guchar, tile_height * drawable->width * bpp);
  924.   pixels      = g_new(guchar *, tile_height);
  925.  
  926.   for (i = 0; i < tile_height; i ++)
  927.     pixels[i]= pixel + drawable->width * bpp * i;
  928.  
  929.   gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width,
  930.                       drawable->height, FALSE, FALSE);
  931.  
  932.   for (pass = 0; pass < num_passes; pass ++)
  933.   {
  934.       /* This works if you are only writing one row at a time... */
  935.     for (begin = 0, end = tile_height;
  936.          begin < drawable->height;
  937.          begin += tile_height, end += tile_height)
  938.       {
  939.     if (end > drawable->height)
  940.       end = drawable->height;
  941.  
  942.     num = end - begin;
  943.     
  944.     gimp_pixel_rgn_get_rect (&pixel_rgn, pixel, 0, begin, drawable->width, num);
  945.         if (info->valid & PNG_INFO_tRNS) {
  946.           for (i = 0; i < num; ++i) {
  947.         fixed= pixels[i];
  948.             for (k = 0; k < drawable->width; ++k) {
  949.               fixed[k] = (fixed[k*2+1] > 127) ? fixed[k*2] + 1 : 0;
  950.             }
  951.           }
  952.        /* Forgot this case before, what if there are too many colors? */
  953.         } else if (info->valid & PNG_INFO_PLTE && bpp == 2) {
  954.           for (i = 0; i < num; ++i) {
  955.         fixed= pixels[i];
  956.             for (k = 0; k < drawable->width; ++k) {
  957.               fixed[k] = fixed[k*2];
  958.             }
  959.           }
  960.         }
  961.     
  962.     png_write_rows (pp, pixels, num);
  963.     
  964.     gimp_progress_update (((double)pass + (double)end /
  965.                     (double)info->height) / (double)num_passes);
  966.       };
  967.   };
  968.  
  969.   png_write_end (pp, info);
  970.   png_write_destroy (pp);
  971.  
  972.   g_free (pixel);
  973.   g_free (pixels);
  974.  
  975.  /*
  976.   * Done with the file...
  977.   */
  978.  
  979.   free (pp);
  980.   free (info);
  981.  
  982.   fclose (fp);
  983.  
  984.   return (1);
  985. }
  986.  
  987. static void
  988. save_ok_callback (GtkWidget *widget,
  989.           gpointer  data)
  990. {
  991.   runme = TRUE;
  992.  
  993.   gtk_widget_destroy (GTK_WIDGET (data));
  994. }
  995.  
  996. /* respin_cmap actually reverse fills the colormap, so that empty entries
  997.    are left at the FRONT, this is only needed to reduce the size of the
  998.    tRNS chunk when saving GIF-like transparent images with colormaps */
  999.  
  1000. static void respin_cmap (png_structp pp, png_infop info, gint32 image_ID) {
  1001.   static const guchar trans[] = { 0 };
  1002.   static guchar after[3 * 256];
  1003.   gint colors;
  1004.   guchar *before;
  1005.  
  1006.   before= gimp_image_get_cmap(image_ID, &colors);
  1007.  
  1008. #if PNG_LIBPNG_VER > 99
  1009.   if (colors < 256) { /* spare space in palette :) */
  1010.     memcpy(after + 3, before, colors * 3);
  1011.  
  1012.     /* Apps with no natural background will use this instead, see
  1013.        elsewhere for the bKGD chunk being written to use index 0 */
  1014.     gimp_palette_get_background(after+0, after+1, after+2);
  1015.  
  1016.     /* One transparent palette entry, alpha == 0 */
  1017.     png_set_tRNS(pp, info, (png_bytep) trans, 1, NULL);
  1018.     png_set_PLTE(pp, info, (png_colorp) after, colors + 1);
  1019.   } else {
  1020.     png_set_PLTE(pp, info, (png_colorp) before, colors);
  1021.   }
  1022. #else
  1023.   info->valid      |= PNG_INFO_PLTE;
  1024.   info->palette=     (png_colorp) before;
  1025.   info->num_palette= colors;
  1026. #endif /* PNG_LIBPNG_VER > 99 */
  1027.  
  1028. }
  1029.  
  1030. static gint
  1031. save_dialog (void)
  1032. {
  1033.   GtkWidget *dlg;
  1034.   GtkWidget *frame;
  1035.   GtkWidget *table;
  1036.   GtkWidget *toggle;
  1037.   GtkWidget *scale;
  1038.   GtkObject *scale_data;
  1039.  
  1040.   dlg = gimp_dialog_new (_("Save as PNG"), "png",
  1041.              gimp_standard_help_func, "filters/png.html",
  1042.              GTK_WIN_POS_MOUSE,
  1043.              FALSE, TRUE, FALSE,
  1044.  
  1045.              _("OK"), save_ok_callback,
  1046.              NULL, NULL, NULL, TRUE, FALSE,
  1047.              _("Cancel"), gtk_widget_destroy,
  1048.              NULL, 1, NULL, FALSE, TRUE,
  1049.  
  1050.              NULL);
  1051.  
  1052.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  1053.               GTK_SIGNAL_FUNC (gtk_main_quit),
  1054.               NULL);
  1055.  
  1056.   frame = gtk_frame_new (_("Parameter Settings"));
  1057.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  1058.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  1059.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  1060.   gtk_widget_show (frame);
  1061.  
  1062.   table = gtk_table_new (2, 7, FALSE);
  1063.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  1064.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  1065.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  1066.   gtk_container_add (GTK_CONTAINER (frame), table);
  1067.   gtk_widget_show (table);
  1068.  
  1069.   toggle = gtk_check_button_new_with_label (_("Interlacing (Adam7)"));
  1070.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 0, 1,
  1071.             GTK_FILL, 0, 0, 0);
  1072.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1073.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1074.               &pngvals.interlaced);
  1075.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.interlaced);
  1076.   gtk_widget_show (toggle);
  1077.  
  1078.   toggle = gtk_check_button_new_with_label (_("Save background color"));
  1079.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 1, 2,
  1080.             GTK_FILL, 0, 0, 0);
  1081.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1082.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1083.               &pngvals.bkgd);
  1084.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.bkgd);
  1085.   gtk_widget_show (toggle);
  1086.  
  1087.   toggle = gtk_check_button_new_with_label (_("Save gamma"));
  1088.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 2, 3,
  1089.             GTK_FILL, 0, 0, 0);
  1090.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1091.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1092.               &pngvals.gama);
  1093.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.gama);
  1094.   gtk_widget_show (toggle);
  1095.  
  1096.   toggle = gtk_check_button_new_with_label (_("Save layer offset"));
  1097.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 3, 4,
  1098.             GTK_FILL, 0, 0, 0);
  1099.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1100.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1101.               &pngvals.offs);
  1102.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.offs);
  1103.   gtk_widget_show (toggle);
  1104.  
  1105.   toggle = gtk_check_button_new_with_label (_("Save resolution"));
  1106.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 4, 5,
  1107.             GTK_FILL, 0, 0, 0);
  1108.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1109.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1110.               &pngvals.phys);
  1111.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.phys);
  1112.   gtk_widget_show (toggle);
  1113.  
  1114.   toggle = gtk_check_button_new_with_label (_("Save creation time"));
  1115.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 5, 6,
  1116.             GTK_FILL, 0, 0, 0);
  1117.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1118.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1119.               &pngvals.time);
  1120.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.time);
  1121.   gtk_widget_show (toggle);
  1122.  
  1123.   scale_data = gtk_adjustment_new (pngvals.compression_level,
  1124.                    1.0, 9.0, 1.0, 1.0, 0.0);
  1125.   scale      = gtk_hscale_new (GTK_ADJUSTMENT (scale_data));
  1126.   gtk_widget_set_usize (scale, SCALE_WIDTH, 0);
  1127.   gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
  1128.   gtk_scale_set_digits (GTK_SCALE (scale), 0);
  1129.   gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
  1130.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 6,
  1131.                  _("Compression Level:"), 1.0, 1.0,
  1132.                  scale, 1, FALSE);
  1133.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  1134.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  1135.               &pngvals.compression_level);
  1136.   gtk_widget_show (scale);
  1137.  
  1138.   gtk_widget_show (dlg);
  1139.  
  1140.   gtk_main ();
  1141.   gdk_flush ();
  1142.  
  1143.   return runme;
  1144. }
  1145.