diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index cdfb8b047..5c452c97b 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -796,6 +796,11 @@ SAVEDLG_PUTTOQUEUETAIL;An das Ende der Warteschlange für Verarbeitung legen SAVEDLG_PUTTOQUEUE;In Warteschlange für Verarbeitung legen SAVEDLG_SAVEIMMEDIATELY;Sofort speichern SAVEDLG_SAVESPP;Prozessparameter mit dem Bild speichern +SAVEDLG_SUBSAMP;Subsampling +SAVEDLG_SUBSAMP_1;Beste Kompression +SAVEDLG_SUBSAMP_2;Ausbalanciert +SAVEDLG_SUBSAMP_3;Beste Qualität +SAVEDLG_SUBSAMP_TOOLTIP;Beste Kompression: 4:1:1\nAusbalanciert: 4:2:2\nBeste Qualität: 4:4:4 SAVEDLG_TIFFFILTER;TIFF-Datei SAVEDLG_TIFFUNCOMPRESSED;Uncompressed TIFF TOOLBAR_TOOLTIP_CROP;Ausschnitt wählen C\n\nZum Verschieben des Ausschnitts muss die Umschalttaste gedrückt gehalten werden diff --git a/rtdata/languages/default b/rtdata/languages/default index 909ab7fe4..ff89b70bf 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -807,6 +807,11 @@ SAVEDLG_PUTTOQUEUETAIL;Put to the end of the processing queue SAVEDLG_PUTTOQUEUE;Put into processing queue SAVEDLG_SAVEIMMEDIATELY;Save immediately SAVEDLG_SAVESPP;Save processing parameters with image +SAVEDLG_SUBSAMP;Subsampling +SAVEDLG_SUBSAMP_1;Best compression +SAVEDLG_SUBSAMP_2;Balanced +SAVEDLG_SUBSAMP_3;Best quality +SAVEDLG_SUBSAMP_TOOLTIP;Best Compression: 4:1:1\nBalanced: 4:2:2\nBest quality: 4:4:4 SAVEDLG_TIFFFILTER;TIFF files SAVEDLG_TIFFUNCOMPRESSED;Uncompressed TIFF SAVEDLG_WARNFILENAME;File will be named diff --git a/rtengine/iimage.h b/rtengine/iimage.h index 148dcc8e8..237dbc1a4 100644 --- a/rtengine/iimage.h +++ b/rtengine/iimage.h @@ -56,7 +56,7 @@ namespace rtengine { * @param fname is the name of the file * @param quality is the quality of the jpeg (0...100), set it to -1 to use default @return the error code, 0 if none */ - virtual int saveAsJPEG (Glib::ustring fname, int quality = 100)=0; + virtual int saveAsJPEG (Glib::ustring fname, int quality = 100, int subSamp = 3 )=0; /** Saves the image to file in a tif format. * @param fname is the name of the file * @param bps can be 8 or 16 depending on the bits per pixels the output file will have diff --git a/rtengine/image16.h b/rtengine/image16.h index f14831a2e..08cdf8d4e 100644 --- a/rtengine/image16.h +++ b/rtengine/image16.h @@ -80,7 +80,7 @@ class Image16 : public ImageIO, public IImage16 { virtual int getBitsPerPixel () { return 16; } virtual int saveToFile (Glib::ustring fname) { return save (fname); } virtual int saveAsPNG (Glib::ustring fname, int compression = -1, int bps = -1) { return savePNG (fname, compression, bps); } - virtual int saveAsJPEG (Glib::ustring fname, int quality = 100) { return saveJPEG (fname, quality); } + virtual int saveAsJPEG (Glib::ustring fname, int quality = 100, int subSamp = 3) { return saveJPEG (fname, quality, subSamp); } virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false) { return saveTIFF (fname, bps, uncompressed); } virtual void setSaveProgressListener (ProgressListener* pl) { return setProgressListener (pl); } virtual void free () { delete this; } diff --git a/rtengine/image8.h b/rtengine/image8.h index 404e602c1..42d91d905 100644 --- a/rtengine/image8.h +++ b/rtengine/image8.h @@ -60,7 +60,7 @@ class Image8 : public ImageIO, public IImage8 { virtual int getBitsPerPixel () { return 16; } virtual int saveToFile (Glib::ustring fname) { return save (fname); } virtual int saveAsPNG (Glib::ustring fname, int compression = -1, int bps = -1) { return savePNG (fname, compression, bps); } - virtual int saveAsJPEG (Glib::ustring fname, int quality = 100) { return saveJPEG (fname, quality); } + virtual int saveAsJPEG (Glib::ustring fname, int quality = 100, int subSamp = 3) { return saveJPEG (fname, quality, subSamp); } virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false) { return saveTIFF (fname, bps, uncompressed); } virtual void setSaveProgressListener (ProgressListener* pl) { setProgressListener (pl); } virtual void free () { delete this; } diff --git a/rtengine/imagefloat.h b/rtengine/imagefloat.h index 5edadb18e..b3bea4b95 100644 --- a/rtengine/imagefloat.h +++ b/rtengine/imagefloat.h @@ -81,7 +81,7 @@ class Imagefloat : public ImageIO, public IImagefloat { virtual int getBitsPerPixel () { return 16; } virtual int saveToFile (Glib::ustring fname) { return save (fname); } virtual int saveAsPNG (Glib::ustring fname, int compression = -1, int bps = -1) { return savePNG (fname, compression, bps); } - virtual int saveAsJPEG (Glib::ustring fname, int quality = 100) { return saveJPEG (fname, quality); } + virtual int saveAsJPEG (Glib::ustring fname, int quality = 100, int subSamp = 3) { return saveJPEG (fname, quality, subSamp); } virtual int saveAsTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false) { return saveTIFF (fname, bps, uncompressed); } virtual void setSaveProgressListener (ProgressListener* pl) { return setProgressListener (pl); } virtual void free () { delete this; } diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index c9f216174..922f70a31 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -610,7 +610,8 @@ int ImageIO::savePNG (Glib::ustring fname, int compression, volatile int bps) { } -int ImageIO::saveJPEG (Glib::ustring fname, int quality) { +// Quality 0..100, subsampling: 1=low quality, 2=medium, 3=high +int ImageIO::saveJPEG (Glib::ustring fname, int quality, int subSamp) { jpeg_compress_struct cinfo; jpeg_error_mgr jerr; @@ -650,6 +651,20 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality) { if (quality>=0 && quality<=100) jpeg_set_quality (&cinfo, quality, true); + cinfo.comp_info[1].h_samp_factor=cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[2].h_samp_factor=cinfo.comp_info[2].v_samp_factor = 1; + + if (subSamp==1) { + // Best compression, default of the JPEG library: 2x2, 1x1, 1x1 (4:1:1) + cinfo.comp_info[0].h_samp_factor=cinfo.comp_info[0].v_samp_factor = 2; + } else if (subSamp==2) { + // Widely used normal ratio 2x1, 1x1, 1x1 (4:2:2) + cinfo.comp_info[0].h_samp_factor = 2; cinfo.comp_info[0].v_samp_factor = 1; + } else if (subSamp==3) { + // Best quality 1x1 1x1 1x1 (4:4:4) + cinfo.comp_info[0].h_samp_factor=cinfo.comp_info[0].v_samp_factor = 1; + } + jpeg_start_compress(&cinfo, TRUE); // buffer for exif and iptc markers diff --git a/rtengine/imageio.h b/rtengine/imageio.h index ea08bbc0f..bfa884c9b 100644 --- a/rtengine/imageio.h +++ b/rtengine/imageio.h @@ -76,7 +76,7 @@ class ImageIO { int loadPPMFromMemory(const char* buffer,int width,int height, bool swap, int bps); int savePNG (Glib::ustring fname, int compression = -1, volatile int bps = -1); - int saveJPEG (Glib::ustring fname, int quality = 100); + int saveJPEG (Glib::ustring fname, int quality = 100, int subSamp=3); int saveTIFF (Glib::ustring fname, int bps = -1, bool uncompressed = false); cmsHPROFILE getEmbeddedProfile () { return embProfile; } diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index f89c4a33b..5e3be8a2b 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -391,7 +391,7 @@ rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImage16* img) { else if (saveFormat.format=="png") err = img->saveAsPNG (fname, saveFormat.pngCompression, saveFormat.pngBits); else if (saveFormat.format=="jpg") - err = img->saveAsJPEG (fname, saveFormat.jpegQuality); + err = img->saveAsJPEG (fname, saveFormat.jpegQuality, saveFormat.jpegSubSamp); img->free (); if (err) throw "Unable to save output file"; diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index 6106775ef..512d990cb 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -949,7 +949,7 @@ bool EditorPanel::idle_saveImage (ProgressConnector *pc, Gl ld->startFunc (sigc::bind(sigc::mem_fun(img, &rtengine::IImage16::saveAsPNG), fname, sf.pngCompression, sf.pngBits), sigc::bind(sigc::mem_fun(*this,&EditorPanel::idle_imageSaved), ld, img, fname, sf)); else if (sf.format=="jpg") - ld->startFunc (sigc::bind(sigc::mem_fun(img, &rtengine::IImage16::saveAsJPEG), fname, sf.jpegQuality), + ld->startFunc (sigc::bind(sigc::mem_fun(img, &rtengine::IImage16::saveAsJPEG), fname, sf.jpegQuality, sf.jpegSubSamp), sigc::bind(sigc::mem_fun(*this,&EditorPanel::idle_imageSaved), ld, img, fname, sf)); } else { Glib::ustring msg_ = Glib::ustring("") + fname + ": Error during image processing\n"; diff --git a/rtgui/options.cc b/rtgui/options.cc index 95c719a1d..7c6dfea1d 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -196,7 +196,8 @@ void Options::setDefaults () { saveAsDialogHeight = 600; savesParamsAtExit = true; saveFormat.format = "jpg"; - saveFormat.jpegQuality = 100; + saveFormat.jpegQuality = 90; + saveFormat.jpegSubSamp = 2; saveFormat.pngCompression = 6; saveFormat.pngBits = 8; saveFormat.tiffBits = 8; @@ -204,7 +205,8 @@ void Options::setDefaults () { saveFormat.saveParams = true; saveFormatBatch.format = "jpg"; - saveFormatBatch.jpegQuality = 100; + saveFormatBatch.jpegQuality = 90; + saveFormatBatch.jpegSubSamp = 2; saveFormatBatch.pngCompression = 6; saveFormatBatch.pngBits = 8; saveFormatBatch.tiffBits = 8; @@ -507,6 +509,7 @@ if (keyFile.has_group ("External Editor")) { if (keyFile.has_group ("Output")) { if (keyFile.has_key ("Output", "Format")) saveFormat.format = keyFile.get_string ("Output", "Format"); if (keyFile.has_key ("Output", "JpegQuality")) saveFormat.jpegQuality = keyFile.get_integer ("Output", "JpegQuality"); + if (keyFile.has_key ("Output", "JpegSubSamp")) saveFormat.jpegSubSamp = keyFile.get_integer ("Output", "JpegSubSamp"); if (keyFile.has_key ("Output", "PngCompression")) saveFormat.pngCompression = keyFile.get_integer ("Output", "PngCompression"); if (keyFile.has_key ("Output", "PngBps")) saveFormat.pngBits = keyFile.get_integer ("Output", "PngBps"); if (keyFile.has_key ("Output", "TiffBps")) saveFormat.tiffBits = keyFile.get_integer ("Output", "TiffBps"); @@ -516,6 +519,7 @@ if (keyFile.has_group ("Output")) { if (keyFile.has_key ("Output", "FormatBatch")) saveFormatBatch.format = keyFile.get_string ("Output", "FormatBatch"); if (keyFile.has_key ("Output", "JpegQualityBatch")) saveFormatBatch.jpegQuality = keyFile.get_integer ("Output", "JpegQualityBatch"); + if (keyFile.has_key ("Output", "JpegSubSampBatch")) saveFormatBatch.jpegSubSamp = keyFile.get_integer ("Output", "JpegSubSampBatch"); if (keyFile.has_key ("Output", "PngCompressionBatch")) saveFormatBatch.pngCompression = keyFile.get_integer ("Output", "PngCompressionBatch"); if (keyFile.has_key ("Output", "PngBpsBatch")) saveFormatBatch.pngBits = keyFile.get_integer ("Output", "PngBpsBatch"); if (keyFile.has_key ("Output", "TiffBpsBatch")) saveFormatBatch.tiffBits = keyFile.get_integer ("Output", "TiffBpsBatch"); @@ -793,6 +797,7 @@ int Options::saveToFile (Glib::ustring fname) { keyFile.set_string ("Output", "Format", saveFormat.format); keyFile.set_integer ("Output", "JpegQuality", saveFormat.jpegQuality); + keyFile.set_integer ("Output", "JpegSubSamp", saveFormat.jpegSubSamp); keyFile.set_integer ("Output", "PngCompression", saveFormat.pngCompression); keyFile.set_integer ("Output", "PngBps", saveFormat.pngBits); keyFile.set_integer ("Output", "TiffBps", saveFormat.tiffBits); @@ -801,6 +806,7 @@ int Options::saveToFile (Glib::ustring fname) { keyFile.set_string ("Output", "FormatBatch", saveFormatBatch.format); keyFile.set_integer ("Output", "JpegQualityBatch", saveFormatBatch.jpegQuality); + keyFile.set_integer ("Output", "JpegSubSampBatch", saveFormatBatch.jpegSubSamp); keyFile.set_integer ("Output", "PngCompressionBatch", saveFormatBatch.pngCompression); keyFile.set_integer ("Output", "PngBpsBatch", saveFormatBatch.pngBits); keyFile.set_integer ("Output", "TiffBpsBatch", saveFormatBatch.tiffBits); diff --git a/rtgui/options.h b/rtgui/options.h index d5a0e8312..00bc5e5b5 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -41,6 +41,7 @@ class SaveFormat { int pngBits; int pngCompression; int jpegQuality; + int jpegSubSamp; // 1=best compression, 3=best quality int tiffBits; bool tiffUncompressed; bool saveParams; diff --git a/rtgui/saveformatpanel.cc b/rtgui/saveformatpanel.cc index 0826f3c5c..9d906e66f 100644 --- a/rtgui/saveformatpanel.cc +++ b/rtgui/saveformatpanel.cc @@ -25,6 +25,25 @@ SaveFormatPanel::SaveFormatPanel () : listener (NULL) { jpegqual = new Adjuster (M("SAVEDLG_JPEGQUAL"), 0, 100, 1, 100); jpegqual->setAdjusterListener (this); jpegqual->show (); + + jpegSubSampBox = Gtk::manage (new Gtk::HBox ()); + + jpegSubSampHead=Gtk::manage (new Gtk::Label (M("SAVEDLG_SUBSAMP") + Glib::ustring(":")) ); + jpegSubSampHead->show (); + jpegSubSampBox->pack_start (*jpegSubSampHead, Gtk::PACK_SHRINK, 4); + + jpegSubSamp = Gtk::manage (new MyComboBoxText ()); + jpegSubSamp->append_text (M("SAVEDLG_SUBSAMP_1")); + jpegSubSamp->append_text (M("SAVEDLG_SUBSAMP_2")); + jpegSubSamp->append_text (M("SAVEDLG_SUBSAMP_3")); + jpegSubSamp->set_tooltip_text (M("SAVEDLG_SUBSAMP_TOOLTIP")); + jpegSubSamp->set_active (2); + jpegSubSamp->signal_changed().connect( sigc::mem_fun(*this, &SaveFormatPanel::formatChanged) ); + jpegSubSamp->show (); + + jpegSubSampBox->pack_end (*jpegSubSamp); + jpegSubSampBox->show (); + pngcompr = new Adjuster (M("SAVEDLG_PNGCOMPR"), 0, 6, 1, 6); pngcompr->setAdjusterListener (this); pngcompr->show (); @@ -49,6 +68,7 @@ SaveFormatPanel::SaveFormatPanel () : listener (NULL) { formatopts = Gtk::manage (new Gtk::VBox ()); formatopts->pack_start (*jpegqual, Gtk::PACK_SHRINK, 4); + formatopts->pack_start (*jpegSubSampBox, Gtk::PACK_SHRINK, 4); pack_start (*formatopts, Gtk::PACK_SHRINK, 4); savespp = Gtk::manage (new Gtk::CheckButton (M("SAVEDLG_SAVESPP"))); @@ -87,6 +107,8 @@ void SaveFormatPanel::init (SaveFormat &sf) { else if (sf.format=="tif" && sf.tiffBits==8) format->set_active (1); + jpegSubSamp->set_active (sf.jpegSubSamp-1); + pngcompr->setValue (sf.pngCompression); jpegqual->setValue (sf.jpegQuality); savespp->set_active (sf.saveParams); @@ -110,6 +132,7 @@ SaveFormat SaveFormatPanel::getFormat () { sf.tiffBits = 8; sf.pngCompression = (int) pngcompr->getValue (); sf.jpegQuality = (int) jpegqual->getValue (); + sf.jpegSubSamp = jpegSubSamp->get_active_row_number()+1; sf.tiffUncompressed = tiffuncompressed->get_active(); sf.saveParams = savespp->get_active (); return sf; @@ -117,9 +140,10 @@ SaveFormat SaveFormatPanel::getFormat () { void SaveFormatPanel::formatChanged () { - if (oformat==0) + if (oformat==0) { removeIfThere (formatopts, jpegqual); - else if (oformat==3 || oformat==4) + removeIfThere (formatopts, jpegSubSampBox); + } else if (oformat==3 || oformat==4) removeIfThere (formatopts, pngcompr); else if (oformat==1 || oformat==2) removeIfThere (formatopts, tiffuncompressed); @@ -129,9 +153,10 @@ void SaveFormatPanel::formatChanged () { return; Glib::ustring fr = fstr[act]; - if (fr=="jpg") + if (fr=="jpg") { formatopts->pack_start (*jpegqual, Gtk::PACK_SHRINK,4); - else if (fr=="png") + formatopts->pack_start (*jpegSubSampBox, Gtk::PACK_SHRINK,4); + } else if (fr=="png") formatopts->pack_start (*pngcompr, Gtk::PACK_SHRINK,4); else if (fr=="tif") formatopts->pack_start (*tiffuncompressed, Gtk::PACK_SHRINK,4); diff --git a/rtgui/saveformatpanel.h b/rtgui/saveformatpanel.h index cf6b148af..3f21dea97 100644 --- a/rtgui/saveformatpanel.h +++ b/rtgui/saveformatpanel.h @@ -38,7 +38,10 @@ class SaveFormatPanel : public Gtk::VBox, public AdjusterListener { Adjuster* pngcompr; Gtk::CheckButton* tiffuncompressed; MyComboBoxText* format; + MyComboBoxText* jpegSubSamp; Gtk::VBox* formatopts; + Gtk::HBox* jpegSubSampBox; + Gtk::Label* jpegSubSampHead; int oformat; FormatChangeListener* listener; Glib::ustring fstr[5];