PIKApp/plug-ins/file-jpeg/jpeg-settings.c

400 lines
13 KiB
C
Raw Normal View History

2023-09-26 00:35:21 +02:00
/* PIKA - Photo and Image Kooker Application
* a rebranding of The GNU Image Manipulation Program (created with heckimp)
* A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio
*
* Original copyright, applying to most contents (license remains unchanged):
* 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 <libpika/pika.h>
#include "libpika/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: 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.
*
* This function must be called after jpeg_read_header() so that
* @cinfo contains the quantization tables and the sampling factors
* for each component.
*
* Returns: TRUE if a parasite has been attached to @image.
*/
gboolean
jpeg_detect_original_settings (struct jpeg_decompress_struct *cinfo,
PikaImage *image)
{
guint parasite_size;
guchar *parasite_data;
PikaParasite *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 = pika_parasite_new ("jpeg-settings",
PIKA_PARASITE_PERSISTENT,
parasite_size,
parasite_data);
g_free (parasite_data);
pika_image_attach_parasite (image, parasite);
pika_parasite_free (parasite);
return TRUE;
}
/*
* TODO: compare the JPEG color space found in the parasite with the
* PIKA 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 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: 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. 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().
*
* Returns: TRUE if a valid parasite was attached to the image
*/
gboolean
jpeg_restore_original_settings (PikaImage *image,
gint *quality,
JpegSubsampling *subsmp,
gint *num_quant_tables)
{
PikaParasite *parasite;
const guchar *src;
guint32 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 = pika_image_get_parasite (image, "jpeg-settings");
if (parasite)
{
src = pika_parasite_get_data (parasite, &src_size);
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 = JPEG_SUBSAMPLING_1x1_1x1_1x1;
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;
}
}
pika_parasite_free (parasite);
return TRUE;
}
}
pika_parasite_free (parasite);
}
*quality = -1;
*subsmp = JPEG_SUBSAMPLING_1x1_1x1_1x1;
*num_quant_tables = 0;
return FALSE;
}
/**
* jpeg_restore_original_tables:
* @image: 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. 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.
*
* Returns: (nullable): an array of quantization tables, or NULL.
*/
guint **
jpeg_restore_original_tables (PikaImage *image,
gint num_quant_tables)
{
PikaParasite *parasite;
const guchar *src;
guint32 src_size;
gint num_components;
gint num_tables;
guint **quant_tables;
gint t;
gint i;
parasite = pika_image_get_parasite (image, "jpeg-settings");
if (parasite)
{
src = pika_parasite_get_data (parasite, &src_size);
if (src_size >= 4)
{
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;
}
}
pika_parasite_free (parasite);
return quant_tables;
}
}
pika_parasite_free (parasite);
}
return NULL;
}
/**
* jpeg_swap_original_settings:
* @image: 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 (PikaImage *image)
{
PikaParasite *parasite;
const guchar *src;
guint32 src_size;
gint num_components;
gint num_tables;
guchar *new_data;
guchar *dest;
gint t;
gint i;
gint j;
parasite = pika_image_get_parasite (image, "jpeg-settings");
if (parasite)
{
src = pika_parasite_get_data (parasite, &src_size);
if (src_size >= 4)
{
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));
}
}
pika_parasite_free (parasite);
parasite = pika_parasite_new ("jpeg-settings",
PIKA_PARASITE_PERSISTENT,
src_size,
new_data);
g_free (new_data);
pika_image_attach_parasite (image, parasite);
}
}
pika_parasite_free (parasite);
}
}