450 lines
12 KiB
C++
450 lines
12 KiB
C++
/*
|
|
* 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 <multilangmgr.h>
|
|
#include <thumbnail.h>
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
#include <options.h>
|
|
#include <mytime.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <glibmm.h>
|
|
#include <imagedata.h>
|
|
#include <glib/gstdio.h>
|
|
#include <guiutils.h>
|
|
#include <profilestore.h>
|
|
|
|
using namespace rtengine::procparams;
|
|
|
|
Thumbnail::Thumbnail (CacheManager* cm, const Glib::ustring& fname, CacheImageData* cf)
|
|
: cachemgr(cm), cfs(*cf), pparamsValid(false), fname(fname),
|
|
lastImg(NULL), ref(1), enqueueNumber(0), tpp(NULL), needsReProcessing(true) {
|
|
|
|
mutex = new Glib::Mutex ();
|
|
cfs.load (getCacheFileName ("data")+".txt");
|
|
loadProcParams ();
|
|
loadThumbnail ();
|
|
generateExifDateTimeStrings ();
|
|
}
|
|
|
|
Thumbnail::Thumbnail (CacheManager* cm, const Glib::ustring& fname, const std::string& md5)
|
|
: cachemgr(cm), pparamsValid(false), lastImg(NULL), fname(fname),
|
|
ref(1), enqueueNumber(0), tpp(NULL), needsReProcessing(true) {
|
|
|
|
mutex = new Glib::Mutex ();
|
|
|
|
cfs.md5 = md5;
|
|
generateThumbnailImage ();
|
|
loadProcParams ();
|
|
cfs.recentlySaved = false;
|
|
}
|
|
|
|
void Thumbnail::generateThumbnailImage (bool internal) {
|
|
|
|
if (!internal)
|
|
mutex->lock ();
|
|
|
|
// delete everything loaded into memory
|
|
delete tpp;
|
|
tpp = NULL;
|
|
delete [] lastImg;
|
|
lastImg = NULL;
|
|
tw = -1;
|
|
th = options.maxThumbnailHeight;
|
|
|
|
// generate thumbnail image
|
|
|
|
Glib::ustring ext = getExtension (fname);
|
|
if (ext=="")
|
|
return;
|
|
cfs.supported = false;
|
|
cfs.exifValid = false;
|
|
cfs.timeValid = false;
|
|
|
|
delete tpp;
|
|
tpp = NULL;
|
|
if (ext.lowercase()=="jpg" || ext.lowercase()=="png" || ext.lowercase()=="tif" || ext.lowercase()=="tiff")
|
|
tpp = rtengine::Thumbnail::loadFromImage (fname, tw, th, 1);
|
|
if (tpp) {
|
|
if (ext.lowercase()=="jpg") {
|
|
cfs.format = FT_Jpeg;
|
|
infoFromImage (fname);
|
|
}
|
|
else if (ext.lowercase()=="png")
|
|
cfs.format = FT_Png;
|
|
else if (ext.lowercase()=="tif" || ext.lowercase()=="tiff") {
|
|
cfs.format = FT_Tiff;
|
|
infoFromImage (fname);
|
|
}
|
|
}
|
|
else {
|
|
rtengine::RawMetaDataLocation ri;
|
|
tpp = rtengine::Thumbnail::loadFromRaw (fname, ri, tw, th, 1);
|
|
if (tpp) {
|
|
cfs.format = FT_Raw;
|
|
infoFromImage (fname, &ri);
|
|
}
|
|
}
|
|
if (tpp) {
|
|
// save thumbnail image to cache
|
|
saveThumbnail ();
|
|
cfs.supported = true;
|
|
}
|
|
needsReProcessing = true;
|
|
|
|
cfs.save (getCacheFileName ("data")+".txt");
|
|
|
|
generateExifDateTimeStrings ();
|
|
|
|
if (!internal)
|
|
mutex->unlock ();
|
|
}
|
|
|
|
bool Thumbnail::isSupported () {
|
|
|
|
return cfs.supported;
|
|
}
|
|
|
|
const ProcParams& Thumbnail::getProcParams () {
|
|
|
|
if (pparamsValid)
|
|
return pparams;
|
|
else {
|
|
pparams = *(profileStore.getDefaultProcParams (getType()==FT_Raw));
|
|
if (pparams.wb.method=="Camera") {
|
|
double ct;
|
|
getCamWB (ct, pparams.wb.green);
|
|
pparams.wb.temperature = ct;
|
|
}
|
|
else if (pparams.wb.method=="Auto") {
|
|
double ct;
|
|
getAutoWB (ct, pparams.wb.green);
|
|
pparams.wb.temperature = ct;
|
|
}
|
|
}
|
|
return pparams; // there is no valid pp to return, but we have to return something
|
|
}
|
|
|
|
void Thumbnail::loadProcParams () {
|
|
|
|
pparamsValid = false;
|
|
if (options.paramsLoadLocation==PLL_Input) {
|
|
// try to load it from params file next to the image file
|
|
int ppres = pparams.load (fname + paramFileExtension);
|
|
pparamsValid = !ppres && pparams.version>=220;
|
|
if (!pparamsValid)
|
|
pparamsValid = !pparams.load (getCacheFileName ("profiles")+paramFileExtension);
|
|
}
|
|
else {
|
|
// try to load it from cache
|
|
pparamsValid = !pparams.load (getCacheFileName ("profiles")+paramFileExtension);
|
|
// if no success, load it from params file next to the image file
|
|
if (!pparamsValid) {
|
|
int ppres = pparams.load (fname + paramFileExtension);
|
|
pparamsValid = !ppres && pparams.version>=220;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Thumbnail::clearProcParams (int whoClearedIt) {
|
|
|
|
cfs.recentlySaved = false;
|
|
pparamsValid = false;
|
|
needsReProcessing = true;
|
|
// remove param file from cache
|
|
Glib::ustring fname_ = getCacheFileName ("profiles")+paramFileExtension;
|
|
if (Glib::file_test (fname_, Glib::FILE_TEST_EXISTS))
|
|
::g_remove (fname_.c_str());
|
|
// remove param file located next to the file
|
|
// fname_ = removeExtension(fname) + paramFileExtension;
|
|
fname_ = fname + paramFileExtension;
|
|
if (Glib::file_test (fname_, Glib::FILE_TEST_EXISTS))
|
|
::g_remove (fname_.c_str());
|
|
fname_ = removeExtension(fname) + paramFileExtension;
|
|
if (Glib::file_test (fname_, Glib::FILE_TEST_EXISTS))
|
|
::g_remove (fname_.c_str());
|
|
|
|
for (int i=0; i<listeners.size(); i++)
|
|
listeners[i]->procParamsChanged (this, whoClearedIt);
|
|
}
|
|
|
|
bool Thumbnail::hasProcParams () {
|
|
|
|
return pparamsValid;
|
|
}
|
|
|
|
void Thumbnail::setProcParams (const ProcParams& pp, int whoChangedIt, bool updateCacheNow) {
|
|
|
|
if (pparams!=pp)
|
|
cfs.recentlySaved = false;
|
|
|
|
pparams = pp;
|
|
pparamsValid = true;
|
|
needsReProcessing = true;
|
|
if (updateCacheNow)
|
|
updateCache ();
|
|
|
|
for (int i=0; i<listeners.size(); i++)
|
|
listeners[i]->procParamsChanged (this, whoChangedIt);
|
|
}
|
|
|
|
bool Thumbnail::isRecentlySaved () {
|
|
|
|
return cfs.recentlySaved;
|
|
}
|
|
|
|
void Thumbnail::imageDeveloped () {
|
|
|
|
cfs.recentlySaved = true;
|
|
cfs.save (getCacheFileName ("data")+".txt");
|
|
pparams.save (getCacheFileName ("profiles")+paramFileExtension);
|
|
}
|
|
|
|
void Thumbnail::imageEnqueued () {
|
|
|
|
enqueueNumber++;
|
|
}
|
|
|
|
void Thumbnail::imageRemovedFromQueue () {
|
|
|
|
enqueueNumber--;
|
|
}
|
|
|
|
bool Thumbnail::isEnqueued () {
|
|
|
|
return enqueueNumber > 0;
|
|
}
|
|
|
|
void Thumbnail::increaseRef () { ref++; }
|
|
void Thumbnail::decreaseRef () { ref--; if (!ref) cachemgr->closeThumbnail (this); }
|
|
|
|
void Thumbnail::getThumbnailSize (int &w, int &h) {
|
|
|
|
if (tpp)
|
|
w = tpp->getImageWidth (getProcParams(), h);
|
|
else
|
|
w = tw * h / th;
|
|
}
|
|
|
|
rtengine::IImage8* Thumbnail::processThumbImage (const rtengine::procparams::ProcParams& pparams, int h, double& scale) {
|
|
|
|
mutex->lock ();
|
|
|
|
if (!tpp)
|
|
return NULL;
|
|
|
|
rtengine::IImage8* res = tpp->processImage (pparams, h, rtengine::TI_Bilinear, scale);
|
|
|
|
mutex->unlock ();
|
|
return res;
|
|
}
|
|
|
|
void Thumbnail::generateExifDateTimeStrings () {
|
|
|
|
exifString = "";
|
|
dateTimeString = "";
|
|
|
|
if (!cfs.exifValid)
|
|
return;
|
|
|
|
exifString = Glib::ustring::compose ("f/%1 %2s %3%4", Glib::ustring(rtengine::ImageData::apertureToString(cfs.fnumber)), Glib::ustring(rtengine::ImageData::shutterToString(cfs.shutter)), M("QINFO_ISO"), cfs.iso);
|
|
|
|
std::string dateFormat = options.dateFormat;
|
|
std::ostringstream ostr;
|
|
bool spec = false;
|
|
for (int i=0; i<dateFormat.size(); i++)
|
|
if (spec && dateFormat[i]=='y') {
|
|
ostr << cfs.year;
|
|
spec = false;
|
|
}
|
|
else if (spec && dateFormat[i]=='m') {
|
|
ostr << (int)cfs.month;
|
|
spec = false;
|
|
}
|
|
else if (spec && dateFormat[i]=='d') {
|
|
ostr << (int)cfs.day;
|
|
spec = false;
|
|
}
|
|
else if (dateFormat[i]=='%')
|
|
spec = true;
|
|
else {
|
|
ostr << (char)dateFormat[i];
|
|
spec = false;
|
|
}
|
|
|
|
ostr << " " << (int)cfs.hour;
|
|
ostr << ":" << std::setw(2) << std::setfill('0') << (int)cfs.min;
|
|
ostr << ":" << std::setw(2) << std::setfill('0') << (int)cfs.sec;
|
|
|
|
dateTimeString = ostr.str ();
|
|
}
|
|
|
|
const Glib::ustring& Thumbnail::getExifString () {
|
|
|
|
return exifString;
|
|
}
|
|
|
|
const Glib::ustring& Thumbnail::getDateTimeString () {
|
|
|
|
return dateTimeString;
|
|
}
|
|
|
|
ThFileType Thumbnail::getType () {
|
|
|
|
return (ThFileType) cfs.format;
|
|
}
|
|
|
|
void Thumbnail::infoFromImage (const Glib::ustring& fname, rtengine::RawMetaDataLocation* rml) {
|
|
|
|
rtengine::ImageMetaData* idata = rtengine::ImageMetaData::fromFile (fname, rml);
|
|
if (!idata)
|
|
return;
|
|
cfs.timeValid = false;
|
|
cfs.exifValid = false;
|
|
if (idata->hasExif()) {
|
|
cfs.shutter = idata->getShutterSpeed ();
|
|
cfs.fnumber = idata->getFNumber ();
|
|
cfs.focalLen = idata->getFocalLen ();
|
|
cfs.iso = idata->getISOSpeed ();
|
|
cfs.year = 1900 + idata->getDateTime().tm_year;
|
|
cfs.month = idata->getDateTime().tm_mon + 1;
|
|
cfs.day = idata->getDateTime().tm_mday;
|
|
cfs.hour = idata->getDateTime().tm_hour;
|
|
cfs.min = idata->getDateTime().tm_min;
|
|
cfs.sec = idata->getDateTime().tm_sec;
|
|
cfs.timeValid = true;
|
|
cfs.exifValid = true;
|
|
cfs.lens = idata->getLens();
|
|
cfs.camera = idata->getMake() + " " + idata->getModel();
|
|
}
|
|
else {
|
|
cfs.lens = "Unknown";
|
|
cfs.camera = "Unknown";
|
|
}
|
|
delete idata;
|
|
}
|
|
|
|
void Thumbnail::loadThumbnail (bool internal, bool firstTrial) {
|
|
|
|
if (!internal)
|
|
mutex->lock ();
|
|
|
|
needsReProcessing = true;
|
|
delete tpp;
|
|
tpp = new rtengine::Thumbnail ();
|
|
tpp->isRaw = (cfs.format == (int) FT_Raw);
|
|
|
|
// load supplementary data
|
|
bool succ = tpp->readData (getCacheFileName ("data")+".txt");
|
|
|
|
// thumbnail image
|
|
succ = succ && tpp->readImage (getCacheFileName ("images"));
|
|
|
|
if (!succ && firstTrial) {
|
|
generateThumbnailImage (true);
|
|
if (cfs.supported && firstTrial)
|
|
loadThumbnail (true, false);
|
|
}
|
|
else if (!succ) {
|
|
delete tpp;
|
|
tpp = NULL;
|
|
}
|
|
else {
|
|
// load aehistogram
|
|
tpp->readAEHistogram (getCacheFileName ("aehistograms"));
|
|
|
|
// load embedded profile
|
|
tpp->readEmbProfile (getCacheFileName ("embprofiles")+".icc");
|
|
|
|
tpp->init ();
|
|
|
|
}
|
|
if (!internal)
|
|
mutex->unlock ();
|
|
}
|
|
|
|
void Thumbnail::saveThumbnail () {
|
|
|
|
if (!tpp)
|
|
return;
|
|
|
|
::g_remove ((getCacheFileName ("images")+".cust").c_str());
|
|
::g_remove ((getCacheFileName ("images")+".cust16").c_str());
|
|
::g_remove ((getCacheFileName ("images")+".jpg").c_str());
|
|
|
|
// save thumbnail image
|
|
if (options.thumbnailFormat == FT_Custom)
|
|
tpp->writeImage (getCacheFileName ("images")+".cust", 1);
|
|
else if (options.thumbnailFormat == FT_Custom16)
|
|
tpp->writeImage (getCacheFileName ("images")+".cust16", 2);
|
|
else if (options.thumbnailFormat == FT_Jpeg)
|
|
tpp->writeImage (getCacheFileName ("images")+".jpg", 3);
|
|
|
|
// save aehistogram
|
|
tpp->writeAEHistogram (getCacheFileName ("aehistograms"));
|
|
|
|
// save embedded profile
|
|
tpp->writeEmbProfile (getCacheFileName ("embprofiles")+".icc");
|
|
|
|
// save supplementary data
|
|
tpp->writeData (getCacheFileName ("data")+".txt");
|
|
}
|
|
|
|
void Thumbnail::updateCache () {
|
|
|
|
if (pparamsValid) {
|
|
if (options.saveParamsCache)
|
|
pparams.save (getCacheFileName ("profiles")+paramFileExtension);
|
|
if (options.saveParamsFile)
|
|
// pparams.save (removeExtension(fname) + paramFileExtension);
|
|
pparams.save (fname + paramFileExtension);
|
|
}
|
|
cfs.save (getCacheFileName ("data")+".txt");
|
|
}
|
|
|
|
Thumbnail::~Thumbnail () {
|
|
|
|
delete [] lastImg;
|
|
delete tpp;
|
|
}
|
|
|
|
Glib::ustring Thumbnail::getCacheFileName (Glib::ustring subdir) {
|
|
|
|
return cachemgr->getCacheFileName (subdir, fname, cfs.md5);
|
|
}
|
|
|
|
void Thumbnail::setFileName (const Glib::ustring fn) {
|
|
|
|
fname = fn;
|
|
cfs.md5 = cachemgr->getMD5 (fname);
|
|
}
|
|
|
|
void Thumbnail::addThumbnailListener (ThumbnailListener* tnl) {
|
|
|
|
listeners.push_back (tnl);
|
|
}
|
|
|
|
void Thumbnail::removeThumbnailListener (ThumbnailListener* tnl) {
|
|
|
|
std::vector<ThumbnailListener*>::iterator f = std::find (listeners.begin(), listeners.end(), tnl);
|
|
if (f!=listeners.end())
|
|
listeners.erase (f);
|
|
}
|
|
|