diff --git a/ChangeLog b/ChangeLog index 49d43054cd..890804d058 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2007-08-10 Raphaƫl Quinet + + * plug-ins/jpeg/jpeg.[ch] + * plug-ins/jpeg/jpeg-save.[ch]: in the JPEG save dialog, removed + the option "Force baseline JPEG" and added the new option "Use + custom quantization tables" to allow resaving a JPEG file with the + same compression settings as the original file. This is useful if + the quantization tables are different from those that can be + created by the IJG JPEG library. + + * plug-ins/jpeg/jpeg-settings.[ch]: updated comments, changed the + return type of jpeg_restore_original_tables() because the IJG JPEG + library expects unsigned integers. + 2007-08-10 Sven Neumann * libgimpwidgets/gimpnumberpairentry.c: store separators as diff --git a/plug-ins/jpeg/jpeg-save.c b/plug-ins/jpeg/jpeg-save.c index 3c5c284c58..bb35e18047 100644 --- a/plug-ins/jpeg/jpeg-save.c +++ b/plug-ins/jpeg/jpeg-save.c @@ -44,6 +44,7 @@ #include "libgimp/stdplugins-intl.h" #include "jpeg.h" +#include "jpeg-settings.h" #include "jpeg-icc.h" #include "jpeg-save.h" @@ -51,18 +52,19 @@ #define SCALE_WIDTH 125 /* See bugs #63610 and #61088 for a discussion about the quality settings */ -#define DEFAULT_QUALITY 85.0 -#define DEFAULT_SMOOTHING 0.0 -#define DEFAULT_OPTIMIZE TRUE -#define DEFAULT_PROGRESSIVE FALSE -#define DEFAULT_BASELINE TRUE -#define DEFAULT_SUBSMP 0 -#define DEFAULT_RESTART 0 -#define DEFAULT_DCT 0 -#define DEFAULT_PREVIEW FALSE -#define DEFAULT_EXIF TRUE -#define DEFAULT_THUMBNAIL FALSE -#define DEFAULT_XMP TRUE +#define DEFAULT_QUALITY 85.0 +#define DEFAULT_SMOOTHING 0.0 +#define DEFAULT_OPTIMIZE TRUE +#define DEFAULT_PROGRESSIVE FALSE +#define DEFAULT_BASELINE TRUE +#define DEFAULT_SUBSMP 0 +#define DEFAULT_RESTART 0 +#define DEFAULT_DCT 0 +#define DEFAULT_PREVIEW FALSE +#define DEFAULT_EXIF TRUE +#define DEFAULT_THUMBNAIL FALSE +#define DEFAULT_XMP TRUE +#define DEFAULT_USE_QUANT_TABLES FALSE #define JPEG_DEFAULTS_PARASITE "jpeg-save-defaults" @@ -98,13 +100,13 @@ typedef struct GtkWidget *optimize; /*optimize togle*/ GtkWidget *progressive; /*progressive toggle*/ GtkWidget *subsmp; /*subsampling side select*/ - GtkWidget *baseline; /*baseling toggle*/ GtkWidget *restart; /*spinner for setting frequency restart markers*/ GtkWidget *dct; /*DCT side select*/ GtkWidget *preview; /*show preview toggle checkbox*/ GtkWidget *save_exif; GtkWidget *save_thumbnail; GtkWidget *save_xmp; + GtkWidget *use_quant_tables; /*quant tables toggle*/ } JpegSaveGui; static void make_preview (void); @@ -113,6 +115,10 @@ static void save_restart_update (GtkAdjustment *adjustment, GtkWidget *toggle); static void subsampling_changed (GtkWidget *combo, GtkObject *entry); +static void quality_changed (GtkObject *scale_entry, + GtkWidget *toggle); +static void use_quant_changed (GtkWidget *toggle, + GtkObject *scale_entry); #ifdef HAVE_EXIF @@ -365,10 +371,30 @@ save_image (const gchar *filename, jpeg_set_defaults (&cinfo); jpeg_set_quality (&cinfo, (gint) (jsvals.quality + 0.5), jsvals.baseline); + + if (jsvals.use_quant_tables && 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); + } + } + cinfo.optimize_coding = jsvals.optimize; /* smoothing is not supported with nonstandard sampling ratios */ - if (jsvals.subsmp != 1) + if (jsvals.subsmp != 1 && jsvals.subsmp != 3) cinfo.smoothing_factor = (gint) (jsvals.smoothing * 100); if (jsvals.progressive) @@ -963,24 +989,9 @@ save_dialog (void) gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), jsvals.progressive); - pg.baseline = toggle = - gtk_check_button_new_with_label (_("Force baseline JPEG")); - gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 2, 3, GTK_FILL, 0, 0, 0); - gtk_widget_show (toggle); - - g_signal_connect (toggle, "toggled", - G_CALLBACK (gimp_toggle_button_update), - &jsvals.baseline); - g_signal_connect (toggle, "toggled", - G_CALLBACK (make_preview), - NULL); - - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), - jsvals.baseline); - #ifdef HAVE_EXIF pg.save_exif = toggle = gtk_check_button_new_with_label (_("Save EXIF data")); - gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 3, 4, GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 2, 3, GTK_FILL, 0, 0, 0); gtk_widget_show (toggle); g_signal_connect (toggle, "toggled", @@ -997,7 +1008,7 @@ save_dialog (void) pg.save_thumbnail = toggle = gtk_check_button_new_with_label (_("Save thumbnail")); - gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 4, 5, GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 3, 4, GTK_FILL, 0, 0, 0); gtk_widget_show (toggle); g_signal_connect (toggle, "toggled", @@ -1013,7 +1024,7 @@ save_dialog (void) /* XMP metadata */ pg.save_xmp = toggle = gtk_check_button_new_with_label (_("Save XMP data")); - gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 5, 6, GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), toggle, 0, 1, 4, 5, GTK_FILL, 0, 0, 0); gtk_widget_show (toggle); g_signal_connect (toggle, "toggled", @@ -1025,6 +1036,29 @@ save_dialog (void) gtk_widget_set_sensitive (toggle, has_metadata); + /* custom quantization tables */ + pg.use_quant_tables = toggle = + gtk_check_button_new_with_label (_("Use custom quantization tables")); + gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 5, 6, GTK_FILL, 0, 0, 0); + gtk_widget_show (toggle); + + g_signal_connect (toggle, "toggled", + G_CALLBACK (gimp_toggle_button_update), + &jsvals.use_quant_tables); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), + jsvals.use_quant_tables + && (num_quant_tables > 0)); + gtk_widget_set_sensitive (toggle, (num_quant_tables > 0)); + + /* changing quality disables custom quantization tables, and vice-versa */ + g_signal_connect (pg.quality, "value-changed", + G_CALLBACK (quality_changed), + pg.use_quant_tables); + g_signal_connect (pg.use_quant_tables, "toggled", + G_CALLBACK (use_quant_changed), + pg.quality); + /* Subsampling */ label = gtk_label_new (_("Subsampling:")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); @@ -1174,18 +1208,19 @@ load_save_defaults (void) JpegSaveVals tmpvals; gint num_fields; - jsvals.quality = DEFAULT_QUALITY; - jsvals.smoothing = DEFAULT_SMOOTHING; - jsvals.optimize = DEFAULT_OPTIMIZE; - 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_thumbnail = DEFAULT_THUMBNAIL; - jsvals.save_xmp = DEFAULT_XMP; + jsvals.quality = DEFAULT_QUALITY; + jsvals.smoothing = DEFAULT_SMOOTHING; + jsvals.optimize = DEFAULT_OPTIMIZE; + 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_thumbnail = DEFAULT_THUMBNAIL; + jsvals.save_xmp = DEFAULT_XMP; + jsvals.use_quant_tables = DEFAULT_USE_QUANT_TABLES; #ifdef HAVE_EXIF if (exif_data && (exif_data->data)) @@ -1261,13 +1296,13 @@ load_gui_defaults (JpegSaveGui *pg) SET_ACTIVE_BTTN (optimize); SET_ACTIVE_BTTN (progressive); - SET_ACTIVE_BTTN (baseline); - SET_ACTIVE_BTTN(preview); + SET_ACTIVE_BTTN (use_quant_tables); + SET_ACTIVE_BTTN (preview); #ifdef HAVE_EXIF - SET_ACTIVE_BTTN(save_exif); - SET_ACTIVE_BTTN(save_thumbnail); + SET_ACTIVE_BTTN (save_exif); + SET_ACTIVE_BTTN (save_thumbnail); #endif - SET_ACTIVE_BTTN(save_xmp); + SET_ACTIVE_BTTN (save_xmp); #undef SET_ACTIVE_BTTN @@ -1289,7 +1324,6 @@ load_gui_defaults (JpegSaveGui *pg) jsvals.subsmp); gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (pg->dct), jsvals.dct); - } static void @@ -1311,11 +1345,34 @@ subsampling_changed (GtkWidget *combo, gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &jsvals.subsmp); /* smoothing is not supported with nonstandard sampling ratios */ - gimp_scale_entry_set_sensitive (entry, jsvals.subsmp != 1); + gimp_scale_entry_set_sensitive (entry, + jsvals.subsmp != 1 && jsvals.subsmp != 3); make_preview (); } +static void +quality_changed (GtkObject *scale_entry, + GtkWidget *toggle) +{ + if (jsvals.use_quant_tables) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), FALSE); + } +} + +static void +use_quant_changed (GtkWidget *toggle, + GtkObject *scale_entry) +{ + if (jsvals.use_quant_tables && orig_quality > 0) + { + g_signal_handlers_block_by_func (scale_entry, quality_changed, toggle); + gtk_adjustment_set_value (GTK_ADJUSTMENT (scale_entry), orig_quality); + g_signal_handlers_unblock_by_func (scale_entry, quality_changed, toggle); + } +} + #ifdef HAVE_EXIF static guchar *tbuffer = NULL; diff --git a/plug-ins/jpeg/jpeg-save.h b/plug-ins/jpeg/jpeg-save.h index 1d659789b0..40bf762233 100644 --- a/plug-ins/jpeg/jpeg-save.h +++ b/plug-ins/jpeg/jpeg-save.h @@ -30,6 +30,7 @@ typedef struct gboolean save_exif; gboolean save_thumbnail; gboolean save_xmp; + gboolean use_quant_tables; } JpegSaveVals; extern JpegSaveVals jsvals; diff --git a/plug-ins/jpeg/jpeg-settings.c b/plug-ins/jpeg/jpeg-settings.c index dd3733d9a8..43773b2936 100644 --- a/plug-ins/jpeg/jpeg-settings.c +++ b/plug-ins/jpeg/jpeg-settings.c @@ -22,11 +22,11 @@ /* * Structure of the "jpeg-settings" parasite: * 1 byte - JPEG color space (JCS_YCbCr, JCS_GRAYSCALE, JCS_CMYK, ...) - * 1 byte - quality (according to the IJG scale, 0..100) + * 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 (if different from IJG tables) + * 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. @@ -157,7 +157,7 @@ jpeg_detect_original_settings (struct jpeg_decompress_struct *cinfo, * and number of quantization tables) from the parasite attached to * @image_ID. If the number of quantization tables is greater than * zero, then these tables can be retrieved from the parasite by - * calling jpeg_get_original_tables(). + * calling jpeg_restore_original_tables(). * * Return Value: TRUE if a valid parasite was attached to the image */ @@ -250,14 +250,17 @@ jpeg_restore_original_settings (gint32 image_ID, * 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. 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. + * matches the number of tables saved in the parasite. These tables + * are returned as arrays of unsigned integers even if they will never + * use more than 16 bits (8 bits in most cases) because the IJG JPEG + * library expects arrays of unsigned integers. When these tables are + * not needed anymore, the caller should free them using g_free(). If + * no parasite exists or if it cannot be used, this function returns + * NULL. * * Return Value: an array of quantization tables, or NULL. */ -guint16 ** +guint ** jpeg_restore_original_tables (gint32 image_ID, gint num_quant_tables) { @@ -266,12 +269,12 @@ jpeg_restore_original_tables (gint32 image_ID, glong src_size; gint num_components; gint num_tables; - guint16 **quant_tables; + guint **quant_tables; gint t; gint i; parasite = gimp_image_parasite_find (image_ID, "jpeg-settings"); - if (! parasite) + if (parasite) { src_size = gimp_parasite_data_size (parasite); if (src_size >= 4) @@ -285,14 +288,14 @@ jpeg_restore_original_tables (gint32 image_ID, && num_tables == num_quant_tables) { src += num_components * 2; - quant_tables = g_new (guint16 *, num_tables); + quant_tables = g_new (guint *, num_tables); for (t = 0; t < num_tables; t++) { - quant_tables[t] = g_new (guint16, 128); - for (i = 0; i < 128; i++) + quant_tables[t] = g_new (guint, 128); + for (i = 0; i < 64; i++) { - guint16 c; + guint c; c = *src++ * 256; c += *src++; diff --git a/plug-ins/jpeg/jpeg-settings.h b/plug-ins/jpeg/jpeg-settings.h index 7b3ecf4efa..d1e3ceb975 100644 --- a/plug-ins/jpeg/jpeg-settings.h +++ b/plug-ins/jpeg/jpeg-settings.h @@ -27,5 +27,5 @@ gboolean jpeg_restore_original_settings (gint32 image_ID, gint *subsmp, gint *num_quant_tables); -guint16 **jpeg_restore_original_tables (gint32 image_ID, +guint **jpeg_restore_original_tables (gint32 image_ID, gint num_quant_tables); diff --git a/plug-ins/jpeg/jpeg.c b/plug-ins/jpeg/jpeg.c index 36367d163d..668b4cf6cb 100644 --- a/plug-ins/jpeg/jpeg.c +++ b/plug-ins/jpeg/jpeg.c @@ -59,6 +59,10 @@ JpegSaveVals jsvals; gint32 orig_image_ID_global; gint32 drawable_ID_global; gboolean has_metadata; +gint orig_quality; +gint orig_subsmp; +gint num_quant_tables; + #ifdef HAVE_EXIF ExifData *exif_data = NULL; @@ -208,6 +212,9 @@ run (const gchar *name, preview_layer_ID = -1; has_metadata = FALSE; + orig_quality = 0; + orig_subsmp = 0; + num_quant_tables = 0; if (strcmp (name, LOAD_PROC) == 0) { @@ -389,6 +396,11 @@ run (const gchar *name, /* Possibly retrieve data */ gimp_get_data (SAVE_PROC, &jsvals); + jpeg_restore_original_settings (orig_image_ID, + &orig_quality, + &orig_subsmp, + &num_quant_tables); + /* load up the previously used values */ parasite = gimp_image_parasite_find (orig_image_ID, "jpeg-save-options"); @@ -396,48 +408,42 @@ run (const gchar *name, { const JpegSaveVals *save_vals = gimp_parasite_data (parasite); - jsvals.quality = save_vals->quality; - jsvals.smoothing = save_vals->smoothing; - jsvals.optimize = save_vals->optimize; - jsvals.progressive = save_vals->progressive; - jsvals.baseline = save_vals->baseline; - jsvals.subsmp = save_vals->subsmp; - jsvals.restart = save_vals->restart; - jsvals.dct = save_vals->dct; - jsvals.preview = save_vals->preview; - jsvals.save_exif = save_vals->save_exif; - jsvals.save_thumbnail = save_vals->save_thumbnail; - jsvals.save_xmp = save_vals->save_xmp; + jsvals.quality = save_vals->quality; + jsvals.smoothing = save_vals->smoothing; + jsvals.optimize = save_vals->optimize; + jsvals.progressive = save_vals->progressive; + jsvals.baseline = save_vals->baseline; + jsvals.subsmp = save_vals->subsmp; + jsvals.restart = save_vals->restart; + jsvals.dct = save_vals->dct; + jsvals.preview = save_vals->preview; + jsvals.save_exif = save_vals->save_exif; + jsvals.save_thumbnail = save_vals->save_thumbnail; + jsvals.save_xmp = save_vals->save_xmp; + jsvals.use_quant_tables = save_vals->use_quant_tables; gimp_parasite_free (parasite); } else { - gint orig_quality; - gint orig_subsmp; - gint orig_quant_tables; - /* We are called with GIMP_RUN_WITH_LAST_VALS but this image * doesn't have a "jpeg-save-options" parasite. It's better * to prompt the user with a dialog now so that she has control * over the JPEG encoding parameters. */ run_mode = GIMP_RUN_INTERACTIVE; - /* If this image was loaded from a JPEG file (and has not been - * saved yet), try to use some of the settings from the + /* 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 (jpeg_restore_original_settings (image_ID, - &orig_quality, - &orig_subsmp, - &orig_quant_tables)) + if (orig_quality > jsvals.quality) { - if (orig_quality > jsvals.quality) - jsvals.quality = orig_quality; - if (orig_subsmp == 2 - || (orig_subsmp > 0 && jsvals.subsmp == 0)) - jsvals.subsmp = orig_subsmp; + jsvals.quality = orig_quality; + jsvals.use_quant_tables = TRUE; } + if (orig_subsmp == 2 + || (orig_subsmp > 0 && jsvals.subsmp == 0)) + jsvals.subsmp = orig_subsmp; } break; } diff --git a/plug-ins/jpeg/jpeg.h b/plug-ins/jpeg/jpeg.h index dbd36e1d79..1702bc78bd 100644 --- a/plug-ins/jpeg/jpeg.h +++ b/plug-ins/jpeg/jpeg.h @@ -49,6 +49,9 @@ extern gboolean load_interactive; extern gint32 display_ID; extern gchar *image_comment; extern gboolean has_metadata; +extern gint orig_quality; +extern gint orig_subsmp; +extern gint num_quant_tables; gint32 load_image (const gchar *filename, GimpRunMode runmode,