Blame operations/external/ppm-load.c

Packit Service 2781ba
/* This file is an image processing operation for GEGL
Packit Service 2781ba
 *
Packit Service 2781ba
 * GEGL is free software; you can redistribute it and/or
Packit Service 2781ba
 * modify it under the terms of the GNU Lesser General Public
Packit Service 2781ba
 * License as published by the Free Software Foundation; either
Packit Service 2781ba
 * version 3 of the License, or (at your option) any later version.
Packit Service 2781ba
 *
Packit Service 2781ba
 * GEGL is distributed in the hope that it will be useful,
Packit Service 2781ba
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 2781ba
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 2781ba
 * Lesser General Public License for more details.
Packit Service 2781ba
 *
Packit Service 2781ba
 * You should have received a copy of the GNU Lesser General Public
Packit Service 2781ba
 * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
Packit Service 2781ba
 *
Packit Service 2781ba
 * Copyright 2009 Henrik Akesson <h.m.akesson (a) gmail.com>
Packit Service 2781ba
 */
Packit Service 2781ba
Packit Service 2781ba
#include "config.h"
Packit Service 2781ba
#include <glib/gi18n-lib.h>
Packit Service 2781ba
Packit Service 2781ba
Packit Service 2781ba
#ifdef GEGL_CHANT_PROPERTIES
Packit Service 2781ba
Packit Service 2781ba
gegl_chant_file_path (path, _("File"), "", _("Path of file to load."))
Packit Service 2781ba
Packit Service 2781ba
#else
Packit Service 2781ba
Packit Service 2781ba
#define GEGL_CHANT_TYPE_SOURCE
Packit Service 2781ba
#define GEGL_CHANT_C_FILE       "ppm-load.c"
Packit Service 2781ba
Packit Service 2781ba
#define MAX_CHARS_IN_ROW        500
Packit Service 2781ba
#define CHANNEL_COUNT           3
Packit Service 2781ba
#define ASCII_P                 80
Packit Service 2781ba
Packit Service 2781ba
#include "gegl-chant.h"
Packit Service 2781ba
#include <stdio.h>
Packit Service 2781ba
#include <stdlib.h>
Packit Service 3704c4
#include <errno.h>
Packit Service 2781ba
Packit Service 2781ba
typedef enum {
Packit Service 2781ba
  PIXMAP_ASCII  = 51,
Packit Service 2781ba
  PIXMAP_RAW    = 54,
Packit Service 2781ba
} map_type;
Packit Service 2781ba
Packit Service 2781ba
typedef struct {
Packit Service 2781ba
	map_type   type;
Packit Service 3704c4
	glong      width;
Packit Service 3704c4
	glong      height;
Packit Service 2781ba
        gsize      numsamples; /* width * height * channels */
Packit Service 2781ba
        gsize      bpc;        /* bytes per channel */
Packit Service 2781ba
	guchar    *data;
Packit Service 2781ba
} pnm_struct;
Packit Service 2781ba
Packit Service 2781ba
static gboolean
Packit Service 2781ba
ppm_load_read_header(FILE       *fp,
Packit Service 2781ba
                     pnm_struct *img)
Packit Service 2781ba
{
Packit Service 2781ba
    /* PPM Headers Variable Declaration */
Packit Service 2781ba
    gchar *ptr;
Packit Service 2781ba
    //gchar *retval;
Packit Service 2781ba
    gchar  header[MAX_CHARS_IN_ROW];
Packit Service 2781ba
    gint   maxval;
Packit Service 2781ba
Packit Service 3704c4
    /* Check the PPM file Type P3 or P6 */
Packit Service 2781ba
    fgets (header,MAX_CHARS_IN_ROW,fp);
Packit Service 2781ba
Packit Service 2781ba
    if (header[0] != ASCII_P ||
Packit Service 2781ba
        (header[1] != PIXMAP_ASCII &&
Packit Service 2781ba
         header[1] != PIXMAP_RAW))
Packit Service 2781ba
      {
Packit Service 2781ba
        g_warning ("Image is not a portable pixmap");
Packit Service 2781ba
        return FALSE;
Packit Service 2781ba
      }
Packit Service 2781ba
Packit Service 2781ba
    img->type = header[1];
Packit Service 2781ba
Packit Service 2781ba
    /* Check the Comments */
Packit Service 2781ba
    fgets (header,MAX_CHARS_IN_ROW,fp);
Packit Service 2781ba
    while(header[0] == '#')
Packit Service 2781ba
      {
Packit Service 2781ba
        fgets (header,MAX_CHARS_IN_ROW,fp);
Packit Service 2781ba
      }
Packit Service 2781ba
Packit Service 2781ba
    /* Get Width and Height */
Packit Service 3704c4
    errno = 0;
Packit Service 3704c4
    img->width  = strtol (header,&ptr,10);
Packit Service 3704c4
    if (errno)
Packit Service 3704c4
      {
Packit Service 3704c4
        g_warning ("Error reading width: %s", strerror(errno));
Packit Service 3704c4
        return FALSE;
Packit Service 3704c4
      }
Packit Service 3704c4
    else if (img->width < 0)
Packit Service 3704c4
      {
Packit Service 3704c4
        g_warning ("Error: width is negative");
Packit Service 3704c4
        return FALSE;
Packit Service 3704c4
      }
Packit Service 3704c4
Packit Service 3704c4
    img->height = strtol (ptr,&ptr,10);
Packit Service 3704c4
    if (errno)
Packit Service 3704c4
      {
Packit Service 3704c4
        g_warning ("Error reading height: %s", strerror(errno));
Packit Service 3704c4
        return FALSE;
Packit Service 3704c4
      }
Packit Service 3704c4
    else if (img->width < 0)
Packit Service 3704c4
      {
Packit Service 3704c4
        g_warning ("Error: height is negative");
Packit Service 3704c4
        return FALSE;
Packit Service 3704c4
      }
Packit Service 2781ba
Packit Service 2781ba
    fgets (header,MAX_CHARS_IN_ROW,fp);
Packit Service 3704c4
    maxval = strtol (header,&ptr,10);
Packit Service 2781ba
Packit Service 2781ba
    if ((maxval != 255) && (maxval != 65535))
Packit Service 2781ba
      {
Packit Service 2781ba
        g_warning ("Image is not an 8-bit or 16-bit portable pixmap");
Packit Service 2781ba
        return FALSE;
Packit Service 2781ba
      }
Packit Service 2781ba
Packit Service 2781ba
  switch (maxval)
Packit Service 2781ba
    {
Packit Service 2781ba
    case 255:
Packit Service 2781ba
      img->bpc = sizeof (guchar);
Packit Service 2781ba
      break;
Packit Service 2781ba
Packit Service 2781ba
    case 65535:
Packit Service 2781ba
      img->bpc = sizeof (gushort);
Packit Service 2781ba
      break;
Packit Service 2781ba
Packit Service 2781ba
    default:
Packit Service 2781ba
      g_warning ("%s: Programmer stupidity error", G_STRLOC);
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 3704c4
    /* Later on, img->numsamples is multiplied with img->bpc to allocate
Packit Service 3704c4
     * memory. Ensure it doesn't overflow. */
Packit Service 3704c4
    if (!img->width || !img->height ||
Packit Service 3704c4
        G_MAXSIZE / img->width / img->height / CHANNEL_COUNT < img->bpc)
Packit Service 3704c4
      {
Packit Service 3704c4
        g_warning ("Illegal width/height: %ld/%ld", img->width, img->height);
Packit Service 3704c4
        return FALSE;
Packit Service 3704c4
      }
Packit Service 3704c4
    img->numsamples = img->width * img->height * CHANNEL_COUNT;
Packit Service 3704c4
Packit Service 2781ba
    return TRUE;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
ppm_load_read_image(FILE       *fp,
Packit Service 2781ba
                    pnm_struct *img)
Packit Service 2781ba
{
Packit Service 2781ba
    guint   i;
Packit Service 2781ba
Packit Service 2781ba
    if (img->type == PIXMAP_RAW)
Packit Service 2781ba
      {
Packit Service 2781ba
        fread (img->data, img->bpc, img->numsamples, fp);
Packit Service 2781ba
Packit Service 2781ba
        /* Fix endianness if necessary */
Packit Service 2781ba
        if (img->bpc > 1)
Packit Service 2781ba
          {
Packit Service 2781ba
            gushort *ptr = (gushort *) img->data;
Packit Service 2781ba
Packit Service 2781ba
            for (i=0; i < img->numsamples; i++)
Packit Service 2781ba
              {
Packit Service 2781ba
                *ptr = GUINT16_FROM_BE (*ptr);
Packit Service 2781ba
                ptr++;
Packit Service 2781ba
              }
Packit Service 2781ba
          }
Packit Service 2781ba
      }
Packit Service 2781ba
    else
Packit Service 2781ba
      {
Packit Service 2781ba
        /* Plain PPM format */
Packit Service 2781ba
Packit Service 2781ba
        if (img->bpc == sizeof (guchar))
Packit Service 2781ba
          {
Packit Service 2781ba
            guchar *ptr = img->data;
Packit Service 2781ba
Packit Service 2781ba
            for (i = 0; i < img->numsamples; i++)
Packit Service 2781ba
              {
Packit Service 2781ba
                guint sample;
Packit Service 2781ba
                fscanf (fp, " %u", &sample);
Packit Service 2781ba
                *ptr++ = sample;
Packit Service 2781ba
              }
Packit Service 2781ba
          }
Packit Service 2781ba
        else if (img->bpc == sizeof (gushort))
Packit Service 2781ba
          {
Packit Service 2781ba
            gushort *ptr = (gushort *) img->data;
Packit Service 2781ba
Packit Service 2781ba
            for (i = 0; i < img->numsamples; i++)
Packit Service 2781ba
              {
Packit Service 2781ba
                guint sample;
Packit Service 2781ba
                fscanf (fp, " %u", &sample);
Packit Service 2781ba
                *ptr++ = sample;
Packit Service 2781ba
              }
Packit Service 2781ba
          }
Packit Service 2781ba
        else
Packit Service 2781ba
          {
Packit Service 2781ba
            g_warning ("%s: Programmer stupidity error", G_STRLOC);
Packit Service 2781ba
          }
Packit Service 2781ba
      }
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static GeglRectangle
Packit Service 2781ba
get_bounding_box (GeglOperation *operation)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglChantO   *o = GEGL_CHANT_PROPERTIES (operation);
Packit Service 2781ba
  GeglRectangle result = {0,0,0,0};
Packit Service 2781ba
  pnm_struct    img;
Packit Service 2781ba
  FILE         *fp;
Packit Service 2781ba
Packit Service 2781ba
  fp = (!strcmp (o->path, "-") ? stdin : fopen (o->path,"rb") );
Packit Service 2781ba
Packit Service 2781ba
  if (!fp)
Packit Service 2781ba
    return result;
Packit Service 2781ba
Packit Service 2781ba
  if (!ppm_load_read_header (fp, &img))
Packit Service 2781ba
    goto out;
Packit Service 2781ba
Packit Service 2781ba
  switch (img.bpc)
Packit Service 2781ba
    {
Packit Service 2781ba
    case 1:
Packit Service 2781ba
      gegl_operation_set_format (operation, "output",
Packit Service 2781ba
                                 babl_format ("R'G'B' u8"));
Packit Service 2781ba
      break;
Packit Service 2781ba
Packit Service 2781ba
    case 2:
Packit Service 2781ba
      gegl_operation_set_format (operation, "output",
Packit Service 2781ba
                                 babl_format ("R'G'B' u16"));
Packit Service 2781ba
      break;
Packit Service 2781ba
Packit Service 2781ba
    default:
Packit Service 2781ba
      g_warning ("%s: Programmer stupidity error", G_STRLOC);
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  result.width = img.width;
Packit Service 2781ba
  result.height = img.height;
Packit Service 2781ba
Packit Service 2781ba
 out:
Packit Service 2781ba
  if (stdin != fp)
Packit Service 2781ba
    fclose (fp);
Packit Service 2781ba
Packit Service 2781ba
  return result;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static gboolean
Packit Service 2781ba
process (GeglOperation       *operation,
Packit Service 2781ba
         GeglBuffer          *output,
Packit Service 2781ba
         const GeglRectangle *result,
Packit Service 2781ba
         gint                 level)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglChantO   *o = GEGL_CHANT_PROPERTIES (operation);
Packit Service 2781ba
  FILE         *fp;
Packit Service 2781ba
  pnm_struct    img;
Packit Service 2781ba
  GeglRectangle rect = {0,0,0,0};
Packit Service 2781ba
  gboolean      ret = FALSE;
Packit Service 2781ba
Packit Service 2781ba
  fp = (!strcmp (o->path, "-") ? stdin : fopen (o->path,"rb"));
Packit Service 2781ba
Packit Service 2781ba
  if (!fp)
Packit Service 2781ba
    return FALSE;
Packit Service 2781ba
Packit Service 2781ba
  if (!ppm_load_read_header (fp, &img))
Packit Service 2781ba
    goto out;
Packit Service 2781ba
Packit Service 2781ba
  /* Allocating Array Size */
Packit Service 3704c4
Packit Service 3704c4
  /* Should use g_try_malloc(), but this causes crashes elsewhere because the
Packit Service 3704c4
   * error signalled by returning FALSE isn't properly acted upon. Therefore
Packit Service 3704c4
   * g_malloc() is used here which aborts if the requested memory size can't be
Packit Service 3704c4
   * allocated causing a controlled crash. */
Packit Service 2781ba
  img.data = (guchar*) g_malloc (img.numsamples * img.bpc);
Packit Service 2781ba
Packit Service 3704c4
  /* No-op without g_try_malloc(), see above. */
Packit Service 3704c4
  if (! img.data)
Packit Service 3704c4
    {
Packit Service 3704c4
      g_warning ("Couldn't allocate %" G_GSIZE_FORMAT " bytes, giving up.", ((gsize)img.numsamples * img.bpc));
Packit Service 3704c4
      goto out;
Packit Service 3704c4
    }
Packit Service 3704c4
Packit Service 3704c4
  rect.height = img.height;
Packit Service 3704c4
  rect.width = img.width;
Packit Service 3704c4
Packit Service 2781ba
  switch (img.bpc)
Packit Service 2781ba
    {
Packit Service 2781ba
    case 1:
Packit Service 2781ba
      gegl_buffer_get (output, &rect, 1.0, babl_format ("R'G'B' u8"), img.data,
Packit Service 2781ba
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
Packit Service 2781ba
      break;
Packit Service 2781ba
Packit Service 2781ba
    case 2:
Packit Service 2781ba
      gegl_buffer_get (output, &rect, 1.0, babl_format ("R'G'B' u16"), img.data,
Packit Service 2781ba
                       GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
Packit Service 2781ba
      break;
Packit Service 2781ba
Packit Service 2781ba
    default:
Packit Service 2781ba
      g_warning ("%s: Programmer stupidity error", G_STRLOC);
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  ppm_load_read_image (fp, &img;;
Packit Service 2781ba
Packit Service 2781ba
  switch (img.bpc)
Packit Service 2781ba
    {
Packit Service 2781ba
    case 1:
Packit Service 2781ba
      gegl_buffer_set (output, &rect, 0, babl_format ("R'G'B' u8"), img.data,
Packit Service 2781ba
                       GEGL_AUTO_ROWSTRIDE);
Packit Service 2781ba
      break;
Packit Service 2781ba
Packit Service 2781ba
    case 2:
Packit Service 2781ba
      gegl_buffer_set (output, &rect, 0, babl_format ("R'G'B' u16"), img.data,
Packit Service 2781ba
                       GEGL_AUTO_ROWSTRIDE);
Packit Service 2781ba
      break;
Packit Service 2781ba
Packit Service 2781ba
    default:
Packit Service 2781ba
      g_warning ("%s: Programmer stupidity error", G_STRLOC);
Packit Service 2781ba
    }
Packit Service 2781ba
Packit Service 2781ba
  g_free (img.data);
Packit Service 2781ba
Packit Service 2781ba
  ret = TRUE;
Packit Service 2781ba
Packit Service 2781ba
 out:
Packit Service 2781ba
  if (stdin != fp)
Packit Service 2781ba
    fclose (fp);
Packit Service 2781ba
Packit Service 2781ba
  return ret;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static GeglRectangle
Packit Service 2781ba
get_cached_region (GeglOperation       *operation,
Packit Service 2781ba
                   const GeglRectangle *roi)
Packit Service 2781ba
{
Packit Service 2781ba
  return get_bounding_box (operation);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void
Packit Service 2781ba
gegl_chant_class_init (GeglChantClass *klass)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglOperationClass       *operation_class;
Packit Service 2781ba
  GeglOperationSourceClass *source_class;
Packit Service 2781ba
Packit Service 2781ba
  operation_class = GEGL_OPERATION_CLASS (klass);
Packit Service 2781ba
  source_class    = GEGL_OPERATION_SOURCE_CLASS (klass);
Packit Service 2781ba
Packit Service 2781ba
  source_class->process = process;
Packit Service 2781ba
  operation_class->get_bounding_box = get_bounding_box;
Packit Service 2781ba
  operation_class->get_cached_region = get_cached_region;
Packit Service 2781ba
Packit Service 2781ba
  gegl_operation_class_set_keys (operation_class,
Packit Service 2781ba
    "name"        , "gegl:ppm-load",
Packit Service 2781ba
    "categories"  , "hidden",
Packit Service 2781ba
    "description" , _("PPM image loader."),
Packit Service 2781ba
    NULL);
Packit Service 2781ba
Packit Service 2781ba
  gegl_extension_handler_register (".ppm", "gegl:ppm-load");
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
#endif