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 / pnm.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-17  |  28.4 KB  |  1,102 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  * PNM reading and writing code Copyright (C) 1996 Erik Nygren
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18.  */
  19.  
  20. /* $Id: pnm.c,v 1.26 2000/12/17 16:50:00 mitch Exp $ */
  21.  
  22. /*
  23.  * The pnm reading and writing code was written from scratch by Erik Nygren
  24.  * (nygren@mit.edu) based on the specifications in the man pages and
  25.  * does not contain any code from the netpbm or pbmplus distributions.
  26.  */
  27.  
  28. #include "config.h"
  29.  
  30. #include <setjmp.h>
  31. #include <sys/types.h>
  32. #include <sys/stat.h>
  33. #include <fcntl.h>
  34. #ifdef HAVE_UNISTD_H
  35. #include <unistd.h>
  36. #endif
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41.  
  42. #include <libgimp/gimp.h>
  43. #include <libgimp/gimpui.h>
  44.  
  45. #include "libgimp/stdplugins-intl.h"
  46.  
  47.  
  48. #ifdef G_OS_WIN32
  49. #include <io.h>
  50. #endif
  51.  
  52. #ifndef _O_BINARY
  53. #define _O_BINARY 0
  54. #endif
  55.  
  56. /* Declare local data types
  57.  */
  58.  
  59. typedef struct _PNMScanner
  60. {
  61.   gint    fd;              /* The file descriptor of the file being read */
  62.   gchar   cur;              /* The current character in the input stream */
  63.   gint    eof;              /* Have we reached end of file? */
  64.   gchar  *inbuf;          /* Input buffer - initially 0 */
  65.   gint    inbufsize;          /* Size of input buffer */
  66.   gint    inbufvalidsize;     /* Size of input buffer with valid data */
  67.   gint    inbufpos;           /* Position in input buffer */
  68. } PNMScanner;
  69.  
  70. typedef struct _PNMInfo
  71. {
  72.   gint       xres, yres;    /* The size of the image */
  73.   gint       maxval;        /* For ascii image files, the max value
  74.                  * which we need to normalize to */
  75.   gint       np;        /* Number of image planes (0 for pbm) */
  76.   gint       asciibody;        /* 1 if ascii body, 0 if raw body */
  77.   jmp_buf    jmpbuf;        /* Where to jump to on an error loading */
  78.   /* Routine to use to load the pnm body */
  79.   void    (* loader) (PNMScanner *, struct _PNMInfo *, GimpPixelRgn *);
  80. } PNMInfo;
  81.  
  82. /* Contains the information needed to write out PNM rows */
  83. typedef struct _PNMRowInfo
  84. {
  85.   gint    fd;        /* File descriptor */
  86.   gchar  *rowbuf;    /* Buffer for writing out rows */
  87.   gint    xres;        /* X resolution */
  88.   gint    np;        /* Number of planes */
  89.   guchar *red;        /* Colormap red */
  90.   guchar *grn;        /* Colormap green */
  91.   guchar *blu;        /* Colormap blue */
  92. } PNMRowInfo;
  93.  
  94. /* Save info  */
  95. typedef struct
  96. {
  97.   gint  raw;  /*  raw or ascii  */
  98. } PNMSaveVals;
  99.  
  100. typedef struct
  101. {
  102.   gint  run;  /*  run  */
  103. } PNMSaveInterface;
  104.  
  105. #define BUFLEN 512        /* The input buffer size for data returned
  106.                  * from the scanner.  Note that lines
  107.                  * aren't allowed to be over 256 characters
  108.                  * by the spec anyways so this shouldn't
  109.                  * be an issue. */
  110.  
  111. #define SAVE_COMMENT_STRING "# CREATOR: The GIMP's PNM Filter Version 1.0\n"
  112.  
  113. /* Declare some local functions.
  114.  */
  115. static void   query      (void);
  116. static void   run        (gchar   *name,
  117.                           gint     nparams,
  118.                           GimpParam  *param,
  119.                           gint    *nreturn_vals,
  120.                           GimpParam **return_vals);
  121. static gint32 load_image (gchar  *filename);
  122. static gint   save_image (gchar  *filename,
  123.               gint32  image_ID,
  124.               gint32  drawable_ID);
  125.  
  126. static gint   save_dialog              (void);
  127. static void   save_ok_callback         (GtkWidget *widget,
  128.                     gpointer   data);
  129.  
  130. static void   pnm_load_ascii           (PNMScanner *scan,
  131.                     PNMInfo    *info,
  132.                     GimpPixelRgn  *pixel_rgn);
  133. static void   pnm_load_raw             (PNMScanner *scan,
  134.                     PNMInfo    *info,
  135.                     GimpPixelRgn  *pixel_rgn);
  136. static void   pnm_load_rawpbm          (PNMScanner *scan,
  137.                     PNMInfo    *info,
  138.                     GimpPixelRgn  *pixel_rgn);
  139.  
  140. static void   pnmsaverow_ascii         (PNMRowInfo *ri,
  141.                     guchar     *data);
  142. static void   pnmsaverow_raw           (PNMRowInfo *ri,
  143.                     guchar     *data);
  144. static void   pnmsaverow_ascii_indexed (PNMRowInfo *ri,
  145.                     guchar     *data);
  146. static void   pnmsaverow_raw_indexed   (PNMRowInfo *ri,
  147.                     guchar     *data);
  148.  
  149. static void   pnmscanner_destroy       (PNMScanner *s);
  150. static void   pnmscanner_createbuffer  (PNMScanner *s,
  151.                     gint        bufsize);
  152. static void   pnmscanner_getchar       (PNMScanner *s);
  153. static void   pnmscanner_eatwhitespace (PNMScanner *s);
  154. static void   pnmscanner_gettoken      (PNMScanner *s,
  155.                     gchar      *buf,
  156.                     gint        bufsize);
  157. static void   pnmscanner_getsmalltoken (PNMScanner *s,
  158.                     gchar      *buf);
  159.  
  160. static PNMScanner * pnmscanner_create  (gint        fd);
  161.  
  162.  
  163. #define pnmscanner_eof(s) ((s)->eof)
  164. #define pnmscanner_fd(s) ((s)->fd)
  165.  
  166. /* Checks for a fatal error */
  167. #define CHECK_FOR_ERROR(predicate, jmpbuf, errmsg) \
  168.         if ((predicate)) \
  169.         { g_message ((errmsg)); longjmp((jmpbuf),1); }
  170.  
  171. static struct struct_pnm_types
  172. {
  173.   gchar   name;
  174.   gint    np;
  175.   gint    asciibody;
  176.   gint    maxval;
  177.   void (* loader) (PNMScanner *, struct _PNMInfo *, GimpPixelRgn *pixel_rgn);
  178. } pnm_types[] =
  179. {
  180.   { '1', 0, 1,   1, pnm_load_ascii },  /* ASCII PBM */
  181.   { '2', 1, 1, 255, pnm_load_ascii },  /* ASCII PGM */
  182.   { '3', 3, 1, 255, pnm_load_ascii },  /* ASCII PPM */
  183.   { '4', 0, 0,   1, pnm_load_rawpbm }, /* RAW   PBM */
  184.   { '5', 1, 0, 255, pnm_load_raw },    /* RAW   PGM */
  185.   { '6', 3, 0, 255, pnm_load_raw },    /* RAW   PPM */
  186.   {  0 , 0, 0,   0, NULL}
  187. };
  188.  
  189. GimpPlugInInfo PLUG_IN_INFO =
  190. {
  191.   NULL,  /* init_proc  */
  192.   NULL,  /* quit_proc  */
  193.   query, /* query_proc */
  194.   run,   /* run_proc   */
  195. };
  196.  
  197. static PNMSaveVals psvals =
  198. {
  199.   TRUE     /* raw? or ascii */
  200. };
  201.  
  202. static PNMSaveInterface psint =
  203. {
  204.   FALSE     /* run */
  205. };
  206.  
  207.  
  208. MAIN ()
  209.  
  210. static void
  211. query (void)
  212. {
  213.   static GimpParamDef load_args[] =
  214.   {
  215.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  216.     { GIMP_PDB_STRING, "filename", "The name of the file to load" },
  217.     { GIMP_PDB_STRING, "raw_filename", "The name of the file to load" }
  218.   };
  219.   static GimpParamDef load_return_vals[] =
  220.   {
  221.     { GIMP_PDB_IMAGE, "image", "Output image" }
  222.   };
  223.   static gint nload_args = sizeof (load_args) / sizeof (load_args[0]);
  224.   static gint nload_return_vals = (sizeof (load_return_vals) /
  225.                    sizeof (load_return_vals[0]));
  226.  
  227.   static GimpParamDef save_args[] =
  228.   {
  229.     { GIMP_PDB_INT32,    "run_mode",     "Interactive, non-interactive" },
  230.     { GIMP_PDB_IMAGE,    "image",        "Input image" },
  231.     { GIMP_PDB_DRAWABLE, "drawable",     "Drawable to save" },
  232.     { GIMP_PDB_STRING,   "filename",     "The name of the file to save the image in" },
  233.     { GIMP_PDB_STRING,   "raw_filename", "The name of the file to save the image in" },
  234.     { GIMP_PDB_INT32,    "raw",          "Specify non-zero for raw output, zero for ascii output" }
  235.   };
  236.   static gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
  237.  
  238.   gimp_install_procedure ("file_pnm_load",
  239.                           "loads files of the pnm file format",
  240.                           "FIXME: write help for pnm_load",
  241.                           "Erik Nygren",
  242.                           "Erik Nygren",
  243.                           "1996",
  244.                           "<Load>/PNM",
  245.               NULL,
  246.                           GIMP_PLUGIN,
  247.                           nload_args, nload_return_vals,
  248.                           load_args, load_return_vals);
  249.  
  250.   gimp_install_procedure ("file_pnm_save",
  251.                           "saves files in the pnm file format",
  252.                           "PNM saving handles all image types except those with alpha channels.",
  253.                           "Erik Nygren",
  254.                           "Erik Nygren",
  255.                           "1996",
  256.                           "<Save>/PNM",
  257.               "RGB, GRAY, INDEXED",
  258.                           GIMP_PLUGIN,
  259.                           nsave_args, 0,
  260.                           save_args, NULL);
  261.  
  262.   gimp_register_magic_load_handler ("file_pnm_load",
  263.                     "pnm,ppm,pgm,pbm",
  264.                     "",
  265.                     "0,string,P1,0,string,P2,0,string,P3,0,"
  266.                     "string,P4,0,string,P5,0,string,P6");
  267.   gimp_register_save_handler       ("file_pnm_save",
  268.                     "pnm,ppm,pgm",
  269.                     "");
  270. }
  271.  
  272. static void
  273. run (gchar   *name,
  274.      gint     nparams,
  275.      GimpParam  *param,
  276.      gint    *nreturn_vals,
  277.      GimpParam **return_vals)
  278. {
  279.   static GimpParam values[2];
  280.   GimpRunModeType  run_mode;
  281.   GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
  282.   gint32        image_ID;
  283.   gint32        drawable_ID;
  284.   GimpExportReturnType export = GIMP_EXPORT_CANCEL;
  285.  
  286.   run_mode = param[0].data.d_int32;
  287.  
  288.   *nreturn_vals = 1;
  289.   *return_vals  = values;
  290.   values[0].type          = GIMP_PDB_STATUS;
  291.   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  292.  
  293.   if (run_mode == GIMP_RUN_NONINTERACTIVE)
  294.     {
  295.       INIT_I18N();
  296.     }
  297.   else
  298.     {
  299.       INIT_I18N_UI();
  300.     }
  301.  
  302.   if (strcmp (name, "file_pnm_load") == 0)
  303.     {
  304.       image_ID = load_image (param[1].data.d_string);
  305.  
  306.       if (image_ID != -1)
  307.     {
  308.       *nreturn_vals = 2;
  309.       values[1].type         = GIMP_PDB_IMAGE;
  310.       values[1].data.d_image = image_ID;
  311.     }
  312.       else
  313.     {
  314.       status = GIMP_PDB_EXECUTION_ERROR;
  315.     }
  316.     }
  317.   else if (strcmp (name, "file_pnm_save") == 0)
  318.     {
  319.       image_ID      = param[1].data.d_int32;
  320.       drawable_ID   = param[2].data.d_int32;
  321.     
  322.       /*  eventually export the image */ 
  323.       switch (run_mode)
  324.     {
  325.     case GIMP_RUN_INTERACTIVE:
  326.     case GIMP_RUN_WITH_LAST_VALS:
  327.       gimp_ui_init ("pnm", FALSE);
  328.       export = gimp_export_image (&image_ID, &drawable_ID, "PNM", 
  329.                       (GIMP_EXPORT_CAN_HANDLE_RGB |
  330.                        GIMP_EXPORT_CAN_HANDLE_GRAY |
  331.                        GIMP_EXPORT_CAN_HANDLE_INDEXED));
  332.       if (export == GIMP_EXPORT_CANCEL)
  333.         {
  334.           values[0].data.d_status = GIMP_PDB_CANCEL;
  335.           return;
  336.         }
  337.     break;
  338.     default:
  339.       break;
  340.     }
  341.  
  342.       switch (run_mode)
  343.     {
  344.     case GIMP_RUN_INTERACTIVE:
  345.       /*  Possibly retrieve data  */
  346.       gimp_get_data ("file_pnm_save", &psvals);
  347.  
  348.       /*  First acquire information with a dialog  */
  349.       if (! save_dialog ())
  350.         status = GIMP_PDB_CANCEL;
  351.       break;
  352.  
  353.     case GIMP_RUN_NONINTERACTIVE:
  354.       /*  Make sure all the arguments are there!  */
  355.       if (nparams != 6)
  356.         {
  357.           status = GIMP_PDB_CALLING_ERROR;
  358.         }
  359.       else
  360.         {
  361.           psvals.raw = (param[5].data.d_int32) ? TRUE : FALSE;
  362.         }
  363.  
  364.     case GIMP_RUN_WITH_LAST_VALS:
  365.       /*  Possibly retrieve data  */
  366.       gimp_get_data ("file_pnm_save", &psvals);
  367.       break;
  368.  
  369.     default:
  370.       break;
  371.     }
  372.  
  373.       if (status == GIMP_PDB_SUCCESS)
  374.     {
  375.       if (save_image (param[3].data.d_string, image_ID, drawable_ID))
  376.         {
  377.           /*  Store psvals data  */
  378.           gimp_set_data ("file_pnm_save", &psvals, sizeof (PNMSaveVals));
  379.         }
  380.       else
  381.         {
  382.           status = GIMP_PDB_EXECUTION_ERROR;
  383.         }
  384.     }
  385.  
  386.       if (export == GIMP_EXPORT_EXPORT)
  387.     gimp_image_delete (image_ID);
  388.     }
  389.   else
  390.     {
  391.       status = GIMP_PDB_CALLING_ERROR;
  392.     }
  393.  
  394.   values[0].data.d_status = status;
  395. }
  396.  
  397. static gint32
  398. load_image (gchar *filename)
  399. {
  400.   GimpPixelRgn pixel_rgn;
  401.   gint32 volatile image_ID = -1;
  402.   gint32 layer_ID;
  403.   GimpDrawable *drawable;
  404.   int fd;            /* File descriptor */
  405.   char *temp;
  406.   char buf[BUFLEN];        /* buffer for random things like scanning */
  407.   PNMInfo *pnminfo;
  408.   PNMScanner * volatile scan;
  409.   int ctr;
  410.  
  411.   temp = g_strdup_printf (_("Loading %s:"), filename);
  412.   gimp_progress_init (temp);
  413.   g_free (temp);
  414.  
  415.   /* open the file */
  416.   fd = open (filename, O_RDONLY | _O_BINARY);
  417.  
  418.   if (fd == -1)
  419.     {
  420.       g_message (_("PNM: Can't open file %s."), filename);
  421.       return -1;
  422.     }
  423.  
  424.   /* allocate the necessary structures */
  425.   pnminfo = (PNMInfo *) g_malloc (sizeof (PNMInfo));
  426.  
  427.   scan = NULL;
  428.   /* set error handling */
  429.   if (setjmp (pnminfo->jmpbuf))
  430.     {
  431.       /* If we get here, we had a problem reading the file */
  432.       if (scan)
  433.     pnmscanner_destroy (scan);
  434.       close (fd);
  435.       g_free (pnminfo);
  436.       if (image_ID != -1)
  437.     gimp_image_delete (image_ID);
  438.       return -1;
  439.     }
  440.  
  441.   if (!(scan = pnmscanner_create(fd)))
  442.     longjmp(pnminfo->jmpbuf,1);
  443.  
  444.   /* Get magic number */
  445.   pnmscanner_gettoken (scan, buf, BUFLEN);
  446.   CHECK_FOR_ERROR(pnmscanner_eof(scan), pnminfo->jmpbuf,
  447.           _("PNM: Premature end of file."));
  448.   CHECK_FOR_ERROR((buf[0] != 'P' || buf[2]), pnminfo->jmpbuf,
  449.           _("PNM: Invalid file."));
  450.  
  451.   /* Look up magic number to see what type of PNM this is */
  452.   for (ctr=0; pnm_types[ctr].name; ctr++)
  453.     if (buf[1] == pnm_types[ctr].name)
  454.       {
  455.     pnminfo->np        = pnm_types[ctr].np;
  456.     pnminfo->asciibody = pnm_types[ctr].asciibody;
  457.     pnminfo->maxval    = pnm_types[ctr].maxval;
  458.     pnminfo->loader    = pnm_types[ctr].loader;
  459.       }
  460.   if (!pnminfo->loader)
  461.     {
  462.       g_message (_("PNM: File not in a supported format."));
  463.       longjmp(pnminfo->jmpbuf,1);
  464.     }
  465.  
  466.   pnmscanner_gettoken(scan, buf, BUFLEN);
  467.   CHECK_FOR_ERROR(pnmscanner_eof(scan), pnminfo->jmpbuf,
  468.           _("PNM: Premature end of file."));
  469.   pnminfo->xres = isdigit(*buf)?atoi(buf):0;
  470.   CHECK_FOR_ERROR(pnminfo->xres<=0, pnminfo->jmpbuf,
  471.           _("PNM: Invalid X resolution."));
  472.  
  473.   pnmscanner_gettoken(scan, buf, BUFLEN);
  474.   CHECK_FOR_ERROR(pnmscanner_eof(scan), pnminfo->jmpbuf,
  475.           _("PNM: Premature end of file."));
  476.   pnminfo->yres = isdigit(*buf)?atoi(buf):0;
  477.   CHECK_FOR_ERROR(pnminfo->yres<=0, pnminfo->jmpbuf,
  478.           _("PNM: Invalid Y resolution."));
  479.  
  480.   if (pnminfo->np != 0)        /* pbm's don't have a maxval field */
  481.     {
  482.       pnmscanner_gettoken(scan, buf, BUFLEN);
  483.       CHECK_FOR_ERROR(pnmscanner_eof(scan), pnminfo->jmpbuf,
  484.               _("PNM: Premature end of file."));
  485.  
  486.       pnminfo->maxval = isdigit(*buf)?atoi(buf):0;
  487.       CHECK_FOR_ERROR(((pnminfo->maxval<=0)
  488.                || (pnminfo->maxval>255 && !pnminfo->asciibody)),
  489.               pnminfo->jmpbuf,
  490.               _("PNM: Invalid maximum value."));
  491.     }
  492.  
  493.   /* Create a new image of the proper size and associate the filename with it.
  494.    */
  495.   image_ID = gimp_image_new (pnminfo->xres, pnminfo->yres,
  496.                  (pnminfo->np >= 3) ? GIMP_RGB : GIMP_GRAY);
  497.   gimp_image_set_filename (image_ID, filename);
  498.  
  499.   layer_ID = gimp_layer_new (image_ID, _("Background"),
  500.                  pnminfo->xres, pnminfo->yres,
  501.                  (pnminfo->np >= 3) ? GIMP_RGB_IMAGE : GIMP_GRAY_IMAGE,
  502.                  100, GIMP_NORMAL_MODE);
  503.   gimp_image_add_layer (image_ID, layer_ID, 0);
  504.  
  505.   drawable = gimp_drawable_get (layer_ID);
  506.   gimp_pixel_rgn_init (&pixel_rgn, drawable,
  507.                0, 0, drawable->width, drawable->height, TRUE, FALSE);
  508.  
  509.   pnminfo->loader (scan, pnminfo, &pixel_rgn);
  510.  
  511.   /* Destroy the scanner */
  512.   pnmscanner_destroy (scan);
  513.  
  514.   /* free the structures */
  515.   g_free (pnminfo);
  516.  
  517.   /* close the file */
  518.   close (fd);
  519.  
  520.   /* Tell the GIMP to display the image.
  521.    */
  522.   gimp_drawable_flush (drawable);
  523.  
  524.   return image_ID;
  525. }
  526.  
  527. static void
  528. pnm_load_ascii (PNMScanner *scan,
  529.         PNMInfo    *info,
  530.         GimpPixelRgn  *pixel_rgn)
  531. {
  532.   unsigned char *data, *d;
  533.   int            x, y, i, b;
  534.   int            start, end, scanlines;
  535.   int            np;
  536.   char           buf[BUFLEN];
  537.  
  538.   np = (info->np)?(info->np):1;
  539.   data = g_malloc (gimp_tile_height () * info->xres * np);
  540.  
  541.   /* Buffer reads to increase performance */
  542.   pnmscanner_createbuffer(scan, 4096);
  543.  
  544.   for (y = 0; y < info->yres; )
  545.     {
  546.       start = y;
  547.       end = y + gimp_tile_height ();
  548.       end = MIN (end, info->yres);
  549.       scanlines = end - start;
  550.       d = data;
  551.  
  552.       for (i = 0; i < scanlines; i++)
  553.     for (x = 0; x < info->xres; x++)
  554.       {
  555.         for (b = 0; b < np; b++)
  556.           {
  557.         /* Truncated files will just have all 0's at the end of the images */
  558.         CHECK_FOR_ERROR(pnmscanner_eof(scan), info->jmpbuf,
  559.                 _("PNM: Premature end of file."));
  560.         if (info->np)
  561.           pnmscanner_gettoken(scan, buf, BUFLEN);
  562.         else
  563.           pnmscanner_getsmalltoken(scan, buf);
  564.         switch (info->maxval)
  565.           {
  566.           case 255:
  567.             d[b] = isdigit(*buf)?atoi(buf):0;
  568.             break;
  569.           case 1:
  570.             d[b] = (*buf=='0')?0xff:0x00;
  571.             break;
  572.           default:
  573.             d[b] = (unsigned char)(255.0*(((double)(isdigit(*buf)?atoi(buf):0))
  574.                           / (double)(info->maxval)));
  575.           }
  576.           }
  577.  
  578.         d += np;
  579.       }
  580.  
  581.       gimp_progress_update ((double) y / (double) info->yres);
  582.       gimp_pixel_rgn_set_rect (pixel_rgn, data, 0, y, info->xres, scanlines);
  583.       y += scanlines;
  584.     }
  585.  
  586.   g_free (data);
  587. }
  588.  
  589. static void
  590. pnm_load_raw (PNMScanner *scan,
  591.           PNMInfo    *info,
  592.           GimpPixelRgn  *pixel_rgn)
  593. {
  594.   unsigned char *data, *d;
  595.   int            x, y, i;
  596.   int            start, end, scanlines;
  597.   int            fd;
  598.  
  599.   data = g_malloc (gimp_tile_height () * info->xres * info->np);
  600.   fd = pnmscanner_fd(scan);
  601.  
  602.   for (y = 0; y < info->yres; )
  603.     {
  604.       start = y;
  605.       end = y + gimp_tile_height ();
  606.       end = MIN (end, info->yres);
  607.       scanlines = end - start;
  608.       d = data;
  609.  
  610.       for (i = 0; i < scanlines; i++)
  611.     {
  612.       CHECK_FOR_ERROR((info->xres*info->np
  613.                != read(fd, d, info->xres*info->np)),
  614.               info->jmpbuf,
  615.               _("PNM: Premature end of file."));
  616.  
  617.       if (info->maxval != 255)    /* Normalize if needed */
  618.         {
  619.           for (x = 0; x < info->xres * info->np; x++)
  620.         d[x] = (unsigned char)(255.0*(double)(d[x]) / (double)(info->maxval));
  621.         }
  622.  
  623.       d += info->xres * info->np;
  624.     }
  625.  
  626.       gimp_progress_update ((double) y / (double) info->yres);
  627.       gimp_pixel_rgn_set_rect (pixel_rgn, data, 0, y, info->xres, scanlines);
  628.       y += scanlines;
  629.     }
  630.  
  631.   g_free (data);
  632. }
  633.  
  634. static void
  635. pnm_load_rawpbm (PNMScanner *scan,
  636.          PNMInfo    *info,
  637.          GimpPixelRgn  *pixel_rgn)
  638. {
  639.   unsigned char *buf;
  640.   unsigned char  curbyte;
  641.   unsigned char *data, *d;
  642.   int            x, y, i;
  643.   int            start, end, scanlines;
  644.   int            fd;
  645.   int            rowlen, bufpos;
  646.  
  647.   fd = pnmscanner_fd(scan);
  648.   rowlen = (int)ceil((double)(info->xres)/8.0);
  649.   data = g_malloc (gimp_tile_height () * info->xres);
  650.   buf = g_malloc(rowlen*sizeof(unsigned char));
  651.  
  652.   for (y = 0; y < info->yres; )
  653.     {
  654.       start = y;
  655.       end = y + gimp_tile_height ();
  656.       end = MIN (end, info->yres);
  657.       scanlines = end - start;
  658.       d = data;
  659.  
  660.       for (i = 0; i < scanlines; i++)
  661.     {
  662.       CHECK_FOR_ERROR((rowlen != read(fd, buf, rowlen)),
  663.               info->jmpbuf, _("PNM: Error reading file."));
  664.       bufpos = 0;
  665.       curbyte = buf[0];
  666.  
  667.       for (x = 0; x < info->xres; x++)
  668.         {
  669.           if ((x % 8) == 0)
  670.         curbyte = buf[bufpos++];
  671.           d[x] = (curbyte&0x80) ? 0x00 : 0xff;
  672.           curbyte <<= 1;
  673.         }
  674.  
  675.       d += info->xres;
  676.     }
  677.  
  678.       gimp_progress_update ((double) y / (double) info->yres);
  679.       gimp_pixel_rgn_set_rect (pixel_rgn, data, 0, y, info->xres, scanlines);
  680.       y += scanlines;
  681.     }
  682.  
  683.   g_free(buf);
  684.   g_free (data);
  685. }
  686.  
  687. /* Writes out RGB and greyscale raw rows */
  688. static void
  689. pnmsaverow_raw (PNMRowInfo    *ri,
  690.         unsigned char *data)
  691. {
  692.   write(ri->fd, data, ri->xres*ri->np);
  693. }
  694.  
  695. /* Writes out indexed raw rows */
  696. static void
  697. pnmsaverow_raw_indexed (PNMRowInfo    *ri,
  698.             unsigned char *data)
  699. {
  700.   int i;
  701.   char *rbcur = ri->rowbuf;
  702.  
  703.   for (i = 0; i < ri->xres; i++)
  704.     {
  705.       *(rbcur++) = ri->red[*data];
  706.       *(rbcur++) = ri->grn[*data];
  707.       *(rbcur++) = ri->blu[*(data++)];
  708.     }
  709.   write(ri->fd, ri->rowbuf, ri->xres*3);
  710. }
  711.  
  712. /* Writes out RGB and greyscale ascii rows */
  713. static void
  714. pnmsaverow_ascii (PNMRowInfo    *ri,
  715.           unsigned char *data)
  716. {
  717.   int i;
  718.   char *rbcur = ri->rowbuf;
  719.  
  720.   for (i = 0; i < ri->xres*ri->np; i++)
  721.     {
  722.       sprintf((char *) rbcur,"%d\n", 0xff & *(data++));
  723.       rbcur += strlen(rbcur);
  724.     }
  725.   write(ri->fd, ri->rowbuf, strlen((char *) ri->rowbuf));
  726. }
  727.  
  728. /* Writes out RGB and greyscale ascii rows */
  729. static void
  730. pnmsaverow_ascii_indexed (PNMRowInfo    *ri,
  731.               unsigned char *data)
  732. {
  733.   int i;
  734.   char *rbcur = ri->rowbuf;
  735.  
  736.   for (i = 0; i < ri->xres; i++)
  737.     {
  738.       sprintf((char *) rbcur,"%d\n", 0xff & ri->red[*(data)]);
  739.       rbcur += strlen(rbcur);
  740.       sprintf((char *) rbcur,"%d\n", 0xff & ri->grn[*(data)]);
  741.       rbcur += strlen(rbcur);
  742.       sprintf((char *) rbcur,"%d\n", 0xff & ri->blu[*(data++)]);
  743.       rbcur += strlen(rbcur);
  744.     }
  745.   write(ri->fd, ri->rowbuf, strlen((char *) ri->rowbuf));
  746. }
  747.  
  748. static gint
  749. save_image (gchar  *filename,
  750.         gint32  image_ID,
  751.         gint32  drawable_ID)
  752. {
  753.   GimpPixelRgn pixel_rgn;
  754.   GimpDrawable *drawable;
  755.   GimpImageType drawable_type;
  756.   PNMRowInfo rowinfo;
  757.   void (*saverow) (PNMRowInfo *, unsigned char *) = NULL;
  758.   unsigned char red[256];
  759.   unsigned char grn[256];
  760.   unsigned char blu[256];
  761.   unsigned char *data, *d;
  762.   char *rowbuf;
  763.   char buf[BUFLEN];
  764.   char *temp;
  765.   int np = 0;
  766.   int xres, yres;
  767.   int ypos, yend;
  768.   int rowbufsize = 0;
  769.   int fd;
  770.  
  771.   drawable = gimp_drawable_get (drawable_ID);
  772.   drawable_type = gimp_drawable_type (drawable_ID);
  773.   gimp_pixel_rgn_init (&pixel_rgn, drawable,
  774.                0, 0, drawable->width, drawable->height, FALSE, FALSE);
  775.  
  776.   /*  Make sure we're not saving an image with an alpha channel  */
  777.   if (gimp_drawable_has_alpha (drawable_ID))
  778.     {
  779.       g_message (_("PNM save cannot handle images with alpha channels."));
  780.       return FALSE;
  781.     }
  782.  
  783.   temp = g_strdup_printf (_("Saving %s:"), filename);
  784.   gimp_progress_init (temp);
  785.   g_free (temp);
  786.  
  787.   /* open the file */
  788.   fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC | _O_BINARY, 0644);
  789.  
  790.   if (fd == -1)
  791.     {
  792.       g_message ("pnm: can't open \"%s\"", filename);
  793.       return FALSE;
  794.     }
  795.  
  796.   xres = drawable->width;
  797.   yres = drawable->height;
  798.  
  799.   /* write out magic number */
  800.   if (psvals.raw == FALSE)
  801.     switch (drawable_type)
  802.       {
  803.       case GIMP_GRAY_IMAGE:
  804.     write(fd, "P2\n", 3);
  805.     np = 1;
  806.     rowbufsize = xres * 4;
  807.     saverow = pnmsaverow_ascii;
  808.     break;
  809.       case GIMP_RGB_IMAGE:
  810.     write(fd, "P3\n", 3);
  811.     np = 3;
  812.     rowbufsize = xres * 12;
  813.     saverow = pnmsaverow_ascii;
  814.     break;
  815.       case GIMP_INDEXED_IMAGE:
  816.     write(fd, "P3\n", 3);
  817.     np = 1;
  818.     rowbufsize = xres * 12;
  819.     saverow = pnmsaverow_ascii_indexed;
  820.     break;
  821.       default:
  822.     g_warning ("pnm: unknown drawable_type\n");
  823.     return FALSE;
  824.       }
  825.   else if (psvals.raw == TRUE)
  826.     switch (drawable_type)
  827.       {
  828.       case GIMP_GRAY_IMAGE:
  829.     write(fd, "P5\n", 3);
  830.     np = 1;
  831.     rowbufsize = xres;
  832.     saverow = pnmsaverow_raw;
  833.     break;
  834.       case GIMP_RGB_IMAGE:
  835.     write(fd, "P6\n", 3);
  836.     np = 3;
  837.     rowbufsize = xres * 3;
  838.     saverow = pnmsaverow_raw;
  839.     break;
  840.       case GIMP_INDEXED_IMAGE:
  841.     write(fd, "P6\n", 3);
  842.     np = 1;
  843.     rowbufsize = xres * 3;
  844.     saverow = pnmsaverow_raw_indexed;
  845.     break;
  846.       default:
  847.     g_warning ("pnm: unknown drawable_type\n");
  848.     return FALSE;
  849.     }
  850.  
  851.   if (drawable_type == GIMP_INDEXED_IMAGE)
  852.     {
  853.       int i;
  854.       guchar *cmap;
  855.       int colors;
  856.  
  857.       cmap = gimp_image_get_cmap (image_ID, &colors);
  858.  
  859.       for (i = 0; i < colors; i++)
  860.     {
  861.       red[i] = *cmap++;
  862.       grn[i] = *cmap++;
  863.       blu[i] = *cmap++;
  864.     }
  865.  
  866.       rowinfo.red = red;
  867.       rowinfo.grn = grn;
  868.       rowinfo.blu = blu;
  869.     }
  870.  
  871.   /* allocate a buffer for retrieving information from the pixel region  */
  872.   data = g_new (guchar, gimp_tile_height () * drawable->width * drawable->bpp);
  873.  
  874.   /* write out comment string */
  875.   write (fd, SAVE_COMMENT_STRING, strlen (SAVE_COMMENT_STRING));
  876.  
  877.   /* write out resolution and maxval */
  878.   sprintf (buf, "%d %d\n255\n", xres, yres);
  879.   write (fd, buf, strlen(buf));
  880.  
  881.   rowbuf = g_malloc (rowbufsize + 1);
  882.   rowinfo.fd = fd;
  883.   rowinfo.rowbuf = rowbuf;
  884.   rowinfo.xres = xres;
  885.   rowinfo.np = np;
  886.  
  887.   d = NULL; /* only to please the compiler */
  888.  
  889.   /* Write the body out */
  890.   for (ypos = 0; ypos < yres; ypos++)
  891.     {      
  892.       if ((ypos % gimp_tile_height ()) == 0)
  893.     {
  894.       yend = ypos + gimp_tile_height ();
  895.       yend = MIN (yend, yres);
  896.       gimp_pixel_rgn_get_rect (&pixel_rgn, data,
  897.                    0, ypos, xres, (yend - ypos));
  898.       d = data;
  899.     }
  900.  
  901.       (*saverow)(&rowinfo, d);
  902.       d += xres*np;
  903.  
  904.       if ((ypos % 20) == 0)
  905.     gimp_progress_update ((double) ypos / (double) yres);
  906.     }
  907.  
  908.   /* close the file */
  909.   close (fd);
  910.  
  911.   g_free (rowbuf);
  912.   g_free (data);
  913.  
  914.   gimp_drawable_detach (drawable);
  915.  
  916.   return TRUE;
  917. }
  918.  
  919. static gint
  920. save_dialog (void)
  921. {
  922.   GtkWidget *dlg;
  923.   GtkWidget *frame;
  924.  
  925.   dlg = gimp_dialog_new (_("Save as PNM"), "pnm",
  926.              gimp_standard_help_func, "filters/pnm.html",
  927.              GTK_WIN_POS_MOUSE,
  928.              FALSE, TRUE, FALSE,
  929.  
  930.              _("OK"), save_ok_callback,
  931.              NULL, NULL, NULL, TRUE, FALSE,
  932.              _("Cancel"), gtk_widget_destroy,
  933.              NULL, 1, NULL, FALSE, TRUE,
  934.  
  935.              NULL);
  936.  
  937.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  938.               GTK_SIGNAL_FUNC (gtk_main_quit),
  939.               NULL);
  940.  
  941.   /*  file save type  */
  942.   frame = gimp_radio_group_new2 (TRUE, _("Data Formatting"),
  943.                  gimp_radio_button_update,
  944.                  &psvals.raw, (gpointer) psvals.raw,
  945.  
  946.                  _("Raw"),   (gpointer) TRUE, NULL,
  947.                  _("Ascii"), (gpointer) FALSE, NULL,
  948.  
  949.                  NULL);
  950.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  951.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, FALSE, TRUE, 0);
  952.  
  953.   gtk_widget_show (frame);
  954.  
  955.   gtk_widget_show (dlg);
  956.  
  957.   gtk_main ();
  958.   gdk_flush ();
  959.  
  960.   return psint.run;
  961. }
  962.  
  963. static void
  964. save_ok_callback (GtkWidget *widget,
  965.           gpointer   data)
  966. {
  967.   psint.run = TRUE;
  968.  
  969.   gtk_widget_destroy (GTK_WIDGET (data));
  970. }
  971.  
  972. /**************** FILE SCANNER UTILITIES **************/
  973.  
  974. /* pnmscanner_create ---
  975.  *    Creates a new scanner based on a file descriptor.  The
  976.  *    look ahead buffer is one character initially.
  977.  */
  978. static PNMScanner *
  979. pnmscanner_create (gint fd)
  980. {
  981.   PNMScanner *s;
  982.  
  983.   s = (PNMScanner *)g_malloc(sizeof(PNMScanner));
  984.   s->fd = fd;
  985.   s->inbuf = 0;
  986.   s->eof = !read(s->fd, &(s->cur), 1);
  987.   return(s);
  988. }
  989.  
  990. /* pnmscanner_destroy ---
  991.  *    Destroys a scanner and its resources.  Doesn't close the fd.
  992.  */
  993. static void
  994. pnmscanner_destroy (PNMScanner *s)
  995. {
  996.   if (s->inbuf) g_free(s->inbuf);
  997.   g_free(s);
  998. }
  999.  
  1000. /* pnmscanner_createbuffer ---
  1001.  *    Creates a buffer so we can do buffered reads.
  1002.  */
  1003. static void
  1004. pnmscanner_createbuffer (PNMScanner *s,
  1005.              gint        bufsize)
  1006. {
  1007.   s->inbuf = g_malloc(sizeof(char)*bufsize);
  1008.   s->inbufsize = bufsize;
  1009.   s->inbufpos = 0;
  1010.   s->inbufvalidsize = read(s->fd, s->inbuf, bufsize);
  1011. }
  1012.  
  1013. /* pnmscanner_gettoken ---
  1014.  *    Gets the next token, eating any leading whitespace.
  1015.  */
  1016. static void
  1017. pnmscanner_gettoken (PNMScanner *s,
  1018.              gchar      *buf,
  1019.              gint        bufsize)
  1020. {
  1021.   int ctr=0;
  1022.  
  1023.   pnmscanner_eatwhitespace(s);
  1024.   while (!(s->eof) && !isspace(s->cur) && (s->cur != '#') && (ctr<bufsize))
  1025.     {
  1026.       buf[ctr++] = s->cur;
  1027.       pnmscanner_getchar(s);
  1028.     }
  1029.   buf[ctr] = '\0';
  1030. }
  1031.  
  1032. /* pnmscanner_getsmalltoken ---
  1033.  *    Gets the next char, eating any leading whitespace.
  1034.  */
  1035. static void
  1036. pnmscanner_getsmalltoken (PNMScanner *s,
  1037.               gchar      *buf)
  1038. {
  1039.   pnmscanner_eatwhitespace(s);
  1040.   if (!(s->eof) && !isspace(s->cur) && (s->cur != '#'))
  1041.     {
  1042.       *buf = s->cur;
  1043.       pnmscanner_getchar(s);
  1044.     }
  1045. }
  1046.  
  1047. /* pnmscanner_getchar ---
  1048.  *    Reads a character from the input stream
  1049.  */
  1050. static void
  1051. pnmscanner_getchar (PNMScanner *s)
  1052. {
  1053.   if (s->inbuf)
  1054.     {
  1055.       s->cur = s->inbuf[s->inbufpos++];
  1056.       if (s->inbufpos >= s->inbufvalidsize)
  1057.     {
  1058.       if (s->inbufsize > s->inbufvalidsize)
  1059.         s->eof = 1;
  1060.       else
  1061.         s->inbufvalidsize = read(s->fd, s->inbuf, s->inbufsize);
  1062.       s->inbufpos = 0;
  1063.     }
  1064.     }
  1065.   else
  1066.     s->eof = !read(s->fd, &(s->cur), 1);
  1067. }
  1068.  
  1069. /* pnmscanner_eatwhitespace ---
  1070.  *    Eats up whitespace from the input and returns when done or eof.
  1071.  *    Also deals with comments.
  1072.  */
  1073. static void
  1074. pnmscanner_eatwhitespace (PNMScanner *s)
  1075. {
  1076.   int state = 0;
  1077.  
  1078.   while (!(s->eof) && (state != -1))
  1079.     {
  1080.       switch (state)
  1081.     {
  1082.     case 0:  /* in whitespace */
  1083.       if (s->cur == '#')
  1084.         {
  1085.           state = 1;  /* goto comment */
  1086.           pnmscanner_getchar(s);
  1087.         }
  1088.       else if (!isspace(s->cur))
  1089.         state = -1;
  1090.       else
  1091.         pnmscanner_getchar(s);
  1092.       break;
  1093.  
  1094.     case 1:  /* in comment */
  1095.       if (s->cur == '\n')
  1096.         state = 0;  /* goto whitespace */
  1097.       pnmscanner_getchar(s);
  1098.       break;
  1099.     }
  1100.     }
  1101. }
  1102.