Commit fc7bc4ce authored by Jean-Baptiste Mardelle's avatar Jean-Baptiste Mardelle
Browse files

Fix fade to alpha broken with MLT-7.

Related to #1171
parent 659a189a
......@@ -9,13 +9,7 @@
<parameter type="fixed" name="in" max="0" min="0" default="0">
<name>In</name>
</parameter>
<parameter type="fixed" name="start" max="0" min="0" default="0">
<name>Start</name>
</parameter>
<parameter type="fixed" name="end" max="1" min="1" default="1">
<name>End</name>
</parameter>
<parameter type="switch" name="alpha" default="-1" min="-1" max="1">
<parameter type="multiswitch" name="level&#10;alpha" default="1&#10;0=0;-1=1" min="1&#10;0=0;-1=1" max="0=0;-1=1&#10;1">
<name>Fade from Black</name>
</parameter>
</effect>
......@@ -9,13 +9,7 @@
<parameter type="fixed" name="out" max="99999" min="0" default="%out">
<name>Out</name>
</parameter>
<parameter type="fixed" name="start" max="1" min="1" default="1">
<name>Start</name>
</parameter>
<parameter type="fixed" name="end" max="0" min="0" default="0">
<name>End</name>
</parameter>
<parameter type="switch" name="alpha" default="-1" min="-1" max="1">
<parameter type="multiswitch" name="level&#10;alpha" value="1&#10;0=1;-1=0" min="1&#10;0=1;-1=0" max="0=1;-1=0&#10;1">
<name>Fade to Black</name>
</parameter>
</effect>
......@@ -20,6 +20,7 @@ set(kdenlive_SRCS
assets/view/widgets/boolparamwidget.cpp
assets/view/widgets/buttonparamwidget.cpp
assets/view/widgets/switchparamwidget.cpp
assets/view/widgets/multiswitchparamwidget.cpp
assets/view/widgets/urllistparamwidget.cpp
assets/view/widgets/urlparamwidget.cpp
assets/view/widgets/doubleparamwidget.cpp
......
......@@ -45,11 +45,40 @@ AssetCommand::AssetCommand(const std::shared_ptr<AssetParameterModel> &model, co
void AssetCommand::undo()
{
if (m_name.contains(QLatin1Char('\n'))) {
// Check if it is a multi param
auto type = m_model->data(m_index, AssetParameterModel::TypeRole).value<ParamType>();
if (type == ParamType::MultiSwitch) {
QStringList names = m_name.split(QLatin1Char('\n'));
QStringList oldValues = m_oldValue.split(QLatin1Char('\n'));
if (names.count() == oldValues.count()) {
for (int i = 0; i < names.count(); i++) {
m_model->setParameter(names.at(i), oldValues.at(i), true, m_index);
}
return;
}
}
}
m_model->setParameter(m_name, m_oldValue, true, m_index);
}
void AssetCommand::redo()
{
if (m_name.contains(QLatin1Char('\n'))) {
// Check if it is a multi param
auto type = m_model->data(m_index, AssetParameterModel::TypeRole).value<ParamType>();
if (type == ParamType::MultiSwitch) {
QStringList names = m_name.split(QLatin1Char('\n'));
QStringList values = m_value.split(QLatin1Char('\n'));
if (names.count() == values.count()) {
for (int i = 0; i < names.count(); i++) {
m_model->setParameter(names.at(i), values.at(i), m_updateView, m_index);
}
m_updateView = true;
return;
}
}
}
m_model->setParameter(m_name, m_value, m_updateView, m_index);
m_updateView = true;
}
......
......@@ -174,6 +174,7 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
case ParamType::Curve:
case ParamType::Geometry:
case ParamType::Switch:
case ParamType::MultiSwitch:
case ParamType::Wipe:
// Pretty sure that those are fine
converted = false;
......@@ -194,7 +195,12 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
qDebug() << "No fixing needed for" << name << "=" << value;
}
}
if (!isFixed) {
currentRow.value = value;
QString title = i18n(currentParameter.firstChildElement(QStringLiteral("name")).text().toUtf8().data());
currentRow.name = title.isEmpty() ? name : title;
m_params[name] = currentRow;
}
if (!name.isEmpty()) {
internalSetParameter(name, value);
// Keep track of param order
......@@ -205,10 +211,6 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
// fixed parameters are not displayed so we don't store them.
continue;
}
currentRow.value = value;
QString title = i18n(currentParameter.firstChildElement(QStringLiteral("name")).text().toUtf8().data());
currentRow.name = title.isEmpty() ? name : title;
m_params[name] = currentRow;
m_rows.push_back(name);
}
if (m_assetId.startsWith(QStringLiteral("sox_"))) {
......@@ -301,29 +303,42 @@ void AssetParameterModel::internalSetParameter(const QString &name, const QStrin
{
Q_ASSERT(m_asset->is_valid());
// TODO: this does not really belong here, but I don't see another way to do it so that undo works
if (data(paramIndex, AssetParameterModel::TypeRole).value<ParamType>() == ParamType::Curve) {
if (m_params.count(name) > 0) {
ParamType type = m_params.at(name).type;
if (type == ParamType::Curve) {
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
QStringList vals = paramValue.split(QLatin1Char(';'), QString::SkipEmptyParts);
QStringList vals = paramValue.split(QLatin1Char(';'), QString::SkipEmptyParts);
#else
QStringList vals = paramValue.split(QLatin1Char(';'), Qt::SkipEmptyParts);
QStringList vals = paramValue.split(QLatin1Char(';'), Qt::SkipEmptyParts);
#endif
int points = vals.size();
m_asset->set("3", points / 10.);
m_params[QStringLiteral("3")].value = points / 10.;
// for the curve, inpoints are numbered: 6, 8, 10, 12, 14
// outpoints, 7, 9, 11, 13,15 so we need to deduce these enums
for (int i = 0; i < points; i++) {
const QString &pointVal = vals.at(i);
int idx = 2 * i + 6;
QString pName = QString::number(idx);
double val = pointVal.section(QLatin1Char('/'), 0, 0).toDouble();
m_asset->set(pName.toLatin1().constData(), val);
m_params[pName].value = val;
idx++;
pName = QString::number(idx);
val = pointVal.section(QLatin1Char('/'), 1, 1).toDouble();
m_asset->set(pName.toLatin1().constData(), val);
m_params[pName].value = val;
int points = vals.size();
m_asset->set("3", points / 10.);
m_params[QStringLiteral("3")].value = points / 10.;
// for the curve, inpoints are numbered: 6, 8, 10, 12, 14
// outpoints, 7, 9, 11, 13,15 so we need to deduce these enums
for (int i = 0; i < points; i++) {
const QString &pointVal = vals.at(i);
int idx = 2 * i + 6;
QString pName = QString::number(idx);
double val = pointVal.section(QLatin1Char('/'), 0, 0).toDouble();
m_asset->set(pName.toLatin1().constData(), val);
m_params[pName].value = val;
idx++;
pName = QString::number(idx);
val = pointVal.section(QLatin1Char('/'), 1, 1).toDouble();
m_asset->set(pName.toLatin1().constData(), val);
m_params[pName].value = val;
}
} else if (type == ParamType::MultiSwitch) {
QStringList names = name.split(QLatin1Char('\n'));
QStringList values = paramValue.split(QLatin1Char('\n'));
if (names.count() == values.count()) {
for (int i = 0; i < names.count(); i++) {
m_asset->set(names.at(i).toLatin1().constData(), values.at(i).toLatin1().constData());
}
m_params[name].value = paramValue;
}
return;
}
}
bool conversionSuccess = true;
......@@ -352,6 +367,24 @@ void AssetParameterModel::internalSetParameter(const QString &name, const QStrin
m_fixedParams[name] = paramValue;
}
}
// Fades need to have their alpha or level param synced to in/out
if (m_assetId.startsWith(QLatin1String("fade_")) && (name == QLatin1String("in") || name == QLatin1String("out"))) {
if (m_assetId.startsWith(QLatin1String("fade_from"))) {
if (getAsset()->get("alpha") == QLatin1String("1")) {
// Adjust level value to match filter end
getAsset()->set("level", "0=0;-1=1");
} else if (getAsset()->get("level") == QLatin1String("1")) {
getAsset()->set("alpha", "0=0;-1=1");
}
} else {
if (getAsset()->get("alpha") == QLatin1String("1")) {
// Adjust level value to match filter end
getAsset()->set("level", "0=1;-1=0");
} else if (getAsset()->get("level") == QLatin1String("1")) {
getAsset()->set("alpha", "0=1;-1=0");
}
}
}
qDebug() << " = = SET EFFECT PARAM: " << name << " = " << m_asset->get(name.toLatin1().constData());
}
......@@ -490,6 +523,23 @@ QVariant AssetParameterModel::data(const QModelIndex &index, int role) const
case AlphaRole:
return element.attribute(QStringLiteral("alpha")) == QLatin1String("1");
case ValueRole: {
if (m_params.at(paramName).type == ParamType::MultiSwitch) {
// Multi params concatenate param names with a '\n' and param values with a space
QStringList paramNames = paramName.split(QLatin1Char('\n'));
QStringList values;
bool valueFound = false;
for (auto &p : paramNames) {
const QString val = m_asset->get(p.toUtf8().constData());
if (!val.isEmpty()) {
valueFound = true;
}
values << val;
}
if (!valueFound) {
return (element.attribute(QStringLiteral("value")).isNull() ? parseAttribute(m_ownerId, QStringLiteral("default"), element) : element.attribute(QStringLiteral("value")));
}
return values.join(QLatin1Char('\n'));
}
QString value(m_asset->get(paramName.toUtf8().constData()));
return value.isEmpty() ? (element.attribute(QStringLiteral("value")).isNull() ? parseAttribute(m_ownerId, QStringLiteral("default"), element)
: element.attribute(QStringLiteral("value")))
......@@ -543,6 +593,12 @@ QVariant AssetParameterModel::data(const QModelIndex &index, int role) const
return QVariant();
}
const QString AssetParameterModel::framesToTime(int t) const
{
return m_asset->frames_to_time(t, mlt_time_clock);
}
int AssetParameterModel::rowCount(const QModelIndex &parent) const
{
//qDebug() << "===================================================== Requested rowCount" << parent << m_rows.size();
......@@ -567,6 +623,9 @@ ParamType AssetParameterModel::paramTypeFromStr(const QString &type)
}
if (type == QLatin1String("switch")) {
return ParamType::Switch;
}
if (type == QLatin1String("multiswitch")) {
return ParamType::MultiSwitch;
} else if (type == QLatin1String("simplekeyframe")) {
return ParamType::KeyframeParam;
} else if (type == QLatin1String("animatedrect") || type == QLatin1String("rect")) {
......
......@@ -42,6 +42,7 @@ enum class ParamType {
UrlList, // File can be chosen from a list of pre-defined ones or a custom file can be used (like url)
Bool,
Switch,
MultiSwitch,
RestrictedAnim, // animated 1 dimensional param with linear support only
Animated,
AnimatedRect, // Animated rects have X, Y, width, height, and opacity (in [0,1])
......@@ -197,6 +198,8 @@ public:
const QString getParam(const QString &paramName);
/** @brief Returns the current asset */
Mlt::Properties *getAsset();
/** @brief Returns a frame time as click time (00:00:00.000) */
const QString framesToTime(int t) const;
public slots:
/** @brief Sets the value of a list of parameters
......
......@@ -38,6 +38,7 @@
#include "positioneditwidget.hpp"
#include "slidewidget.hpp"
#include "switchparamwidget.hpp"
#include "multiswitchparamwidget.hpp"
#include "urllistparamwidget.h"
#include "urlparamwidget.hpp"
......@@ -116,6 +117,9 @@ AbstractParamWidget *AbstractParamWidget::construct(const std::shared_ptr<AssetP
case ParamType::Switch:
widget = new SwitchParamWidget(model, index, parent);
break;
case ParamType::MultiSwitch:
widget = new MultiSwitchParamWidget(model, index, parent);
break;
case ParamType::Url:
widget = new UrlParamWidget(model, index, parent);
break;
......
/***************************************************************************
* Copyright (C) 2021 by Jean-Baptiste Mardelle *
* 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 "multiswitchparamwidget.hpp"
#include "assets/model/assetparametermodel.hpp"
MultiSwitchParamWidget::MultiSwitchParamWidget(std::shared_ptr<AssetParameterModel> model, QModelIndex index, QWidget *parent)
: AbstractParamWidget(std::move(model), index, parent)
{
setupUi(this);
// setup the comment
QString comment = m_model->data(m_index, AssetParameterModel::CommentRole).toString();
setToolTip(comment);
m_labelComment->setText(comment);
m_widgetComment->setHidden(true);
// setup the name
m_labelName->setText(m_model->data(m_index, Qt::DisplayRole).toString());
setMinimumHeight(m_labelName->sizeHint().height());
// set check state
slotRefresh();
// emit the signal of the base class when appropriate
connect(this->m_checkBox, &QCheckBox::stateChanged, this, [this](int state) {
QString value;
if (state == Qt::Checked) {
value = m_model->data(m_index, AssetParameterModel::MaxRole).toString();
if (value.contains(QLatin1String("0="))) {
value.replace(QLatin1String("0="), QLatin1String("00:00:00.000="));
}
if (value.contains(QLatin1String("-1="))) {
// Replace -1 with out position
int out = m_model->data(m_index, AssetParameterModel::OutRole).toInt() - m_model->data(m_index, AssetParameterModel::InRole).toInt();
value.replace(QLatin1String("-1="), QString("%1=").arg(m_model->framesToTime(out)));
}
} else {
value = m_model->data(m_index, AssetParameterModel::MinRole).toString();
if (value.contains(QLatin1String("0="))) {
value.replace(QLatin1String("0="), QLatin1String("00:00:00.000="));
}
if (value.contains(QLatin1String("-1="))) {
// Replace -1 with out position
int out = m_model->data(m_index, AssetParameterModel::OutRole).toInt() - m_model->data(m_index, AssetParameterModel::InRole).toInt();
value.replace(QLatin1String("-1="), QString("%1=").arg(m_model->framesToTime(out)));
}
}
emit valueChanged(m_index, value, true);
});
}
void MultiSwitchParamWidget::slotShowComment(bool show)
{
if (!m_labelComment->text().isEmpty()) {
m_widgetComment->setVisible(show);
}
}
void MultiSwitchParamWidget::slotRefresh()
{
const QSignalBlocker bk(m_checkBox);
QString max = m_model->data(m_index, AssetParameterModel::MaxRole).toString();
const QString value = m_model->data(m_index, AssetParameterModel::ValueRole).toString();
bool convertToTime = false;
if (value.contains(QLatin1Char(':'))) {
convertToTime = true;
}
if (max.contains(QLatin1String("0=")) && convertToTime) {
max.replace(QLatin1String("0="), QLatin1String("00:00:00.000="));
}
if (max.contains(QLatin1String("-1=")) && !value.contains(QLatin1String("-1="))) {
// Replace -1 with out position
int out = m_model->data(m_index, AssetParameterModel::OutRole).toInt() - m_model->data(m_index, AssetParameterModel::InRole).toInt();
qDebug()<<"=== REPLACING WITH MAX OUT: "<<out;
if (convertToTime) {
max.replace(QLatin1String("-1="), QString("%1=").arg(m_model->framesToTime(out)));
} else {
max.replace(QLatin1String("-1="), QString("%1=").arg(out));
}
}
qDebug()<<"=== GOT FILTER IN ROLE: "<<m_model->data(m_index, AssetParameterModel::InRole).toInt()<<" / OUT: "<<m_model->data(m_index, AssetParameterModel::OutRole).toInt();
qDebug()<<"==== COMPARING MULTISWITCH: "<<value<<" = "<<max;
m_checkBox->setChecked(value == max);
}
/***************************************************************************
* Copyright (C) 2018 by Jean-Baptiste Mardelle *
* 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 MULTISWITCHPARAMWIDGET_H
#define MULTISWITCHPARAMWIDGET_H
#include "abstractparamwidget.hpp"
#include "ui_boolparamwidget_ui.h"
#include <QWidget>
/** @brief This class represents a parameter that requires
the user to choose tick a checkbox
*/
class MultiSwitchParamWidget : public AbstractParamWidget, public Ui::BoolParamWidget_UI
{
Q_OBJECT
public:
/** @brief Constructor for the widgetComment
@param name String containing the name of the parameter
@param comment Optional string containing the comment associated to the parameter
@param checked Boolean indicating whether the checkbox should initially be checked
@param parent Parent widget
*/
MultiSwitchParamWidget(std::shared_ptr<AssetParameterModel> model, QModelIndex index, QWidget *parent);
public slots:
/** @brief Toggle the comments on or off
*/
void slotShowComment(bool show) override;
/** @brief refresh the properties to reflect changes in the model
*/
void slotRefresh() override;
};
#endif
......@@ -1841,6 +1841,38 @@ bool DocumentValidator::upgrade(double version, const double currentVersion)
pCore->window()->slotReloadEffects(changedEffects);
}
}
// Doc 1.03: Kdenlive 21.08.2
if (version < 1.03) {
// Fades: replace deprecated syntax (using start/end properties and alpha=-1) with level and alpha animated properties
QDomNodeList effects = m_doc.elementsByTagName(QStringLiteral("filter"));
int max = effects.count();
QStringList changedEffects;
for (int i = 0; i < max; ++i) {
QDomElement t = effects.at(i).toElement();
QString kdenliveId = Xml::getXmlProperty(t, QStringLiteral("kdenlive_id"));
if (kdenliveId.startsWith(QLatin1String("fade_"))) {
bool fadeIn = kdenliveId == QLatin1String("fade_from_black");
bool isAlpha = Xml::getXmlProperty(t, QStringLiteral("alpha")).toInt() == -1;
// Clear unused properties
Xml::removeXmlProperty(t, QStringLiteral("start"));
Xml::removeXmlProperty(t, QStringLiteral("end"));
Xml::removeXmlProperty(t, QStringLiteral("alpha"));
QString params;
if (fadeIn) {
params = QStringLiteral("0=0;-1=1");
} else {
params = QStringLiteral("0=1;-1=0");
}
if (isAlpha) {
Xml::setXmlProperty(t, QStringLiteral("level"), QStringLiteral("1"));
Xml::setXmlProperty(t, QStringLiteral("alpha"), params);
} else {
Xml::setXmlProperty(t, QStringLiteral("level"), params);
Xml::setXmlProperty(t, QStringLiteral("alpha"), QStringLiteral("1"));
}
}
}
}
m_modified = true;
return true;
......
......@@ -68,7 +68,7 @@
#include <xlocale.h>
#endif
const double DOCUMENTVERSION = 1.02;
const double DOCUMENTVERSION = 1.03;
KdenliveDoc::KdenliveDoc(const QUrl &url, QString projectFolder, QUndoGroup *undoGroup, const QString &profileName, const QMap<QString, QString> &properties,
const QMap<QString, QString> &metadata, const QPair<int, int> &tracks, int audioChannels, bool *openBackup, MainWindow *parent)
......
......@@ -79,6 +79,17 @@ std::shared_ptr<EffectItemModel> EffectItemModel::construct(std::unique_ptr<Mlt:
for (int i = 0; i < params.count(); ++i) {
QDomElement currentParameter = params.item(i).toElement();
QString paramName = currentParameter.attribute(QStringLiteral("name"));
QString paramType = currentParameter.attribute(QStringLiteral("type"));
if (paramType == QLatin1String("multiswitch")) {
// multiswitch params have a composited param name, skip
QStringList names = paramName.split(QLatin1Char('\n'));
QStringList paramValues;
for (const QString &n : names) {
paramValues << effect->get(n.toUtf8().constData());
}
currentParameter.setAttribute(QStringLiteral("value"), paramValues.join(QLatin1Char('\n')));
continue;
}
QString paramValue = effect->get(paramName.toUtf8().constData());
qDebug() << effectId << ": Setting parameter " << paramName << " to " << paramValue;
currentParameter.setAttribute(QStringLiteral("value"), paramValue);
......
......@@ -379,12 +379,28 @@ bool EffectStackModel::fromXml(const QDomElement &effectsXml, Fun &undo, Fun &re
int duration = effect->filter().get_length() - 1;
effect->filter().set("in", currentIn);
effect->filter().set("out", currentIn + duration);
if (effectId.startsWith(QLatin1String("fade_"))) {
if (effect->filter().get("alpha") == QLatin1String("1")) {
// Adjust level value to match filter end
effect->filter().set("level", "0=0;-1=1");
} else if (effect->filter().get("level") == QLatin1String("1")) {
effect->filter().set("alpha", "0=0;-1=1");
}
}
} else if (effectId == QLatin1String("fadeout") || effectId == QLatin1String("fade_to_black")) {
m_fadeOuts.insert(effect->getId());
int duration = effect->filter().get_length() - 1;
int filterOut = pCore->getItemIn(m_ownerId) + pCore->getItemDuration(m_ownerId) - 1;
effect->filter().set("in", filterOut - duration);
effect->filter().set("out", filterOut);
if (effectId.startsWith(QLatin1String("fade_"))) {
if (effect->filter().get("alpha") == QLatin1String("1")) {
// Adjust level value to match filter end
effect->filter().set("level", "0=1;-1=0");
} else if (effect->filter().get("level") == QLatin1String("1")) {
effect->filter().set("alpha", "0=1;-1=0");
}
}
}
local_redo();
effectAdded = true;
......@@ -729,6 +745,12 @@ bool EffectStackModel::adjustFadeLength(int duration, bool fromStart, bool audio
duration = qMin(pCore->getItemDuration(m_ownerId), duration);
effect->filter().set("out", in + duration);
indexes << getIndexFromItem(effect);
if (effect->filter().get("alpha") == QLatin1String("1")) {
// Adjust level value to match filter end
effect->filter().set("level", "0=0;-1=1");
} else if (effect->filter().get("level") == QLatin1String("1")) {
effect->filter().set("alpha", "0=0;-1=1");
}
}
}
if (!indexes.isEmpty()) {
......@@ -772,6 +794,12 @@ bool EffectStackModel::adjustFadeLength(int duration, bool fromStart, bool audio
duration = qMin(itemDuration, duration);
effect->filter().set("in", out - duration);
indexes << getIndexFromItem(effect);
if (effect->filter().get("alpha") == QLatin1String("1")) {
// Adjust level value to match filter end
effect->filter().set("level", "0=1;-1=0");
} else if (effect->filter().get("level") == QLatin1String("1")) {
effect->filter().set("alpha", "0=1;-1=0");
}
}
}
if (!indexes.isEmpty()) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment