XMP/IPTC metadata tunneling option, plus new EXIF tags; see issue #460
This commit is contained in:
@@ -472,6 +472,7 @@ PREFERENCES_INTENT_RELATIVE;Relative Colorimetric
|
||||
PREFERENCES_INTENT_SATURATION;Saturation
|
||||
PREFERENCES_LINEDENOISE;Line noise filter
|
||||
PREFERENCES_LIVETHUMBNAILS;Live Thumbnails (slower)
|
||||
PREFERENCES_METADATA;Metadata
|
||||
PREFERENCES_MONITORICC;Monitor Profile
|
||||
PREFERENCES_MULTITAB;Multiple tabs mode
|
||||
PREFERENCES_MULTITABDUALMON;Multiple tabs mode, if available on second monitor
|
||||
@@ -490,10 +491,10 @@ PREFERENCES_PARSEDEXTDELHINT;Delete selected extension from the list
|
||||
PREFERENCES_PRESER;Exposure before interpolation\n :preserve highlights (EV)
|
||||
PREFERENCES_PROFILEHANDLING;Processing Profile Handling
|
||||
PREFERENCES_PROFILELOADPR;Profile Loading Priority
|
||||
PREFERENCES_PROFILEPRCACHE;Profile in Cache
|
||||
PREFERENCES_PROFILEPRFILE;Profile Next to the Input File
|
||||
PREFERENCES_PROFILESAVECACHE;Save Processing Parameters to the Cache
|
||||
PREFERENCES_PROFILESAVEINPUT;Save Processing Parameters Next to the Input File
|
||||
PREFERENCES_PROFILEPRCACHE;Profile in cache
|
||||
PREFERENCES_PROFILEPRFILE;Profile next to the input file
|
||||
PREFERENCES_PROFILESAVECACHE;Save processing parameters to the cache
|
||||
PREFERENCES_PROFILESAVEINPUT;Save processing parameters next to the input file
|
||||
PREFERENCES_PROPERTY;Property
|
||||
PREFERENCES_PSPATH;Adobe Photoshop installation directory
|
||||
PREFERENCES_SELECTFONT;Select font
|
||||
@@ -520,6 +521,7 @@ PREFERENCES_TAB_IMPROC;Image Processing
|
||||
PREFERENCES_TAB_OUTPUT;Output Options
|
||||
PREFERENCES_TAB_SOUND;Sounds
|
||||
PREFERENCES_THUMBSIZE;Thumbnail Size
|
||||
PREFERENCES_TUNNELMETADATA;Tunnel IPTC/XMP unchanged to output file
|
||||
PREFERENCES_USESYSTEMTHEME; Use System Theme
|
||||
PREFERENCES_WORKFLOW;Workflow
|
||||
PROFILEPANEL_FILEDLGFILTERANY;Any files
|
||||
|
@@ -45,6 +45,22 @@ using namespace rtengine::procparams;
|
||||
|
||||
Glib::ustring ImageIO::errorMsg[6] = {"Success", "Cannot read file.", "Invalid header.","Error while reading header.","File reading error", "Image format not supported."};
|
||||
|
||||
// For only copying the raw input data
|
||||
void ImageIO::setMetadata (const rtexif::TagDirectory* eroot) {
|
||||
if (exifRoot!=NULL) { delete exifRoot; exifRoot = NULL; }
|
||||
|
||||
if (eroot) {
|
||||
rtexif::TagDirectory* td = ((rtexif::TagDirectory*)eroot)->clone (NULL);
|
||||
|
||||
// make IPTC and XMP pass through
|
||||
td->keepTag(0x83bb); // IPTC
|
||||
td->keepTag(0x02bc); // XMP
|
||||
|
||||
exifRoot=td;
|
||||
}
|
||||
}
|
||||
|
||||
// For merging with RT specific data
|
||||
void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const std::vector<ExifPair>& exif, const std::vector<IPTCPair>& iptcc) {
|
||||
|
||||
// store exif info
|
||||
@@ -53,15 +69,13 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const std::vector<
|
||||
exifChange[i].first = exif[i].field;
|
||||
exifChange[i].second = exif[i].value;
|
||||
}
|
||||
delete exifRoot;
|
||||
|
||||
exifRoot = NULL;
|
||||
if (exifRoot!=NULL) { delete exifRoot; exifRoot = NULL; }
|
||||
|
||||
if (eroot)
|
||||
exifRoot = ((rtexif::TagDirectory*)eroot)->clone (NULL);
|
||||
|
||||
if (iptc)
|
||||
iptc_data_free (iptc);
|
||||
iptc = NULL;
|
||||
if (iptc!=NULL) { iptc_data_free (iptc); iptc = NULL; }
|
||||
|
||||
// build iptc structures for libiptcdata
|
||||
if (iptcc.size()==0)
|
||||
|
@@ -82,6 +82,7 @@ class ImageIO {
|
||||
cmsHPROFILE getEmbeddedProfile () { return embProfile; }
|
||||
void getEmbeddedProfileData (int& length, unsigned char*& pdata) { length = loadedProfileLength; pdata = (unsigned char*)loadedProfileData; }
|
||||
|
||||
void setMetadata (const rtexif::TagDirectory* eroot);
|
||||
void setMetadata (const rtexif::TagDirectory* eroot, const std::vector<rtengine::procparams::ExifPair>& exif, const std::vector<rtengine::procparams::IPTCPair>& iptcc);
|
||||
void setOutputProfile (char* pdata, int plen);
|
||||
Glib::Mutex& mutex () { return imutex; }
|
||||
|
@@ -348,8 +348,9 @@ namespace rtengine {
|
||||
* @param job the ProcessingJob to cancel.
|
||||
* @param errorCode is the error code if an error occured (e.g. the input image could not be loaded etc.)
|
||||
* @param pl is an optional ProgressListener if you want to keep track of the progress
|
||||
* @param tunnelMetaData tunnels IPTC and XMP to output without change
|
||||
* @return the resulting image, with the output profile applied, exif and iptc data set. You have to save it or you can access the pixel data directly. */
|
||||
IImage16* processImage (ProcessingJob* job, int& errorCode, ProgressListener* pl = NULL);
|
||||
IImage16* processImage (ProcessingJob* job, int& errorCode, ProgressListener* pl = NULL, bool tunnelMetaData=false);
|
||||
|
||||
/** This class is used to control the batch processing. The class implementing this interface will be called when the full processing of an
|
||||
* image is ready and the next job to process is needed. */
|
||||
@@ -366,8 +367,9 @@ namespace rtengine {
|
||||
* with processing. If no new job is given, it finishes.
|
||||
* The ProcessingJob passed becomes invalid, you can not use it any more.
|
||||
* @param job the ProcessingJob to cancel.
|
||||
* @param bpl is the BatchProcessingListener that is called when the image is ready or the next job is needed. It also acts as a ProgressListener. */
|
||||
void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl);
|
||||
* @param bpl is the BatchProcessingListener that is called when the image is ready or the next job is needed. It also acts as a ProgressListener.
|
||||
* @param tunnelMetaData tunnels IPTC and XMP to output without change */
|
||||
void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData);
|
||||
|
||||
|
||||
extern Glib::Mutex* lcmsMutex;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
* This file is part of RawTherapee.
|
||||
*
|
||||
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
||||
* Copyright (c) 2010 Oliver Duis <www.oliverduis.de>
|
||||
*
|
||||
* RawTherapee is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -31,7 +32,8 @@
|
||||
|
||||
namespace rtengine {
|
||||
|
||||
IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* pl) {
|
||||
// tunnelMetaData copies IPTC and XMP untouched to output
|
||||
IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* pl, bool tunnelMetaData) {
|
||||
|
||||
errorCode = 0;
|
||||
|
||||
@@ -275,12 +277,14 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p
|
||||
}
|
||||
}
|
||||
|
||||
if (tunnelMetaData)
|
||||
readyImg->setMetadata (ii->getMetaData()->getExifData ());
|
||||
else
|
||||
readyImg->setMetadata (ii->getMetaData()->getExifData (), params.exif, params.iptc);
|
||||
|
||||
if (pl)
|
||||
pl->setProgress (1.0);
|
||||
|
||||
readyImg->setMetadata (ii->getMetaData()->getExifData (), params.exif, params.iptc);
|
||||
|
||||
ProfileContent pc;
|
||||
if (params.icm.output.compare (0, 6, "No ICM") && params.icm.output!="")
|
||||
pc = iccStore->getContent (params.icm.output);
|
||||
@@ -302,23 +306,23 @@ IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* p
|
||||
return readyImg;
|
||||
}
|
||||
|
||||
void batchProcessingThread (ProcessingJob* job, BatchProcessingListener* bpl) {
|
||||
void batchProcessingThread (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData) {
|
||||
|
||||
ProcessingJob* currentJob = job;
|
||||
|
||||
while (currentJob) {
|
||||
int errorCode;
|
||||
IImage16* img = processImage (currentJob, errorCode, bpl);
|
||||
IImage16* img = processImage (currentJob, errorCode, bpl, tunnelMetaData);
|
||||
if (errorCode)
|
||||
bpl->error ("Can not load input image.");
|
||||
currentJob = bpl->imageReady (img);
|
||||
}
|
||||
}
|
||||
|
||||
void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl) {
|
||||
void startBatchProcessing (ProcessingJob* job, BatchProcessingListener* bpl, bool tunnelMetaData) {
|
||||
|
||||
if (bpl)
|
||||
Glib::Thread::create(sigc::bind(sigc::ptr_fun(batchProcessingThread), job, bpl), 0, true, true, Glib::THREAD_PRIORITY_LOW);
|
||||
Glib::Thread::create(sigc::bind(sigc::ptr_fun(batchProcessingThread), job, bpl, tunnelMetaData), 0, true, true, Glib::THREAD_PRIORITY_LOW);
|
||||
|
||||
}
|
||||
|
||||
|
@@ -208,6 +208,11 @@ Tag* TagDirectory::findTag (const char* name) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TagDirectory::keepTag (int ID) {
|
||||
for (int i=0; i<tags.size(); i++)
|
||||
if (tags[i]->getID()==ID) tags[i]->setKeep(true);
|
||||
}
|
||||
|
||||
int TagDirectory::calculateSize () {
|
||||
|
||||
int size = 2; // space to store the number of tags
|
||||
|
@@ -80,6 +80,8 @@ class TagDirectory {
|
||||
virtual Tag* getTag (const char* name) const;
|
||||
virtual Tag* getTag (int ID) const;
|
||||
virtual Tag* findTag (const char* name) const;
|
||||
|
||||
void keepTag (int ID);
|
||||
virtual void addTag (Tag* a);
|
||||
virtual void addTagFront (Tag* a);
|
||||
virtual void replaceTag (Tag* a);
|
||||
|
@@ -2,6 +2,7 @@
|
||||
* This file is part of RawTherapee.
|
||||
*
|
||||
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
||||
* Copyright (c) 2010 Oliver Duis <www.oliverduis.de>
|
||||
*
|
||||
* RawTherapee is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -374,6 +375,12 @@ public:
|
||||
};
|
||||
UnitsInterpreter unitsInterpreter;
|
||||
|
||||
class UTF8BinInterpreter : public Interpreter {
|
||||
public:
|
||||
UTF8BinInterpreter () {}
|
||||
};
|
||||
UTF8BinInterpreter utf8BinInterpreter;
|
||||
|
||||
const TagAttrib exifAttribs[] = {
|
||||
{0, 2, 0, 0, 0x0100, "ImageWidth", &stdInterpreter},
|
||||
{0, 2, 0, 0, 0x0101, "ImageHeight", &stdInterpreter},
|
||||
@@ -442,7 +449,6 @@ const TagAttrib exifAttribs[] = {
|
||||
{0, 1, 0, 0, 0xA420, "ImageUniqueID", &stdInterpreter},
|
||||
{0, 1, 0, 0, 0xa432, "LensInfo", &stdInterpreter},
|
||||
{0, 1, 0, 0, 0xa434, "LensModel", &stdInterpreter},
|
||||
{0, 1, 0, 0, 0xc630, "DNGLensInfo", &stdInterpreter},
|
||||
{-1, 0, 0, 0, 0, "", NULL }};
|
||||
|
||||
|
||||
@@ -517,6 +523,7 @@ const TagAttrib iopAttribs[] = {
|
||||
{0, 2, 0, 0, 0x0212, "YCbCrSubSampling", &stdInterpreter},
|
||||
{0, 2, 0, 0, 0x0213, "YCbCrPositioning", &stdInterpreter},
|
||||
{0, 2, 0, 0, 0x0214, "ReferenceBlackWhite", &stdInterpreter},
|
||||
{0, 2, 0, 0, 0x02bc, "ApplicationNotes", &utf8BinInterpreter}, // XMP
|
||||
{0, 1, 0, 0, 0x4746, "Rating",&stdInterpreter},
|
||||
{0, 1, 0, 0, 0x4749, "RatingPercent",&stdInterpreter},
|
||||
{0, 1, 0, 0, 0x828d, "CFAPatternDim", &stdInterpreter},
|
||||
@@ -528,8 +535,13 @@ const TagAttrib iopAttribs[] = {
|
||||
{0, 1, 0, gpsAttribs, 0x8825, "GPSInfo", &stdInterpreter},
|
||||
{0, 1, 0, 0, 0x9003, "DateTimeOriginal", &stdInterpreter},
|
||||
{0, 1, 0, 0, 0x9004, "DateTimeDigitized", &stdInterpreter},
|
||||
{0, 1, 0, 0, 0x9211, "ImageNumber", &stdInterpreter},
|
||||
{0, 1, 0, iopAttribs, 0xA005, "Interoperability", &stdInterpreter},
|
||||
{0, 0, 0, 0, 0xC4A5, "PrintIMInformation", &stdInterpreter},
|
||||
{0, 1, 0, 0, 0xc62f, "CameraSerialNumber", &stdInterpreter},
|
||||
{0, 2, 0, 0, 0xc630, "DNGLensInfo", &stdInterpreter},
|
||||
{0, 1, 0, 0, 0xc65d, "RawDataUniqueID", &stdInterpreter},
|
||||
{0, 0, 0, 0, 0xc761, "NoiseProfile", &stdInterpreter},
|
||||
{0, 2, 0, 0, 0x00fe, "NewSubFileType", &stdInterpreter},
|
||||
{-1, 0, 0, 0, 0, "", NULL}};
|
||||
|
||||
|
@@ -281,7 +281,7 @@ void BatchQueue::startProcessing () {
|
||||
// remove button set
|
||||
next->removeButtonSet ();
|
||||
// start batch processing
|
||||
rtengine::startBatchProcessing (next->job, this);
|
||||
rtengine::startBatchProcessing (next->job, this, options.tunnelMetaData);
|
||||
queue_draw ();
|
||||
}
|
||||
}
|
||||
|
@@ -900,7 +900,7 @@ void EditorPanel::saveAsPressed () {
|
||||
rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams);
|
||||
fname = removeExtension (fname);
|
||||
ProgressConnector<rtengine::IImage16*> *ld = new ProgressConnector<rtengine::IImage16*>();
|
||||
ld->startFunc(sigc::bind(sigc::ptr_fun(&rtengine::processImage), job, err, parent->getProgressListener() ),
|
||||
ld->startFunc(sigc::bind(sigc::ptr_fun(&rtengine::processImage), job, err, parent->getProgressListener(), options.tunnelMetaData ),
|
||||
sigc::bind(sigc::mem_fun( *this,&EditorPanel::idle_saveImage ),ld,fname,sf,false ));
|
||||
saveimgas->set_sensitive(false);
|
||||
sendtogimp->set_sensitive(false);
|
||||
@@ -929,7 +929,7 @@ void EditorPanel::sendToGimpPressed () {
|
||||
ipc->getParams (&pparams);
|
||||
rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams);
|
||||
ProgressConnector<rtengine::IImage16*> *ld = new ProgressConnector<rtengine::IImage16*>();
|
||||
ld->startFunc(sigc::bind(sigc::ptr_fun(&rtengine::processImage), job, err, parent->getProgressListener() ),
|
||||
ld->startFunc(sigc::bind(sigc::ptr_fun(&rtengine::processImage), job, err, parent->getProgressListener(), options.tunnelMetaData ),
|
||||
sigc::bind(sigc::mem_fun( *this,&EditorPanel::idle_sendToGimp ),ld ));
|
||||
saveimgas->set_sensitive(false);
|
||||
sendtogimp->set_sensitive(false);
|
||||
|
@@ -349,7 +349,7 @@ int processLineParams( int argc, char **argv )
|
||||
}
|
||||
|
||||
// Process image
|
||||
rtengine::IImage16* resultImage = rtengine::processImage (job, errorCode, NULL);
|
||||
rtengine::IImage16* resultImage = rtengine::processImage (job, errorCode, NULL, options.tunnelMetaData);
|
||||
if( !resultImage ){
|
||||
errors++;
|
||||
std::cerr << "Error processing:"<< inputFile << std::endl;
|
||||
|
@@ -123,6 +123,7 @@ void Options::setDefaults () {
|
||||
showFileNames = true;
|
||||
tabbedUI = false;
|
||||
multiDisplayMode = 0;
|
||||
tunnelMetaData = false;
|
||||
|
||||
cutOverlayBrush = std::vector<double> (4);
|
||||
cutOverlayBrush[3] = 0.667;
|
||||
@@ -214,6 +215,7 @@ if (keyFile.has_group ("Output")) {
|
||||
if (keyFile.has_key ("Output", "UsePathTemplate")) saveUsePathTemplate = keyFile.get_boolean("Output", "UsePathTemplate");
|
||||
if (keyFile.has_key ("Output", "LastSaveAsPath")) lastSaveAsPath = keyFile.get_string ("Output", "LastSaveAsPath");
|
||||
if (keyFile.has_key ("Output", "OverwriteOutputFile")) overwriteOutputFile = keyFile.get_boolean("Output", "OverwriteOutputFile");
|
||||
if (keyFile.has_key ("Output", "TunnelMetaData")) tunnelMetaData = keyFile.get_boolean("Output", "TunnelMetaData");
|
||||
}
|
||||
|
||||
if (keyFile.has_group ("Profiles")) {
|
||||
@@ -387,6 +389,7 @@ int Options::saveToFile (Glib::ustring fname) {
|
||||
keyFile.set_boolean ("Output", "UsePathTemplate", saveUsePathTemplate);
|
||||
keyFile.set_string ("Output", "LastSaveAsPath", lastSaveAsPath);
|
||||
keyFile.set_boolean ("Output", "OverwriteOutputFile", overwriteOutputFile);
|
||||
keyFile.set_boolean ("Output", "TunnelMetaData", tunnelMetaData);
|
||||
|
||||
keyFile.set_string ("Profiles", "Directory", profilePath);
|
||||
keyFile.set_string ("Profiles", "RawDefault", defProfRaw);
|
||||
|
@@ -139,7 +139,7 @@ class Options {
|
||||
Glib::ustring sndBatchQueueDone;
|
||||
Glib::ustring sndLngEditProcDone;
|
||||
double sndLngEditProcDoneSecs; // Minimum processing time seconds till the sound is played
|
||||
|
||||
bool tunnelMetaData; // Pass through IPTC and XMP unchanged
|
||||
|
||||
Options ();
|
||||
|
||||
|
@@ -286,6 +286,13 @@ Gtk::Widget* Preferences::getProcParamsPanel () {
|
||||
|
||||
dfconn = darkFrameDir->signal_file_set().connect ( sigc::mem_fun(*this, &Preferences::darkFrameChanged), true);
|
||||
|
||||
Gtk::Frame* fmd = Gtk::manage (new Gtk::Frame (M("PREFERENCES_METADATA")));
|
||||
Gtk::VBox* vbmd = Gtk::manage (new Gtk::VBox ());
|
||||
ckbTunnelMetaData = Gtk::manage (new Gtk::CheckButton (M("PREFERENCES_TUNNELMETADATA")));
|
||||
vbmd->pack_start (*ckbTunnelMetaData, Gtk::PACK_SHRINK, 4);
|
||||
fmd->add (*vbmd);
|
||||
mvbpp->pack_start (*fmd, Gtk::PACK_SHRINK, 4);
|
||||
|
||||
return mvbpp;
|
||||
}
|
||||
|
||||
@@ -816,6 +823,8 @@ void Preferences::storePreferences () {
|
||||
moptions.saveParamsCache = saveParamsCache->get_active ();
|
||||
moptions.paramsLoadLocation = (PPLoadLocation)loadParamsPreference->get_active_row_number ();
|
||||
|
||||
moptions.tunnelMetaData = ckbTunnelMetaData->get_active ();
|
||||
|
||||
moptions.rtSettings.darkFramesPath = darkFrameDir->get_filename();
|
||||
|
||||
int i = 0;
|
||||
@@ -914,6 +923,8 @@ void Preferences::fillPreferences () {
|
||||
saveParamsCache->set_active (moptions.saveParamsCache);
|
||||
loadParamsPreference->set_active (moptions.paramsLoadLocation);
|
||||
|
||||
ckbTunnelMetaData->set_active (moptions.tunnelMetaData);
|
||||
|
||||
if (!moptions.tabbedUI)
|
||||
editorLayout->set_active(moptions.mainNBVertical ? 1 : 0);
|
||||
else
|
||||
|
@@ -111,6 +111,8 @@ class Preferences : public Gtk::Dialog {
|
||||
Gtk::Entry* txtSndLngEditProcDone;
|
||||
Gtk::SpinButton* spbSndLngEditProcDoneSecs;
|
||||
|
||||
Gtk::CheckButton* ckbTunnelMetaData;
|
||||
|
||||
Options moptions;
|
||||
sigc::connection tconn, fconn, usethcon, addc, setc, dfconn;
|
||||
Glib::ustring initialTheme;
|
||||
|
Reference in New Issue
Block a user