Initial checkin of Pika from heckimp
This commit is contained in:
49
plug-ins/file-jpeg/COPYING.ijg
Normal file
49
plug-ins/file-jpeg/COPYING.ijg
Normal file
@ -0,0 +1,49 @@
|
||||
LICENSE extracted from IJG's jpeg distribution:
|
||||
-----------------------------------------------
|
||||
|
||||
In plain English:
|
||||
|
||||
1. We don't promise that this software works. (But if you find any bugs,
|
||||
please let us know!)
|
||||
2. You can use this software for whatever you want. You don't have to pay us.
|
||||
3. You may not pretend that you wrote this software. If you use it in a
|
||||
program, you must acknowledge somewhere in your documentation that
|
||||
you've used the IJG code.
|
||||
|
||||
In legalese:
|
||||
|
||||
The authors make NO WARRANTY or representation, either express or implied,
|
||||
with respect to this software, its quality, accuracy, merchantability, or
|
||||
fitness for a particular purpose. This software is provided "AS IS", and you,
|
||||
its user, assume the entire risk as to its quality and accuracy.
|
||||
|
||||
This software is copyright 1997-1998, Thomas G. Lane, Todd Newman.
|
||||
All Rights Reserved except as specified below.
|
||||
|
||||
Permission is hereby granted to use, copy, modify, and distribute this
|
||||
software (or portions thereof) for any purpose, without fee, subject to these
|
||||
conditions:
|
||||
(1) If any part of the source code for this software is distributed, then this
|
||||
README file must be included, with this copyright and no-warranty notice
|
||||
unaltered; and any additions, deletions, or changes to the original files
|
||||
must be clearly indicated in accompanying documentation.
|
||||
(2) If only executable code is distributed, then the accompanying
|
||||
documentation must state that "this software is based in part on the work of
|
||||
the Independent JPEG Group".
|
||||
(3) Permission for use of this software is granted only if the user accepts
|
||||
full responsibility for any undesirable consequences; the authors accept
|
||||
NO LIABILITY for damages of any kind.
|
||||
|
||||
These conditions apply to any software derived from or based on the IJG code,
|
||||
not just to the unmodified library. If you use our work, you ought to
|
||||
acknowledge us.
|
||||
|
||||
Permission is NOT granted for the use of any IJG author's name or company name
|
||||
in advertising or publicity relating to this software or products derived from
|
||||
it. This software may be referred to only as "the Independent JPEG Group's
|
||||
software".
|
||||
|
||||
We specifically permit and encourage the use of this software as the basis of
|
||||
commercial products, provided that all warranty or liability claims are
|
||||
assumed by the product vendor.
|
||||
|
244
plug-ins/file-jpeg/jpeg-icc.c
Normal file
244
plug-ins/file-jpeg/jpeg-icc.c
Normal file
@ -0,0 +1,244 @@
|
||||
/* jpeg-icc.c
|
||||
*
|
||||
* This file provides code to read and write International Color
|
||||
* Consortium (ICC) device profiles embedded in JFIF JPEG image files.
|
||||
* The ICC has defined a standard format for including such data in
|
||||
* JPEG "APP2" markers. The code given here does not know anything
|
||||
* about the internal structure of the ICC profile data; it just knows
|
||||
* how to put the profile data into a JPEG file being written, or get
|
||||
* it back out when reading.
|
||||
*
|
||||
* This code depends on new features added to the IJG JPEG library as of
|
||||
* IJG release 6b; it will not compile or work with older IJG versions.
|
||||
*/
|
||||
|
||||
/* This code was originally proposed for the Independent JPEG Group's
|
||||
* software, but that fell through, and only recently got merged into
|
||||
* libjpeg-turbo. This code comes with the following copyright notice:
|
||||
*
|
||||
* Copyright (C) 1997-1998, Thomas G. Lane, Todd Newman.
|
||||
*
|
||||
* For conditions of distribution and use, see the accompanying
|
||||
* COPYING.ijg file.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <jpeglib.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "jpeg-icc.h"
|
||||
|
||||
|
||||
/*
|
||||
* Since an ICC profile can be larger than the maximum size of a JPEG
|
||||
* marker (64K), we need provisions to split it into multiple markers.
|
||||
* The format defined by the ICC specifies one or more APP2 markers
|
||||
* containing the following data:
|
||||
*
|
||||
* Identifying string ASCII "ICC_PROFILE\0" (12 bytes)
|
||||
* Marker sequence number 1 for first APP2, 2 for next, etc (1 byte)
|
||||
* Number of markers Total number of APP2's used (1 byte)
|
||||
* Profile data (remainder of APP2 data)
|
||||
|
||||
* Decoders should use the marker sequence numbers to reassemble the
|
||||
* profile, rather than assuming that the APP2 markers appear in the
|
||||
* correct sequence.
|
||||
*/
|
||||
|
||||
#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */
|
||||
#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */
|
||||
#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */
|
||||
#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
|
||||
|
||||
|
||||
/*
|
||||
* This routine writes the given ICC profile data into a JPEG file.
|
||||
* It *must* be called AFTER calling jpeg_start_compress() and BEFORE
|
||||
* the first call to jpeg_write_scanlines().
|
||||
* (This ordering ensures that the APP2 marker(s) will appear after the
|
||||
* SOI and JFIF or Adobe markers, but before all else.)
|
||||
*/
|
||||
|
||||
void
|
||||
jpeg_icc_write_profile (j_compress_ptr cinfo,
|
||||
const guchar *icc_data_ptr,
|
||||
guint icc_data_len)
|
||||
{
|
||||
unsigned int num_markers; /* total number of markers we'll write */
|
||||
int cur_marker = 1; /* per spec, counting starts at 1 */
|
||||
unsigned int length; /* number of bytes to write in this marker */
|
||||
|
||||
/* Calculate the number of markers we'll need, rounding up of course */
|
||||
num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER;
|
||||
if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len)
|
||||
num_markers++;
|
||||
|
||||
while (icc_data_len > 0) {
|
||||
/* length of profile to put in this marker */
|
||||
length = icc_data_len;
|
||||
if (length > MAX_DATA_BYTES_IN_MARKER)
|
||||
length = MAX_DATA_BYTES_IN_MARKER;
|
||||
icc_data_len -= length;
|
||||
|
||||
/* Write the JPEG marker header (APP2 code and marker length) */
|
||||
jpeg_write_m_header(cinfo, ICC_MARKER,
|
||||
(unsigned int) (length + ICC_OVERHEAD_LEN));
|
||||
|
||||
/* Write the marker identifying string "ICC_PROFILE" (null-terminated).
|
||||
* We code it in this less-than-transparent way so that the code works
|
||||
* even if the local character set is not ASCII.
|
||||
*/
|
||||
jpeg_write_m_byte(cinfo, 0x49);
|
||||
jpeg_write_m_byte(cinfo, 0x43);
|
||||
jpeg_write_m_byte(cinfo, 0x43);
|
||||
jpeg_write_m_byte(cinfo, 0x5F);
|
||||
jpeg_write_m_byte(cinfo, 0x50);
|
||||
jpeg_write_m_byte(cinfo, 0x52);
|
||||
jpeg_write_m_byte(cinfo, 0x4F);
|
||||
jpeg_write_m_byte(cinfo, 0x46);
|
||||
jpeg_write_m_byte(cinfo, 0x49);
|
||||
jpeg_write_m_byte(cinfo, 0x4C);
|
||||
jpeg_write_m_byte(cinfo, 0x45);
|
||||
jpeg_write_m_byte(cinfo, 0x0);
|
||||
|
||||
/* Add the sequencing info */
|
||||
jpeg_write_m_byte(cinfo, cur_marker);
|
||||
jpeg_write_m_byte(cinfo, (int) num_markers);
|
||||
|
||||
/* Add the profile data */
|
||||
while (length--) {
|
||||
jpeg_write_m_byte(cinfo, *icc_data_ptr);
|
||||
icc_data_ptr++;
|
||||
}
|
||||
cur_marker++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handy subroutine to test whether a saved marker is an ICC profile marker.
|
||||
*/
|
||||
|
||||
static boolean
|
||||
marker_is_icc (jpeg_saved_marker_ptr marker)
|
||||
{
|
||||
return
|
||||
marker->marker == ICC_MARKER &&
|
||||
marker->data_length >= ICC_OVERHEAD_LEN &&
|
||||
/* verify the identifying string */
|
||||
GETJOCTET(marker->data[0]) == 0x49 &&
|
||||
GETJOCTET(marker->data[1]) == 0x43 &&
|
||||
GETJOCTET(marker->data[2]) == 0x43 &&
|
||||
GETJOCTET(marker->data[3]) == 0x5F &&
|
||||
GETJOCTET(marker->data[4]) == 0x50 &&
|
||||
GETJOCTET(marker->data[5]) == 0x52 &&
|
||||
GETJOCTET(marker->data[6]) == 0x4F &&
|
||||
GETJOCTET(marker->data[7]) == 0x46 &&
|
||||
GETJOCTET(marker->data[8]) == 0x49 &&
|
||||
GETJOCTET(marker->data[9]) == 0x4C &&
|
||||
GETJOCTET(marker->data[10]) == 0x45 &&
|
||||
GETJOCTET(marker->data[11]) == 0x0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* See if there was an ICC profile in the JPEG file being read;
|
||||
* if so, reassemble and return the profile data.
|
||||
*
|
||||
* TRUE is returned if an ICC profile was found, FALSE if not.
|
||||
* If TRUE is returned, *icc_data_ptr is set to point to the
|
||||
* returned data, and *icc_data_len is set to its length.
|
||||
*
|
||||
* NOTE: if the file contains invalid ICC APP2 markers, we just silently
|
||||
* return FALSE. You might want to issue an error message instead.
|
||||
*/
|
||||
|
||||
gboolean
|
||||
jpeg_icc_read_profile (j_decompress_ptr cinfo,
|
||||
guchar **icc_data_ptr,
|
||||
guint *icc_data_len)
|
||||
{
|
||||
jpeg_saved_marker_ptr marker;
|
||||
int num_markers = 0;
|
||||
int seq_no;
|
||||
JOCTET *icc_data;
|
||||
unsigned int total_length;
|
||||
#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */
|
||||
char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */
|
||||
unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */
|
||||
unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */
|
||||
|
||||
*icc_data_ptr = NULL; /* avoid confusion if FALSE return */
|
||||
*icc_data_len = 0;
|
||||
|
||||
/* This first pass over the saved markers discovers whether there are
|
||||
* any ICC markers and verifies the consistency of the marker numbering.
|
||||
*/
|
||||
|
||||
for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++)
|
||||
marker_present[seq_no] = 0;
|
||||
|
||||
for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
|
||||
if (marker_is_icc(marker)) {
|
||||
if (num_markers == 0)
|
||||
num_markers = GETJOCTET(marker->data[13]);
|
||||
else if (num_markers != GETJOCTET(marker->data[13]))
|
||||
return FALSE; /* inconsistent num_markers fields */
|
||||
seq_no = GETJOCTET(marker->data[12]);
|
||||
if (seq_no <= 0 || seq_no > num_markers)
|
||||
return FALSE; /* bogus sequence number */
|
||||
if (marker_present[seq_no])
|
||||
return FALSE; /* duplicate sequence numbers */
|
||||
marker_present[seq_no] = 1;
|
||||
data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
|
||||
}
|
||||
}
|
||||
|
||||
if (num_markers == 0)
|
||||
return FALSE;
|
||||
|
||||
/* Check for missing markers, count total space needed,
|
||||
* compute offset of each marker's part of the data.
|
||||
*/
|
||||
|
||||
total_length = 0;
|
||||
for (seq_no = 1; seq_no <= num_markers; seq_no++) {
|
||||
if (marker_present[seq_no] == 0)
|
||||
return FALSE; /* missing sequence number */
|
||||
data_offset[seq_no] = total_length;
|
||||
total_length += data_length[seq_no];
|
||||
}
|
||||
|
||||
if (total_length <= 0)
|
||||
return FALSE; /* found only empty markers? */
|
||||
|
||||
/* Allocate space for assembled data */
|
||||
icc_data = g_try_malloc (total_length * sizeof(JOCTET));
|
||||
if (icc_data == NULL)
|
||||
return FALSE; /* oops, out of memory */
|
||||
|
||||
/* and fill it in */
|
||||
for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
|
||||
if (marker_is_icc(marker)) {
|
||||
JOCTET FAR *src_ptr;
|
||||
JOCTET *dst_ptr;
|
||||
unsigned int length;
|
||||
seq_no = GETJOCTET(marker->data[12]);
|
||||
dst_ptr = icc_data + data_offset[seq_no];
|
||||
src_ptr = marker->data + ICC_OVERHEAD_LEN;
|
||||
length = data_length[seq_no];
|
||||
while (length--) {
|
||||
*dst_ptr++ = *src_ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*icc_data_ptr = icc_data;
|
||||
*icc_data_len = total_length;
|
||||
|
||||
return TRUE;
|
||||
}
|
50
plug-ins/file-jpeg/jpeg-icc.h
Normal file
50
plug-ins/file-jpeg/jpeg-icc.h
Normal file
@ -0,0 +1,50 @@
|
||||
/* jpeg-icc.h
|
||||
*
|
||||
* This code was originally proposed for the Independent JPEG Group's
|
||||
* software, but that fell through, and only recently got merged into
|
||||
* libjpeg-turbo. This code comes with the following copyright notice:
|
||||
*
|
||||
* Copyright (C) 1997-1998, Thomas G. Lane, Todd Newman.
|
||||
*
|
||||
* For conditions of distribution and use, see the accompanying
|
||||
* COPYING.ijg file.
|
||||
*/
|
||||
|
||||
#ifndef __JPEG_ICC_H__
|
||||
#define __JPEG_ICC_H__
|
||||
|
||||
void jpeg_icc_write_profile (j_compress_ptr cinfo,
|
||||
const guchar *icc_data_ptr,
|
||||
guint icc_data_len);
|
||||
|
||||
|
||||
/*
|
||||
* Reading a JPEG file that may contain an ICC profile requires two steps:
|
||||
*
|
||||
* 1. After jpeg_create_decompress() but before jpeg_read_header(),
|
||||
* ask the IJG library to save in memory any APP2 markers it may find
|
||||
* in the file.
|
||||
*
|
||||
* 2. After jpeg_read_header(), call jpeg_icc_read_profile() to find
|
||||
* out whether there was a profile and obtain it if so.
|
||||
*
|
||||
* See if there was an ICC profile in the JPEG file being read;
|
||||
* if so, reassemble and return the profile data.
|
||||
*
|
||||
* TRUE is returned if an ICC profile was found, FALSE if not.
|
||||
* If TRUE is returned, *icc_data_ptr is set to point to the
|
||||
* returned data, and *icc_data_len is set to its length.
|
||||
*
|
||||
* IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
|
||||
* and must be freed by the caller with free() when the caller no longer
|
||||
* needs it. (Alternatively, we could write this routine to use the
|
||||
* IJG library's memory allocator, so that the data would be freed implicitly
|
||||
* at jpeg_finish_decompress() time. But it seems likely that many apps
|
||||
* will prefer to have the data stick around after decompression finishes.)
|
||||
*/
|
||||
|
||||
gboolean jpeg_icc_read_profile (j_decompress_ptr cinfo,
|
||||
guchar **icc_data_ptr,
|
||||
guint *icc_data_len);
|
||||
|
||||
#endif /* __JPEG_ICC_H__ */
|
697
plug-ins/file-jpeg/jpeg-load.c
Normal file
697
plug-ins/file-jpeg/jpeg-load.c
Normal file
@ -0,0 +1,697 @@
|
||||
/* 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
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <gexiv2/gexiv2.h>
|
||||
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
|
||||
#include <lcms2.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
#include "jpeg.h"
|
||||
#include "jpeg-icc.h"
|
||||
#include "jpeg-settings.h"
|
||||
#include "jpeg-load.h"
|
||||
|
||||
static gboolean jpeg_load_resolution (PikaImage *image,
|
||||
struct jpeg_decompress_struct
|
||||
*cinfo);
|
||||
|
||||
static void jpeg_load_sanitize_comment (gchar *comment);
|
||||
|
||||
|
||||
PikaImage * volatile preview_image;
|
||||
PikaLayer * preview_layer;
|
||||
|
||||
PikaImage *
|
||||
load_image (GFile *file,
|
||||
PikaRunMode runmode,
|
||||
gboolean preview,
|
||||
gboolean *resolution_loaded,
|
||||
gboolean *ps_metadata_loaded,
|
||||
GError **error)
|
||||
{
|
||||
PikaImage * volatile image;
|
||||
PikaLayer *layer;
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct my_error_mgr jerr;
|
||||
jpeg_saved_marker_ptr marker;
|
||||
FILE *infile;
|
||||
guchar *buf;
|
||||
guchar **rowbuf;
|
||||
PikaImageBaseType image_type;
|
||||
PikaImageType layer_type;
|
||||
GeglBuffer *buffer = NULL;
|
||||
const Babl *format;
|
||||
const Babl *space;
|
||||
const gchar *encoding;
|
||||
const gchar *layer_name = NULL;
|
||||
PikaColorProfile *cmyk_profile = NULL;
|
||||
gint tile_height;
|
||||
gint i;
|
||||
guchar *photoshop_data = NULL;
|
||||
guint photoshop_len = 0;
|
||||
|
||||
/* We set up the normal JPEG error routines. */
|
||||
cinfo.err = jpeg_std_error (&jerr.pub);
|
||||
jerr.pub.error_exit = my_error_exit;
|
||||
|
||||
if (! preview)
|
||||
{
|
||||
jerr.pub.output_message = my_output_message;
|
||||
|
||||
pika_progress_init_printf (_("Opening '%s'"),
|
||||
pika_file_get_utf8_name (file));
|
||||
}
|
||||
|
||||
infile = g_fopen (g_file_peek_path (file), "rb");
|
||||
|
||||
if (! infile)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Could not open '%s' for reading: %s"),
|
||||
pika_file_get_utf8_name (file), g_strerror (errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
image = NULL;
|
||||
|
||||
/* Establish the setjmp return context for my_error_exit to use. */
|
||||
if (setjmp (jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error.
|
||||
* We need to clean up the JPEG object, close the input file, and return.
|
||||
*/
|
||||
jpeg_destroy_decompress (&cinfo);
|
||||
if (infile)
|
||||
fclose (infile);
|
||||
|
||||
if (image && !preview)
|
||||
pika_image_delete (image);
|
||||
|
||||
if (preview)
|
||||
destroy_preview ();
|
||||
|
||||
if (buffer)
|
||||
g_object_unref (buffer);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Now we can initialize the JPEG decompression object. */
|
||||
jpeg_create_decompress (&cinfo);
|
||||
|
||||
/* Step 2: specify data source (eg, a file) */
|
||||
|
||||
jpeg_stdio_src (&cinfo, infile);
|
||||
|
||||
if (! preview)
|
||||
{
|
||||
/* - step 2.1: tell the lib to save the comments */
|
||||
jpeg_save_markers (&cinfo, JPEG_COM, 0xffff);
|
||||
|
||||
/* - step 2.2: tell the lib to save APP1 data (Exif or XMP) */
|
||||
jpeg_save_markers (&cinfo, JPEG_APP0 + 1, 0xffff);
|
||||
|
||||
/* - step 2.3: tell the lib to save APP2 data (ICC profiles) */
|
||||
jpeg_save_markers (&cinfo, JPEG_APP0 + 2, 0xffff);
|
||||
|
||||
/* - step 2.4: tell the lib to save APP13 data (clipping path) */
|
||||
jpeg_save_markers (&cinfo, JPEG_APP0 + 13, 0xffff);
|
||||
}
|
||||
|
||||
/* Step 3: read file parameters with jpeg_read_header() */
|
||||
|
||||
jpeg_read_header (&cinfo, TRUE);
|
||||
|
||||
/* We can ignore the return value from jpeg_read_header since
|
||||
* (a) suspension is not possible with the stdio data source, and
|
||||
* (b) we passed TRUE to reject a tables-only JPEG file as an error.
|
||||
* See libjpeg.doc for more info.
|
||||
*/
|
||||
|
||||
/* Step 4: set parameters for decompression */
|
||||
|
||||
/* In this example, we don't need to change any of the defaults set by
|
||||
* jpeg_read_header(), so we do nothing here, except set the DCT
|
||||
* method.
|
||||
*/
|
||||
|
||||
cinfo.dct_method = JDCT_FLOAT;
|
||||
|
||||
/* Step 5: Start decompressor */
|
||||
|
||||
jpeg_start_decompress (&cinfo);
|
||||
|
||||
/* We may need to do some setup of our own at this point before reading
|
||||
* the data. After jpeg_start_decompress() we have the correct scaled
|
||||
* output image dimensions available, as well as the output colormap
|
||||
* if we asked for color quantization.
|
||||
*/
|
||||
|
||||
/* temporary buffer */
|
||||
tile_height = pika_tile_height ();
|
||||
buf = g_new (guchar,
|
||||
tile_height * cinfo.output_width * cinfo.output_components);
|
||||
|
||||
rowbuf = g_new (guchar *, tile_height);
|
||||
|
||||
for (i = 0; i < tile_height; i++)
|
||||
rowbuf[i] = buf + cinfo.output_width * cinfo.output_components * i;
|
||||
|
||||
switch (cinfo.output_components)
|
||||
{
|
||||
case 1:
|
||||
image_type = PIKA_GRAY;
|
||||
layer_type = PIKA_GRAY_IMAGE;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
image_type = PIKA_RGB;
|
||||
layer_type = PIKA_RGB_IMAGE;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (cinfo.out_color_space == JCS_CMYK)
|
||||
{
|
||||
image_type = PIKA_RGB;
|
||||
layer_type = PIKA_RGB_IMAGE;
|
||||
break;
|
||||
}
|
||||
/*fallthrough*/
|
||||
|
||||
default:
|
||||
g_message ("Don't know how to load JPEG images "
|
||||
"with %d color channels, using colorspace %d (%d).",
|
||||
cinfo.output_components, cinfo.out_color_space,
|
||||
cinfo.jpeg_color_space);
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (preview)
|
||||
{
|
||||
layer_name = _("JPEG preview");
|
||||
|
||||
image = preview_image;
|
||||
}
|
||||
else
|
||||
{
|
||||
GString *comment_buffer = NULL;
|
||||
guint8 *icc_data = NULL;
|
||||
guint icc_length = 0;
|
||||
|
||||
layer_name = _("Background");
|
||||
|
||||
image = pika_image_new_with_precision (cinfo.output_width,
|
||||
cinfo.output_height,
|
||||
image_type,
|
||||
PIKA_PRECISION_U8_NON_LINEAR);
|
||||
|
||||
pika_image_undo_disable (image);
|
||||
|
||||
/* Step 5.0: save the original JPEG settings in a parasite */
|
||||
jpeg_detect_original_settings (&cinfo, image);
|
||||
|
||||
/* Step 5.1: check for comments, or Exif metadata in APP1 markers */
|
||||
for (marker = cinfo.marker_list; marker; marker = marker->next)
|
||||
{
|
||||
const gchar *data = (const gchar *) marker->data;
|
||||
gsize len = marker->data_length;
|
||||
|
||||
if (marker->marker == JPEG_COM)
|
||||
{
|
||||
#ifdef PIKA_UNSTABLE
|
||||
g_print ("jpeg-load: found image comment (%d bytes)\n",
|
||||
marker->data_length);
|
||||
#endif
|
||||
|
||||
if (! comment_buffer)
|
||||
{
|
||||
comment_buffer = g_string_new_len (data, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* concatenate multiple comments, separate them with LF */
|
||||
g_string_append_c (comment_buffer, '\n');
|
||||
g_string_append_len (comment_buffer, data, len);
|
||||
}
|
||||
}
|
||||
else if ((marker->marker == JPEG_APP0 + 1)
|
||||
&& (len > sizeof (JPEG_APP_HEADER_EXIF) + 8)
|
||||
&& ! strcmp (JPEG_APP_HEADER_EXIF, data))
|
||||
{
|
||||
#ifdef PIKA_UNSTABLE
|
||||
g_print ("jpeg-load: found Exif block (%d bytes)\n",
|
||||
(gint) (len - sizeof (JPEG_APP_HEADER_EXIF)));
|
||||
#endif
|
||||
}
|
||||
else if ((marker->marker == JPEG_APP0 + 13))
|
||||
{
|
||||
photoshop_data = g_new (guchar, len);
|
||||
photoshop_len = len;
|
||||
memcpy (photoshop_data, (guchar *) marker->data, len);
|
||||
|
||||
*ps_metadata_loaded = TRUE;
|
||||
|
||||
#ifdef PIKA_UNSTABLE
|
||||
g_print ("jpeg-load: found Photoshop block (%d bytes) %s\n",
|
||||
(gint) (len - sizeof (JPEG_APP_HEADER_EXIF)), data);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (jpeg_load_resolution (image, &cinfo))
|
||||
{
|
||||
if (resolution_loaded)
|
||||
*resolution_loaded = TRUE;
|
||||
}
|
||||
|
||||
/* if we found any comments, then make a parasite for them */
|
||||
if (comment_buffer && comment_buffer->len)
|
||||
{
|
||||
PikaParasite *parasite;
|
||||
|
||||
jpeg_load_sanitize_comment (comment_buffer->str);
|
||||
parasite = pika_parasite_new ("pika-comment",
|
||||
PIKA_PARASITE_PERSISTENT,
|
||||
strlen (comment_buffer->str) + 1,
|
||||
comment_buffer->str);
|
||||
pika_image_attach_parasite (image, parasite);
|
||||
pika_parasite_free (parasite);
|
||||
|
||||
g_string_free (comment_buffer, TRUE);
|
||||
}
|
||||
|
||||
/* Step 5.3: check for an embedded ICC profile in APP2 markers */
|
||||
jpeg_icc_read_profile (&cinfo, &icc_data, &icc_length);
|
||||
|
||||
if (icc_data)
|
||||
{
|
||||
PikaColorProfile *profile;
|
||||
|
||||
profile = pika_color_profile_new_from_icc_profile (icc_data,
|
||||
icc_length,
|
||||
NULL);
|
||||
if (cinfo.out_color_space == JCS_CMYK)
|
||||
{
|
||||
/* don't attach the profile if we are transforming */
|
||||
cmyk_profile = profile;
|
||||
profile = NULL;
|
||||
}
|
||||
|
||||
if (profile)
|
||||
{
|
||||
pika_image_set_color_profile (image, profile);
|
||||
g_object_unref (profile);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (icc_data);
|
||||
|
||||
/* Do not attach the "jpeg-save-options" parasite to the image
|
||||
* because this conflicts with the global defaults (bug #75398).
|
||||
*/
|
||||
}
|
||||
|
||||
layer = pika_layer_new (image, layer_name,
|
||||
cinfo.output_width,
|
||||
cinfo.output_height,
|
||||
layer_type,
|
||||
100,
|
||||
pika_image_get_default_new_layer_mode (image));
|
||||
|
||||
if (preview)
|
||||
preview_layer = layer;
|
||||
|
||||
/* Step 6: while (scan lines remain to be read) */
|
||||
/* jpeg_read_scanlines(...); */
|
||||
|
||||
/* Here we use the library's state variable cinfo.output_scanline as the
|
||||
* loop counter, so that we don't have to keep track ourselves.
|
||||
*/
|
||||
|
||||
buffer = pika_drawable_get_buffer (PIKA_DRAWABLE (layer));
|
||||
|
||||
if (cinfo.out_color_space == JCS_CMYK)
|
||||
{
|
||||
encoding = "cmyk u8";
|
||||
if (cmyk_profile)
|
||||
{
|
||||
space = pika_color_profile_get_space (cmyk_profile,
|
||||
PIKA_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC,
|
||||
error);
|
||||
pika_image_set_simulation_profile (image, cmyk_profile);
|
||||
}
|
||||
else
|
||||
{
|
||||
space = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (image_type == PIKA_RGB)
|
||||
encoding = "R'G'B' u8";
|
||||
else
|
||||
encoding = "Y' u8";
|
||||
space = pika_drawable_get_format (PIKA_DRAWABLE (layer));
|
||||
}
|
||||
format = babl_format_with_space (encoding, space);
|
||||
|
||||
while (cinfo.output_scanline < cinfo.output_height)
|
||||
{
|
||||
gint start, end;
|
||||
gint scanlines;
|
||||
gboolean image_truncated = FALSE;
|
||||
|
||||
start = cinfo.output_scanline;
|
||||
end = cinfo.output_scanline + tile_height;
|
||||
end = MIN (end, cinfo.output_height);
|
||||
|
||||
scanlines = end - start;
|
||||
|
||||
/* in case of error we now jump here, so pertially loaded imaged
|
||||
* don't get discarded
|
||||
*/
|
||||
if (setjmp (jerr.setjmp_buffer))
|
||||
{
|
||||
image_truncated = TRUE;
|
||||
|
||||
goto set_buffer;
|
||||
}
|
||||
|
||||
for (i = 0; i < scanlines; i++)
|
||||
jpeg_read_scanlines (&cinfo, (JSAMPARRAY) &rowbuf[i], 1);
|
||||
|
||||
set_buffer:
|
||||
gegl_buffer_set (buffer,
|
||||
GEGL_RECTANGLE (0, start, cinfo.output_width, scanlines),
|
||||
0,
|
||||
format,
|
||||
buf,
|
||||
GEGL_AUTO_ROWSTRIDE);
|
||||
|
||||
if (image_truncated)
|
||||
/* jumping to finish skips jpeg_finish_decompress(), its state
|
||||
* might be broken by whatever caused the loading failure
|
||||
*/
|
||||
goto finish;
|
||||
|
||||
if (! preview && (cinfo.output_scanline % 32) == 0)
|
||||
pika_progress_update ((gdouble) cinfo.output_scanline /
|
||||
(gdouble) cinfo.output_height);
|
||||
}
|
||||
|
||||
/* Step 7: Finish decompression */
|
||||
|
||||
jpeg_finish_decompress (&cinfo);
|
||||
/* We can ignore the return value since suspension is not possible
|
||||
* with the stdio data source.
|
||||
*/
|
||||
|
||||
finish:
|
||||
|
||||
g_clear_object (&cmyk_profile);
|
||||
/* Step 8: Release JPEG decompression object */
|
||||
|
||||
/* This is an important step since it will release a good deal of memory. */
|
||||
jpeg_destroy_decompress (&cinfo);
|
||||
|
||||
g_object_unref (buffer);
|
||||
|
||||
/* free up the temporary buffers */
|
||||
g_free (rowbuf);
|
||||
g_free (buf);
|
||||
|
||||
/* After finish_decompress, we can close the input file.
|
||||
* Here we postpone it until after no more JPEG errors are possible,
|
||||
* so as to simplify the setjmp error logic above. (Actually, I don't
|
||||
* think that jpeg_destroy can do an error exit, but why assume anything...)
|
||||
*/
|
||||
fclose (infile);
|
||||
|
||||
/* At this point you may want to check to see whether any corrupt-data
|
||||
* warnings occurred (test whether jerr.num_warnings is nonzero).
|
||||
*/
|
||||
|
||||
/* Detach from the drawable and add it to the image.
|
||||
*/
|
||||
if (! preview)
|
||||
{
|
||||
pika_progress_update (1.0);
|
||||
}
|
||||
|
||||
pika_image_insert_layer (image, layer, NULL, 0);
|
||||
|
||||
/* Step 9: Load PSD-format metadata if applicable */
|
||||
if (photoshop_len > 0)
|
||||
{
|
||||
FILE *fp;
|
||||
GFile *temp_file = NULL;
|
||||
PikaValueArray *return_vals = NULL;
|
||||
|
||||
temp_file = pika_temp_file ("tmp");
|
||||
fp = g_fopen (g_file_peek_path (temp_file), "wb");
|
||||
|
||||
if (! fp)
|
||||
{
|
||||
g_message (_("Error trying to open temporary %s file '%s' "
|
||||
"for jpeg metadata loading: %s"),
|
||||
"tmp",
|
||||
pika_file_get_utf8_name (temp_file),
|
||||
g_strerror (errno));
|
||||
}
|
||||
|
||||
fwrite (photoshop_data + (sizeof (JPEG_APP_HEADER_EXIF) * 2),
|
||||
sizeof (guchar), photoshop_len - sizeof (JPEG_APP_HEADER_EXIF),
|
||||
fp);
|
||||
fclose (fp);
|
||||
|
||||
g_free (photoshop_data);
|
||||
|
||||
return_vals =
|
||||
pika_pdb_run_procedure (pika_get_pdb (),
|
||||
"file-psd-load-metadata",
|
||||
PIKA_TYPE_RUN_MODE, PIKA_RUN_NONINTERACTIVE,
|
||||
G_TYPE_FILE, temp_file,
|
||||
G_TYPE_INT, photoshop_len,
|
||||
PIKA_TYPE_IMAGE, image,
|
||||
G_TYPE_BOOLEAN, FALSE,
|
||||
G_TYPE_NONE);
|
||||
|
||||
g_file_delete (temp_file, NULL, NULL);
|
||||
g_object_unref (temp_file);
|
||||
pika_value_array_unref (return_vals);
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
jpeg_load_resolution (PikaImage *image,
|
||||
struct jpeg_decompress_struct *cinfo)
|
||||
{
|
||||
if (cinfo->saw_JFIF_marker && cinfo->X_density != 0 && cinfo->Y_density != 0)
|
||||
{
|
||||
gdouble xresolution = cinfo->X_density;
|
||||
gdouble yresolution = cinfo->Y_density;
|
||||
gdouble asymmetry = 1.0;
|
||||
|
||||
switch (cinfo->density_unit)
|
||||
{
|
||||
case 0: /* unknown -> set the aspect ratio but use the default
|
||||
* image resolution
|
||||
*/
|
||||
asymmetry = xresolution / yresolution;
|
||||
|
||||
pika_image_get_resolution (image, &xresolution, &yresolution);
|
||||
|
||||
xresolution *= asymmetry;
|
||||
break;
|
||||
|
||||
case 1: /* dots per inch */
|
||||
break;
|
||||
|
||||
case 2: /* dots per cm */
|
||||
xresolution *= 2.54;
|
||||
yresolution *= 2.54;
|
||||
pika_image_set_unit (image, PIKA_UNIT_MM);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_message ("Unknown density unit %d, assuming dots per inch.",
|
||||
cinfo->density_unit);
|
||||
break;
|
||||
}
|
||||
|
||||
pika_image_set_resolution (image, xresolution, yresolution);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* A number of JPEG files have comments written in a local character set
|
||||
* instead of UTF-8. Some of these files may have been saved by older
|
||||
* versions of PIKA. It is not possible to reliably detect the character
|
||||
* set used, but it is better to keep all characters in the ASCII range
|
||||
* and replace the non-ASCII characters instead of discarding the whole
|
||||
* comment. This is especially useful if the comment contains only a few
|
||||
* non-ASCII characters such as a copyright sign, a soft hyphen, etc.
|
||||
*/
|
||||
static void
|
||||
jpeg_load_sanitize_comment (gchar *comment)
|
||||
{
|
||||
const gchar *start_invalid;
|
||||
|
||||
if (! g_utf8_validate (comment, -1, &start_invalid))
|
||||
{
|
||||
guchar *c;
|
||||
|
||||
for (c = (guchar *) start_invalid; *c; c++)
|
||||
{
|
||||
if (*c > 126 || (*c < 32 && *c != '\t' && *c != '\n' && *c != '\r'))
|
||||
*c = '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PikaImage *
|
||||
load_thumbnail_image (GFile *file,
|
||||
gint *width,
|
||||
gint *height,
|
||||
PikaImageType *type,
|
||||
GError **error)
|
||||
{
|
||||
PikaImage * volatile image = NULL;
|
||||
struct jpeg_decompress_struct cinfo;
|
||||
struct my_error_mgr jerr;
|
||||
FILE *infile = NULL;
|
||||
|
||||
pika_progress_init_printf (_("Opening thumbnail for '%s'"),
|
||||
g_file_get_parse_name (file));
|
||||
|
||||
image = pika_image_metadata_load_thumbnail (file, error);
|
||||
if (! image)
|
||||
return NULL;
|
||||
|
||||
cinfo.err = jpeg_std_error (&jerr.pub);
|
||||
jerr.pub.error_exit = my_error_exit;
|
||||
jerr.pub.output_message = my_output_message;
|
||||
|
||||
if ((infile = g_fopen (g_file_peek_path (file), "rb")) == NULL)
|
||||
{
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
|
||||
_("Could not open '%s' for reading: %s"),
|
||||
g_file_get_parse_name (file), g_strerror (errno));
|
||||
|
||||
if (image)
|
||||
pika_image_delete (image);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Establish the setjmp return context for my_error_exit to use. */
|
||||
if (setjmp (jerr.setjmp_buffer))
|
||||
{
|
||||
/* If we get here, the JPEG code has signaled an error. We
|
||||
* need to clean up the JPEG object, close the input file,
|
||||
* and return.
|
||||
*/
|
||||
jpeg_destroy_decompress (&cinfo);
|
||||
|
||||
if (image)
|
||||
pika_image_delete (image);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Now we can initialize the JPEG decompression object. */
|
||||
jpeg_create_decompress (&cinfo);
|
||||
|
||||
/* Step 2: specify data source (eg, a file) */
|
||||
|
||||
jpeg_stdio_src (&cinfo, infile);
|
||||
|
||||
/* Step 3: read file parameters with jpeg_read_header() */
|
||||
|
||||
jpeg_read_header (&cinfo, TRUE);
|
||||
|
||||
jpeg_start_decompress (&cinfo);
|
||||
|
||||
*width = cinfo.output_width;
|
||||
*height = cinfo.output_height;
|
||||
|
||||
switch (cinfo.output_components)
|
||||
{
|
||||
case 1:
|
||||
*type = PIKA_GRAY_IMAGE;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
*type = PIKA_RGB_IMAGE;
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (cinfo.out_color_space == JCS_CMYK)
|
||||
{
|
||||
*type = PIKA_RGB_IMAGE;
|
||||
break;
|
||||
}
|
||||
/*fallthrough*/
|
||||
|
||||
default:
|
||||
g_message ("Don't know how to load JPEG images "
|
||||
"with %d color channels, using colorspace %d (%d).",
|
||||
cinfo.output_components, cinfo.out_color_space,
|
||||
cinfo.jpeg_color_space);
|
||||
|
||||
pika_image_delete (image);
|
||||
image = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Step 4: Release JPEG decompression object */
|
||||
|
||||
/* This is an important step since it will release a good deal
|
||||
* of memory.
|
||||
*/
|
||||
jpeg_destroy_decompress (&cinfo);
|
||||
|
||||
fclose (infile);
|
||||
|
||||
return image;
|
||||
}
|
38
plug-ins/file-jpeg/jpeg-load.h
Normal file
38
plug-ins/file-jpeg/jpeg-load.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* 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
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __JPEG_LOAD_H__
|
||||
#define __JPEG_LOAD_H__
|
||||
|
||||
PikaImage * load_image (GFile *file,
|
||||
PikaRunMode runmode,
|
||||
gboolean preview,
|
||||
gboolean *resolution_loaded,
|
||||
gboolean *ps_metadata_loaded,
|
||||
GError **error);
|
||||
|
||||
PikaImage * load_thumbnail_image (GFile *file,
|
||||
gint *width,
|
||||
gint *height,
|
||||
PikaImageType *type,
|
||||
GError **error);
|
||||
|
||||
#endif /* __JPEG_LOAD_H__ */
|
152
plug-ins/file-jpeg/jpeg-quality.c
Normal file
152
plug-ins/file-jpeg/jpeg-quality.c
Normal file
@ -0,0 +1,152 @@
|
||||
/* 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-quality.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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib/gstdio.h>
|
||||
|
||||
#include <jpeglib.h>
|
||||
|
||||
#include "jpeg-quality.h"
|
||||
|
||||
/*
|
||||
* Note that although 0 is a valid quality for IJG's JPEG library, the
|
||||
* baseline quantization tables for quality 0 and 1 are identical (all
|
||||
* divisors are set to the maximum value 255). So 0 can be used here
|
||||
* to flag unusual settings.
|
||||
*/
|
||||
|
||||
/* sum of the luminance divisors for quality = 0..100 (IJG standard tables) */
|
||||
static gint std_luminance_sum[101] =
|
||||
{
|
||||
G_MAXINT,
|
||||
16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859, 12560, 12240,
|
||||
11861, 11456, 11081, 10714, 10360, 10027, 9679, 9368, 9056, 8680, 8331,
|
||||
7995, 7668, 7376, 7084, 6823, 6562, 6345, 6125, 5939, 5756, 5571,
|
||||
5421, 5240, 5086, 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166,
|
||||
4092, 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396, 3323,
|
||||
3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727, 2657, 2583, 2509,
|
||||
2437, 2362, 2290, 2211, 2136, 2068, 1996, 1915, 1858, 1773, 1692,
|
||||
1620, 1552, 1477, 1398, 1326, 1251, 1179, 1109, 1031, 961, 884,
|
||||
814, 736, 667, 592, 518, 441, 369, 292, 221, 151, 86,
|
||||
64
|
||||
};
|
||||
|
||||
/* sum of the chrominance divisors for quality = 0..100 (IJG standard tables) */
|
||||
static gint std_chrominance_sum[101] =
|
||||
{
|
||||
G_MAXINT,
|
||||
16320, 16320, 16320, 16218, 16010, 15731, 15523, 15369, 15245, 15110, 14985,
|
||||
14864, 14754, 14635, 14526, 14429, 14346, 14267, 14204, 13790, 13121, 12511,
|
||||
11954, 11453, 11010, 10567, 10175, 9787, 9455, 9122, 8844, 8565, 8288,
|
||||
8114, 7841, 7616, 7447, 7227, 7060, 6897, 6672, 6562, 6396, 6226,
|
||||
6116, 5948, 5838, 5729, 5614, 5505, 5396, 5281, 5172, 5062, 4947,
|
||||
4837, 4726, 4614, 4506, 4395, 4282, 4173, 4061, 3950, 3839, 3727,
|
||||
3617, 3505, 3394, 3284, 3169, 3060, 2949, 2836, 2780, 2669, 2556,
|
||||
2445, 2336, 2221, 2111, 2000, 1888, 1778, 1666, 1555, 1444, 1332,
|
||||
1223, 1110, 999, 891, 779, 668, 558, 443, 333, 224, 115,
|
||||
64
|
||||
};
|
||||
|
||||
/**
|
||||
* jpeg_detect_quality:
|
||||
* @cinfo: a pointer to a JPEG decompressor info.
|
||||
*
|
||||
* Returns the exact or estimated quality value that was used to save
|
||||
* the JPEG image by analyzing the quantization table divisors.
|
||||
*
|
||||
* If an exact match for the IJG quantization tables is found, then a
|
||||
* quality setting in the range 1..100 is returned. If the quality
|
||||
* can only be estimated, then a negative number in the range -1..-100
|
||||
* is returned; its absolute value represents the maximum IJG quality
|
||||
* setting to use. If the quality cannot be reliably determined, then
|
||||
* 0 is returned.
|
||||
*
|
||||
* This function must be called after jpeg_read_header() so that
|
||||
* @cinfo contains the quantization tables read from the DQT markers
|
||||
* in the file.
|
||||
*
|
||||
* Returns: the JPEG quality setting in the range 1..100, -1..-100 or 0.
|
||||
*/
|
||||
gint
|
||||
jpeg_detect_quality (struct jpeg_decompress_struct *cinfo)
|
||||
{
|
||||
gint t;
|
||||
gint i;
|
||||
gint sum[3];
|
||||
gint q;
|
||||
|
||||
/* files using CMYK or having 4 quantization tables are unusual */
|
||||
if (!cinfo || cinfo->output_components > 3 || cinfo->quant_tbl_ptrs[3])
|
||||
return 0;
|
||||
|
||||
/* Most files use table 0 for luminance divisors (Y) and table 1 for
|
||||
* chrominance divisors (Cb and Cr). Some files use separate tables
|
||||
* for Cb and Cr, so table 2 may also be used.
|
||||
*/
|
||||
for (t = 0; t < 3; t++)
|
||||
{
|
||||
sum[t] = 0;
|
||||
if (cinfo->quant_tbl_ptrs[t])
|
||||
for (i = 0; i < DCTSIZE2; i++)
|
||||
sum[t] += cinfo->quant_tbl_ptrs[t]->quantval[i];
|
||||
}
|
||||
|
||||
if (cinfo->output_components > 1)
|
||||
{
|
||||
gint sums;
|
||||
|
||||
if (sum[0] < 64 || sum[1] < 64)
|
||||
return 0;
|
||||
|
||||
/* compare with the chrominance table having the lowest sum */
|
||||
if (sum[1] < sum[2] || sum[2] <= 0)
|
||||
sums = sum[0] + sum[1];
|
||||
else
|
||||
sums = sum[0] + sum[2];
|
||||
|
||||
q = 100;
|
||||
while (sums > std_luminance_sum[q] + std_chrominance_sum[q])
|
||||
q--;
|
||||
|
||||
if (sum[0] == std_luminance_sum[q] && sum[1] == std_chrominance_sum[q])
|
||||
return q;
|
||||
else
|
||||
return -q;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sum[0] < 64)
|
||||
return 0;
|
||||
|
||||
q = 100;
|
||||
while (sum[0] > std_luminance_sum[q])
|
||||
q--;
|
||||
|
||||
if (sum[0] == std_luminance_sum[q])
|
||||
return q;
|
||||
else
|
||||
return -q;
|
||||
}
|
||||
}
|
30
plug-ins/file-jpeg/jpeg-quality.h
Normal file
30
plug-ins/file-jpeg/jpeg-quality.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* 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-quality.h
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __JPEG_QUALITY_H__
|
||||
#define __JPEG_QUALITY_H__
|
||||
|
||||
gint jpeg_detect_quality (struct jpeg_decompress_struct *cinfo);
|
||||
|
||||
#endif /* __JPEG_QUALITY_H__ */
|
1106
plug-ins/file-jpeg/jpeg-save.c
Normal file
1106
plug-ins/file-jpeg/jpeg-save.c
Normal file
File diff suppressed because it is too large
Load Diff
42
plug-ins/file-jpeg/jpeg-save.h
Normal file
42
plug-ins/file-jpeg/jpeg-save.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* 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
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __JPEG_SAVE_H__
|
||||
#define __JPEG_SAVE_H__
|
||||
|
||||
|
||||
extern PikaImage *orig_image_global;
|
||||
extern PikaDrawable *drawable_global;
|
||||
|
||||
|
||||
gboolean save_image (GFile *file,
|
||||
PikaProcedureConfig *config,
|
||||
PikaImage *image,
|
||||
PikaDrawable *drawable,
|
||||
PikaImage *orig_image,
|
||||
gboolean preview,
|
||||
GError **error);
|
||||
gboolean save_dialog (PikaProcedure *procedure,
|
||||
PikaProcedureConfig *config,
|
||||
PikaDrawable *drawable,
|
||||
PikaImage *image);
|
||||
|
||||
#endif /* __JPEG_SAVE_H__ */
|
399
plug-ins/file-jpeg/jpeg-settings.c
Normal file
399
plug-ins/file-jpeg/jpeg-settings.c
Normal file
@ -0,0 +1,399 @@
|
||||
/* 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);
|
||||
}
|
||||
}
|
41
plug-ins/file-jpeg/jpeg-settings.h
Normal file
41
plug-ins/file-jpeg/jpeg-settings.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* 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.h
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __JPEG_SETTINGS_H__
|
||||
#define __JPEG_SETTINGS_H__
|
||||
|
||||
gboolean jpeg_detect_original_settings (struct jpeg_decompress_struct *cinfo,
|
||||
PikaImage *image);
|
||||
|
||||
gboolean jpeg_restore_original_settings (PikaImage *image,
|
||||
gint *quality,
|
||||
JpegSubsampling *subsmp,
|
||||
gint *num_quant_tables);
|
||||
|
||||
guint **jpeg_restore_original_tables (PikaImage *image,
|
||||
gint num_quant_tables);
|
||||
|
||||
void jpeg_swap_original_settings (PikaImage *image);
|
||||
|
||||
#endif /* __JPEG_SETTINGS_H__ */
|
668
plug-ins/file-jpeg/jpeg.c
Normal file
668
plug-ins/file-jpeg/jpeg.c
Normal file
@ -0,0 +1,668 @@
|
||||
/* 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
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <setjmp.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
|
||||
#include <libpika/pika.h>
|
||||
#include <libpika/pikaui.h>
|
||||
|
||||
#include "libpika/stdplugins-intl.h"
|
||||
|
||||
#include "jpeg.h"
|
||||
#include "jpeg-settings.h"
|
||||
#include "jpeg-load.h"
|
||||
#include "jpeg-save.h"
|
||||
|
||||
|
||||
typedef struct _Jpeg Jpeg;
|
||||
typedef struct _JpegClass JpegClass;
|
||||
|
||||
struct _Jpeg
|
||||
{
|
||||
PikaPlugIn parent_instance;
|
||||
};
|
||||
|
||||
struct _JpegClass
|
||||
{
|
||||
PikaPlugInClass parent_class;
|
||||
};
|
||||
|
||||
|
||||
#define JPEG_TYPE (jpeg_get_type ())
|
||||
#define JPEG (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), JPEG_TYPE, Jpeg))
|
||||
|
||||
GType jpeg_get_type (void) G_GNUC_CONST;
|
||||
|
||||
static GList * jpeg_query_procedures (PikaPlugIn *plug_in);
|
||||
static PikaProcedure * jpeg_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name);
|
||||
|
||||
static PikaValueArray * jpeg_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static PikaValueArray * jpeg_load_thumb (PikaProcedure *procedure,
|
||||
GFile *file,
|
||||
gint size,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
static PikaValueArray * jpeg_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (Jpeg, jpeg, PIKA_TYPE_PLUG_IN)
|
||||
|
||||
PIKA_MAIN (JPEG_TYPE)
|
||||
DEFINE_STD_SET_I18N
|
||||
|
||||
|
||||
gboolean undo_touched = FALSE;
|
||||
PikaDisplay *display = NULL;
|
||||
gboolean separate_display = FALSE;
|
||||
PikaImage *orig_image_global = NULL;
|
||||
PikaDrawable *drawable_global = NULL;
|
||||
|
||||
|
||||
static void
|
||||
jpeg_class_init (JpegClass *klass)
|
||||
{
|
||||
PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass);
|
||||
|
||||
plug_in_class->query_procedures = jpeg_query_procedures;
|
||||
plug_in_class->create_procedure = jpeg_create_procedure;
|
||||
plug_in_class->set_i18n = STD_SET_I18N;
|
||||
}
|
||||
|
||||
static void
|
||||
jpeg_init (Jpeg *jpeg)
|
||||
{
|
||||
}
|
||||
|
||||
static GList *
|
||||
jpeg_query_procedures (PikaPlugIn *plug_in)
|
||||
{
|
||||
GList *list = NULL;
|
||||
|
||||
list = g_list_append (list, g_strdup (LOAD_THUMB_PROC));
|
||||
list = g_list_append (list, g_strdup (LOAD_PROC));
|
||||
list = g_list_append (list, g_strdup (SAVE_PROC));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PikaProcedure *
|
||||
jpeg_create_procedure (PikaPlugIn *plug_in,
|
||||
const gchar *name)
|
||||
{
|
||||
PikaProcedure *procedure = NULL;
|
||||
|
||||
if (! strcmp (name, LOAD_PROC))
|
||||
{
|
||||
procedure = pika_load_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
jpeg_load, NULL, NULL);
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("JPEG image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Loads files in the JPEG file format",
|
||||
"Loads files in the JPEG file format",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Spencer Kimball, Peter Mattis & others",
|
||||
"Spencer Kimball & Peter Mattis",
|
||||
"1995-2007");
|
||||
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/jpeg");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"jpg,jpeg,jpe");
|
||||
pika_file_procedure_set_magics (PIKA_FILE_PROCEDURE (procedure),
|
||||
"0,string,\xff\xd8\xff");
|
||||
|
||||
pika_load_procedure_set_thumbnail_loader (PIKA_LOAD_PROCEDURE (procedure),
|
||||
LOAD_THUMB_PROC);
|
||||
}
|
||||
else if (! strcmp (name, LOAD_THUMB_PROC))
|
||||
{
|
||||
procedure = pika_thumbnail_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
jpeg_load_thumb, NULL, NULL);
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Loads a thumbnail from a JPEG image",
|
||||
"Loads a thumbnail from a JPEG image, "
|
||||
"if one exists",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Mukund Sivaraman <muks@mukund.org>, "
|
||||
"Sven Neumann <sven@gimp.org>",
|
||||
"Mukund Sivaraman <muks@mukund.org>, "
|
||||
"Sven Neumann <sven@gimp.org>",
|
||||
"November 15, 2004");
|
||||
}
|
||||
else if (! strcmp (name, SAVE_PROC))
|
||||
{
|
||||
procedure = pika_save_procedure_new (plug_in, name,
|
||||
PIKA_PDB_PROC_TYPE_PLUGIN,
|
||||
jpeg_save, NULL, NULL);
|
||||
|
||||
pika_procedure_set_image_types (procedure, "RGB*, GRAY*");
|
||||
|
||||
pika_procedure_set_menu_label (procedure, _("JPEG image"));
|
||||
|
||||
pika_procedure_set_documentation (procedure,
|
||||
"Saves files in the JPEG file format",
|
||||
"Saves files in the lossy, widely "
|
||||
"supported JPEG format",
|
||||
name);
|
||||
pika_procedure_set_attribution (procedure,
|
||||
"Spencer Kimball, Peter Mattis & others",
|
||||
"Spencer Kimball & Peter Mattis",
|
||||
"1995-2007");
|
||||
|
||||
pika_file_procedure_set_format_name (PIKA_FILE_PROCEDURE (procedure),
|
||||
_("JPEG"));
|
||||
pika_file_procedure_set_mime_types (PIKA_FILE_PROCEDURE (procedure),
|
||||
"image/jpeg");
|
||||
pika_file_procedure_set_extensions (PIKA_FILE_PROCEDURE (procedure),
|
||||
"jpg,jpeg,jpe");
|
||||
|
||||
/* See bugs #63610 and #61088 for a discussion about the quality
|
||||
* settings
|
||||
*/
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "quality",
|
||||
_("_Quality"),
|
||||
_("Quality of exported image"),
|
||||
0.0, 1.0, 0.9,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_DOUBLE (procedure, "smoothing",
|
||||
_("S_moothing"),
|
||||
_("Smoothing factor for exported image"),
|
||||
0.0, 1.0, 0.0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "optimize",
|
||||
_("Optimi_ze"),
|
||||
_("Use optimized tables during Huffman coding"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "progressive",
|
||||
_("_Progressive"),
|
||||
_("Create progressive JPEG images"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "cmyk",
|
||||
_("Export as CM_YK"),
|
||||
_("Create a CMYK JPEG image using the soft-proofing color profile"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "sub-sampling",
|
||||
_("Su_bsampling"),
|
||||
_("Sub-sampling type { 0 == 4:2:0 (chroma quartered), "
|
||||
"1 == 4:2:2 (chroma halved horizontally), "
|
||||
"2 == 4:4:4 (best quality), "
|
||||
"3 == 4:4:0 (chroma halved vertically)"),
|
||||
JPEG_SUBSAMPLING_2x2_1x1_1x1,
|
||||
JPEG_SUBSAMPLING_1x2_1x1_1x1,
|
||||
JPEG_SUBSAMPLING_1x1_1x1_1x1,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_BOOLEAN (procedure, "baseline",
|
||||
_("Baseline"),
|
||||
_("Force creation of a baseline JPEG "
|
||||
"(non-baseline JPEGs can't be read by all decoders)"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "restart",
|
||||
_("Inter_val (MCU rows):"),
|
||||
_("Interval of restart markers "
|
||||
"(in MCU rows, 0 = no restart markers)"),
|
||||
0, 64, 0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_ARG_INT (procedure, "dct",
|
||||
_("_DCT method"),
|
||||
_("DCT method to use { INTEGER (0), FIXED (1), "
|
||||
"FLOAT (2) }"),
|
||||
0, 2, 0,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
/* Some auxiliary arguments mostly for interactive usage. */
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "use-original-quality",
|
||||
_("_Use quality settings from original image"),
|
||||
_("If the original image was loaded from a JPEG "
|
||||
"file using non-standard quality settings "
|
||||
"(quantization tables), enable this option to "
|
||||
"get almost the same quality and file size."),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
PIKA_PROC_AUX_ARG_INT (procedure, "original-quality",
|
||||
NULL, NULL,
|
||||
-1, 100, -1,
|
||||
G_PARAM_READWRITE);
|
||||
PIKA_PROC_AUX_ARG_INT (procedure, "original-sub-sampling",
|
||||
NULL, NULL,
|
||||
JPEG_SUBSAMPLING_2x2_1x1_1x1,
|
||||
JPEG_SUBSAMPLING_1x2_1x1_1x1,
|
||||
JPEG_SUBSAMPLING_2x2_1x1_1x1,
|
||||
G_PARAM_READWRITE);
|
||||
PIKA_PROC_AUX_ARG_INT (procedure, "original-num-quant-tables",
|
||||
NULL, NULL,
|
||||
-1, 4, -1,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "show-preview",
|
||||
_("Sho_w preview in image window"),
|
||||
_("Creates a temporary layer with an export preview"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "use-arithmetic-coding",
|
||||
_("Use _arithmetic coding"),
|
||||
_("Older software may have trouble opening "
|
||||
"arithmetic-coded images"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
PIKA_PROC_AUX_ARG_BOOLEAN (procedure, "use-restart",
|
||||
_("Use restart mar_kers"),
|
||||
NULL, FALSE,
|
||||
G_PARAM_READWRITE);
|
||||
|
||||
pika_save_procedure_set_support_exif (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
pika_save_procedure_set_support_iptc (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
pika_save_procedure_set_support_xmp (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
pika_save_procedure_set_support_profile (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
pika_save_procedure_set_support_thumbnail (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
pika_save_procedure_set_support_comment (PIKA_SAVE_PROCEDURE (procedure), TRUE);
|
||||
}
|
||||
|
||||
return procedure;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
jpeg_load (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
gboolean resolution_loaded = FALSE;
|
||||
gboolean ps_metadata_loaded = FALSE;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
preview_image = NULL;
|
||||
preview_layer = NULL;
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
image = load_image (file, run_mode, FALSE,
|
||||
&resolution_loaded, &ps_metadata_loaded, &error);
|
||||
|
||||
if (image)
|
||||
{
|
||||
PikaMetadata *metadata;
|
||||
|
||||
metadata = pika_image_metadata_load_prepare (image, "image/jpeg",
|
||||
file, NULL);
|
||||
|
||||
if (metadata)
|
||||
{
|
||||
PikaMetadataLoadFlags flags = PIKA_METADATA_LOAD_ALL;
|
||||
|
||||
if (resolution_loaded)
|
||||
flags &= ~PIKA_METADATA_LOAD_RESOLUTION;
|
||||
|
||||
pika_image_metadata_load_finish (image, "image/jpeg",
|
||||
metadata, flags);
|
||||
|
||||
g_object_unref (metadata);
|
||||
}
|
||||
}
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
jpeg_load_thumb (PikaProcedure *procedure,
|
||||
GFile *file,
|
||||
gint size,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaValueArray *return_vals;
|
||||
PikaImage *image;
|
||||
gint width = 0;
|
||||
gint height = 0;
|
||||
PikaImageType type = -1;
|
||||
GError *error = NULL;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
preview_image = NULL;
|
||||
preview_layer = NULL;
|
||||
|
||||
image = load_thumbnail_image (file, &width, &height, &type,
|
||||
&error);
|
||||
|
||||
|
||||
if (! image)
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_EXECUTION_ERROR,
|
||||
error);
|
||||
|
||||
return_vals = pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_SUCCESS,
|
||||
NULL);
|
||||
|
||||
PIKA_VALUES_SET_IMAGE (return_vals, 1, image);
|
||||
PIKA_VALUES_SET_INT (return_vals, 2, width);
|
||||
PIKA_VALUES_SET_INT (return_vals, 3, height);
|
||||
PIKA_VALUES_SET_ENUM (return_vals, 4, type);
|
||||
PIKA_VALUES_SET_INT (return_vals, 5, 1); /* 1 layer */
|
||||
|
||||
return return_vals;
|
||||
}
|
||||
|
||||
static PikaValueArray *
|
||||
jpeg_save (PikaProcedure *procedure,
|
||||
PikaRunMode run_mode,
|
||||
PikaImage *image,
|
||||
gint n_drawables,
|
||||
PikaDrawable **drawables,
|
||||
GFile *file,
|
||||
const PikaValueArray *args,
|
||||
gpointer run_data)
|
||||
{
|
||||
PikaPDBStatusType status = PIKA_PDB_SUCCESS;
|
||||
PikaProcedureConfig *config;
|
||||
PikaMetadata *metadata;
|
||||
PikaImage *orig_image;
|
||||
PikaExportReturn export = PIKA_EXPORT_CANCEL;
|
||||
GError *error = NULL;
|
||||
|
||||
gint orig_num_quant_tables = -1;
|
||||
gint orig_quality = -1;
|
||||
JpegSubsampling orig_subsmp = JPEG_SUBSAMPLING_2x2_1x1_1x1;
|
||||
|
||||
gegl_init (NULL, NULL);
|
||||
|
||||
config = pika_procedure_create_config (procedure);
|
||||
metadata = pika_procedure_config_begin_export (config, image, run_mode,
|
||||
args, "image/jpeg");
|
||||
preview_image = NULL;
|
||||
preview_layer = NULL;
|
||||
|
||||
orig_image = image;
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
pika_ui_init (PLUG_IN_BINARY);
|
||||
|
||||
export = pika_export_image (&image, &n_drawables, &drawables, "JPEG",
|
||||
PIKA_EXPORT_CAN_HANDLE_RGB |
|
||||
PIKA_EXPORT_CAN_HANDLE_GRAY);
|
||||
|
||||
switch (export)
|
||||
{
|
||||
case PIKA_EXPORT_EXPORT:
|
||||
display = NULL;
|
||||
separate_display = TRUE;
|
||||
break;
|
||||
|
||||
case PIKA_EXPORT_IGNORE:
|
||||
break;
|
||||
|
||||
case PIKA_EXPORT_CANCEL:
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CANCEL,
|
||||
NULL);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (n_drawables != 1)
|
||||
{
|
||||
g_set_error (&error, G_FILE_ERROR, 0,
|
||||
_("JPEG format does not support multiple layers."));
|
||||
|
||||
return pika_procedure_new_return_values (procedure,
|
||||
PIKA_PDB_CALLING_ERROR,
|
||||
error);
|
||||
}
|
||||
|
||||
/* Override preferences from JPG export defaults (if saved). */
|
||||
|
||||
switch (run_mode)
|
||||
{
|
||||
case PIKA_RUN_NONINTERACTIVE:
|
||||
g_object_set (config, "show-preview", FALSE, NULL);
|
||||
break;
|
||||
|
||||
case PIKA_RUN_INTERACTIVE:
|
||||
case PIKA_RUN_WITH_LAST_VALS:
|
||||
{
|
||||
/* restore the values found when loading the file (if available) */
|
||||
gdouble dquality;
|
||||
gint quality;
|
||||
gint subsmp;
|
||||
|
||||
jpeg_restore_original_settings (orig_image,
|
||||
&orig_quality,
|
||||
&orig_subsmp,
|
||||
&orig_num_quant_tables);
|
||||
|
||||
g_object_get (config,
|
||||
"quality", &dquality,
|
||||
"sub-sampling", &subsmp,
|
||||
NULL);
|
||||
|
||||
quality = (gint) (dquality * 100.0);
|
||||
|
||||
/* If this image was loaded from a JPEG file and has not
|
||||
* been saved yet, try to use some of the settings from the
|
||||
* original file if they are better than the default values.
|
||||
*/
|
||||
if (orig_quality > quality)
|
||||
{
|
||||
quality = orig_quality;
|
||||
dquality = (gdouble) quality / 100.0;
|
||||
g_object_set (config, "quality", dquality, NULL);
|
||||
}
|
||||
|
||||
if (orig_quality > 0)
|
||||
{
|
||||
/* Skip changing subsampling to original if we already have
|
||||
* best setting or if original have worst setting
|
||||
*/
|
||||
if (!(subsmp == JPEG_SUBSAMPLING_1x1_1x1_1x1 ||
|
||||
orig_subsmp == JPEG_SUBSAMPLING_2x2_1x1_1x1))
|
||||
{
|
||||
subsmp = orig_subsmp;
|
||||
g_object_set (config, "sub-sampling", orig_subsmp, NULL);
|
||||
}
|
||||
|
||||
if (orig_quality == quality && orig_subsmp == subsmp)
|
||||
{
|
||||
g_object_set (config, "use-original-quality", TRUE, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
g_object_set (config,
|
||||
"original-sub-sampling", orig_subsmp,
|
||||
"original-quality", orig_quality,
|
||||
"original-num-quant-tables", orig_num_quant_tables,
|
||||
NULL);
|
||||
|
||||
if (run_mode == PIKA_RUN_INTERACTIVE)
|
||||
{
|
||||
gboolean show_preview = FALSE;
|
||||
|
||||
g_object_get (config, "show-preview", &show_preview, NULL);
|
||||
if (show_preview)
|
||||
{
|
||||
/* we freeze undo saving so that we can avoid sucking up
|
||||
* tile cache with our unneeded preview steps. */
|
||||
pika_image_undo_freeze (image);
|
||||
|
||||
undo_touched = TRUE;
|
||||
}
|
||||
|
||||
/* prepare for the preview */
|
||||
preview_image = image;
|
||||
orig_image_global = orig_image;
|
||||
drawable_global = drawables[0];
|
||||
|
||||
/* First acquire information with a dialog */
|
||||
if (! save_dialog (procedure, config, drawables[0], orig_image))
|
||||
{
|
||||
status = PIKA_PDB_CANCEL;
|
||||
}
|
||||
|
||||
if (undo_touched)
|
||||
{
|
||||
/* thaw undo saving and flush the displays to have them
|
||||
* reflect the current shortcuts
|
||||
*/
|
||||
pika_image_undo_thaw (image);
|
||||
pika_displays_flush ();
|
||||
}
|
||||
}
|
||||
|
||||
if (status == PIKA_PDB_SUCCESS)
|
||||
{
|
||||
if (! save_image (file, config,
|
||||
image, drawables[0], orig_image, FALSE,
|
||||
&error))
|
||||
{
|
||||
status = PIKA_PDB_EXECUTION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (status == PIKA_PDB_SUCCESS)
|
||||
{
|
||||
if (metadata)
|
||||
pika_metadata_set_bits_per_sample (metadata, 8);
|
||||
}
|
||||
|
||||
pika_procedure_config_end_export (config, image, file, status);
|
||||
g_object_unref (config);
|
||||
|
||||
if (export == PIKA_EXPORT_EXPORT)
|
||||
{
|
||||
/* If the image was exported, delete the new display. This also
|
||||
* deletes the image.
|
||||
*/
|
||||
if (display)
|
||||
{
|
||||
pika_display_delete (display);
|
||||
pika_display_present (pika_default_display ());
|
||||
}
|
||||
else
|
||||
{
|
||||
pika_image_delete (image);
|
||||
}
|
||||
|
||||
g_free (drawables);
|
||||
}
|
||||
|
||||
return pika_procedure_new_return_values (procedure, status, error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Here's the routine that will replace the standard error_exit method:
|
||||
*/
|
||||
|
||||
void
|
||||
my_error_exit (j_common_ptr cinfo)
|
||||
{
|
||||
/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
|
||||
my_error_ptr myerr = (my_error_ptr) cinfo->err;
|
||||
|
||||
/* Always display the message. */
|
||||
/* We could postpone this until after returning, if we chose. */
|
||||
(*cinfo->err->output_message) (cinfo);
|
||||
|
||||
/* Return control to the setjmp point */
|
||||
longjmp (myerr->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
my_output_message (j_common_ptr cinfo)
|
||||
{
|
||||
gchar buffer[JMSG_LENGTH_MAX + 1];
|
||||
|
||||
(*cinfo->err->format_message)(cinfo, buffer);
|
||||
|
||||
g_message ("%s", buffer);
|
||||
}
|
74
plug-ins/file-jpeg/jpeg.h
Normal file
74
plug-ins/file-jpeg/jpeg.h
Normal file
@ -0,0 +1,74 @@
|
||||
/* 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
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef __JPEG_H__
|
||||
#define __JPEG_H__
|
||||
|
||||
#define LOAD_PROC "file-jpeg-load"
|
||||
#define LOAD_THUMB_PROC "file-jpeg-load-thumb"
|
||||
#define SAVE_PROC "file-jpeg-save"
|
||||
#define PLUG_IN_BINARY "file-jpeg"
|
||||
#define PLUG_IN_ROLE "pika-file-jpeg"
|
||||
|
||||
/* headers used in some APPn markers */
|
||||
#define JPEG_APP_HEADER_EXIF "Exif\0\0"
|
||||
#define JPEG_APP_HEADER_XMP "http://ns.adobe.com/xap/1.0/"
|
||||
|
||||
typedef struct my_error_mgr
|
||||
{
|
||||
struct jpeg_error_mgr pub; /* "public" fields */
|
||||
|
||||
#ifdef __ia64__
|
||||
/* Ugh, the jmp_buf field needs to be 16-byte aligned on ia64 and some
|
||||
* glibc/icc combinations don't guarantee this. So we pad. See bug #138357
|
||||
* for details.
|
||||
*/
|
||||
long double dummy;
|
||||
#endif
|
||||
|
||||
jmp_buf setjmp_buffer; /* for return to caller */
|
||||
} *my_error_ptr;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
JPEG_SUBSAMPLING_2x2_1x1_1x1 = 0, /* smallest file */
|
||||
JPEG_SUBSAMPLING_2x1_1x1_1x1 = 1, /* 4:2:2 */
|
||||
JPEG_SUBSAMPLING_1x1_1x1_1x1 = 2,
|
||||
JPEG_SUBSAMPLING_1x2_1x1_1x1 = 3
|
||||
} JpegSubsampling;
|
||||
|
||||
extern PikaImage * volatile preview_image;
|
||||
extern PikaLayer * preview_layer;
|
||||
extern gboolean undo_touched;
|
||||
extern gboolean load_interactive;
|
||||
extern PikaDisplay *display;
|
||||
extern gboolean separate_display;
|
||||
|
||||
|
||||
void destroy_preview (void);
|
||||
|
||||
void my_error_exit (j_common_ptr cinfo);
|
||||
void my_emit_message (j_common_ptr cinfo,
|
||||
int msg_level);
|
||||
void my_output_message (j_common_ptr cinfo);
|
||||
|
||||
|
||||
#endif /* __JPEG_H__ */
|
1083
plug-ins/file-jpeg/jpegqual.c
Normal file
1083
plug-ins/file-jpeg/jpegqual.c
Normal file
File diff suppressed because it is too large
Load Diff
36
plug-ins/file-jpeg/meson.build
Normal file
36
plug-ins/file-jpeg/meson.build
Normal file
@ -0,0 +1,36 @@
|
||||
plugin_name = 'file-jpeg'
|
||||
|
||||
plugin_sources = [
|
||||
'jpeg-icc.c',
|
||||
'jpeg-load.c',
|
||||
'jpeg-quality.c',
|
||||
'jpeg-save.c',
|
||||
'jpeg-settings.c',
|
||||
'jpeg.c',
|
||||
]
|
||||
|
||||
if platform_windows
|
||||
plugin_sources += windows.compile_resources(
|
||||
pika_plugins_rc,
|
||||
args: [
|
||||
'--define', 'ORIGINALFILENAME_STR="@0@"'.format(plugin_name+'.exe'),
|
||||
'--define', 'INTERNALNAME_STR="@0@"' .format(plugin_name),
|
||||
'--define', 'TOP_SRCDIR="@0@"' .format(meson.project_source_root()),
|
||||
],
|
||||
include_directories: [
|
||||
rootInclude, appInclude,
|
||||
],
|
||||
)
|
||||
endif
|
||||
|
||||
executable(plugin_name,
|
||||
plugin_sources,
|
||||
dependencies: [
|
||||
libpikaui_dep,
|
||||
libjpeg,
|
||||
lcms,
|
||||
gexiv2,
|
||||
],
|
||||
install: true,
|
||||
install_dir: pikaplugindir / 'plug-ins' / plugin_name,
|
||||
)
|
Reference in New Issue
Block a user