Blame operations/external/exr-save.cc

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 2011 Rasmus Hahn <rassahah@googlemail.com>
Packit Service 2781ba
 */
Packit Service 2781ba
Packit Service 2781ba
#ifdef GEGL_CHANT_PROPERTIES
Packit Service 2781ba
Packit Service 2781ba
gegl_chant_string  (path, "File", "", "path of file to write to.")
Packit Service 2781ba
gegl_chant_int  (tile, "Tile", 0, 2048, 0, "tile size to use.")
Packit Service 2781ba
Packit Service 2781ba
#else
Packit Service 2781ba
Packit Service 2781ba
#define GEGL_CHANT_TYPE_SINK
Packit Service 2781ba
#define GEGL_CHANT_C_FILE "exr-save.cc"
Packit Service 2781ba
Packit Service 2781ba
extern "C" {
Packit Service 2781ba
#include "gegl-chant.h"
Packit Service 2781ba
} /* extern "C" */
Packit Service 2781ba
Packit Service 2781ba
#include "config.h"
Packit Service 2781ba
#include <exception>
Packit Service 2781ba
#include <ImfTiledOutputFile.h>
Packit Service 2781ba
#include <ImfOutputFile.h>
Packit Service 2781ba
#include <ImfChannelList.h>
Packit Service 2781ba
Packit Service 2781ba
/**
Packit Service 2781ba
 * create an Imf::Header for writing up to 4 channels (given in d).
Packit Service 2781ba
 * d must be between 1 and 4.
Packit Service 2781ba
 */
Packit Service 2781ba
static Imf::Header
Packit Service 2781ba
create_header (int w,
Packit Service 2781ba
               int h,
Packit Service 2781ba
               int d)
Packit Service 2781ba
{
Packit Service 2781ba
  Imf::Header header (w, h);
Packit Service 2781ba
  Imf::FrameBuffer fbuf;
Packit Service 2781ba
Packit Service 2781ba
  if (d <= 2)
Packit Service 2781ba
    {
Packit Service 2781ba
      header.channels ().insert ("Y", Imf::Channel (Imf::FLOAT));
Packit Service 2781ba
    }
Packit Service 2781ba
  else
Packit Service 2781ba
    {
Packit Service 2781ba
      header.channels ().insert ("R", Imf::Channel (Imf::FLOAT));
Packit Service 2781ba
      header.channels ().insert ("G", Imf::Channel (Imf::FLOAT));
Packit Service 2781ba
      header.channels ().insert ("B", Imf::Channel (Imf::FLOAT));
Packit Service 2781ba
    }
Packit Service 2781ba
  if (d == 2 || d == 4)
Packit Service 2781ba
    {
Packit Service 2781ba
      header.channels ().insert ("A", Imf::Channel (Imf::FLOAT));
Packit Service 2781ba
    }
Packit Service 2781ba
  return header;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
/**
Packit Service 2781ba
 * create an Imf::FrameBuffer object for w*h*d floats and return it.
Packit Service 2781ba
 */
Packit Service 2781ba
static Imf::FrameBuffer
Packit Service 2781ba
create_frame_buffer (int          w,
Packit Service 2781ba
                     int          h,
Packit Service 2781ba
                     int          d,
Packit Service 2781ba
                     const float *data)
Packit Service 2781ba
{
Packit Service 2781ba
  Imf::FrameBuffer fbuf;
Packit Service 2781ba
Packit Service 2781ba
  if (d <= 2)
Packit Service 2781ba
    {
Packit Service 2781ba
      fbuf.insert ("Y", Imf::Slice (Imf::FLOAT, (char *) (&data[0] + 0),
Packit Service 2781ba
          d * sizeof *data, d * sizeof *data * w));
Packit Service 2781ba
    }
Packit Service 2781ba
  else
Packit Service 2781ba
    {
Packit Service 2781ba
      fbuf.insert ("R", Imf::Slice (Imf::FLOAT, (char *) (&data[0] + 0),
Packit Service 2781ba
          d * sizeof *data, d * sizeof *data * w));
Packit Service 2781ba
      fbuf.insert ("G", Imf::Slice (Imf::FLOAT, (char *) (&data[0] + 1),
Packit Service 2781ba
          d * sizeof *data, d * sizeof *data * w));
Packit Service 2781ba
      fbuf.insert ("B", Imf::Slice (Imf::FLOAT, (char *) (&data[0] + 2),
Packit Service 2781ba
          d * sizeof *data, d * sizeof *data * w));
Packit Service 2781ba
    }
Packit Service 2781ba
  if (d == 2 || d == 4)
Packit Service 2781ba
    {
Packit Service 2781ba
      fbuf.insert ("A", Imf::Slice (Imf::FLOAT, (char *) (&data[0] + (d - 1)),
Packit Service 2781ba
          d * sizeof *data, d * sizeof *data * w));
Packit Service 2781ba
    }
Packit Service 2781ba
  return fbuf;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
/**
Packit Service 2781ba
 * write the float buffer to an exr file with tile-size tw x th.
Packit Service 2781ba
 * The buffer must contain w*h*d floats.
Packit Service 2781ba
 * Currently supported are only values 1-4 for d:
Packit Service 2781ba
 * d=1: write a single Y-channel.
Packit Service 2781ba
 * d=2: write Y and A.
Packit Service 2781ba
 * d=3: write RGB.
Packit Service 2781ba
 * d=4: write RGB and A.
Packit Service 2781ba
 */
Packit Service 2781ba
static void
Packit Service 2781ba
write_tiled_exr (const float       *pixels,
Packit Service 2781ba
                 int                w,
Packit Service 2781ba
                 int                h,
Packit Service 2781ba
                 int                d,
Packit Service 2781ba
                 int                tw,
Packit Service 2781ba
                 int                th,
Packit Service 2781ba
                 const std::string &filename)
Packit Service 2781ba
{
Packit Service 2781ba
  Imf::Header header (create_header (w, h, d));
Packit Service 2781ba
  header.setTileDescription (Imf::TileDescription (tw, th, Imf::ONE_LEVEL));
Packit Service 2781ba
  Imf::TiledOutputFile out (filename.c_str (), header);
Packit Service 2781ba
  Imf::FrameBuffer fbuf (create_frame_buffer (w, h, d, pixels));
Packit Service 2781ba
  out.setFrameBuffer (fbuf);
Packit Service 2781ba
  out.writeTiles (0, out.numXTiles () - 1, 0, out.numYTiles () - 1);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
/**
Packit Service 2781ba
 * write an openexr file in scanline mode.
Packit Service 2781ba
 * pixels contains the image data as an array of w*h*d floats.
Packit Service 2781ba
 * The data is written to the file named filename.
Packit Service 2781ba
 */
Packit Service 2781ba
static void
Packit Service 2781ba
write_scanline_exr (const float       *pixels,
Packit Service 2781ba
                    int                w,
Packit Service 2781ba
                    int                h,
Packit Service 2781ba
                    int                d,
Packit Service 2781ba
                    const std::string &filename)
Packit Service 2781ba
{
Packit Service 2781ba
  Imf::Header header (create_header (w, h, d));
Packit Service 2781ba
  Imf::OutputFile out (filename.c_str (), header);
Packit Service 2781ba
  Imf::FrameBuffer fbuf (create_frame_buffer (w, h, d, pixels));
Packit Service 2781ba
  out.setFrameBuffer (fbuf);
Packit Service 2781ba
  out.writePixels (h);
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
/**
Packit Service 2781ba
 * write the given pixel buffer, which is w * h * d to filename using the
Packit Service 2781ba
 * tilesize as tile width and height. This is the only function calling
Packit Service 2781ba
 * the openexr lib and therefore should be exception save.
Packit Service 2781ba
 */
Packit Service 2781ba
static void
Packit Service 2781ba
exr_save_process (const float       *pixels,
Packit Service 2781ba
                  int                w,
Packit Service 2781ba
                  int                h,
Packit Service 2781ba
                  int                d,
Packit Service 2781ba
                  int                tile_size,
Packit Service 2781ba
                  const std::string &filename)
Packit Service 2781ba
{
Packit Service 2781ba
  if (tile_size == 0)
Packit Service 2781ba
    {
Packit Service 2781ba
      /* write a scanline exr image. */
Packit Service 2781ba
      write_scanline_exr (pixels, w, h, d, filename);
Packit Service 2781ba
    }
Packit Service 2781ba
  else
Packit Service 2781ba
    {
Packit Service 2781ba
      /* write a tiled exr image. */
Packit Service 2781ba
      write_tiled_exr (pixels, w, h, d, tile_size, tile_size, filename);
Packit Service 2781ba
    }
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
/**
Packit Service 2781ba
 * main entry function for the exr saver.
Packit Service 2781ba
 */
Packit Service 2781ba
static gboolean
Packit Service 2781ba
gegl_exr_save_process (GeglOperation       *operation,
Packit Service 2781ba
                       GeglBuffer          *input,
Packit Service 2781ba
                       const GeglRectangle *rect,
Packit Service 2781ba
                       gint                 level)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
Packit Service 2781ba
  std::string filename (o->path);
Packit Service 2781ba
  std::string output_format;
Packit Service 2781ba
  int tile_size (o->tile);
Packit Service 2781ba
  /*
Packit Service 2781ba
   * determine the number of channels in the input buffer and determine
Packit Service 2781ba
   * format, data is written with. Currently this only checks for the
Packit Service 2781ba
   * numbers of channels and writes only Y or RGB data with optional alpha.
Packit Service 2781ba
   */
Packit Service 2781ba
  const Babl *original_format = gegl_buffer_get_format (input);
Packit Service 2781ba
  unsigned depth = babl_format_get_n_components (original_format);
Packit Service 2781ba
  switch (depth)
Packit Service 2781ba
    {
Packit Service 2781ba
      case 1: output_format = "Y float";    break;
Packit Service 2781ba
      case 2: output_format = "YA float";   break;
Packit Service 2781ba
      case 3: output_format = "RGB float";  break;
Packit Service 2781ba
      case 4: output_format = "RGBA float"; break;
Packit Service 2781ba
      default:
Packit Service 2781ba
        g_warning ("exr-save: cannot write exr with depth %d.", depth);
Packit Service 2781ba
        return FALSE;
Packit Service 2781ba
        break;
Packit Service 2781ba
    }
Packit Service 2781ba
  /*
Packit Service 2781ba
   * get the pixel data. The position of the rectangle is effectively
Packit Service 2781ba
   * ignored. Always write a file width x height; @todo: check if exr
Packit Service 2781ba
   * can set the origin.
Packit Service 2781ba
   */
Packit Service 2781ba
  float *pixels
Packit Service 2781ba
    = (float *) g_malloc (rect->width * rect->height * depth * sizeof *pixels);
Packit Service 2781ba
  if (pixels == 0)
Packit Service 2781ba
    {
Packit Service 2781ba
      g_warning ("exr-save: could allocate %d*%d*%d pixels.",
Packit Service 2781ba
        rect->width, rect->height, depth);
Packit Service 2781ba
      return FALSE;
Packit Service 2781ba
    }
Packit Service 2781ba
  gegl_buffer_get (input, rect, 1.0, babl_format (output_format.c_str ()),
Packit Service 2781ba
                   pixels, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
Packit Service 2781ba
  bool status;
Packit Service 2781ba
  try
Packit Service 2781ba
    {
Packit Service 2781ba
      exr_save_process (pixels, rect->width, rect->height,
Packit Service 2781ba
                        depth, tile_size, filename);
Packit Service 2781ba
      status = TRUE;
Packit Service 2781ba
    }
Packit Service 2781ba
  catch (std::exception &e)
Packit Service 2781ba
    {
Packit Service 2781ba
      g_warning ("exr-save: failed to write to '%s': %s",
Packit Service 2781ba
         filename.c_str (), e.what ());
Packit Service 2781ba
      status = FALSE;
Packit Service 2781ba
    }
Packit Service 2781ba
  g_free (pixels);
Packit Service 2781ba
  return status;
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
static void gegl_chant_class_init (GeglChantClass *klass)
Packit Service 2781ba
{
Packit Service 2781ba
  GeglOperationClass *operation_class;
Packit Service 2781ba
  GeglOperationSinkClass *sink_class;
Packit Service 2781ba
Packit Service 2781ba
  operation_class = GEGL_OPERATION_CLASS (klass);
Packit Service 2781ba
  sink_class      = GEGL_OPERATION_SINK_CLASS (klass);
Packit Service 2781ba
Packit Service 2781ba
  sink_class->process = gegl_exr_save_process;
Packit Service 2781ba
  sink_class->needs_full = TRUE;
Packit Service 2781ba
Packit Service 2781ba
  gegl_operation_class_set_keys (operation_class,
Packit Service 2781ba
    "name"        , "gegl:exr-save",
Packit Service 2781ba
    "categories"  , "output",
Packit Service 2781ba
    "description" , "OpenEXR image saver",
Packit Service 2781ba
    NULL);
Packit Service 2781ba
Packit Service 2781ba
  gegl_extension_handler_register_saver (".exr", "gegl:exr-save");
Packit Service 2781ba
}
Packit Service 2781ba
Packit Service 2781ba
#endif
Packit Service 2781ba