/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Pix
*
* Copyright (C) 2001, 2002, 2009 The Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
/* based upon file jpegtran.c from the libjpeg package, original copyright
* note follows:
*
* jpegtran.c
*
* Copyright (C) 1995-1997, Thomas G. Lane.
* This file is part of the Independent JPEG Group's software.
* For conditions of distribution and use, see the accompanying README file.
*
* This file contains a command-line user interface for JPEG transcoding.
* It is very similar to cjpeg.c, but provides lossless transcoding between
* different JPEG file formats. It also provides some lossless and sort-of-
* lossless transformations of JPEG data.
*/
#include
#ifdef HAVE_LIBJPEG
#include
#include
#include
#include
#include
#include
#include
#include
#include "jmemorydest.h"
#include "jmemorysrc.h"
#include "jpegtran.h"
#include "transupp.h"
GQuark
jpeg_error_quark (void)
{
static GQuark quark;
if (! quark)
quark = g_quark_from_static_string ("jpeg_error");
return quark;
}
/* error handler data */
struct error_handler_data {
struct jpeg_error_mgr pub;
sigjmp_buf setjmp_buffer;
GError **error;
};
static void
fatal_error_handler (j_common_ptr cinfo)
{
struct error_handler_data *errmgr;
char buffer[JMSG_LENGTH_MAX];
errmgr = (struct error_handler_data *) cinfo->err;
/* Create the message */
(* cinfo->err->format_message) (cinfo, buffer);
if ((errmgr->error != NULL) && (*errmgr->error == NULL))
g_set_error (errmgr->error,
JPEG_ERROR,
JPEG_ERROR_FAILED,
"Error interpreting JPEG image\n\n%s",
buffer);
siglongjmp (errmgr->setjmp_buffer, 1);
g_assert_not_reached ();
}
static void
output_message_handler (j_common_ptr cinfo)
{
/* void */
}
#if JPEG_LIB_VERSION < 80
static boolean
jtransform_perfect_transform (JDIMENSION image_width,
JDIMENSION image_height,
int MCU_width,
int MCU_height,
JXFORM_CODE transform)
{
boolean result = TRUE;
/* This function determines if it is possible to perform a lossless
jpeg transformation without trimming, based on the image dimensions
and MCU size. Further details at http://jpegclub.org/jpegtran. */
switch (transform) {
case JXFORM_FLIP_H:
case JXFORM_ROT_270:
if (image_width % (JDIMENSION) MCU_width)
result = FALSE;
break;
case JXFORM_FLIP_V:
case JXFORM_ROT_90:
if (image_height % (JDIMENSION) MCU_height)
result = FALSE;
break;
case JXFORM_TRANSVERSE:
case JXFORM_ROT_180:
if (image_width % (JDIMENSION) MCU_width)
result = FALSE;
if (image_height % (JDIMENSION) MCU_height)
result = FALSE;
break;
default:
break;
}
return result;
}
#endif
static gboolean
jpegtran_internal (struct jpeg_decompress_struct *srcinfo,
struct jpeg_compress_struct *dstinfo,
GthTransform transformation,
JCOPY_OPTION option,
JpegMcuAction mcu_action,
GError **error)
{
jpeg_transform_info transformoption;
jvirt_barray_ptr *src_coef_arrays;
jvirt_barray_ptr *dst_coef_arrays;
JXFORM_CODE transform;
switch (transformation) {
case GTH_TRANSFORM_NONE:
default:
transform = JXFORM_NONE;
break;
case GTH_TRANSFORM_FLIP_H:
transform = JXFORM_FLIP_H;
break;
case GTH_TRANSFORM_FLIP_V:
transform = JXFORM_FLIP_V;
break;
case GTH_TRANSFORM_TRANSPOSE:
transform = JXFORM_TRANSPOSE;
break;
case GTH_TRANSFORM_TRANSVERSE:
transform = JXFORM_TRANSVERSE;
break;
case GTH_TRANSFORM_ROTATE_90:
transform = JXFORM_ROT_90;
break;
case GTH_TRANSFORM_ROTATE_180:
transform = JXFORM_ROT_180;
break;
case GTH_TRANSFORM_ROTATE_270:
transform = JXFORM_ROT_270;
break;
}
transformoption.transform = transform;
transformoption.trim = (mcu_action == JPEG_MCU_ACTION_TRIM);
transformoption.force_grayscale = FALSE;
#if JPEG_LIB_VERSION >= 80
transformoption.perfect = (mcu_action == JPEG_MCU_ACTION_ABORT);
transformoption.crop = FALSE;
transformoption.crop_width_set = FALSE;
transformoption.crop_height_set = FALSE;
transformoption.crop_xoffset_set = FALSE;
transformoption.crop_yoffset_set = FALSE;
#ifdef LIBJPEG_TURBO_VERSION
transformoption.slow_hflip = FALSE;
#endif
#endif
/* Enable saving of extra markers that we want to copy */
jcopy_markers_setup (srcinfo, option);
/* Read file header */
(void) jpeg_read_header (srcinfo, TRUE);
/* Check JPEG Minimal Coding Unit (mcu) */
if ((mcu_action == JPEG_MCU_ACTION_ABORT)
&& ! jtransform_perfect_transform (srcinfo->image_width,
srcinfo->image_height,
srcinfo->max_h_samp_factor * DCTSIZE,
srcinfo->max_v_samp_factor * DCTSIZE,
transform))
{
if (error != NULL)
g_set_error (error, JPEG_ERROR, JPEG_ERROR_MCU, "MCU Error");
return FALSE;
}
/* Any space needed by a transform option must be requested before
* jpeg_read_coefficients so that memory allocation will be done right.
*/
jtransform_request_workspace (srcinfo, &transformoption);
/* Read source file as DCT coefficients */
src_coef_arrays = jpeg_read_coefficients (srcinfo);
/* Initialize destination compression parameters from source values */
jpeg_copy_critical_parameters (srcinfo, dstinfo);
/* Do not output a JFIF marker for EXIF thumbnails.
* This is not the optimal way to detect the difference
* between a thumbnail and a normal image, but it works
* well for Pix. */
if (option == JCOPYOPT_NONE)
dstinfo->write_JFIF_header = FALSE;
/* Adjust destination parameters if required by transform options;
* also find out which set of coefficient arrays will hold the output.
*/
dst_coef_arrays = jtransform_adjust_parameters (srcinfo,
dstinfo,
src_coef_arrays,
&transformoption);
/* Start compressor (note no image data is actually written here) */
jpeg_write_coefficients (dstinfo, dst_coef_arrays);
/* Copy to the output file any extra markers that we want to
* preserve */
jcopy_markers_execute (srcinfo, dstinfo, option);
/* Execute image transformation, if any */
jtransform_execute_transformation (srcinfo,
dstinfo,
src_coef_arrays,
&transformoption);
/* Finish compression */
jpeg_finish_compress (dstinfo);
jpeg_finish_decompress (srcinfo);
return TRUE;
}
gboolean
jpegtran (void *in_buffer,
gsize in_buffer_size,
void **out_buffer,
gsize *out_buffer_size,
GthTransform transformation,
JpegMcuAction mcu_action,
GError **error)
{
struct jpeg_decompress_struct srcinfo;
struct jpeg_compress_struct dstinfo;
struct error_handler_data jsrcerr, jdsterr;
gboolean success;
*out_buffer = NULL;
*out_buffer_size = 0;
/* Initialize the JPEG decompression object with default error
* handling. */
srcinfo.err = jpeg_std_error (&(jsrcerr.pub));
jsrcerr.pub.error_exit = fatal_error_handler;
jsrcerr.pub.output_message = output_message_handler;
jsrcerr.error = error;
jpeg_create_decompress (&srcinfo);
/* Initialize the JPEG compression object with default error
* handling. */
dstinfo.err = jpeg_std_error (&(jdsterr.pub));
jdsterr.pub.error_exit = fatal_error_handler;
jdsterr.pub.output_message = output_message_handler;
jdsterr.error = error;
jpeg_create_compress (&dstinfo);
dstinfo.err->trace_level = 0;
dstinfo.arith_code = FALSE;
dstinfo.optimize_coding = FALSE;
jsrcerr.pub.trace_level = jdsterr.pub.trace_level;
srcinfo.mem->max_memory_to_use = dstinfo.mem->max_memory_to_use;
/* Decompression error handler */
if (sigsetjmp (jsrcerr.setjmp_buffer, 1)) {
jpeg_destroy_compress (&dstinfo);
jpeg_destroy_decompress (&srcinfo);
return FALSE;
}
/* Compression error handler */
if (sigsetjmp (jdsterr.setjmp_buffer, 1)) {
jpeg_destroy_compress (&dstinfo);
jpeg_destroy_decompress (&srcinfo);
return FALSE;
}
/* Specify data source for decompression */
_jpeg_memory_src (&srcinfo, in_buffer, in_buffer_size);
/* Specify data destination for compression */
_jpeg_memory_dest (&dstinfo, out_buffer, out_buffer_size);
/* Apply transformation */
success = jpegtran_internal (&srcinfo, &dstinfo, transformation, JCOPYOPT_ALL, mcu_action, error);
/* Release memory */
jpeg_destroy_compress (&dstinfo);
jpeg_destroy_decompress (&srcinfo);
if (success) {
JpegTranInfo info;
info.in_buffer = in_buffer;
info.in_buffer_size = in_buffer_size;
info.out_buffer = out_buffer;
info.out_buffer_size = out_buffer_size;
info.transformation = transformation;
gth_hook_invoke ("jpegtran-after", &info);
}
else {
g_free (*out_buffer);
*out_buffer_size = 0;
}
return success;
}
#endif /* HAVE_LIBJPEG */