diff --git a/core/libs/dimg/filters/film/filmfilter.cpp b/core/libs/dimg/filters/film/filmfilter.cpp index 2e66b01c47..89149f7e64 100644 --- a/core/libs/dimg/filters/film/filmfilter.cpp +++ b/core/libs/dimg/filters/film/filmfilter.cpp @@ -1,429 +1,467 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-02-05 * Description : film color negative inverter filter * * Copyright (C) 2012 by Matthias Welwarsky * * This program 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 2, or (at your option) * any later version. * * This program 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. * * ============================================================ */ #include "filmfilter_p.h" // C++ includes #include // Qt includes #include // KDE includes #include // Local includes #include "invertfilter.h" namespace Digikam { FilmContainer::FilmContainer() : d(QSharedPointer(new Private)) { } FilmContainer::FilmContainer(CNFilmProfile profile, double gamma, bool sixteenBit) : d(QSharedPointer(new Private)) { d->gamma = gamma; d->sixteenBit = sixteenBit; d->whitePoint = DColor(QColor("white"), sixteenBit); setCNType(profile); } void FilmContainer::setWhitePoint(const DColor& wp) { d->whitePoint = wp; } DColor FilmContainer::whitePoint() const { return d->whitePoint; } void FilmContainer::setExposure(double strength) { d->exposure = strength; } double FilmContainer::exposure() const { return d->exposure; } void FilmContainer::setSixteenBit(bool val) { d->sixteenBit = val; } void FilmContainer::setGamma(double val) { d->gamma = val; } double FilmContainer::gamma() const { return d->gamma; } void FilmContainer::setCNType(CNFilmProfile profile) { d->cnType = profile; switch (profile) { default: d->profile = FilmProfile(1.0, 1.0, 1.0); d->cnType = CNNeutral; break; + case CNKodakGold100: d->profile = FilmProfile(1.53, 2.00, 2.40); // check break; + case CNKodakGold200: d->profile = FilmProfile(1.53, 2.00, 2.40); // check break; + case CNKodakEktar100: d->profile = FilmProfile(1.40, 1.85, 2.34); break; + case CNKodakProfessionalPortra160NC: d->profile = FilmProfile(1.49, 1.96, 2.46); // check break; + case CNKodakProfessionalPortra160VC: d->profile = FilmProfile(1.56, 2.03, 2.55); // check break; + case CNKodakProfessionalPortra400NC: d->profile = FilmProfile(1.69, 2.15, 2.69); // check break; + case CNKodakProfessionalPortra400VC: d->profile = FilmProfile(1.78, 2.21, 2.77); // check break; + case CNKodakProfessionalPortra800Box: d->profile = FilmProfile(1.89, 2.29, 2.89); // check break; + case CNKodakProfessionalPortra800P1: d->profile = FilmProfile(1.53, 2.01, 2.46); // check break; + case CNKodakProfessionalPortra800P2: d->profile = FilmProfile(1.74, 2.22, 2.64); // check break; + case CNKodakProfessionalNewPortra160: d->profile = FilmProfile(1.41, 1.88, 2.32); break; + case CNKodakProfessionalNewPortra400: d->profile = FilmProfile(1.69, 2.15, 2.68); // check break; + case CNKodakFarbwelt100: d->profile = FilmProfile(1.86, 2.33, 2.77); // fix, check break; + case CNKodakFarbwelt200: d->profile = FilmProfile(1.55, 2.03, 2.42); // check break; + case CNKodakFarbwelt400: d->profile = FilmProfile(1.93, 2.43, 2.95); // fix, check break; + case CNKodakRoyalGold400: d->profile = FilmProfile(2.24, 2.76, 3.27); // fix, check break; + case CNAgfaphotoVistaPlus200: d->profile = FilmProfile(1.70, 2.13, 2.50); break; + case CNAgfaphotoVistaPlus400: d->profile = FilmProfile(1.86, 2.35, 2.67); // fix, check break; + case CNFujicolorPro160S: d->profile = FilmProfile(1.73, 2.27, 2.53); // fix, check break; + case CNFujicolorPro160C: d->profile = FilmProfile(1.96, 2.46, 2.69); // fix, check break; + case CNFujicolorNPL160: d->profile = FilmProfile(2.13, 2.36, 2.92); // fix, check break; + case CNFujicolorPro400H: d->profile = FilmProfile(1.95, 2.37, 2.62); // fix, check break; + case CNFujicolorPro800Z: d->profile = FilmProfile(2.12, 2.37, 2.56); // fix, check break; + case CNFujicolorSuperiaReala: d->profile = FilmProfile(1.79, 2.14, 2.49); // check break; + case CNFujicolorSuperia100: d->profile = FilmProfile(2.02, 2.46, 2.81); // fix, check break; + case CNFujicolorSuperia200: d->profile = FilmProfile(2.11, 2.50, 2.79); // check break; + case CNFujicolorSuperiaXtra400: d->profile = FilmProfile(2.11, 2.58, 2.96); // check break; + case CNFujicolorSuperiaXtra800: d->profile = FilmProfile(2.44, 2.83, 3.18); // fix, check break; + case CNFujicolorTrueDefinition400: d->profile = FilmProfile(1.93, 2.21, 2.39); // fix, check break; + case CNFujicolorSuperia1600: d->profile = FilmProfile(2.35, 2.68, 2.96); // fix, check break; } } FilmContainer::CNFilmProfile FilmContainer::cnType() const { return d->cnType; } void FilmContainer::setApplyBalance(bool val) { d->applyBalance = val; } bool FilmContainer::applyBalance() const { return d->applyBalance; } int FilmContainer::whitePointForChannel(int ch) const { int max = d->sixteenBit ? 65535 : 255; switch (ch) { case RedChannel: return d->whitePoint.red(); case GreenChannel: return d->whitePoint.green(); case BlueChannel: return d->whitePoint.blue(); default: return max; } // not reached return max; } double FilmContainer::blackPointForChannel(int ch) const { - if (ch == LuminosityChannel || ch == AlphaChannel) + if ((ch == LuminosityChannel) || (ch == AlphaChannel)) + { return 0.0; + } return pow(10, -d->profile.dmax(ch)); } double FilmContainer::gammaForChannel(int ch) const { int max = d->sixteenBit ? 65535 : 255; - if (ch == GreenChannel || ch == BlueChannel) + if ((ch == GreenChannel) || (ch == BlueChannel)) { double bpc = blackPointForChannel(ch)*d->exposure; double wpc = (double)whitePointForChannel(ch)/(double)max; double bpr = blackPointForChannel(RedChannel)*d->exposure; double wpr = (double)whitePointForChannel(RedChannel)/(double)max; - return log10( bpc / wpc ) / log10( bpr / wpr ); + return (log10(bpc / wpc) / log10(bpr / wpr)); } return 1.0; } LevelsContainer FilmContainer::toLevels() const { LevelsContainer l; int max = d->sixteenBit ? 65535 : 255; for (int i = LuminosityChannel ; i <= AlphaChannel ; ++i) { l.lInput[i] = blackPointForChannel(i) * max * d->exposure; l.hInput[i] = whitePointForChannel(i) * d->profile.wp(i); l.lOutput[i] = 0; l.hOutput[i] = max; if (d->applyBalance) + { l.gamma[i] = gammaForChannel(i); + } else + { l.gamma[i] = 1.0; + } } return l; } CBContainer FilmContainer::toCB() const { CBContainer cb; cb.red = d->profile.balance(RedChannel); cb.green = d->profile.balance(GreenChannel); cb.blue = d->profile.balance(BlueChannel); cb.gamma = 1.0; return cb; } QList FilmContainer::profileItemList(QListWidget* view) { QList itemList; QMap::ConstIterator it; for (it = profileMap.constBegin() ; it != profileMap.constEnd() ; ++it) + { itemList << new ListItem(it.value(), view, (CNFilmProfile)it.key()); + } return itemList; } QMap FilmContainer::profileMapInitializer() { QMap profileMap; profileMap[CNNeutral] = QLatin1String("Neutral"); profileMap[CNKodakGold100] = QLatin1String("Kodak Gold 100"); profileMap[CNKodakGold200] = QLatin1String("Kodak Gold 200"); profileMap[CNKodakProfessionalNewPortra160] = QLatin1String("Kodak Professional New Portra 160"); profileMap[CNKodakProfessionalNewPortra400] = QLatin1String("Kodak Professional New Portra 400"); profileMap[CNKodakEktar100] = QLatin1String("Kodak Ektar 100"); profileMap[CNKodakFarbwelt100] = QLatin1String("Kodak Farbwelt 100"); profileMap[CNKodakFarbwelt200] = QLatin1String("Kodak Farbwelt 200"); profileMap[CNKodakFarbwelt400] = QLatin1String("Kodak Farbwelt 400"); profileMap[CNKodakProfessionalPortra160NC] = QLatin1String("Kodak Professional Portra 160NC"); profileMap[CNKodakProfessionalPortra160VC] = QLatin1String("Kodak Professional Portra 160VC"); profileMap[CNKodakProfessionalPortra400NC] = QLatin1String("Kodak Professional Portra 400NC"); profileMap[CNKodakProfessionalPortra400VC] = QLatin1String("Kodak Professional Portra 400VC"); profileMap[CNKodakProfessionalPortra800Box] = QLatin1String("Kodak Professional Portra 800 (Box Speed"); profileMap[CNKodakProfessionalPortra800P1] = QLatin1String("Kodak Professional Portra 800 (Push 1 stop"); profileMap[CNKodakProfessionalPortra800P2] = QLatin1String("Kodak Professional Portra 800 (Push 2 stop"); profileMap[CNKodakRoyalGold400] = QLatin1String("Kodak Royal Gold 400"); profileMap[CNAgfaphotoVistaPlus200] = QLatin1String("Agfaphoto Vista Plus 200"); profileMap[CNAgfaphotoVistaPlus400] = QLatin1String("Agfaphoto Vista Plus 400"); profileMap[CNFujicolorPro160S] = QLatin1String("Fujicolor Pro 160S"); profileMap[CNFujicolorPro160C] = QLatin1String("Fujicolor Pro 160C"); profileMap[CNFujicolorNPL160] = QLatin1String("Fujicolor NPL 160"); profileMap[CNFujicolorPro400H] = QLatin1String("Fujicolor Pro 400H"); profileMap[CNFujicolorPro800Z] = QLatin1String("Fujicolor Pro 800Z"); profileMap[CNFujicolorSuperiaReala] = QLatin1String("Fujicolor Superia Reala"); profileMap[CNFujicolorSuperia100] = QLatin1String("Fujicolor Superia 100"); profileMap[CNFujicolorSuperia200] = QLatin1String("Fujicolor Superia 200"); profileMap[CNFujicolorSuperiaXtra400] = QLatin1String("Fujicolor Superia X-Tra 400"); profileMap[CNFujicolorSuperiaXtra800] = QLatin1String("Fujicolor Superia X-Tra 800"); profileMap[CNFujicolorTrueDefinition400] = QLatin1String("Fujicolor Superia True Definition 400"); profileMap[CNFujicolorSuperia1600] = QLatin1String("Fujicolor Superia 1600"); return profileMap; } const QMap FilmContainer::profileMap = FilmContainer::profileMapInitializer(); // ------------------------------------------------------------------ FilmFilter::FilmFilter(QObject* const parent) : DImgThreadedFilter(parent, QLatin1String("FilmFilter")), d(new Private()) { d->film = FilmContainer(); initFilter(); } FilmFilter::FilmFilter(DImg* const orgImage, QObject* const parent, const FilmContainer& settings) : DImgThreadedFilter(orgImage, parent, QLatin1String("FilmFilter")), d(new Private()) { d->film = settings; initFilter(); } FilmFilter::~FilmFilter() { cancelFilter(); delete d; } QString FilmFilter::DisplayableName() { return QString::fromUtf8(I18N_NOOP("Color Negative Inverter")); } void FilmFilter::filterImage() { DImg tmpLevel; DImg tmpGamma; DImg tmpInv; LevelsContainer l = d->film.toLevels(); CBContainer cb = d->film.toCB(); CBContainer gamma; // level the image first, this removes the orange mask and corrects // colors according to the density ranges of the film profile LevelsFilter(l, this, m_orgImage, tmpLevel, 0, 40); // in case of a linear raw scan, gamma needs to be // applied after leveling the image, otherwise the image will // look too bright. The standard value is 2.2, but 1.8 is also // frequently found in literature gamma.gamma = d->film.gamma(); CBFilter(gamma, this, tmpLevel, tmpGamma, 40, 80); // invert the image to have a positive image InvertFilter(this, tmpGamma, tmpInv, 80, 100); m_destImage = tmpInv; postProgress(100); } FilterAction FilmFilter::filterAction() { FilterAction action(FilterIdentifier(), CurrentVersion()); action.setDisplayableName(DisplayableName()); action.addParameter(QLatin1String("CNType"), d->film.cnType()); action.addParameter(QLatin1String("ProfileName"), FilmContainer::profileMap[d->film.cnType()]); action.addParameter(QLatin1String("Exposure"), d->film.exposure()); action.addParameter(QLatin1String("Gamma"), d->film.gamma()); action.addParameter(QLatin1String("ApplyColorBalance"), d->film.applyBalance()); action.addParameter(QLatin1String("WhitePointRed"), d->film.whitePoint().red()); action.addParameter(QLatin1String("WhitePointGreen"), d->film.whitePoint().green()); action.addParameter(QLatin1String("WhitePointBlue"), d->film.whitePoint().blue()); action.addParameter(QLatin1String("WhitePointAlpha"), d->film.whitePoint().alpha()); action.addParameter(QLatin1String("WhitePointSixteenBit"), d->film.whitePoint().sixteenBit()); return action; } void FilmFilter::readParameters(const FilterAction& action) { double red = action.parameter(QLatin1String("WhitePointRed")).toDouble(); double green = action.parameter(QLatin1String("WhitePointGreen")).toDouble(); double blue = action.parameter(QLatin1String("WhitePointBlue")).toDouble(); double alpha = action.parameter(QLatin1String("WhitePointAlpha")).toDouble(); bool sb = action.parameter(QLatin1String("WhitePointSixteenBit")).toBool(); bool balance = action.parameter(QLatin1String("ApplyColorBalance")).toBool(); d->film.setWhitePoint(DColor(red, green, blue, alpha, sb)); d->film.setExposure(action.parameter(QLatin1String("Exposure")).toDouble()); d->film.setGamma(action.parameter(QLatin1String("Gamma")).toDouble()); d->film.setCNType((FilmContainer::CNFilmProfile)(action.parameter(QLatin1String("CNType")).toInt())); d->film.setApplyBalance(balance); } } // namespace Digikam diff --git a/core/libs/dimg/filters/film/filmfilter.h b/core/libs/dimg/filters/film/filmfilter.h index 372124eedb..f13cc7671c 100644 --- a/core/libs/dimg/filters/film/filmfilter.h +++ b/core/libs/dimg/filters/film/filmfilter.h @@ -1,178 +1,178 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-02-05 * Description : film color negative inverter filter * * Copyright (C) 2012 by Matthias Welwarsky * * This program 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 2, or (at your option) * any later version. * * This program 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. * * ============================================================ */ #ifndef DIGIKAM_FILM_FILTER_H #define DIGIKAM_FILM_FILTER_H // Qt includes #include #include #include #include // Local includes #include "dimgthreadedfilter.h" #include "levelsfilter.h" #include "cbfilter.h" namespace Digikam { class DIGIKAM_EXPORT FilmContainer { public: enum CNFilmProfile { CNNeutral = 0, CNKodakGold100, CNKodakGold200, CNKodakEktar100, CNKodakProfessionalPortra160NC, CNKodakProfessionalPortra160VC, CNKodakProfessionalPortra400NC, CNKodakProfessionalPortra400VC, CNKodakProfessionalPortra800Box, CNKodakProfessionalPortra800P1, CNKodakProfessionalPortra800P2, CNKodakProfessionalNewPortra160, CNKodakProfessionalNewPortra400, CNKodakFarbwelt100, CNKodakFarbwelt200, CNKodakFarbwelt400, CNKodakRoyalGold400, CNAgfaphotoVistaPlus200, CNAgfaphotoVistaPlus400, CNFujicolorPro160S, CNFujicolorPro160C, CNFujicolorNPL160, CNFujicolorPro400H, CNFujicolorPro800Z, CNFujicolorSuperiaReala, CNFujicolorSuperia100, CNFujicolorSuperia200, CNFujicolorSuperiaXtra400, CNFujicolorSuperiaXtra800, CNFujicolorTrueDefinition400, CNFujicolorSuperia1600 }; class ListItem : public QListWidgetItem { public: explicit ListItem(const QString& text, QListWidget* const parent, CNFilmProfile type) : QListWidgetItem(text, parent, type + QListWidgetItem::UserType) { } }; explicit FilmContainer(); explicit FilmContainer(CNFilmProfile profile, double gamma, bool sixteenBit); void setWhitePoint(const DColor& wp); - DColor whitePoint() const; + DColor whitePoint() const; void setExposure(double strength); - double exposure() const; + double exposure() const; void setSixteenBit(bool val); void setGamma(double val); - double gamma() const; + double gamma() const; void setCNType(CNFilmProfile profile); - CNFilmProfile cnType() const; + CNFilmProfile cnType() const; void setApplyBalance(bool val); - bool applyBalance() const; + bool applyBalance() const; - LevelsContainer toLevels() const; - CBContainer toCB() const; + LevelsContainer toLevels() const; + CBContainer toCB() const; static const QMap profileMap; static QList profileItemList(QListWidget* const view); private: - int whitePointForChannel(int channel) const; - double blackPointForChannel(int ch) const; - double gammaForChannel(int ch) const; + int whitePointForChannel(int channel) const; + double blackPointForChannel(int ch) const; + double gammaForChannel(int ch) const; static QMap profileMapInitializer(); private: class Private; QSharedPointer d; }; // --------------------------------------------------------------------------------------------------- class DIGIKAM_EXPORT FilmFilter: public DImgThreadedFilter { public: explicit FilmFilter(QObject* const parent=nullptr); explicit FilmFilter(DImg* const orgImage, QObject* const parent=nullptr, const FilmContainer& settings=FilmContainer()); virtual ~FilmFilter(); static QString FilterIdentifier() { return QLatin1String("digikam:FilmFilter"); } static QString DisplayableName(); static QList SupportedVersions() { return QList() << 1; } static int CurrentVersion() { return 1; } - virtual QString filterIdentifier() const override + virtual QString filterIdentifier() const override { return FilterIdentifier(); } - virtual FilterAction filterAction() override; - virtual void readParameters(const FilterAction& action) override; + virtual FilterAction filterAction() override; + virtual void readParameters(const FilterAction& action) override; private: - void filterImage() override; + void filterImage() override; private: class Private; Private* d; }; } // namespace Digikam #endif // DIGIKAM_FILM_FILTER_H diff --git a/core/libs/dimg/filters/film/filmfilter_p.h b/core/libs/dimg/filters/film/filmfilter_p.h index 496ffe24e2..162bbad10c 100644 --- a/core/libs/dimg/filters/film/filmfilter_p.h +++ b/core/libs/dimg/filters/film/filmfilter_p.h @@ -1,164 +1,187 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2012-02-05 * Description : film color negative inverter filter * * Copyright (C) 2012 by Matthias Welwarsky * * This program 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 2, or (at your option) * any later version. * * This program 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. * * ============================================================ */ #ifndef DIGIKAM_FILM_FILTER_PRIVATE_H #define DIGIKAM_FILM_FILTER_PRIVATE_H #include "filmfilter.h" namespace Digikam { class DColor; class Q_DECL_HIDDEN FilmProfile { public: explicit FilmProfile(double rdm=0.0, double gdm=0.0, double bdm=0.0) : redDmax(rdm), greenDmax(gdm), blueDmax(bdm), rBalance(1.0), gBalance(1.0), bBalance(1.0), wpRed(1.0), wpGreen(1.0), wpBlue(1.0) { } double dmax(int channel) const { switch (channel) { - case RedChannel: return redDmax; - case GreenChannel: return greenDmax; - case BlueChannel: return blueDmax; - default: return 0.0; + case RedChannel: + return redDmax; + + case GreenChannel: + return greenDmax; + + case BlueChannel: + return blueDmax; + + default: + return 0.0; } } FilmProfile& setBalance(double rB, double gB, double bB) { rBalance = rB; gBalance = gB; bBalance = bB; + return *this; } double balance(int channel) const { switch (channel) { - case RedChannel: return rBalance; - case GreenChannel: return gBalance; - case BlueChannel: return bBalance; - default: return 1.0; + case RedChannel: + return rBalance; + + case GreenChannel: + return gBalance; + + case BlueChannel: + return bBalance; + + default: + return 1.0; } } FilmProfile& setWp(double rWp, double gWp, double bWp) { wpRed = rWp; wpGreen = gWp; wpBlue = bWp; + return *this; } double wp(int channel) const { switch (channel) { - case RedChannel: return wpRed; - case GreenChannel: return wpGreen; - case BlueChannel: return wpBlue; - default: return 1.0; + case RedChannel: + return wpRed; + + case GreenChannel: + return wpGreen; + + case BlueChannel: + return wpBlue; + + default: + return 1.0; } } private: double redDmax; double greenDmax; double blueDmax; double rBalance; double gBalance; double bBalance; double wpRed; double wpGreen; double wpBlue; }; // -------------------------------------------------------------------------------------------------------- class Q_DECL_HIDDEN FilmContainer::Private { public: explicit Private() : gamma(1.0), exposure(1.0), sixteenBit(false), profile(FilmProfile(1.0, 1.0, 1.0)), cnType(CNNeutral), whitePoint(DColor(QColor("white"), false)), applyBalance(true) { } ~Private() { } double gamma; double exposure; bool sixteenBit; FilmProfile profile; CNFilmProfile cnType; DColor whitePoint; bool applyBalance; }; // ------------------------------------------------------------------ class Q_DECL_HIDDEN FilmFilter::Private { public: explicit Private() { } ~Private() { } FilmContainer film; }; } // namespace Digikam #endif // DIGIKAM_FILM_FILTER_PRIVATE_H