[gegl/gsoc2011-opencl: 7/18] exr-save: add new op from Rasmus Hahn



commit 2be24fb9a6e541ebe25ee267e9974b4f2413aaf8
Author: Ãyvind KolÃs <pippin gimp org>
Date:   Mon Jul 18 20:56:50 2011 +0100

    exr-save: add new op from Rasmus Hahn
    
    Op from Rasmus Hahn - rassahah googlemail com, original email follows:
    
    Hi,
    
    i have written an operation for gegl that writes OpenEXR float images, since
    i noticed there does not seem to be one already and i could use it. This
    version is quite usable within it's limitations. Limitations currently are:
    
     - writes only 32 bit linear floating point images.
     - only writes Y or RGB images with optional alpha.
     - does not make use of OpenEXR specialties like layers, mipmaps
       and multichannel.
    
    It does support the tiled format; for simplicity, however, tiles are always
    square.
    
    I would like to see it included in the distribution; for now i have attached
    a patch, which creates a new file gegl/operations/external/exr-save.cc and
    makes a modification to the Makefile.am to build it. The patch is against
    version gegl-0.1.6, but works with the git-version (as of 2011-07-13), too.
    Sorry, for not providing a URL, but i do not have one right now.
    
    The built operator is named 'gegl:exr-save' and has two properties:
     path (string) - the filename to write to
     tile (int) - the width of a tile (used for both x and y size);
       the default is 0, which means a scanline file is written.
    
    Any comments or suggestions for this?
    
    Greetings - Rasmus

 operations/external/Makefile.am |    5 +
 operations/external/exr-save.cc |  200 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 205 insertions(+), 0 deletions(-)
---
diff --git a/operations/external/Makefile.am b/operations/external/Makefile.am
index 90419bf..3663a3c 100644
--- a/operations/external/Makefile.am
+++ b/operations/external/Makefile.am
@@ -66,6 +66,11 @@ ops += exr-load.la
 exr_load_la_SOURCES = exr-load.cpp
 exr_load_la_LIBADD = $(op_libs) $(OPENEXR_LIBS)
 exr_load_la_CXXFLAGS = $(AM_CFLAGS) $(OPENEXR_CFLAGS)
+
+ops += exr-save.la
+exr_save_la_SOURCES = exr-save.cc
+exr_save_la_LIBADD = $(op_libs) $(OPENEXR_LIBS)
+exr_save_la_CXXFLAGS = $(AM_CFLAGS) $(OPENEXR_CFLAGS)
 endif
 
 if HAVE_SDL
diff --git a/operations/external/exr-save.cc b/operations/external/exr-save.cc
new file mode 100644
index 0000000..1eddc62
--- /dev/null
+++ b/operations/external/exr-save.cc
@@ -0,0 +1,200 @@
+#ifdef GEGL_CHANT_PROPERTIES
+
+gegl_chant_string  (path, "File", "", "path of file to write to.")
+gegl_chant_int  (tile, "Tile", 0, 2048, 0, "tile size to use.")
+
+#else
+
+#define GEGL_CHANT_TYPE_SINK
+#define GEGL_CHANT_C_FILE "exr-save.cc"
+
+extern "C" {
+#include "gegl-chant.h"
+} /* extern "C" */
+
+#include "config.h"
+#include <exception>
+#include <OpenEXR/ImfTiledOutputFile.h>
+#include <OpenEXR/ImfOutputFile.h>
+#include <OpenEXR/ImfChannelList.h>
+
+/**
+ * create an Imf::Header for writing up to 4 channels (given in d).
+ * d must be between 1 and 4.
+ */
+static Imf::Header create_header (int w, int h, int d)
+{
+  Imf::Header header (w, h);
+  Imf::FrameBuffer fbuf;
+  if (d <= 2) {
+    header.channels ().insert ("Y", Imf::Channel (Imf::FLOAT));
+  } else {
+    header.channels ().insert ("R", Imf::Channel (Imf::FLOAT));
+    header.channels ().insert ("G", Imf::Channel (Imf::FLOAT));
+    header.channels ().insert ("B", Imf::Channel (Imf::FLOAT));
+  }
+  if (d == 2 || d == 4) {
+    header.channels ().insert ("A", Imf::Channel (Imf::FLOAT));
+  }
+  return header;
+}
+
+/**
+ * create an Imf::FrameBuffer object for w*h*d floats and return it.
+ */
+static Imf::FrameBuffer create_frame_buffer
+  (int w, int h, int d, const float *data)
+{
+  Imf::FrameBuffer fbuf;
+  if (d <= 2) {
+    fbuf.insert ("Y", Imf::Slice (Imf::FLOAT, (char *) (&data[0] + 0),
+        d * sizeof *data, d * sizeof *data * w));
+  } else {
+    fbuf.insert ("R", Imf::Slice (Imf::FLOAT, (char *) (&data[0] + 0),
+        d * sizeof *data, d * sizeof *data * w));
+    fbuf.insert ("G", Imf::Slice (Imf::FLOAT, (char *) (&data[0] + 1),
+        d * sizeof *data, d * sizeof *data * w));
+    fbuf.insert ("B", Imf::Slice (Imf::FLOAT, (char *) (&data[0] + 2),
+        d * sizeof *data, d * sizeof *data * w));
+  }
+  if (d == 2 || d == 4) {
+    fbuf.insert ("A", Imf::Slice (Imf::FLOAT, (char *) (&data[0] + (d - 1)),
+        d * sizeof *data, d * sizeof *data * w));
+  }
+  return fbuf;
+}
+
+/**
+ * write the float buffer to an exr file with tile-size tw x th.
+ * The buffer must contain w*h*d floats.
+ * Currently supported are only values 1-4 for d:
+ * d=1: write a single Y-channel.
+ * d=2: write Y and A.
+ * d=3: write RGB.
+ * d=4: write RGB and A.
+ */
+static void write_tiled_exr
+  (const float *pixels, int w, int h, int d, int tw, int th,
+    const std::string &filename)
+{
+  Imf::Header header (create_header (w, h, d));
+  header.setTileDescription (Imf::TileDescription (tw, th, Imf::ONE_LEVEL));
+  Imf::TiledOutputFile out (filename.c_str (), header);
+  Imf::FrameBuffer fbuf (create_frame_buffer (w, h, d, pixels));
+  out.setFrameBuffer (fbuf);
+  out.writeTiles (0, out.numXTiles () - 1, 0, out.numYTiles () - 1);
+}
+
+/**
+ * write an openexr file in scanline mode.
+ * pixels contains the image data as an array of w*h*d floats.
+ * The data is written to the file named filename.
+ */
+static void write_scanline_exr
+  (const float *pixels, int w, int h, int d, const std::string &filename)
+{
+  Imf::Header header (create_header (w, h, d));
+  Imf::OutputFile out (filename.c_str (), header);
+  Imf::FrameBuffer fbuf (create_frame_buffer (w, h, d, pixels));
+  out.setFrameBuffer (fbuf);
+  out.writePixels (h);
+}
+
+/**
+ * write the given pixel buffer, which is w * h * d to filename using the
+ * tilesize as tile width and height. This is the only function calling
+ * the openexr lib and therefore should be exception save.
+ */
+static void exr_save_process
+  (const float *pixels, int w, int h, int d, int tile_size,
+    const std::string &filename)
+{
+  if (tile_size == 0) {
+    /*
+     * write a scanline exr image.
+     */
+    write_scanline_exr (pixels, w, h, d, filename);
+  } else {
+    /*
+     * write a tiled exr image.
+     */
+    write_tiled_exr (pixels, w, h, d, tile_size, tile_size, filename);
+  }
+}
+
+/**
+ * main entry function for the exr saver.
+ */
+static gboolean gegl_exr_save_process
+  (GeglOperation *operation, GeglBuffer *input, const GeglRectangle *rect)
+{
+  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+  std::string filename (o->path);
+  std::string output_format;
+  int tile_size (o->tile);
+  /*
+   * determine the number of channels in the input buffer and determine
+   * format, data is written with. Currently this only checks for the
+   * numbers of channels and writes only Y or RGB data with optional alpha.
+   */
+  const Babl *original_format = gegl_buffer_get_format (input);
+  unsigned depth = babl_format_get_n_components (original_format);
+  switch (depth) {
+  case 1: output_format = "Y float"; break;
+  case 2: output_format = "YA float"; break;
+  case 3: output_format = "RGB float"; break;
+  case 4: output_format = "RGBA float"; break;
+  default:
+    g_warning ("exr-save: cannot write exr with depth %d.", depth);
+    return FALSE;
+    break;
+  }
+  /*
+   * get the pixel data. The position of the rectangle is effectively
+   * ignored. Always write a file width x height; @todo: check if exr
+   * can set the origin.
+   */
+  float *pixels
+    = (float *) g_malloc (rect->width * rect->height * depth * sizeof *pixels);
+  if (pixels == 0) {
+    g_warning ("exr-save: could allocate %d*%d*%d pixels.",
+      rect->width, rect->height, depth);
+    return FALSE;
+  }
+  gegl_buffer_get
+    (input, 1.0, rect, babl_format (output_format.c_str ()),
+      pixels, GEGL_AUTO_ROWSTRIDE);
+  bool status;
+  try {
+    exr_save_process
+      (pixels, rect->width, rect->height, depth, tile_size, filename);
+    status = TRUE;
+  } catch (std::exception &e) {
+    g_warning ("exr-save: failed to write to '%s': %s",
+      filename.c_str (), e.what ());
+    status = FALSE;
+  }
+  g_free (pixels);
+  return status;
+}
+
+static void gegl_chant_class_init (GeglChantClass *klass)
+{
+  GeglOperationClass *operation_class;
+  GeglOperationSinkClass *sink_class;
+
+  operation_class = GEGL_OPERATION_CLASS (klass);
+  sink_class      = GEGL_OPERATION_SINK_CLASS (klass);
+
+  sink_class->process = gegl_exr_save_process;
+  sink_class->needs_full = TRUE;
+
+  operation_class->name        = "gegl:exr-save";
+  operation_class->categories  = "output";
+  operation_class->description = "OpenEXR image saver";
+
+  gegl_extension_handler_register_saver (".exr", "gegl:exr-save");
+}
+
+#endif
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]