diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 03:13:10 +0000 |
commit | 3c57dd931145d43f2b0aef96c4d178135956bf91 (patch) | |
tree | 3de698981e9f0cc2c4f9569b19a5f3595e741f6b /plug-ins/file-jpeg/jpeg-save.c | |
parent | Initial commit. (diff) | |
download | gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.tar.xz gimp-3c57dd931145d43f2b0aef96c4d178135956bf91.zip |
Adding upstream version 2.10.36.upstream/2.10.36
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | plug-ins/file-jpeg/jpeg-save.c | 1524 |
1 files changed, 1524 insertions, 0 deletions
diff --git a/plug-ins/file-jpeg/jpeg-save.c b/plug-ins/file-jpeg/jpeg-save.c new file mode 100644 index 0000000..4af4d1d --- /dev/null +++ b/plug-ins/file-jpeg/jpeg-save.c @@ -0,0 +1,1524 @@ +/* GIMP - The GNU Image Manipulation Program + * 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 <stdlib.h> +#include <string.h> +#include <errno.h> +#include <setjmp.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <glib/gstdio.h> + +#include <jpeglib.h> +#include <jerror.h> + +#include <libgimp/gimp.h> +#include <libgimp/gimpui.h> + +#include "libgimp/stdplugins-intl.h" + +#include "jpeg.h" +#include "jpeg-icc.h" +#include "jpeg-load.h" +#include "jpeg-save.h" +#include "jpeg-settings.h" + +#ifdef C_ARITH_CODING_SUPPORTED +static gboolean arithc_supported = TRUE; +#else +static gboolean arithc_supported = FALSE; +#endif + +#define SCALE_WIDTH 125 + +/* See bugs #63610 and #61088 for a discussion about the quality settings */ +#define DEFAULT_IJG_QUALITY 90.0 +#define DEFAULT_SMOOTHING 0.0 +#define DEFAULT_OPTIMIZE TRUE +#define DEFAULT_ARITHMETIC_CODING FALSE +#define DEFAULT_PROGRESSIVE TRUE +#define DEFAULT_BASELINE TRUE +#define DEFAULT_SUBSMP JPEG_SUBSAMPLING_1x1_1x1_1x1 +#define DEFAULT_RESTART 0 +#define DEFAULT_RESTART_MCU_ROWS 16 +#define DEFAULT_DCT 0 +#define DEFAULT_PREVIEW FALSE +#define DEFAULT_EXIF FALSE +#define DEFAULT_XMP FALSE +#define DEFAULT_IPTC FALSE +#define DEFAULT_THUMBNAIL FALSE +#define DEFAULT_PROFILE TRUE +#define DEFAULT_USE_ORIG_QUALITY FALSE + +#define JPEG_DEFAULTS_PARASITE "jpeg-save-defaults" + + +typedef struct +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + gint tile_height; + FILE *outfile; + gboolean has_alpha; + gint rowstride; + guchar *data; + guchar *src; + GeglBuffer *buffer; + const Babl *format; + const gchar *file_name; + gboolean abort_me; + guint source_id; +} PreviewPersistent; + +/*le added : struct containing pointers to export dialog*/ +typedef struct +{ + gboolean run; + GtkWidget *use_restart_markers; /*checkbox setting use restart markers*/ + GtkTextBuffer *text_buffer; + GtkAdjustment *scale_data; /*for restart markers*/ + gulong handler_id_restart; + + GtkAdjustment *quality; /*quality slidebar*/ + GtkAdjustment *smoothing; /*smoothing slidebar*/ + GtkWidget *optimize; /*optimize toggle*/ + GtkWidget *arithmetic_coding; /*arithmetic coding toggle*/ + GtkWidget *progressive; /*progressive toggle*/ + GtkWidget *subsmp; /*subsampling side select*/ + GtkWidget *restart; /*spinner for setting frequency restart markers*/ + GtkWidget *dct; /*DCT side select*/ + GtkWidget *preview; /*show preview toggle checkbox*/ + GtkWidget *save_exif; + GtkWidget *save_xmp; + GtkWidget *save_iptc; + GtkWidget *save_thumbnail; + GtkWidget *save_profile; + GtkWidget *use_orig_quality; /*quant tables toggle*/ +} JpegSaveGui; + +static void make_preview (void); + +static void save_restart_update (GtkAdjustment *adjustment, + GtkWidget *toggle); +static void subsampling_changed (GtkWidget *combo, + GtkAdjustment *entry); +static void quality_changed (GtkAdjustment *scale_entry, + GtkWidget *toggle); +static void subsampling_changed2 (GtkWidget *combo, + GtkWidget *toggle); +static void use_orig_qual_changed (GtkWidget *toggle, + GtkAdjustment *scale_entry); +static void use_orig_qual_changed2 (GtkWidget *toggle, + GtkWidget *combo); + + +static GtkWidget *restart_markers_scale = NULL; +static GtkWidget *restart_markers_label = NULL; +static GtkWidget *preview_size = NULL; +static PreviewPersistent *prev_p = NULL; + +static void save_dialog_response (GtkWidget *widget, + gint response_id, + gpointer data); + +static void load_gui_defaults (JpegSaveGui *pg); +static void save_defaults (void); + + +/* + * sg - This is the best I can do, I'm afraid... I think it will fail + * if something bad really happens (but it might not). If you have a + * better solution, send it ;-) + */ +static void +background_error_exit (j_common_ptr cinfo) +{ + if (prev_p) + prev_p->abort_me = TRUE; + (*cinfo->err->output_message) (cinfo); +} + +static gboolean +background_jpeg_save (PreviewPersistent *pp) +{ + gint yend; + + if (pp->abort_me || (pp->cinfo.next_scanline >= pp->cinfo.image_height)) + { + /* clean up... */ + if (pp->abort_me) + { + jpeg_abort_compress (&(pp->cinfo)); + } + else + { + jpeg_finish_compress (&(pp->cinfo)); + } + + fclose (pp->outfile); + jpeg_destroy_compress (&(pp->cinfo)); + + g_free (pp->data); + + if (pp->buffer) + g_object_unref (pp->buffer); + + /* display the preview stuff */ + if (!pp->abort_me) + { + GFile *file = g_file_new_for_path (pp->file_name); + GFileInfo *info; + gchar *text; + GError *error = NULL; + + info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_SIZE, + G_FILE_QUERY_INFO_NONE, + NULL, &error); + + if (info) + { + goffset size = g_file_info_get_size (info); + gchar *size_text; + + size_text = g_format_size (size); + text = g_strdup_printf (_("File size: %s"), size_text); + g_free (size_text); + + g_object_unref (info); + } + else + { + text = g_strdup_printf (_("File size: %s"), error->message); + g_clear_error (&error); + } + + gtk_label_set_text (GTK_LABEL (preview_size), text); + g_free (text); + + g_object_unref (file); + + /* and load the preview */ + load_image (pp->file_name, GIMP_RUN_NONINTERACTIVE, TRUE, NULL, NULL); + } + + /* we cleanup here (load_image doesn't run in the background) */ + g_unlink (pp->file_name); + + g_free (pp); + prev_p = NULL; + + gimp_displays_flush (); + gdk_flush (); + + return FALSE; + } + else + { + if ((pp->cinfo.next_scanline % pp->tile_height) == 0) + { + yend = pp->cinfo.next_scanline + pp->tile_height; + yend = MIN (yend, pp->cinfo.image_height); + gegl_buffer_get (pp->buffer, + GEGL_RECTANGLE (0, pp->cinfo.next_scanline, + pp->cinfo.image_width, + (yend - pp->cinfo.next_scanline)), + 1.0, + pp->format, + pp->data, + GEGL_AUTO_ROWSTRIDE, + GEGL_ABYSS_NONE); + pp->src = pp->data; + } + + jpeg_write_scanlines (&(pp->cinfo), (JSAMPARRAY) &(pp->src), 1); + pp->src += pp->rowstride; + + return TRUE; + } +} + +gboolean +save_image (const gchar *filename, + gint32 image_ID, + gint32 drawable_ID, + gint32 orig_image_ID, + gboolean preview, + GError **error) +{ + static struct jpeg_compress_struct cinfo; + static struct my_error_mgr jerr; + + GimpImageType drawable_type; + GeglBuffer *buffer; + const Babl *format; + JpegSubsampling subsampling; + FILE * volatile outfile; + guchar *data; + guchar *src; + GimpColorProfile *profile = NULL; + + gboolean has_alpha; + gboolean out_linear = FALSE; + gint rowstride, yend; + + drawable_type = gimp_drawable_type (drawable_ID); + buffer = gimp_drawable_get_buffer (drawable_ID); + + if (! preview) + gimp_progress_init_printf (_("Exporting '%s'"), + gimp_filename_to_utf8 (filename)); + + /* Step 1: allocate and initialize JPEG compression object */ + + /* We have to set up the error handler first, in case the initialization + * step fails. (Unlikely, but it could happen if you are out of memory.) + * This routine fills in the contents of struct jerr, and returns jerr's + * address which we place into the link field in cinfo. + */ + cinfo.err = jpeg_std_error (&jerr.pub); + jerr.pub.error_exit = my_error_exit; + + outfile = 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_compress (&cinfo); + if (outfile) + fclose (outfile); + if (buffer) + g_object_unref (buffer); + + return FALSE; + } + + /* Now we can initialize the JPEG compression object. */ + jpeg_create_compress (&cinfo); + + /* Step 2: specify data destination (eg, a file) */ + /* Note: steps 2 and 3 can be done in either order. */ + + /* Here we use the library-supplied code to send compressed data to a + * stdio stream. You can also write your own code to do something else. + * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that + * requires it in order to write binary files. + */ + if ((outfile = g_fopen (filename, "wb")) == NULL) + { + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), + _("Could not open '%s' for writing: %s"), + gimp_filename_to_utf8 (filename), g_strerror (errno)); + return FALSE; + } + + /* When we don't save profiles, we convert data to sRGB because + * that's what most/all readers expect on a no-profile JPEG. + * If we save an assigned profile, let's just follow its TRC. + * If we save the default linear profile (i.e. no assigned + * profile), we convert it to sRGB, except when it is 8-bit linear. + */ + if (jsvals.save_profile) + { + profile = gimp_image_get_color_profile (orig_image_ID); + + /* If a profile is explicitly set, follow its TRC, whatever the + * storage format. + */ + if (profile && gimp_color_profile_is_linear (profile)) + out_linear = TRUE; + + if (! profile) + { + /* There is always an effective profile. */ + profile = gimp_image_get_effective_color_profile (orig_image_ID); + + if (gimp_color_profile_is_linear (profile)) + { + if (gimp_image_get_precision (image_ID) != GIMP_PRECISION_U8_LINEAR) + { + GimpColorProfile *saved_profile; + + saved_profile = gimp_color_profile_new_srgb_trc_from_color_profile (profile); + g_object_unref (profile); + profile = saved_profile; + } + else + { + /* Keep linear profile as-is for 8-bit linear image. */ + out_linear = TRUE; + } + } + } + } + + jpeg_stdio_dest (&cinfo, outfile); + + /* Get the input image and a pointer to its data. + */ + switch (drawable_type) + { + case GIMP_RGB_IMAGE: + /* # of color components per pixel */ + cinfo.input_components = 3; + has_alpha = FALSE; + + if (out_linear) + format = babl_format ("RGB u8"); + else + format = babl_format ("R'G'B' u8"); + break; + + case GIMP_GRAY_IMAGE: + /* # of color components per pixel */ + cinfo.input_components = 1; + has_alpha = FALSE; + + if (out_linear) + format = babl_format ("Y u8"); + else + format = babl_format ("Y' u8"); + break; + + case GIMP_RGBA_IMAGE: + /* # of color components per pixel (minus the GIMP alpha channel) */ + cinfo.input_components = 4 - 1; + has_alpha = TRUE; + + if (out_linear) + format = babl_format ("RGB u8"); + else + format = babl_format ("R'G'B' u8"); + break; + + case GIMP_GRAYA_IMAGE: + /* # of color components per pixel (minus the GIMP alpha channel) */ + cinfo.input_components = 2 - 1; + has_alpha = TRUE; + if (out_linear) + format = babl_format ("Y u8"); + else + format = babl_format ("Y' u8"); + break; + + case GIMP_INDEXED_IMAGE: + default: + return FALSE; + } + + /* Step 3: set parameters for compression */ + + /* First we supply a description of the input image. + * Four fields of the cinfo struct must be filled in: + */ + /* image width and height, in pixels */ + cinfo.image_width = gegl_buffer_get_width (buffer); + cinfo.image_height = gegl_buffer_get_height (buffer); + /* colorspace of input image */ + cinfo.in_color_space = (drawable_type == GIMP_RGB_IMAGE || + drawable_type == GIMP_RGBA_IMAGE) + ? JCS_RGB : JCS_GRAYSCALE; + /* Now use the library's routine to set default compression parameters. + * (You must set at least cinfo.in_color_space before calling this, + * since the defaults depend on the source color space.) + */ + jpeg_set_defaults (&cinfo); + + jpeg_set_quality (&cinfo, (gint) (jsvals.quality + 0.5), jsvals.baseline); + + if (jsvals.use_orig_quality && num_quant_tables > 0) + { + guint **quant_tables; + gint t; + + /* override tables generated by jpeg_set_quality() with custom tables */ + quant_tables = jpeg_restore_original_tables (image_ID, num_quant_tables); + if (quant_tables) + { + for (t = 0; t < num_quant_tables; t++) + { + jpeg_add_quant_table (&cinfo, t, quant_tables[t], + 100, jsvals.baseline); + g_free (quant_tables[t]); + } + g_free (quant_tables); + } + } + + if (arithc_supported) + { + cinfo.arith_code = jsvals.arithmetic_coding; + if (!jsvals.arithmetic_coding) + cinfo.optimize_coding = jsvals.optimize; + } + else + cinfo.optimize_coding = jsvals.optimize; + + subsampling = (gimp_drawable_is_rgb (drawable_ID) ? + jsvals.subsmp : JPEG_SUBSAMPLING_1x1_1x1_1x1); + + /* smoothing is not supported with nonstandard sampling ratios */ + if (subsampling != JPEG_SUBSAMPLING_2x1_1x1_1x1 && + subsampling != JPEG_SUBSAMPLING_1x2_1x1_1x1) + { + cinfo.smoothing_factor = (gint) (jsvals.smoothing * 100); + } + + if (jsvals.progressive) + { + jpeg_simple_progression (&cinfo); + } + + switch (subsampling) + { + case JPEG_SUBSAMPLING_2x2_1x1_1x1: + default: + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 2; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + break; + + case JPEG_SUBSAMPLING_2x1_1x1_1x1: + cinfo.comp_info[0].h_samp_factor = 2; + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + break; + + case JPEG_SUBSAMPLING_1x1_1x1_1x1: + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + break; + + case JPEG_SUBSAMPLING_1x2_1x1_1x1: + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 2; + cinfo.comp_info[1].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor = 1; + cinfo.comp_info[2].v_samp_factor = 1; + break; + } + + cinfo.restart_interval = 0; + cinfo.restart_in_rows = jsvals.restart; + + switch (jsvals.dct) + { + case 0: + default: + cinfo.dct_method = JDCT_ISLOW; + break; + + case 1: + cinfo.dct_method = JDCT_IFAST; + break; + + case 2: + cinfo.dct_method = JDCT_FLOAT; + break; + } + + { + gdouble xresolution; + gdouble yresolution; + + gimp_image_get_resolution (orig_image_ID, &xresolution, &yresolution); + + if (xresolution > 1e-5 && yresolution > 1e-5) + { + gdouble factor; + + factor = gimp_unit_get_factor (gimp_image_get_unit (orig_image_ID)); + + if (factor == 2.54 /* cm */ || + factor == 25.4 /* mm */) + { + cinfo.density_unit = 2; /* dots per cm */ + + xresolution /= 2.54; + yresolution /= 2.54; + } + else + { + cinfo.density_unit = 1; /* dots per inch */ + } + + cinfo.X_density = xresolution; + cinfo.Y_density = yresolution; + } + } + + /* Step 4: Start compressor */ + + /* TRUE ensures that we will write a complete interchange-JPEG file. + * Pass TRUE unless you are very sure of what you're doing. + */ + jpeg_start_compress (&cinfo, TRUE); + + /* Step 4.1: Write the comment out - pw */ + if (image_comment && *image_comment) + { +#ifdef GIMP_UNSTABLE + g_print ("jpeg-save: saving image comment (%d bytes)\n", + (int) strlen (image_comment)); +#endif + jpeg_write_marker (&cinfo, JPEG_COM, + (guchar *) image_comment, strlen (image_comment)); + } + + /* Step 4.2: store the color profile */ + if (jsvals.save_profile) + { + const guint8 *icc_data; + gsize icc_length; + + icc_data = gimp_color_profile_get_icc_profile (profile, &icc_length); + jpeg_icc_write_profile (&cinfo, icc_data, icc_length); + + g_object_unref (profile); + } + + /* Step 5: while (scan lines remain to be written) */ + /* jpeg_write_scanlines(...); */ + + /* Here we use the library's state variable cinfo.next_scanline as the + * loop counter, so that we don't have to keep track ourselves. + * To keep things simple, we pass one scanline per call; you can pass + * more if you wish, though. + */ + /* JSAMPLEs per row in image_buffer */ + rowstride = cinfo.input_components * cinfo.image_width; + data = g_new (guchar, rowstride * gimp_tile_height ()); + + /* fault if cinfo.next_scanline isn't initially a multiple of + * gimp_tile_height */ + src = NULL; + + /* + * sg - if we preview, we want this to happen in the background -- do + * not duplicate code in the future; for now, it's OK + */ + + if (preview) + { + PreviewPersistent *pp = g_new (PreviewPersistent, 1); + + /* pass all the information we need */ + pp->cinfo = cinfo; + pp->tile_height = gimp_tile_height(); + pp->data = data; + pp->outfile = outfile; + pp->has_alpha = has_alpha; + pp->rowstride = rowstride; + pp->data = data; + pp->buffer = buffer; + pp->format = format; + pp->src = NULL; + pp->file_name = filename; + pp->abort_me = FALSE; + + g_warn_if_fail (prev_p == NULL); + prev_p = pp; + + pp->cinfo.err = jpeg_std_error(&(pp->jerr)); + pp->jerr.error_exit = background_error_exit; + + gtk_label_set_text (GTK_LABEL (preview_size), + _("Calculating file size...")); + + pp->source_id = g_idle_add ((GSourceFunc) background_jpeg_save, pp); + + /* background_jpeg_save() will cleanup as needed */ + return TRUE; + } + + while (cinfo.next_scanline < cinfo.image_height) + { + if ((cinfo.next_scanline % gimp_tile_height ()) == 0) + { + yend = cinfo.next_scanline + gimp_tile_height (); + yend = MIN (yend, cinfo.image_height); + gegl_buffer_get (buffer, + GEGL_RECTANGLE (0, cinfo.next_scanline, + cinfo.image_width, + (yend - cinfo.next_scanline)), + 1.0, + format, + data, + GEGL_AUTO_ROWSTRIDE, + GEGL_ABYSS_NONE); + src = data; + } + + jpeg_write_scanlines (&cinfo, (JSAMPARRAY) &src, 1); + src += rowstride; + + if ((cinfo.next_scanline % 32) == 0) + gimp_progress_update ((gdouble) cinfo.next_scanline / + (gdouble) cinfo.image_height); + } + + /* Step 6: Finish compression */ + jpeg_finish_compress (&cinfo); + /* After finish_compress, we can close the output file. */ + fclose (outfile); + + /* Step 7: release JPEG compression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_compress (&cinfo); + + /* free the temporary buffer */ + g_free (data); + + /* And we're done! */ + gimp_progress_update (1.0); + + g_object_unref (buffer); + + return TRUE; +} + +static void +make_preview (void) +{ + destroy_preview (); + + if (jsvals.preview) + { + gchar *tn = gimp_temp_name ("jpeg"); + + if (! undo_touched) + { + /* we freeze undo saving so that we can avoid sucking up + * tile cache with our unneeded preview steps. */ + gimp_image_undo_freeze (preview_image_ID); + + undo_touched = TRUE; + } + + save_image (tn, + preview_image_ID, + drawable_ID_global, + orig_image_ID_global, + TRUE, NULL); + + if (display_ID == -1) + display_ID = gimp_display_new (preview_image_ID); + } + else + { + gtk_label_set_text (GTK_LABEL (preview_size), _("File size: unknown")); + + gimp_displays_flush (); + } +} + +void +destroy_preview (void) +{ + if (prev_p && !prev_p->abort_me) + { + guint id = prev_p->source_id; + prev_p->abort_me = TRUE; /* signal the background save to stop */ + background_jpeg_save (prev_p); + g_source_remove (id); + } + + if (gimp_image_is_valid (preview_image_ID) && + gimp_item_is_valid (preview_layer_ID)) + { + /* assuming that reference counting is working correctly, + we do not need to delete the layer, removing it from + the image should be sufficient */ + gimp_image_remove_layer (preview_image_ID, preview_layer_ID); + + preview_layer_ID = -1; + } +} + +static void +toggle_arithmetic_coding (GtkToggleButton *togglebutton, + gpointer user_data) +{ + GtkWidget *optimize = GTK_WIDGET (user_data); + + gtk_widget_set_sensitive (optimize, + !gtk_toggle_button_get_active (togglebutton)); +} + +gboolean +save_dialog (void) +{ + JpegSaveGui pg; + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *vbox2; + GtkAdjustment *entry; + GtkWidget *table; + GtkWidget *table2; + GtkWidget *tabledefaults; + GtkWidget *expander; + GtkWidget *frame; + GtkWidget *toggle; + GtkWidget *spinbutton; + GtkWidget *label; + GtkWidget *combo; + GtkWidget *text_view; + GtkTextBuffer *text_buffer; + GtkWidget *scrolled_window; + GtkWidget *button; + gchar *text; + gint row; + + dialog = gimp_export_dialog_new (_("JPEG"), PLUG_IN_BINARY, SAVE_PROC); + + g_signal_connect (dialog, "response", + G_CALLBACK (save_dialog_response), + &pg); + g_signal_connect (dialog, "destroy", + G_CALLBACK (gtk_main_quit), + NULL); + + gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 12); + gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)), + vbox, TRUE, TRUE, 0); + gtk_widget_show (vbox); + + vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); + gtk_widget_show (vbox2); + + table = gtk_table_new (1, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_box_pack_start (GTK_BOX (vbox2), table, FALSE, FALSE, 0); + gtk_widget_show (table); + + pg.quality = entry = (GtkAdjustment *) + gimp_scale_entry_new (GTK_TABLE (table), 0, 0, + _("_Quality:"), + SCALE_WIDTH, 0, jsvals.quality, + 0.0, 100.0, 1.0, 10.0, 0, + TRUE, 0.0, 0.0, + _("JPEG quality parameter"), + "file-jpeg-save-quality"); + + g_signal_connect (entry, "value-changed", + G_CALLBACK (gimp_double_adjustment_update), + &jsvals.quality); + g_signal_connect (entry, "value-changed", + G_CALLBACK (make_preview), + NULL); + + /* custom quantization tables - now used also for original quality */ + pg.use_orig_quality = toggle = + gtk_check_button_new_with_mnemonic (_("_Use quality settings from original " + "image")); + gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); + + gimp_help_set_help_data (toggle, + _("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."), + NULL); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.use_orig_quality); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + jsvals.use_orig_quality + && (orig_quality > 0) + && (orig_subsmp == jsvals.subsmp) + ); + gtk_widget_set_sensitive (toggle, (orig_quality > 0)); + + /* changing quality disables custom quantization tables, and vice-versa */ + g_signal_connect (pg.quality, "value-changed", + G_CALLBACK (quality_changed), + pg.use_orig_quality); + g_signal_connect (pg.use_orig_quality, "toggled", + G_CALLBACK (use_orig_qual_changed), + pg.quality); + + /* File size */ + vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); + gtk_widget_show (vbox2); + + preview_size = gtk_label_new (_("File size: unknown")); + gtk_label_set_xalign (GTK_LABEL (preview_size), 0.0); + gtk_label_set_ellipsize (GTK_LABEL (preview_size), PANGO_ELLIPSIZE_END); + gimp_label_set_attributes (GTK_LABEL (preview_size), + PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC, + -1); + gtk_box_pack_start (GTK_BOX (vbox2), preview_size, FALSE, FALSE, 0); + gtk_widget_show (preview_size); + + gimp_help_set_help_data (preview_size, + _("Enable preview to obtain the file size."), NULL); + + pg.preview = toggle = + gtk_check_button_new_with_mnemonic (_("Sho_w preview in image window")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.preview); + gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.preview); + g_signal_connect (toggle, "toggled", + G_CALLBACK (make_preview), + NULL); + + vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + gtk_box_pack_start (GTK_BOX (vbox), vbox2, FALSE, FALSE, 0); + gtk_widget_show (vbox2); + + /* Save EXIF data */ + pg.save_exif = toggle = + gtk_check_button_new_with_mnemonic (_("Save _Exif data")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_exif); + gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.save_exif); + g_signal_connect (toggle, "toggled", + G_CALLBACK (make_preview), + NULL); + + /* Save XMP metadata */ + pg.save_xmp = toggle = + gtk_check_button_new_with_mnemonic (_("Save _XMP data")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_xmp); + gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.save_xmp); + g_signal_connect (toggle, "toggled", + G_CALLBACK (make_preview), + NULL); + + /* Save IPTC metadata */ + pg.save_iptc = toggle = + gtk_check_button_new_with_mnemonic (_("Save _IPTC data")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_iptc); + gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.save_iptc); + g_signal_connect (toggle, "toggled", + G_CALLBACK (make_preview), + NULL); + + /* Save thumbnail */ + pg.save_thumbnail = toggle = + gtk_check_button_new_with_mnemonic (_("Save _thumbnail")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_thumbnail); + gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.save_thumbnail); + g_signal_connect (toggle, "toggled", + G_CALLBACK (make_preview), + NULL); + + /* Save color profile */ + pg.save_profile = toggle = + gtk_check_button_new_with_mnemonic (_("Save color _profile")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.save_profile); + gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0); + gtk_widget_show (toggle); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.save_profile); + g_signal_connect (toggle, "toggled", + G_CALLBACK (make_preview), + NULL); + + /* Comment */ + frame = gimp_frame_new (_("Comment")); + gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); + gtk_widget_show (frame); + + scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_SHADOW_IN); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_widget_set_size_request (scrolled_window, 250, 50); + gtk_container_add (GTK_CONTAINER (frame), scrolled_window); + gtk_widget_show (scrolled_window); + + pg.text_buffer = text_buffer = gtk_text_buffer_new (NULL); + if (image_comment) + gtk_text_buffer_set_text (text_buffer, image_comment, -1); + + text_view = gtk_text_view_new_with_buffer (text_buffer); + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (text_view), GTK_WRAP_WORD); + + gtk_container_add (GTK_CONTAINER (scrolled_window), text_view); + gtk_widget_show (text_view); + + g_object_unref (text_buffer); + + /* Advanced expander */ + text = g_strdup_printf ("<b>%s</b>", _("_Advanced Options")); + expander = gtk_expander_new_with_mnemonic (text); + gtk_expander_set_use_markup (GTK_EXPANDER (expander), TRUE); + g_free (text); + + gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0); + gtk_widget_show (expander); + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12); + gtk_container_add (GTK_CONTAINER (expander), vbox); + gtk_widget_show (vbox); + + frame = gimp_frame_new ("<expander>"); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + gtk_widget_show (frame); + + table = gtk_table_new (4, 8, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table), 6); + gtk_table_set_row_spacings (GTK_TABLE (table), 6); + gtk_table_set_col_spacing (GTK_TABLE (table), 1, 12); + gtk_container_add (GTK_CONTAINER (frame), table); + gtk_widget_show (table); + + table2 = gtk_table_new (1, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (table2), 6); + gtk_table_attach (GTK_TABLE (table), table2, + 2, 6, 0, 1, GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (table2); + + pg.smoothing = entry = (GtkAdjustment *) + gimp_scale_entry_new (GTK_TABLE (table2), 0, 0, + _("S_moothing:"), + 100, 0, jsvals.smoothing, + 0.0, 1.0, 0.01, 0.1, 2, + TRUE, 0.0, 0.0, + NULL, + "file-jpeg-save-smoothing"); + g_signal_connect (entry, "value-changed", + G_CALLBACK (gimp_double_adjustment_update), + &jsvals.smoothing); + g_signal_connect (entry, "value-changed", + G_CALLBACK (make_preview), + NULL); + + restart_markers_label = gtk_label_new (_("Interval (MCU rows):")); + gtk_label_set_xalign (GTK_LABEL (restart_markers_label), 1.0); + gtk_table_attach (GTK_TABLE (table), restart_markers_label, 4, 5, 1, 2, + GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_widget_show (restart_markers_label); + + pg.scale_data = (GtkAdjustment *) + gtk_adjustment_new (((jsvals.restart == 0) ? + DEFAULT_RESTART_MCU_ROWS : jsvals.restart), + 1.0, 64.0, 1.0, 1.0, 0); + pg.restart = restart_markers_scale = spinbutton = + gimp_spin_button_new (pg.scale_data, 1.0, 0); + gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE); + gtk_table_attach (GTK_TABLE (table), spinbutton, 5, 6, 1, 2, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (spinbutton); + + pg.use_restart_markers = toggle = + gtk_check_button_new_with_mnemonic (_("Use _restart markers")); + gtk_table_attach (GTK_TABLE (table), toggle, 2, 4, 1, 2, GTK_FILL, 0, 0, 0); + gtk_widget_show (toggle); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.restart); + + gtk_widget_set_sensitive (restart_markers_label, jsvals.restart); + gtk_widget_set_sensitive (restart_markers_scale, jsvals.restart); + + g_signal_connect (pg.scale_data, "value-changed", + G_CALLBACK (save_restart_update), + toggle); + pg.handler_id_restart = g_signal_connect_swapped (toggle, "toggled", + G_CALLBACK (save_restart_update), + pg.scale_data); + + row = 0; + + /* Optimize */ + pg.optimize = toggle = gtk_check_button_new_with_mnemonic (_("_Optimize")); + gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, + row, row + 1, GTK_FILL, 0, 0, 0); + gtk_widget_show (toggle); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.optimize); + g_signal_connect (toggle, "toggled", + G_CALLBACK (make_preview), + NULL); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.optimize); + + if (arithc_supported) + gtk_widget_set_sensitive (toggle, !jsvals.arithmetic_coding); + + row++; + + if (arithc_supported) + { + /* Arithmetic coding */ + pg.arithmetic_coding = toggle = gtk_check_button_new_with_mnemonic + (_("Use arithmetic _coding")); + gtk_widget_set_tooltip_text + (toggle, _("Older software may have trouble opening " + "arithmetic-coded images")); + gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, + row, row + 1, GTK_FILL, 0, 0, 0); + gtk_widget_show (toggle); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.arithmetic_coding); + g_signal_connect (toggle, "toggled", + G_CALLBACK (make_preview), + NULL); + g_signal_connect (toggle, "toggled", + G_CALLBACK (toggle_arithmetic_coding), + pg.optimize); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + jsvals.arithmetic_coding); + + row++; + } + + /* Progressive */ + pg.progressive = toggle = + gtk_check_button_new_with_mnemonic (_("_Progressive")); + gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, + row, row + 1, GTK_FILL, 0, 0, 0); + gtk_widget_show (toggle); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.progressive); + g_signal_connect (toggle, "toggled", + G_CALLBACK (make_preview), + NULL); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + jsvals.progressive); + + row++; + + /* Subsampling */ + label = gtk_label_new_with_mnemonic (_("Su_bsampling:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 2, 3, 2, 3, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + pg.subsmp = + combo = gimp_int_combo_box_new (_("4:4:4 (best quality)"), + JPEG_SUBSAMPLING_1x1_1x1_1x1, + _("4:2:2 horizontal (chroma halved)"), + JPEG_SUBSAMPLING_2x1_1x1_1x1, + _("4:2:2 vertical (chroma halved)"), + JPEG_SUBSAMPLING_1x2_1x1_1x1, + _("4:2:0 (chroma quartered)"), + JPEG_SUBSAMPLING_2x2_1x1_1x1, + NULL); + gtk_table_attach (GTK_TABLE (table), combo, 3, 6, 2, 3, + GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_widget_show (combo); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + + if (gimp_drawable_is_rgb (drawable_ID_global)) + { + gimp_int_combo_box_connect (GIMP_INT_COMBO_BOX (combo), + jsvals.subsmp, + G_CALLBACK (subsampling_changed), + entry); + g_signal_connect (pg.subsmp, "changed", + G_CALLBACK (subsampling_changed2), + pg.use_orig_quality); + g_signal_connect (pg.use_orig_quality, "toggled", + G_CALLBACK (use_orig_qual_changed2), + pg.subsmp); + } + else + { + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), + JPEG_SUBSAMPLING_1x1_1x1_1x1); + + gtk_widget_set_sensitive (combo, FALSE); + } + + + /* DCT method */ + label = gtk_label_new_with_mnemonic (_("_DCT method:")); + gtk_label_set_xalign (GTK_LABEL (label), 0.0); + gtk_table_attach (GTK_TABLE (table), label, 2, 3, 3, 4, + GTK_FILL, GTK_FILL, 0, 0); + gtk_widget_show (label); + + pg.dct = combo = gimp_int_combo_box_new (_("Fast Integer"), 1, + _("Integer"), 0, + _("Floating-Point"), 2, + NULL); + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), jsvals.dct); + gtk_table_attach (GTK_TABLE (table), combo, 3, 6, 3, 4, + GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_widget_show (combo); + + gtk_label_set_mnemonic_widget (GTK_LABEL (label), combo); + + g_signal_connect (combo, "changed", + G_CALLBACK (gimp_int_combo_box_get_active), + &jsvals.dct); + g_signal_connect (combo, "changed", + G_CALLBACK (make_preview), + NULL); + + /* Load/Save defaults */ + tabledefaults = gtk_table_new (1, 3, FALSE); + gtk_table_set_col_spacings (GTK_TABLE (tabledefaults), 6); + gtk_container_set_border_width (GTK_CONTAINER (tabledefaults), 12); + gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)), + tabledefaults, FALSE, FALSE, 0); + gtk_widget_show (tabledefaults); + + button = gtk_button_new_with_mnemonic (_("_Load Defaults")); + gtk_table_attach (GTK_TABLE (tabledefaults), + button, 0, 1, 1, 2, GTK_FILL, 0, 0, 0); + gtk_widget_show (button); + + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (load_gui_defaults), + &pg); + + button = gtk_button_new_with_mnemonic (_("Sa_ve Defaults")); + gtk_table_attach (GTK_TABLE (tabledefaults), + button, 1, 2, 1, 2, GTK_FILL, 0, 0, 0); + gtk_widget_show (button); + + g_signal_connect_swapped (button, "clicked", + G_CALLBACK (save_defaults), + &pg); + + gtk_widget_show (dialog); + + make_preview (); + + pg.run = FALSE; + + gtk_main (); + + destroy_preview (); + + return pg.run; +} + +static void +save_dialog_response (GtkWidget *widget, + gint response_id, + gpointer data) +{ + JpegSaveGui *pg = data; + GtkTextIter start_iter; + GtkTextIter end_iter; + + switch (response_id) + { + case GTK_RESPONSE_OK: + gtk_text_buffer_get_bounds (pg->text_buffer, &start_iter, &end_iter); + image_comment = gtk_text_buffer_get_text (pg->text_buffer, + &start_iter, &end_iter, FALSE); + pg->run = TRUE; + /* fallthrough */ + + default: + gtk_widget_destroy (widget); + break; + } +} + +void +load_defaults (void) +{ + jsvals.quality = DEFAULT_IJG_QUALITY; + jsvals.smoothing = DEFAULT_SMOOTHING; + jsvals.optimize = DEFAULT_OPTIMIZE; + jsvals.arithmetic_coding= DEFAULT_ARITHMETIC_CODING; + jsvals.progressive = DEFAULT_PROGRESSIVE; + jsvals.baseline = DEFAULT_BASELINE; + jsvals.subsmp = DEFAULT_SUBSMP; + jsvals.restart = DEFAULT_RESTART; + jsvals.dct = DEFAULT_DCT; + jsvals.preview = DEFAULT_PREVIEW; + jsvals.save_exif = DEFAULT_EXIF; + jsvals.save_xmp = DEFAULT_XMP; + jsvals.save_iptc = DEFAULT_IPTC; + jsvals.save_thumbnail = DEFAULT_THUMBNAIL; + jsvals.save_profile = DEFAULT_PROFILE; + jsvals.use_orig_quality = DEFAULT_USE_ORIG_QUALITY; +} + +void +load_parasite (void) +{ + GimpParasite *parasite; + gchar *def_str; + JpegSaveVals tmpvals; + gint num_fields; + gint subsampling; + + parasite = gimp_get_parasite (JPEG_DEFAULTS_PARASITE); + + if (! parasite) + return; + + def_str = g_strndup (gimp_parasite_data (parasite), + gimp_parasite_data_size (parasite)); + + gimp_parasite_free (parasite); + + /* Initialize tmpvals in case fewer fields exist in the parasite + (e.g., when importing from a previous version of GIMP). */ + memcpy(&tmpvals, &jsvals, sizeof jsvals); + + num_fields = sscanf (def_str, + "%lf %lf %d %d %d %d %d %d %d %d %d %d %d %d %d %d", + &tmpvals.quality, + &tmpvals.smoothing, + &tmpvals.optimize, + &tmpvals.progressive, + &subsampling, + &tmpvals.baseline, + &tmpvals.restart, + &tmpvals.dct, + &tmpvals.preview, + &tmpvals.save_exif, + &tmpvals.save_thumbnail, + &tmpvals.save_xmp, + &tmpvals.use_orig_quality, + &tmpvals.save_iptc, + &tmpvals.arithmetic_coding, + &tmpvals.save_profile); + + tmpvals.subsmp = subsampling; + + if (num_fields == 13 || num_fields == 15 || num_fields == 16) + { + memcpy (&jsvals, &tmpvals, sizeof (tmpvals)); + } + + g_free (def_str); +} + +static void +save_defaults (void) +{ + GimpParasite *parasite; + gchar *def_str; + + def_str = g_strdup_printf ("%lf %lf %d %d %d %d %d %d %d %d %d %d %d %d %d %d", + jsvals.quality, + jsvals.smoothing, + jsvals.optimize, + jsvals.progressive, + (gint) jsvals.subsmp, + jsvals.baseline, + jsvals.restart, + jsvals.dct, + jsvals.preview, + jsvals.save_exif, + jsvals.save_thumbnail, + jsvals.save_xmp, + jsvals.use_orig_quality, + jsvals.save_iptc, + jsvals.arithmetic_coding, + jsvals.save_profile); + parasite = gimp_parasite_new (JPEG_DEFAULTS_PARASITE, + GIMP_PARASITE_PERSISTENT, + strlen (def_str), def_str); + + gimp_attach_parasite (parasite); + + gimp_parasite_free (parasite); + g_free (def_str); +} + +static void +load_gui_defaults (JpegSaveGui *pg) +{ + GtkAdjustment *restart_markers; + + load_defaults (); + load_parasite (); + +#define SET_ACTIVE_BTTN(field) \ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pg->field), jsvals.field) + + SET_ACTIVE_BTTN (optimize); + SET_ACTIVE_BTTN (progressive); + SET_ACTIVE_BTTN (use_orig_quality); + SET_ACTIVE_BTTN (preview); + SET_ACTIVE_BTTN (save_exif); + SET_ACTIVE_BTTN (save_xmp); + SET_ACTIVE_BTTN (save_iptc); + SET_ACTIVE_BTTN (save_thumbnail); + SET_ACTIVE_BTTN (save_profile); + +#undef SET_ACTIVE_BTTN + +/*spin button stuff*/ + g_signal_handler_block (pg->use_restart_markers, pg->handler_id_restart); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pg->use_restart_markers), + jsvals.restart); + restart_markers = GTK_ADJUSTMENT (pg->scale_data); + gtk_adjustment_set_value (restart_markers, jsvals.restart); + g_signal_handler_unblock (pg->use_restart_markers, pg->handler_id_restart); + + gtk_adjustment_set_value (GTK_ADJUSTMENT (pg->smoothing), + jsvals.smoothing); + + /* Don't override quality and subsampling setting if we already set it from original */ + if (!jsvals.use_orig_quality) + { + gtk_adjustment_set_value (GTK_ADJUSTMENT (pg->quality), + jsvals.quality); + + if (gimp_drawable_is_rgb (drawable_ID_global)) + { + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (pg->subsmp), + jsvals.subsmp); + } + } + + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (pg->dct), + jsvals.dct); +} + +static void +save_restart_update (GtkAdjustment *adjustment, + GtkWidget *toggle) +{ + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle))) + jsvals.restart = gtk_adjustment_get_value (adjustment); + else + jsvals.restart = 0; + + gtk_widget_set_sensitive (restart_markers_label, jsvals.restart); + gtk_widget_set_sensitive (restart_markers_scale, jsvals.restart); + + make_preview (); +} + +static void +subsampling_changed (GtkWidget *combo, + GtkAdjustment *entry) +{ + gint value; + + gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &value); + + jsvals.subsmp = value; + + /* smoothing is not supported with nonstandard sampling ratios */ + gimp_scale_entry_set_sensitive ((gpointer) entry, + jsvals.subsmp != JPEG_SUBSAMPLING_2x1_1x1_1x1 && + jsvals.subsmp != JPEG_SUBSAMPLING_1x2_1x1_1x1); + + make_preview (); +} + +static void +quality_changed (GtkAdjustment *scale_entry, + GtkWidget *toggle) +{ + if (jsvals.use_orig_quality) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE); + } +} + +static void +subsampling_changed2 (GtkWidget *combo, + GtkWidget *toggle) +{ + if (jsvals.use_orig_quality && orig_subsmp != jsvals.subsmp) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE); + } +} + +static void +use_orig_qual_changed (GtkWidget *toggle, + GtkAdjustment *scale_entry) +{ + if (jsvals.use_orig_quality && orig_quality > 0) + { + g_signal_handlers_block_by_func (scale_entry, quality_changed, toggle); + gtk_adjustment_set_value (scale_entry, orig_quality); + g_signal_handlers_unblock_by_func (scale_entry, quality_changed, toggle); + } +} + +static void +use_orig_qual_changed2 (GtkWidget *toggle, + GtkWidget *combo) +{ + /* the test is (orig_quality > 0), not (orig_subsmp > 0) - this is normal */ + if (jsvals.use_orig_quality && orig_quality > 0) + { + gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo), orig_subsmp); + } +} |