Blob Blame History Raw
/* 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 <http://www.gnu.org/licenses/>.
 *
 * Copyright 2011 Mukund Sivaraman <muks@banu.com>
 */

#include "config.h"
#include <glib/gi18n-lib.h>


#ifdef GEGL_CHANT_PROPERTIES

gegl_chant_string (path, _("File"), "",
                   _("Target path and filename, use '-' for stdout"))
gegl_chant_int    (quality, _("Quality"), 1, 100, 90,
                   _("JPEG compression quality (between 1 and 100)"))
gegl_chant_int    (smoothing, _("Smoothing"),
                   0, 100, 0, _("Smoothing factor from 1 to 100; 0 disables smoothing"))
gegl_chant_boolean (optimize, _("Optimize"), TRUE,
                    _("Use optimized huffman tables"))
gegl_chant_boolean (progressive, _("Progressive"), TRUE,
                    _("Create progressive JPEG images"))
gegl_chant_boolean (grayscale, _("Grayscale"), FALSE,
                    _("Create a grayscale (monochrome) image"))

#else

#define GEGL_CHANT_TYPE_SINK
#define GEGL_CHANT_C_FILE       "jpg-save.c"

#include "gegl-chant.h"
#include <stdio.h>
#include <jpeglib.h>

static gint
gegl_buffer_export_jpg (GeglBuffer  *gegl_buffer,
                        const gchar *path,
                        gint         quality,
                        gint         smoothing,
                        gboolean     optimize,
                        gboolean     progressive,
                        gboolean     grayscale,
                        gint         src_x,
                        gint         src_y,
                        gint         width,
                        gint         height)
{
  FILE *fp;
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
  JSAMPROW row_pointer[1];
  const Babl *format;

  if (!strcmp (path, "-"))
    {
      fp = stdout;
    }
  else
    {
      fp = fopen (path, "wb");
    }
  if (!fp)
    {
      return -1;
    }

  cinfo.err = jpeg_std_error (&jerr);
  jpeg_create_compress (&cinfo);

  jpeg_stdio_dest (&cinfo, fp);

  cinfo.image_width = width;
  cinfo.image_height = height;

  if (!grayscale)
    {
      cinfo.input_components = 3;
      cinfo.in_color_space = JCS_RGB;
    }
  else
    {
      cinfo.input_components = 1;
      cinfo.in_color_space = JCS_GRAYSCALE;
    }

  jpeg_set_defaults (&cinfo);
  jpeg_set_quality (&cinfo, quality, TRUE);
  cinfo.smoothing_factor = smoothing;
  cinfo.optimize_coding = optimize;
  if (progressive)
    jpeg_simple_progression (&cinfo);

  /* Use 1x1,1x1,1x1 MCUs and no subsampling */
  cinfo.comp_info[0].h_samp_factor = 1;
  cinfo.comp_info[0].v_samp_factor = 1;

  if (!grayscale)
    {
      cinfo.comp_info[1].h_samp_factor = 1;
      cinfo.comp_info[1].v_samp_factor = 1;
      cinfo.comp_info[2].h_samp_factor = 1;
      cinfo.comp_info[2].v_samp_factor = 1;
    }

  /* No restart markers */
  cinfo.restart_interval = 0;
  cinfo.restart_in_rows = 0;

  jpeg_start_compress (&cinfo, TRUE);

  if (!grayscale)
    {
      format = babl_format ("R'G'B' u8");
      row_pointer[0] = g_malloc (width * 3);
    }
  else
    {
      format = babl_format ("Y' u8");
      row_pointer[0] = g_malloc (width);
    }

  while (cinfo.next_scanline < cinfo.image_height) {
    GeglRectangle rect;

    rect.x = src_x;
    rect.y = src_y + cinfo.next_scanline;
    rect.width = width;
    rect.height = 1;

    gegl_buffer_get (gegl_buffer, &rect, 1.0, format,
                     row_pointer[0], GEGL_AUTO_ROWSTRIDE,
                     GEGL_ABYSS_NONE);

    jpeg_write_scanlines (&cinfo, row_pointer, 1);
  }

  jpeg_finish_compress (&cinfo);
  jpeg_destroy_compress (&cinfo);

  g_free (row_pointer[0]);

  if (stdout != fp)
    fclose (fp);

  return 0;
}

static gboolean
gegl_jpg_save_process (GeglOperation       *operation,
                       GeglBuffer          *input,
                       const GeglRectangle *result,
                       int                  level)
{
  GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);

  gegl_buffer_export_jpg (input, o->path, o->quality, o->smoothing,
                          o->optimize, o->progressive, o->grayscale,
                          result->x, result->y,
                          result->width, result->height);
  return  TRUE;
}


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_jpg_save_process;
  sink_class->needs_full = TRUE;

  gegl_operation_class_set_keys (operation_class,
    "name"        , "gegl:jpg-save",
    "categories"  , "output",
    "description" ,
    _("JPEG image saver (passes the buffer through, saves as a side-effect)"),
    NULL);

  gegl_extension_handler_register_saver (".jpg", "gegl:jpg-save");
}

#endif