summaryrefslogtreecommitdiffstats
path: root/plug-ins/file-jpeg/jpeg-settings.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--plug-ins/file-jpeg/jpeg-settings.c397
1 files changed, 397 insertions, 0 deletions
diff --git a/plug-ins/file-jpeg/jpeg-settings.c b/plug-ins/file-jpeg/jpeg-settings.c
new file mode 100644
index 0000000..df6ace2
--- /dev/null
+++ b/plug-ins/file-jpeg/jpeg-settings.c
@@ -0,0 +1,397 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * jpeg-settings.c
+ * Copyright (C) 2007 Raphaƫl Quinet <raphael@gimp.org>
+ *
+ * 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 3 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 <https://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Structure of the "jpeg-settings" parasite:
+ * 1 byte - JPEG color space (JCS_YCbCr, JCS_GRAYSCALE, JCS_CMYK, ...)
+ * 1 byte - quality (1..100 according to the IJG scale, or 0)
+ * 1 byte - number of components (0..4)
+ * 1 byte - number of quantization tables (0..4)
+ * C * 2 bytes - sampling factors for each component (1..4)
+ * T * 128 bytes - quantization tables (only if different from IJG tables)
+ *
+ * Additional data following the quantization tables is currently
+ * ignored and can be used for future extensions.
+ *
+ * In order to improve the compatibility with future versions of the
+ * plug-in that may support more subsampling types ("subsmp"), the
+ * parasite contains the original subsampling for each component
+ * instead of saving only one byte containing the subsampling type as
+ * used by the jpeg plug-in. The same applies to the other settings:
+ * for example, up to 4 quantization tables will be saved in the
+ * parasite even if the current code cannot restore more than 2 of
+ * them (4 tables may be needed by unusual JPEG color spaces such as
+ * JCS_CMYK or JCS_YCCK).
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <setjmp.h>
+
+#include <glib/gstdio.h>
+
+#include <jpeglib.h>
+
+#include <libgimp/gimp.h>
+
+#include "libgimp/stdplugins-intl.h"
+
+#include "jpeg.h"
+#include "jpeg-quality.h"
+#include "jpeg-settings.h"
+
+/**
+ * jpeg_detect_original_settings:
+ * @cinfo: a pointer to a JPEG decompressor info.
+ * @image_ID: the image to which the parasite should be attached.
+ *
+ * Analyze the image being decompressed (@cinfo) and extract the
+ * sampling factors, quantization tables and overall image quality.
+ * Store this information in a parasite and attach it to @image_ID.
+ *
+ * This function must be called after jpeg_read_header() so that
+ * @cinfo contains the quantization tables and the sampling factors
+ * for each component.
+ *
+ * Return Value: TRUE if a parasite has been attached to @image_ID.
+ */
+gboolean
+jpeg_detect_original_settings (struct jpeg_decompress_struct *cinfo,
+ gint32 image_ID)
+{
+ guint parasite_size;
+ guchar *parasite_data;
+ GimpParasite *parasite;
+ guchar *dest;
+ gint quality;
+ gint num_quant_tables = 0;
+ gint t;
+ gint i;
+
+ g_return_val_if_fail (cinfo != NULL, FALSE);
+ if (cinfo->jpeg_color_space == JCS_UNKNOWN
+ || cinfo->out_color_space == JCS_UNKNOWN)
+ return FALSE;
+
+ quality = jpeg_detect_quality (cinfo);
+ /* no need to attach quantization tables if they are the ones from IJG */
+ if (quality <= 0)
+ {
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ num_quant_tables++;
+ }
+
+ parasite_size = 4 + cinfo->num_components * 2 + num_quant_tables * 128;
+ parasite_data = g_new (guchar, parasite_size);
+ dest = parasite_data;
+
+ *dest++ = CLAMP0255 (cinfo->jpeg_color_space);
+ *dest++ = ABS (quality);
+ *dest++ = CLAMP0255 (cinfo->num_components);
+ *dest++ = num_quant_tables;
+
+ for (i = 0; i < cinfo->num_components; i++)
+ {
+ *dest++ = CLAMP0255 (cinfo->comp_info[i].h_samp_factor);
+ *dest++ = CLAMP0255 (cinfo->comp_info[i].v_samp_factor);
+ }
+
+ if (quality <= 0)
+ {
+ for (t = 0; t < 4; t++)
+ if (cinfo->quant_tbl_ptrs[t])
+ for (i = 0; i < DCTSIZE2; i++)
+ {
+ guint16 c = cinfo->quant_tbl_ptrs[t]->quantval[i];
+ *dest++ = c / 256;
+ *dest++ = c & 255;
+ }
+ }
+
+ parasite = gimp_parasite_new ("jpeg-settings",
+ GIMP_PARASITE_PERSISTENT,
+ parasite_size,
+ parasite_data);
+ g_free (parasite_data);
+ gimp_image_attach_parasite (image_ID, parasite);
+ gimp_parasite_free (parasite);
+ return TRUE;
+}
+
+
+/*
+ * TODO: compare the JPEG color space found in the parasite with the
+ * GIMP color space of the drawable to be saved. If one of them is
+ * grayscale and the other isn't, then the quality setting may be used
+ * but the subsampling parameters and quantization tables should be
+ * ignored. The drawable_ID needs to be passed around because the
+ * color space of the drawable may be different from that of the image
+ * (e.g., when saving a mask or channel).
+ */
+
+/**
+ * jpeg_restore_original_settings:
+ * @image_ID: the image that may contain original jpeg settings in a parasite.
+ * @quality: where to store the original jpeg quality.
+ * @subsmp: where to store the original subsampling type.
+ * @num_quant_tables: where to store the number of quantization tables found.
+ *
+ * Retrieve the original JPEG settings (quality, type of subsampling
+ * and number of quantization tables) from the parasite attached to
+ * @image_ID. If the number of quantization tables is greater than
+ * zero, then these tables can be retrieved from the parasite by
+ * calling jpeg_restore_original_tables().
+ *
+ * Return Value: TRUE if a valid parasite was attached to the image
+ */
+gboolean
+jpeg_restore_original_settings (gint32 image_ID,
+ gint *quality,
+ JpegSubsampling *subsmp,
+ gint *num_quant_tables)
+{
+ GimpParasite *parasite;
+ const guchar *src;
+ glong src_size;
+ gint color_space;
+ gint q;
+ gint num_components;
+ gint num_tables;
+ guchar h[3];
+ guchar v[3];
+
+ g_return_val_if_fail (quality != NULL, FALSE);
+ g_return_val_if_fail (subsmp != NULL, FALSE);
+ g_return_val_if_fail (num_quant_tables != NULL, FALSE);
+
+ parasite = gimp_image_get_parasite (image_ID, "jpeg-settings");
+ if (parasite)
+ {
+ src = gimp_parasite_data (parasite);
+ src_size = gimp_parasite_data_size (parasite);
+ if (src_size >= 4)
+ {
+ color_space = *src++;
+ q = *src++;
+ num_components = *src++;
+ num_tables = *src++;
+
+ if (src_size >= (4 + num_components * 2 + num_tables * 128)
+ && q <= 100 && num_tables <= 4)
+ {
+ *quality = q;
+
+ /* the current plug-in can only create grayscale or YCbCr JPEGs */
+ if (color_space == JCS_GRAYSCALE || color_space == JCS_YCbCr)
+ *num_quant_tables = num_tables;
+ else
+ *num_quant_tables = -1;
+
+ /* the current plug-in can only use subsampling for YCbCr (3) */
+ *subsmp = -1;
+ if (num_components == 3)
+ {
+ h[0] = *src++;
+ v[0] = *src++;
+ h[1] = *src++;
+ v[1] = *src++;
+ h[2] = *src++;
+ v[2] = *src++;
+
+ if (h[1] == 1 && v[1] == 1 && h[2] == 1 && v[2] == 1)
+ {
+ if (h[0] == 1 && v[0] == 1)
+ *subsmp = JPEG_SUBSAMPLING_1x1_1x1_1x1;
+ else if (h[0] == 2 && v[0] == 1)
+ *subsmp = JPEG_SUBSAMPLING_2x1_1x1_1x1;
+ else if (h[0] == 1 && v[0] == 2)
+ *subsmp = JPEG_SUBSAMPLING_1x2_1x1_1x1;
+ else if (h[0] == 2 && v[0] == 2)
+ *subsmp = JPEG_SUBSAMPLING_2x2_1x1_1x1;
+ }
+ }
+
+ gimp_parasite_free (parasite);
+ return TRUE;
+ }
+ }
+
+ gimp_parasite_free (parasite);
+ }
+
+ *quality = -1;
+ *subsmp = -1;
+ *num_quant_tables = 0;
+
+ return FALSE;
+}
+
+
+/**
+ * jpeg_restore_original_tables:
+ * @image_ID: the image that may contain original jpeg settings in a parasite.
+ * @num_quant_tables: the number of quantization tables to restore.
+ *
+ * Retrieve the original quantization tables from the parasite
+ * attached to @image_ID. Each table is an array of coefficients that
+ * can be associated with a component of a JPEG image when saving it.
+ *
+ * An array of newly allocated tables is returned if @num_quant_tables
+ * matches the number of tables saved in the parasite. These tables
+ * are returned as arrays of unsigned integers even if they will never
+ * use more than 16 bits (8 bits in most cases) because the IJG JPEG
+ * library expects arrays of unsigned integers. When these tables are
+ * not needed anymore, the caller should free them using g_free(). If
+ * no parasite exists or if it cannot be used, this function returns
+ * NULL.
+ *
+ * Return Value: an array of quantization tables, or NULL.
+ */
+guint **
+jpeg_restore_original_tables (gint32 image_ID,
+ gint num_quant_tables)
+{
+ GimpParasite *parasite;
+ const guchar *src;
+ glong src_size;
+ gint num_components;
+ gint num_tables;
+ guint **quant_tables;
+ gint t;
+ gint i;
+
+ parasite = gimp_image_get_parasite (image_ID, "jpeg-settings");
+ if (parasite)
+ {
+ src_size = gimp_parasite_data_size (parasite);
+ if (src_size >= 4)
+ {
+ src = gimp_parasite_data (parasite);
+ num_components = src[2];
+ num_tables = src[3];
+
+ if (src_size >= (4 + num_components * 2 + num_tables * 128)
+ && num_tables == num_quant_tables)
+ {
+ src += 4 + num_components * 2;
+ quant_tables = g_new (guint *, num_tables);
+
+ for (t = 0; t < num_tables; t++)
+ {
+ quant_tables[t] = g_new (guint, 128);
+ for (i = 0; i < 64; i++)
+ {
+ guint c;
+
+ c = *src++ * 256;
+ c += *src++;
+ quant_tables[t][i] = c;
+ }
+ }
+ gimp_parasite_free (parasite);
+ return quant_tables;
+ }
+ }
+ gimp_parasite_free (parasite);
+ }
+ return NULL;
+}
+
+
+/**
+ * jpeg_swap_original_settings:
+ * @image_ID: the image that may contain original jpeg settings in a parasite.
+ *
+ * Swap the horizontal and vertical axis for the saved subsampling
+ * parameters and quantization tables. This should be done if the
+ * image has been rotated by +90 or -90 degrees or if it has been
+ * mirrored along its diagonal.
+ */
+void
+jpeg_swap_original_settings (gint32 image_ID)
+{
+ GimpParasite *parasite;
+ const guchar *src;
+ glong src_size;
+ gint num_components;
+ gint num_tables;
+ guchar *new_data;
+ guchar *dest;
+ gint t;
+ gint i;
+ gint j;
+
+ parasite = gimp_image_get_parasite (image_ID, "jpeg-settings");
+ if (parasite)
+ {
+ src_size = gimp_parasite_data_size (parasite);
+ if (src_size >= 4)
+ {
+ src = gimp_parasite_data (parasite);
+ num_components = src[2];
+ num_tables = src[3];
+
+ if (src_size >= (4 + num_components * 2 + num_tables * 128))
+ {
+ new_data = g_new (guchar, src_size);
+ dest = new_data;
+ *dest++ = *src++;
+ *dest++ = *src++;
+ *dest++ = *src++;
+ *dest++ = *src++;
+ for (i = 0; i < num_components; i++)
+ {
+ dest[0] = src[1];
+ dest[1] = src[0];
+ dest += 2;
+ src += 2;
+ }
+ for (t = 0; t < num_tables; t++)
+ {
+ for (i = 0; i < 8; i++)
+ {
+ for (j = 0; j < 8; j++)
+ {
+ dest[i * 16 + j * 2] = src[j * 16 + i * 2];
+ dest[i * 16 + j * 2 + 1] = src[j * 16 + i * 2 + 1];
+ }
+ }
+ dest += 128;
+ src += 128;
+ if (src_size > (4 + num_components * 2 + num_tables * 128))
+ {
+ memcpy (dest, src, src_size - (4 + num_components * 2
+ + num_tables * 128));
+ }
+ }
+ gimp_parasite_free (parasite);
+ parasite = gimp_parasite_new ("jpeg-settings",
+ GIMP_PARASITE_PERSISTENT,
+ src_size,
+ new_data);
+ g_free (new_data);
+ gimp_image_attach_parasite (image_ID, parasite);
+ }
+ }
+ gimp_parasite_free (parasite);
+ }
+}