New color correction effect based on avfilter selective color (CMYK adjustments)

parent 3e4c0cfb
......@@ -139,4 +139,5 @@ avfilter.ass
avfilter.acompressor
avfilter.aecho
avfilter.agate
avfilter.selectivecolor
......@@ -3,5 +3,5 @@ INSTALL (FILES
acompressor.xml
aecho.xml
agate.xml
selectivecolor.xml
DESTINATION ${DATA_INSTALL_DIR}/kdenlive/effects)
<!DOCTYPE kpartgui>
<effect tag="avfilter.selectivecolor" id="avfilter.selectivecolor">
<name>CMYK adjust (avfilter)</name>
<description>Apply CMYK correction to specific color ranges</description>
<author>libavfilter</author>
<parameter type="cmyk" name="av.reds" default="">
<name>Reds</name>
</parameter>
<parameter type="cmyk" name="av.yellows" default="">
<name>Yellows</name>
</parameter>
<parameter type="cmyk" name="av.greens" default="">
<name>Greens</name>
</parameter>
<parameter type="cmyk" name="av.cyans" default="">
<name>Cyans</name>
</parameter>
<parameter type="cmyk" name="av.blues" default="">
<name>Blues</name>
</parameter>
<parameter type="cmyk" name="av.magentas" default="">
<name>Magentas</name>
</parameter>
<parameter type="cmyk" name="av.whites" default="">
<name>Whites</name>
</parameter>
<parameter type="cmyk" name="av.neutrals" default="">
<name>Neutrals</name>
</parameter>
<parameter type="cmyk" name="av.blacks" default="">
<name>Blacks</name>
</parameter>
<parameter type="list" name="av.correction_method" default="0" paramlist="0;1">
<paramlistdisplay>Absolute,Relative</paramlistdisplay>
<name>Correction Method</name>
</parameter>
</effect>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
<data name="effects" version="0">
<group list="lift_gamma_gain,brightness,gamma,frei0r.colgate,frei0r.balanc0r,frei0r.brightness,frei0r.levels,frei0r.three_point_balance,frei0r.curves,frei0r.coloradj_RGB,frei0r.sopsat,frei0r.bezier_curves">
<group list="lift_gamma_gain,brightness,gamma,frei0r.colgate,frei0r.balanc0r,frei0r.brightness,frei0r.levels,frei0r.three_point_balance,frei0r.curves,frei0r.coloradj_RGB,frei0r.sopsat,frei0r.bezier_curves,avfilter.selectivecolor">
<text>Colour correction</text>
</group>
<group list="invert,sepia,tcolor,greyscale,frei0r.B,frei0r.G,frei0r.R,frei0r.contrast0r,frei0r.saturat0r,frei0r.tint0r,frei0r.primaries,chroma_hold,frei0r.colorize,frei0r.equaliz0r,frei0r.hueshift0r,frei0r.luminance,lumaliftgaingamma">
......
......@@ -204,6 +204,7 @@ ki18n_wrap_ui(kdenlive_UIS
ui/cutjobdialog_ui.ui
ui/scenecutdialog_ui.ui
ui/gradientedit_ui.ui
ui/selectivecolor_ui.ui
)
if(BUILD_JogShuttle)
......
......@@ -25,5 +25,6 @@ set(kdenlive_SRCS
effectstack/widgets/colorwheel.cpp
effectstack/widgets/lumaliftgain.cpp
effectstack/widgets/keyframeimport.cpp
effectstack/widgets/selectivecolor.cpp
PARENT_SCOPE)
......@@ -789,7 +789,6 @@ void EffectStackView2::setActiveKeyframe(int frame)
void EffectStackView2::slotDeleteGroup(QDomDocument doc)
{
ClipItem * clip = NULL;
int ix = -1;
if (m_status == MASTER_CLIP) {
......
......@@ -31,6 +31,7 @@
#include "widgets/bezier/beziersplinewidget.h"
#include "effectstack/widgets/lumaliftgain.h"
#include "effectstack/widgets/animationwidget.h"
#include "effectstack/widgets/selectivecolor.h"
#include "kdenlivesettings.h"
#include "mainwindow.h"
......@@ -195,6 +196,11 @@ ParameterContainer::ParameterContainer(const QDomElement &effect, const ItemInfo
m_vbox->addWidget(gainWidget);
m_valueItems[effect.attribute(QStringLiteral("id"))] = gainWidget;
connect(gainWidget, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
} else if (effect.attribute(QStringLiteral("id")) == QLatin1String("avfilter.selectivecolor")) {
SelectiveColor *cmykAdjust = new SelectiveColor(effect);
connect(cmykAdjust, SIGNAL(valueChanged()), this, SLOT(slotCollectAllParameters()));
m_vbox->addWidget(cmykAdjust);
m_valueItems[effect.attribute(QStringLiteral("id"))] = cmykAdjust;
}
else for (int i = 0; i < namenode.count() ; ++i) {
QDomElement pa = namenode.item(i).toElement();
......@@ -927,6 +933,12 @@ void ParameterContainer::slotCollectAllParameters()
emit parameterChanged(oldparam, m_effect, m_effect.attribute(QStringLiteral("kdenlive_ix")).toInt());
return;
}
if (m_effect.attribute(QStringLiteral("tag")) == QLatin1String("avfilter.selectivecolor")) {
SelectiveColor *cmykAdjust = static_cast<SelectiveColor*>(m_valueItems.value(m_effect.attribute(QStringLiteral("id"))));
cmykAdjust->updateEffect(m_effect);
emit parameterChanged(oldparam, m_effect, m_effect.attribute(QStringLiteral("kdenlive_ix")).toInt());
return;
}
QDomNodeList namenode = m_effect.elementsByTagName(QStringLiteral("parameter"));
......
......@@ -49,7 +49,7 @@ private:
public slots:
void slotColorModified(const QColor &color);
private slots:
/** @brief Updates the different color choosing options to have all selected @param color. */
void setColor(const QColor &color);
......
......@@ -88,15 +88,15 @@ void LumaLiftGain::updateEffect(QDomElement &effect)
values.insert(QStringLiteral("lift_r"), lift.redF());
values.insert(QStringLiteral("lift_g"), lift.greenF());
values.insert(QStringLiteral("lift_b"), lift.blueF());
values.insert(QStringLiteral("gamma_r"), gamma.redF() * GAMMA_FACTOR);
values.insert(QStringLiteral("gamma_g"), gamma.greenF() * GAMMA_FACTOR);
values.insert(QStringLiteral("gamma_b"), gamma.blueF() * GAMMA_FACTOR);
values.insert(QStringLiteral("gain_r"), gain.redF() * GAIN_FACTOR);
values.insert(QStringLiteral("gain_g"), gain.greenF() * GAIN_FACTOR);
values.insert(QStringLiteral("gain_b"), gain.blueF() * GAIN_FACTOR);
QDomNodeList namenode = effect.childNodes();
for (int i = 0; i < namenode.count() ; ++i) {
QDomElement pa = namenode.item(i).toElement();
......@@ -104,6 +104,6 @@ void LumaLiftGain::updateEffect(QDomElement &effect)
if (values.contains(pa.attribute(QStringLiteral("name")))) {
pa.setAttribute(QStringLiteral("value"), (int) (values.value(pa.attribute(QStringLiteral("name"))) * m_locale.toDouble(pa.attribute(QStringLiteral("factor"), QStringLiteral("1")))));
}
}
}
}
/*
Copyright (C) 2016 Jean-Baptiste Mardelle <jb@kdenlive.org>
This file is part of Kdenlive. See www.kdenlive.org.
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 of
the License or (at your option) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "effectstack/widgets/selectivecolor.h"
#include "utils/KoIconUtils.h"
#include <QLabel>
#include <QComboBox>
#include <QDebug>
#include <KLocalizedString>
SelectiveColor::SelectiveColor(const QDomElement effect, QWidget* parent) :
QWidget(parent)
{
setupUi(this);
QDomNodeList namenode = effect.elementsByTagName(QStringLiteral("parameter"));
for (int i = 0; i < namenode.count() ; ++i) {
QDomElement pa = namenode.item(i).toElement();
QDomElement na = pa.firstChildElement(QStringLiteral("name"));
QString type = pa.attribute(QStringLiteral("type"));
QString paramName = na.isNull() ? pa.attribute(QStringLiteral("name")) : i18n(na.text().toUtf8().data());
if (type == QLatin1String("cmyk"))
addParam(pa, paramName);
else if (pa.attribute(QStringLiteral("name")) == QLatin1String("av.correction_method")) {
if (pa.attribute(QStringLiteral("value")).toInt() == 1)
relative->setChecked(true);
else
absolute->setChecked(true);
}
}
connect(range, SIGNAL(currentIndexChanged(int)), this, SLOT(updateValues()));
connect(slider_black, SIGNAL(valueChanged(int)), this, SLOT(effectChanged()));
connect(slider_yell, SIGNAL(valueChanged(int)), this, SLOT(effectChanged()));
connect(slider_mag, SIGNAL(valueChanged(int)), this, SLOT(effectChanged()));
connect(slider_cyan, SIGNAL(valueChanged(int)), this, SLOT(effectChanged()));
connect(relative, SIGNAL(toggled(bool)), this, SLOT(effectChanged()));
updateValues();
}
void SelectiveColor::addParam(QDomElement &effect, QString name)
{
QString tag = effect.attribute(QStringLiteral("name"));
if (name.isEmpty()) {
name = tag;
}
QString value = effect.attribute(QStringLiteral("value"));
if (value.isEmpty())
value = effect.attribute(QStringLiteral("default"));
QIcon icon;
if (!value.isEmpty())
icon = KoIconUtils::themedIcon(QStringLiteral("dialog-ok-apply"));
range->addItem(icon, name, QStringList() << effect.attribute(QStringLiteral("name")) << value);
}
void SelectiveColor::updateValues()
{
QStringList values = range->currentData().toStringList();
if (values.count() < 2) {
// Something is wrong, abort
return;
}
blockSignals(true);
spin_black->setValue(0);
spin_yell->setValue(0);
spin_mag->setValue(0);
spin_cyan->setValue(0);
QStringList vals = values.at(1).split(QStringLiteral(" "));
switch (vals.count()) {
case 4 :
spin_black->setValue(vals.at(3).toDouble() * 100);
case 3:
spin_yell->setValue(vals.at(2).toDouble() * 100);
case 2:
spin_mag->setValue(vals.at(1).toDouble() * 100);
case 1:
spin_cyan->setValue(vals.at(0).toDouble() * 100);
break;
default:
break;
}
blockSignals(false);
}
void SelectiveColor::effectChanged()
{
int vBlack = spin_black->value();
int vYell = spin_yell->value();
int vMag = spin_mag->value();
int vCyan = spin_cyan->value();
QString result;
if (vBlack == 0 && vYell == 0 && vMag == 0 && vCyan == 0) {
// default empty val
} else {
result = QString("%1 %2 %3 %4").arg(vCyan / 100.0).arg(vMag / 100.0).arg(vYell / 100.0).arg(vBlack / 100.0);
}
QStringList values = range->currentData().toStringList();
if (values.isEmpty()) {
// Something is wrong, abort
return;
}
if (values.at(1).isEmpty() && !result.isEmpty()) {
range->setItemIcon(range->currentIndex(), KoIconUtils::themedIcon(QStringLiteral("dialog-ok-apply")));
} else if (!values.at(1).isEmpty() && result.isEmpty()) {
range->setItemIcon(range->currentIndex(), QIcon());
}
QStringList newData = QStringList() << values.at(0) << result;
range->setItemData(range->currentIndex(), newData);
emit valueChanged();
}
void SelectiveColor::updateEffect(QDomElement &effect)
{
QMap <QString, QString> values;
for (int i = 0; i < range->count(); i++) {
QStringList vals = range->itemData(i).toStringList();
values.insert(vals.at(0), vals.at(1));
}
QDomNodeList namenode = effect.childNodes();
for (int i = 0; i < namenode.count() ; ++i) {
QDomElement pa = namenode.item(i).toElement();
if (pa.tagName() != QLatin1String("parameter")) continue;
QString paramName = pa.attribute(QStringLiteral("name"));
if (values.contains(paramName)) {
pa.setAttribute(QStringLiteral("value"), values.value(paramName));
} else if (paramName == QLatin1String("av.correction_method")) {
pa.setAttribute(QStringLiteral("value"), relative->isChecked() ? QStringLiteral("1") : QStringLiteral("0"));
}
}
}
/*
Copyright (C) 2016 Jean-Baptiste Mardelle <jb@kdenlive.org>
This file is part of Kdenlive. See www.kdenlive.org.
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 of
the License or (at your option) version 3 or any later version
accepted by the membership of KDE e.V. (or its successor approved
by the membership of KDE e.V.), which shall act as a proxy
defined in Section 14 of version 3 of the license.
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.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SELECTIVECOLORWIDGET_H
#define SELECTIVECOLORWIDGET_H
#include "ui_selectivecolor_ui.h"
#include <QWidget>
#include <QDomNodeList>
#include <QLocale>
/**
* @class SelectiveColor
* @brief Provides options to adjust CMYK factor of a color range.
* @author Jean-Baptiste Mardelle
*/
class SelectiveColor : public QWidget, public Ui::SelectiveColor
{
Q_OBJECT
public:
/** @brief Sets up the widget.
* @param text (optional) What the color will be used for
* @param color (optional) initial color
* @param alphaEnabled (optional) Should transparent colors be enabled */
explicit SelectiveColor(const QDomElement effect, QWidget* parent = 0);
void addParam(QDomElement &effect, QString name);
void updateEffect(QDomElement &effect);
private:
QLocale m_locale;
private slots:
void updateValues();
void effectChanged();
signals:
/** @brief Emitted whenever a different color was choosen. */
void valueChanged();
};
#endif
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SelectiveColor</class>
<widget class="QWidget" name="SelectiveColor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>413</width>
<height>455</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Preset</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QComboBox" name="preset"/>
</item>
<item>
<widget class="QToolButton" name="add_preset">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="remove_preset">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Color range</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="range"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Cyan</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSlider" name="slider_cyan">
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QSpinBox" name="spin_cyan">
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Magenta</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSlider" name="slider_mag">
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QSpinBox" name="spin_mag">
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Yellow</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSlider" name="slider_yell">
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="4" column="2">
<widget class="QSpinBox" name="spin_yell">
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Black</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSlider" name="slider_black">
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QSpinBox" name="spin_black">
<property name="suffix">
<string>%</string>
</property>
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="6" column="0" colspan="3">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QRadioButton" name="absolute">
<property name="text">
<string>Absol&amp;ute</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="relative">
<property name="text">
<string>Re&amp;lative</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>122</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>slider_cyan</sender>
<signal>valueChanged(int)</signal>
<receiver>spin_cyan</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>212</x>
<y>121</y>
</hint>
<hint type="destinationlabel">
<x>360</x>
<y>121</y>
</hint>
</hints>
</connection>
<connection>
<sender>spin_cyan</sender>
<signal>valueChanged(int)</signal>
<receiver>slider_cyan</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>360</x>
<y>121</y>
</hint>
<hint type="destinationlabel">
<x>212</x>
<y>121</y>
</hint>
</hints>
</connection>
<connection>
<sender>slider_mag</sender>
<signal>valueChanged(int)</signal>
<receiver>spin_mag</receiver>
<slot>setValue(int)</slot>
<hints>
<hint type="sourcelabel">
<x>212</x>
<y>167</y>
</hint>
<hint type="destinationlabel">
<x>360</x>