Initial checkin of Pika from heckimp

This commit is contained in:
2023-09-25 15:35:21 -07:00
commit 891e999216
6761 changed files with 5240685 additions and 0 deletions

View 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.

View 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;
}

View 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__ */

View 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;
}

View 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__ */

View 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;
}
}

View 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__ */

File diff suppressed because it is too large Load Diff

View 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__ */

View 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);
}
}

View 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
View 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
View 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__ */

File diff suppressed because it is too large Load Diff

View 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,
)