Changes to black compression and saturation controls. Black compression from 0-50 acts the same as 0-100 on the previous version, compressing dark tones without crushing blacks. 50-100 then starts crushing blacks until by 100 on the slider, all tones up to the set black point are sent to zero. In the new saturation control, negative values of the slider set a linear curve rather than an inverted S curve, and smoothly decrease saturation to zero across the board.

This commit is contained in:
Emil Martinec
2010-10-26 22:59:18 -05:00
commit 926056c2c2
620 changed files with 130476 additions and 0 deletions

425
rtgui/batchqueue.cc Normal file
View File

@@ -0,0 +1,425 @@
/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* RawTherapee 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.
*
* RawTherapee 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 RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#include <batchqueue.h>
#include <glibmm.h>
#include <glib/gstdio.h>
#include <multilangmgr.h>
#include <filecatalog.h>
#include <batchqueuebuttonset.h>
#include <guiutils.h>
#include <safegtk.h>
using namespace rtengine;
BatchQueue::BatchQueue () : processing(NULL), listener(NULL) {
int p = 0;
pmenu = new Gtk::Menu ();
pmenu->attach (*(cancel = new Gtk::MenuItem (M("FILEBROWSER_POPUPCANCELJOB"))), 0, 1, p, p+1); p++;
pmenu->attach (*(new Gtk::SeparatorMenuItem ()), 0, 1, p, p+1); p++;
pmenu->attach (*(head = new Gtk::MenuItem (M("FILEBROWSER_POPUPMOVEHEAD"))), 0, 1, p, p+1); p++;
pmenu->attach (*(tail = new Gtk::MenuItem (M("FILEBROWSER_POPUPMOVEEND"))), 0, 1, p, p+1); p++;
pmenu->attach (*(new Gtk::SeparatorMenuItem ()), 0, 1, p, p+1); p++;
pmenu->attach (*(selall = new Gtk::MenuItem (M("FILEBROWSER_POPUPSELECTALL"))), 0, 1, p, p+1); p++;
pmenu->show_all ();
cancel->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &BatchQueue::cancelItems), &selected));
head->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &BatchQueue::headItems), &selected));
tail->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &BatchQueue::tailItems), &selected));
selall->signal_activate().connect (sigc::mem_fun(*this, &BatchQueue::selectAll));
}
void BatchQueue::rightClicked (ThumbBrowserEntryBase* entry) {
pmenu->popup (3, 0);
}
void BatchQueue::addEntry (BatchQueueEntry* entry, bool head) {
entry->setParent (this);
entry->resize (options.thumbSize);
entry->selected = false;
if (!head)
fd.push_back (entry);
else {
std::vector<ThumbBrowserEntryBase*>::iterator pos;
for (pos=fd.begin(); pos!=fd.end(); pos++)
if (!(*pos)->processing) {
fd.insert (pos, entry);
break;
}
if (pos==fd.end())
fd.push_back (entry);
}
if (entry->thumbnail)
entry->thumbnail->imageEnqueued ();
BatchQueueButtonSet* bqbs = new BatchQueueButtonSet (entry);
bqbs->setButtonListener (this);
entry->addButtonSet (bqbs);
arrangeFiles ();
queue_draw ();
notifyListener ();
}
int deleteitem (void* data) {
gdk_threads_enter ();
delete (BatchQueueEntry*)data;
gdk_threads_leave ();
return 0;
}
void BatchQueue::cancelItems (std::vector<ThumbBrowserEntryBase*>* items) {
for (int i=0; i<items->size(); i++) {
BatchQueueEntry* entry = (BatchQueueEntry*)(*items)[i];
if (entry->processing)
continue;
std::vector<ThumbBrowserEntryBase*>::iterator pos = std::find (fd.begin(), fd.end(), entry);
if (pos!=fd.end()) {
fd.erase (pos);
rtengine::ProcessingJob::destroy (entry->job);
if (entry->thumbnail)
entry->thumbnail->imageRemovedFromQueue ();
g_idle_add (deleteitem, entry);
}
}
for (int i=0; i<fd.size(); i++)
fd[i]->selected = false;
lastClicked = NULL;
selected.clear ();
redraw ();
notifyListener ();
}
void BatchQueue::headItems (std::vector<ThumbBrowserEntryBase*>* items) {
for (int i=items->size()-1; i>=0; i--) {
BatchQueueEntry* entry = (BatchQueueEntry*)(*items)[i];
if (entry->processing)
continue;
std::vector<ThumbBrowserEntryBase*>::iterator pos = std::find (fd.begin(), fd.end(), entry);
if (pos!=fd.end() && pos!=fd.begin()) {
fd.erase (pos);
// find the first item that is not under processing
for (pos=fd.begin(); pos!=fd.end(); pos++)
if (!(*pos)->processing) {
fd.insert (pos, entry);
break;
}
}
}
redraw ();
}
void BatchQueue::tailItems (std::vector<ThumbBrowserEntryBase*>* items) {
for (int i=0; i<items->size(); i++) {
BatchQueueEntry* entry = (BatchQueueEntry*)(*items)[i];
if (entry->processing)
continue;
std::vector<ThumbBrowserEntryBase*>::iterator pos = std::find (fd.begin(), fd.end(), entry);
if (pos!=fd.end()) {
fd.erase (pos);
fd.push_back (entry);
}
}
redraw ();
}
void BatchQueue::selectAll () {
lastClicked = NULL;
selected.clear ();
for (int i=0; i<fd.size(); i++) {
if (fd[i]->processing)
continue;
fd[i]->selected = true;
selected.push_back (fd[i]);
}
queue_draw ();
}
void BatchQueue::startProcessing () {
if (!processing && fd.size()>0) {
BatchQueueEntry* next = (BatchQueueEntry*)fd[0];
// tag it as processing
next->processing = true;
processing = next;
// remove from selection
if (processing->selected) {
std::vector<ThumbBrowserEntryBase*>::iterator pos = std::find (selected.begin(), selected.end(), processing);
if (pos!=selected.end())
selected.erase (pos);
processing->selected = false;
}
// remove button set
next->removeButtonSet ();
// start batch processing
rtengine::startBatchProcessing (next->job, this);
queue_draw ();
}
}
rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImage16* img) {
gdk_threads_enter ();
// save image img
Glib::ustring fname;
SaveFormat saveFormat;
if (processing->outFileName=="") { // auto file name
fname = obtainFileName (processing->filename);
saveFormat = options.saveFormat;
}
else { // use the save-as filename with automatic completion for uniqueness
fname = autoCompleteFileName (removeExtension(processing->outFileName), getExtension(processing->outFileName));
saveFormat = processing->saveFormat;
}
printf ("fname=%s, %s\n", fname.c_str(), removeExtension(fname).c_str());
if (img && fname!="") {
int err = 0;
if (saveFormat.format=="tif")
err = img->saveAsTIFF (fname, saveFormat.tiffBits,saveFormat.tiffUncompressed);
else if (saveFormat.format=="png")
err = img->saveAsPNG (fname, saveFormat.pngCompression, saveFormat.pngBits);
else if (saveFormat.format=="jpg")
err = img->saveAsJPEG (fname, saveFormat.jpegQuality);
img->free ();
if (!err && saveFormat.saveParams)
// We keep the extension to avoid overwriting the profile when we have
// the same output filename with different extension
//processing->params.save (removeExtension(fname) + paramFileExtension);
processing->params.save (fname + paramFileExtension);
else {
printf("Unable to process or save %s\n", fname.c_str());
}
if (processing->thumbnail) {
processing->thumbnail->imageDeveloped ();
processing->thumbnail->imageRemovedFromQueue ();
if (listener)
listener->imageProcessingReady (processing->filename);
}
}
// delete from the queue
delete processing;
processing = NULL;
fd.erase (fd.begin());
// return next job
if (fd.size()==0) {
if (listener)
listener->queueEmpty ();
}
else if (listener && listener->canStartNext ()) {
BatchQueueEntry* next = (BatchQueueEntry*)fd[0];
// tag it as selected
next->processing = true;
processing = next;
// remove from selection
if (processing->selected) {
std::vector<ThumbBrowserEntryBase*>::iterator pos = std::find (selected.begin(), selected.end(), processing);
if (pos!=selected.end())
selected.erase (pos);
processing->selected = false;
}
// remove button set
next->removeButtonSet ();
}
redraw ();
notifyListener ();
gdk_threads_leave ();
return processing ? processing->job : NULL;
}
Glib::ustring BatchQueue::obtainFileName (const Glib::ustring& origFileName) {
std::vector<Glib::ustring> pa;
std::vector<Glib::ustring> da;
for (int i=0; i<origFileName.size(); i++) {
while ((i<origFileName.size()) && (origFileName[i]=='\\' || origFileName[i]=='/'))
i++;
if (i>=origFileName.size())
break;
Glib::ustring tok = "";
while ((i<origFileName.size()) && !(origFileName[i]=='\\' || origFileName[i]=='/'))
tok = tok + origFileName[i++];
da.push_back (tok);
}
if (origFileName[0]=='/' || origFileName[0]=='\\')
pa.push_back ("/" + da[0]);
else
pa.push_back (da[0]);
for (int i=1; i<da.size(); i++)
pa.push_back (pa[i-1] + "/" + da[i]);
// for (int i=0; i<da.size(); i++)
// printf ("da: %s\n", da[i].c_str());
// for (int i=0; i<pa.size(); i++)
// printf ("pa: %s\n", pa[i].c_str());
// extracting filebase
Glib::ustring filename;
int extpos = origFileName.size()-1;
for (; extpos>=0 && origFileName[extpos]!='.'; extpos--);
for (int k=extpos-1; k>=0 && origFileName[k]!='/' && origFileName[k]!='\\'; k--)
filename = origFileName[k] + filename;
// printf ("%d, |%s|\n", extpos, filename.c_str());
// constructing full output path
// printf ("path=|%s|\n", options.savePath.c_str());
Glib::ustring path="";
if (options.saveUsePathTemplate) {
int ix=0;
while (options.savePathTemplate[ix]!=0) {
if (options.savePathTemplate[ix]=='%') {
ix++;
if (options.savePathTemplate[ix]=='p') {
ix++;
int i = options.savePathTemplate[ix]-'0';
if (i<pa.size())
path = path + pa[pa.size()-i-1] + '/';
ix++;
}
else if (options.savePathTemplate[ix]=='d') {
ix++;
int i = options.savePathTemplate[ix]-'0';
if (i<da.size())
path = path + da[da.size()-i-1] + '/';
ix++;
}
else if (options.savePathTemplate[ix]=='f') {
path = path + filename;
}
}
else
path = path + options.savePathTemplate[ix];
ix++;
}
}
else
path = Glib::build_filename (options.savePathFolder, filename);
return autoCompleteFileName (path, options.saveFormat.format);
}
Glib::ustring BatchQueue::autoCompleteFileName (const Glib::ustring& fileName, const Glib::ustring& format) {
// separate filename and the path to the destination directory
Glib::ustring dstdir = Glib::path_get_dirname (fileName);
Glib::ustring dstfname = Glib::path_get_basename (fileName);
// create directory, if does not exist
if (g_mkdir_with_parents (dstdir.c_str(), 0755) )
return "";
// In overwrite mode we TRY to delete the old file first.
// if that's not possible (e.g. locked by viewer, R/O), we revert to the standard naming scheme
bool inOverwriteMode=options.overwriteOutputFile;
for (int tries=0; tries<100; tries++) {
Glib::ustring fname;
if (tries==0)
fname = Glib::ustring::compose ("%1.%2", Glib::build_filename (dstdir, dstfname), format);
else
fname = Glib::ustring::compose ("%1-%2.%3", Glib::build_filename (dstdir, dstfname), tries, format);
int fileExists=Glib::file_test (fname, Glib::FILE_TEST_EXISTS);
if (inOverwriteMode && fileExists) {
// do NOT use g_remove as it has compiler problems on Unix and OSX (GTK namespace problem)
if (::remove(safe_locale_from_utf8(fname).c_str ()) == -1)
inOverwriteMode = false; // failed to delete- revert to old naming scheme
else
fileExists = false; // deleted now
}
if (!fileExists) {
return fname;
}
}
}
int bqredraw (void* p) {
gdk_threads_enter ();
((BatchQueue*)p)->redraw();
gdk_threads_leave ();
return 0;
}
void BatchQueue::setProgress (double p) {
if (processing)
processing->progress = p;
g_idle_add (bqredraw, this);
}
void BatchQueue::buttonPressed (LWButton* button, int actionCode, void* actionData) {
std::vector<ThumbBrowserEntryBase*> bqe;
bqe.push_back ((BatchQueueEntry*)actionData);
if (actionCode==10) // cancel
cancelItems (&bqe);
else if (actionCode==8) // to head
headItems (&bqe);
else if (actionCode==9) // to tail
tailItems (&bqe);
}
struct NLParams {
BatchQueueListener* listener;
int qsize;
};
int bqnotifylistener (void* data) {
gdk_threads_enter ();
NLParams* params = (NLParams*)data;
params->listener->queueSizeChanged (params->qsize);
delete params;
gdk_threads_leave ();
return 0;
}
void BatchQueue::notifyListener () {
if (listener) {
NLParams* params = new NLParams;
params->listener = listener;
params->qsize = fd.size();
g_idle_add (bqnotifylistener, params);
}
}
void BatchQueue::redrawNeeded (LWButton* button) {
queue_draw ();
}