/* This file is an image processing operation for GEGL * * GEGL is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * GEGL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with GEGL; if not, see . * * Copyright 2009 Henrik Akesson */ #include "config.h" #include #ifdef GEGL_CHANT_PROPERTIES gegl_chant_file_path (path, _("File"), "", _("Path of file to load.")) #else #define GEGL_CHANT_TYPE_SOURCE #define GEGL_CHANT_C_FILE "ppm-load.c" #define MAX_CHARS_IN_ROW 500 #define CHANNEL_COUNT 3 #define ASCII_P 80 #include "gegl-chant.h" #include #include typedef enum { PIXMAP_ASCII = 51, PIXMAP_RAW = 54, } map_type; typedef struct { map_type type; gint width; gint height; gsize numsamples; /* width * height * channels */ gsize bpc; /* bytes per channel */ guchar *data; } pnm_struct; static gboolean ppm_load_read_header(FILE *fp, pnm_struct *img) { /* PPM Headers Variable Declaration */ gchar *ptr; //gchar *retval; gchar header[MAX_CHARS_IN_ROW]; gint maxval; /* Check the PPM file Type P2 or P5 */ fgets (header,MAX_CHARS_IN_ROW,fp); if (header[0] != ASCII_P || (header[1] != PIXMAP_ASCII && header[1] != PIXMAP_RAW)) { g_warning ("Image is not a portable pixmap"); return FALSE; } img->type = header[1]; /* Check the Comments */ fgets (header,MAX_CHARS_IN_ROW,fp); while(header[0] == '#') { fgets (header,MAX_CHARS_IN_ROW,fp); } /* Get Width and Height */ img->width = strtol (header,&ptr,0); img->height = atoi (ptr); img->numsamples = img->width * img->height * CHANNEL_COUNT; fgets (header,MAX_CHARS_IN_ROW,fp); maxval = strtol (header,&ptr,0); if ((maxval != 255) && (maxval != 65535)) { g_warning ("Image is not an 8-bit or 16-bit portable pixmap"); return FALSE; } switch (maxval) { case 255: img->bpc = sizeof (guchar); break; case 65535: img->bpc = sizeof (gushort); break; default: g_warning ("%s: Programmer stupidity error", G_STRLOC); } return TRUE; } static void ppm_load_read_image(FILE *fp, pnm_struct *img) { guint i; if (img->type == PIXMAP_RAW) { fread (img->data, img->bpc, img->numsamples, fp); /* Fix endianness if necessary */ if (img->bpc > 1) { gushort *ptr = (gushort *) img->data; for (i=0; i < img->numsamples; i++) { *ptr = GUINT16_FROM_BE (*ptr); ptr++; } } } else { /* Plain PPM format */ if (img->bpc == sizeof (guchar)) { guchar *ptr = img->data; for (i = 0; i < img->numsamples; i++) { guint sample; fscanf (fp, " %u", &sample); *ptr++ = sample; } } else if (img->bpc == sizeof (gushort)) { gushort *ptr = (gushort *) img->data; for (i = 0; i < img->numsamples; i++) { guint sample; fscanf (fp, " %u", &sample); *ptr++ = sample; } } else { g_warning ("%s: Programmer stupidity error", G_STRLOC); } } } static GeglRectangle get_bounding_box (GeglOperation *operation) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); GeglRectangle result = {0,0,0,0}; pnm_struct img; FILE *fp; fp = (!strcmp (o->path, "-") ? stdin : fopen (o->path,"rb") ); if (!fp) return result; if (!ppm_load_read_header (fp, &img)) goto out; switch (img.bpc) { case 1: gegl_operation_set_format (operation, "output", babl_format ("R'G'B' u8")); break; case 2: gegl_operation_set_format (operation, "output", babl_format ("R'G'B' u16")); break; default: g_warning ("%s: Programmer stupidity error", G_STRLOC); } result.width = img.width; result.height = img.height; out: if (stdin != fp) fclose (fp); return result; } static gboolean process (GeglOperation *operation, GeglBuffer *output, const GeglRectangle *result, gint level) { GeglChantO *o = GEGL_CHANT_PROPERTIES (operation); FILE *fp; pnm_struct img; GeglRectangle rect = {0,0,0,0}; gboolean ret = FALSE; fp = (!strcmp (o->path, "-") ? stdin : fopen (o->path,"rb")); if (!fp) return FALSE; if (!ppm_load_read_header (fp, &img)) goto out; rect.height = img.height; rect.width = img.width; /* Allocating Array Size */ img.data = (guchar*) g_malloc (img.numsamples * img.bpc); switch (img.bpc) { case 1: gegl_buffer_get (output, &rect, 1.0, babl_format ("R'G'B' u8"), img.data, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); break; case 2: gegl_buffer_get (output, &rect, 1.0, babl_format ("R'G'B' u16"), img.data, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); break; default: g_warning ("%s: Programmer stupidity error", G_STRLOC); } ppm_load_read_image (fp, &img); switch (img.bpc) { case 1: gegl_buffer_set (output, &rect, 0, babl_format ("R'G'B' u8"), img.data, GEGL_AUTO_ROWSTRIDE); break; case 2: gegl_buffer_set (output, &rect, 0, babl_format ("R'G'B' u16"), img.data, GEGL_AUTO_ROWSTRIDE); break; default: g_warning ("%s: Programmer stupidity error", G_STRLOC); } g_free (img.data); ret = TRUE; out: if (stdin != fp) fclose (fp); return ret; } static GeglRectangle get_cached_region (GeglOperation *operation, const GeglRectangle *roi) { return get_bounding_box (operation); } static void gegl_chant_class_init (GeglChantClass *klass) { GeglOperationClass *operation_class; GeglOperationSourceClass *source_class; operation_class = GEGL_OPERATION_CLASS (klass); source_class = GEGL_OPERATION_SOURCE_CLASS (klass); source_class->process = process; operation_class->get_bounding_box = get_bounding_box; operation_class->get_cached_region = get_cached_region; gegl_operation_class_set_keys (operation_class, "name" , "gegl:ppm-load", "categories" , "hidden", "description" , _("PPM image loader."), NULL); gegl_extension_handler_register (".ppm", "gegl:ppm-load"); } #endif