mirror of
https://invent.kde.org/multimedia/kdenlive
synced 2025-12-05 15:59:59 +01:00
Merge branch 'work/fakerectparam' into 'master'
Draft: Introduce new fakerect and animatedfakerect parameter types that handle... See merge request multimedia/kdenlive!764
This commit is contained in:
@@ -11,16 +11,11 @@
|
||||
<parameter type="fixedcolor" name="av.color" default="black" alpha="1">
|
||||
<name>Color</name>
|
||||
</parameter>
|
||||
<parameter type="animated" name="av.left" default="0" min="0" max="%width" factor="1">
|
||||
<name>Left</name>
|
||||
</parameter>
|
||||
<parameter type="animated" name="av.right" default="0" min="0" max="%width" factor="1">
|
||||
<name>Right</name>
|
||||
</parameter>
|
||||
<parameter type="animated" name="av.top" default="0" min="0" max="%height" factor="1">
|
||||
<name>Top</name>
|
||||
</parameter>
|
||||
<parameter type="animated" name="av.bottom" default="0" min="0" max="%height" factor="1">
|
||||
<name>Bottom</name>
|
||||
<parameter type="animatedfakerect" name="kdenlive:fakerect" default="0 0 100% 100%" opacity="false">
|
||||
<name>Rectangle</name>
|
||||
<parammap position="0" src="av.left" max="%width" min="0" default="0" />
|
||||
<parammap position="1" src="av.top" max="%height" min="0" default="0" />
|
||||
<parammap position="2" src="av.right" max="%width" min="0" default="0" from="%width" fromborder="1"/>
|
||||
<parammap position="3" src="av.bottom" max="%height" min="0" default="0" from="%height" fromborder="1"/>
|
||||
</parameter>
|
||||
</effect>
|
||||
|
||||
@@ -4,17 +4,12 @@
|
||||
<name>Rectangular Alpha mask</name>
|
||||
<description>Creates an square alpha-channel mask</description>
|
||||
<author>Richard Spindler</author>
|
||||
<parameter type="animated" name="Left" max="1000" min="0" default="0" factor="1000">
|
||||
<name>Left</name>
|
||||
</parameter>
|
||||
<parameter type="animated" name="Right" max="1000" min="0" default="0" factor="1000">
|
||||
<name>Right</name>
|
||||
</parameter>
|
||||
<parameter type="animated" name="Top" max="1000" min="0" default="0" factor="1000">
|
||||
<name>Top</name>
|
||||
</parameter>
|
||||
<parameter type="animated" name="Bottom" max="1000" min="0" default="0" factor="1000">
|
||||
<name>Bottom</name>
|
||||
<parameter type="fakerect" name="kdenlive:fakerect" default="0 0 100% 100%" opacity="false">
|
||||
<name>Rectangle</name>
|
||||
<parammap position="0" src="Left" max="%width" min="0" default="0" factor="%width"/>
|
||||
<parammap position="1" src="Top" max="%height" min="0" default="0" factor="%height"/>
|
||||
<parammap position="2" src="Right" max="%width" min="0" default="0" factor="%width" from="%width" fromborder="1"/>
|
||||
<parammap position="3" src="Bottom" max="%height" min="0" default="0" factor="%height" from="%height" fromborder="1"/>
|
||||
</parameter>
|
||||
<parameter type="bool" name="Invert" default="1">
|
||||
<name>Invert</name>
|
||||
|
||||
@@ -359,7 +359,7 @@ bool KeyframeModel::moveOneKeyframe(GenTime oldPos, GenTime pos, QVariant newVal
|
||||
// no change
|
||||
return true;
|
||||
}
|
||||
if (m_paramType == ParamType::AnimatedRect) {
|
||||
if (m_paramType == ParamType::AnimatedRect || m_paramType == ParamType::AnimatedFakeRect) {
|
||||
return updateKeyframe(pos, newVal);
|
||||
}
|
||||
// Calculate real value from normalized
|
||||
@@ -381,7 +381,7 @@ bool KeyframeModel::moveOneKeyframe(GenTime oldPos, GenTime pos, QVariant newVal
|
||||
qDebug() << "Move keyframe finished deletion:" << res;
|
||||
qDebug() << getAnimProperty();
|
||||
if (res) {
|
||||
if (m_paramType == ParamType::AnimatedRect) {
|
||||
if (m_paramType == ParamType::AnimatedRect || m_paramType == ParamType::AnimatedFakeRect) {
|
||||
if (!newVal.isValid()) {
|
||||
newVal = oldValue;
|
||||
}
|
||||
@@ -672,7 +672,7 @@ QVariant KeyframeModel::data(const QModelIndex &index, int role) const
|
||||
return false;
|
||||
}
|
||||
case NormalizedValueRole: {
|
||||
if (m_paramType == ParamType::AnimatedRect) {
|
||||
if (m_paramType == ParamType::AnimatedRect || m_paramType == ParamType::AnimatedFakeRect) {
|
||||
const QString &data = it->second.second.toString();
|
||||
bool ok;
|
||||
double converted = data.section(QLatin1Char(' '), -1).toDouble(&ok);
|
||||
@@ -908,6 +908,7 @@ QString KeyframeModel::getAnimProperty() const
|
||||
for (const auto &keyframe : m_keyframeList) {
|
||||
switch (m_paramType) {
|
||||
case ParamType::AnimatedRect:
|
||||
case ParamType::AnimatedFakeRect:
|
||||
case ParamType::Color:
|
||||
mlt_prop.anim_set("key", keyframe.second.second.toString().toUtf8().constData(), keyframe.first.frames(pCore->getCurrentFps()));
|
||||
break;
|
||||
@@ -983,7 +984,8 @@ void KeyframeModel::parseAnimProperty(const QString &prop, int in, int out)
|
||||
}
|
||||
QVariant value;
|
||||
switch (m_paramType) {
|
||||
case ParamType::AnimatedRect: {
|
||||
case ParamType::AnimatedRect:
|
||||
case ParamType::AnimatedFakeRect: {
|
||||
const QString rect_str(mlt_prop.get("key"));
|
||||
mlt_rect rect = mlt_prop.anim_get_rect("key", frame);
|
||||
if (rect_str.contains(QLatin1Char('%'))) {
|
||||
@@ -1041,6 +1043,8 @@ void KeyframeModel::resetAnimProperty(const QString &prop)
|
||||
ptr->passProperties(mlt_prop);
|
||||
if (m_paramType == ParamType::AnimatedRect) {
|
||||
useOpacity = ptr->data(m_index, AssetParameterModel::OpacityRole).toBool();
|
||||
} else if (m_paramType == ParamType::AnimatedFakeRect) {
|
||||
useOpacity = false;
|
||||
}
|
||||
}
|
||||
mlt_prop.set("key", prop.toUtf8().constData());
|
||||
@@ -1061,7 +1065,8 @@ void KeyframeModel::resetAnimProperty(const QString &prop)
|
||||
}
|
||||
QVariant value;
|
||||
switch (m_paramType) {
|
||||
case ParamType::AnimatedRect: {
|
||||
case ParamType::AnimatedRect:
|
||||
case ParamType::AnimatedFakeRect: {
|
||||
const QString rect_str(mlt_prop.get("key"));
|
||||
mlt_rect rect = mlt_prop.anim_get_rect("key", frame);
|
||||
if (rect_str.contains(QLatin1Char('%'))) {
|
||||
@@ -1243,7 +1248,7 @@ QVariant KeyframeModel::getInterpolatedValue(const GenTime &pos) const
|
||||
(void)mlt_prop.anim_get_double("key", 0, out);
|
||||
return QVariant(mlt_prop.anim_get_double("key", pos.frames(pCore->getCurrentFps())));
|
||||
}
|
||||
if (!animData.isEmpty() && m_paramType == ParamType::AnimatedRect) {
|
||||
if (!animData.isEmpty() && (m_paramType == ParamType::AnimatedRect || m_paramType == ParamType::AnimatedFakeRect)) {
|
||||
mlt_prop.set("key", animData.toUtf8().constData());
|
||||
// This is a fake query to force the animation to be parsed
|
||||
(void)mlt_prop.anim_get_double("key", 0, out);
|
||||
@@ -1487,7 +1492,8 @@ const QString KeyframeModel::getAnimationStringWithOffset(std::shared_ptr<AssetP
|
||||
if (lastPos > duration) {
|
||||
QVariant value;
|
||||
switch (paramType) {
|
||||
case ParamType::AnimatedRect: {
|
||||
case ParamType::AnimatedRect:
|
||||
case ParamType::AnimatedFakeRect: {
|
||||
mlt_rect rect = mlt_prop.anim_get_rect("key", duration);
|
||||
QString res = QStringLiteral("%1 %2 %3 %4").arg(int(rect.x)).arg(int(rect.y)).arg(int(rect.w)).arg(int(rect.h));
|
||||
if (useOpacity) {
|
||||
|
||||
@@ -77,7 +77,7 @@ void KeyframeMonitorHelper::refreshParams(int pos)
|
||||
std::shared_ptr<KeyframeModelList> keyframes = m_model->getKeyframeModel();
|
||||
for (const auto &ix : std::as_const(m_indexes)) {
|
||||
auto type = m_model->data(ix, AssetParameterModel::TypeRole).value<ParamType>();
|
||||
if (type != ParamType::AnimatedRect) {
|
||||
if (type != ParamType::AnimatedRect && type != ParamType::AnimatedFakeRect) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ void KeyframeMonitorHelper::slotUpdateFromMonitorData(const QVariantList ¢er
|
||||
}
|
||||
for (const auto &ix : std::as_const(m_indexes)) {
|
||||
auto type = m_model->data(ix, AssetParameterModel::TypeRole).value<ParamType>();
|
||||
if (type != ParamType::AnimatedRect) {
|
||||
if (type != ParamType::AnimatedRect && type != ParamType::AnimatedFakeRect) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ QPersistentModelIndex RotatedRectHelper::findAnimatedRectParameter() const
|
||||
{
|
||||
for (const auto &ix : std::as_const(m_indexes)) {
|
||||
auto type = m_model->data(ix, AssetParameterModel::TypeRole).value<ParamType>();
|
||||
if (type == ParamType::AnimatedRect) {
|
||||
if (type == ParamType::AnimatedRect || type == ParamType::AnimatedFakeRect) {
|
||||
return ix;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,8 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
|
||||
value.replace(posRegexp, "\\1.\\2");
|
||||
break;
|
||||
}
|
||||
case ParamType::AnimatedRect: {
|
||||
case ParamType::AnimatedRect:
|
||||
case ParamType::AnimatedFakeRect: {
|
||||
// Fix values like <position>=50 20 1920 1080 0,75
|
||||
static const QRegularExpression animatedRegexp(R"((=\d+ \d+ \d+ \d+ \d+),(\d+))");
|
||||
value.replace(animatedRegexp, "\\1.\\2");
|
||||
@@ -172,6 +173,7 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
|
||||
break;
|
||||
case ParamType::Curve:
|
||||
case ParamType::Geometry:
|
||||
case ParamType::FakeRect:
|
||||
case ParamType::Switch:
|
||||
case ParamType::MultiSwitch:
|
||||
case ParamType::Wipe:
|
||||
@@ -184,6 +186,9 @@ AssetParameterModel::AssetParameterModel(std::unique_ptr<Mlt::Properties> asset,
|
||||
// Not sure if fine
|
||||
converted = false;
|
||||
break;
|
||||
case ParamType::Unknown:
|
||||
qDebug() << "//// WARNING UNKNOWN PARAM TYPE REQUESTED IN: " << m_assetId;
|
||||
break;
|
||||
}
|
||||
if (converted) {
|
||||
if (value != originalValue) {
|
||||
@@ -320,8 +325,9 @@ void AssetParameterModel::internalSetParameter(const QString name, const QString
|
||||
{
|
||||
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
|
||||
ParamType type = ParamType::Unknown;
|
||||
if (m_params.count(name) > 0) {
|
||||
ParamType type = m_params.at(name).type;
|
||||
type = m_params.at(name).type;
|
||||
if (type == ParamType::Curve) {
|
||||
QStringList vals = paramValue.split(QLatin1Char(';'), Qt::SkipEmptyParts);
|
||||
int points = vals.size();
|
||||
@@ -373,18 +379,96 @@ void AssetParameterModel::internalSetParameter(const QString name, const QString
|
||||
m_fixedParams[name] = doubleValue;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "::::::::::SETTING EFFECT PARAM: " << name << " = " << paramValue;
|
||||
m_asset->set(name.toLatin1().constData(), paramValue.toUtf8().constData());
|
||||
if (m_fixedParams.count(name) == 0) {
|
||||
m_params[name].value = paramValue;
|
||||
qDebug() << ":::: SETTING PARAM: " << name << " = " << paramValue << "\nHHHHHHHHHHHHHHHHHH";
|
||||
KeyframeModel *km = nullptr;
|
||||
if (m_keyframes) {
|
||||
// check if this parameter is keyframable
|
||||
km = m_keyframes->getKeyModel(paramIndex);
|
||||
}
|
||||
if (km) {
|
||||
// This is a fake query to force the animation to be parsed
|
||||
(void)m_asset->anim_get_int(name.toLatin1().constData(), 0, -1);
|
||||
KeyframeModel *km = m_keyframes->getKeyModel(paramIndex);
|
||||
if (km) {
|
||||
if (type == ParamType::AnimatedFakeRect) {
|
||||
QStringList xAnim;
|
||||
QStringList yAnim;
|
||||
QStringList wAnim;
|
||||
QStringList hAnim;
|
||||
mlt_profile profile = (mlt_profile)m_asset->get_data("_profile");
|
||||
Mlt::Animation anim = m_asset->get_animation(name.toLatin1().constData());
|
||||
QVariantMap mappedParams = data(paramIndex, FakeRectRole).toMap();
|
||||
for (int i = 0; i < anim.key_count(); ++i) {
|
||||
int frame;
|
||||
mlt_keyframe_type type;
|
||||
anim.key_get(i, frame, type);
|
||||
mlt_rect rect = m_asset->anim_get_rect(name.toLatin1().constData(), frame);
|
||||
if (paramValue.contains(QLatin1Char('%'))) {
|
||||
rect.x *= profile->width;
|
||||
rect.w *= profile->width;
|
||||
rect.y *= profile->height;
|
||||
rect.h *= profile->height;
|
||||
}
|
||||
// TODO DOPE: keyframe type
|
||||
for (auto i = mappedParams.cbegin(), end = mappedParams.cend(); i != end; ++i) {
|
||||
const AssetRectInfo paramInfo = i.value().value<AssetRectInfo>();
|
||||
double val = paramInfo.getValue(rect);
|
||||
switch (paramInfo.position) {
|
||||
case 0:
|
||||
xAnim << QStringLiteral("%1=%2").arg(frame).arg(val);
|
||||
break;
|
||||
case 1:
|
||||
yAnim << QStringLiteral("%1=%2").arg(frame).arg(val);
|
||||
break;
|
||||
case 2:
|
||||
wAnim << QStringLiteral("%1=%2").arg(frame).arg(val);
|
||||
break;
|
||||
case 3:
|
||||
hAnim << QStringLiteral("%1=%2").arg(frame).arg(val);
|
||||
break;
|
||||
default:
|
||||
qDebug() << "::: UNEXPECTED FAKE RECT INDEX: " << paramInfo.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto i = mappedParams.cbegin(), end = mappedParams.cend(); i != end; ++i) {
|
||||
const AssetRectInfo paramInfo = i.value().value<AssetRectInfo>();
|
||||
switch (paramInfo.position) {
|
||||
case 0:
|
||||
m_asset->set(paramInfo.destName.toUtf8().constData(), xAnim.join(QLatin1Char(';')).toUtf8().constData());
|
||||
break;
|
||||
case 1:
|
||||
m_asset->set(paramInfo.destName.toUtf8().constData(), yAnim.join(QLatin1Char(';')).toUtf8().constData());
|
||||
break;
|
||||
case 2:
|
||||
m_asset->set(paramInfo.destName.toUtf8().constData(), wAnim.join(QLatin1Char(';')).toUtf8().constData());
|
||||
break;
|
||||
case 3:
|
||||
m_asset->set(paramInfo.destName.toUtf8().constData(), hAnim.join(QLatin1Char(';')).toUtf8().constData());
|
||||
break;
|
||||
default:
|
||||
qDebug() << "::: UNEXPECTED FAKE RECT INDEX: " << paramInfo.position;
|
||||
}
|
||||
}
|
||||
}
|
||||
km->refresh();
|
||||
} else {
|
||||
qDebug() << "====ERROR KFMODEL NOT FOUND FOR: " << name << ", " << paramIndex;
|
||||
// Fixed value
|
||||
if (type == ParamType::FakeRect) {
|
||||
// Pass the values to the real MLT params
|
||||
qDebug() << "/// PASSINF PARAMVALUE: " << paramValue;
|
||||
QVariantMap mappedParams = data(paramIndex, FakeRectRole).toMap();
|
||||
const QStringList splitValue = paramValue.split(QLatin1Char(' '));
|
||||
for (auto i = mappedParams.cbegin(), end = mappedParams.cend(); i != end; ++i) {
|
||||
const AssetRectInfo paramInfo = i.value().value<AssetRectInfo>();
|
||||
double val = 0;
|
||||
if (paramInfo.position >= splitValue.count()) {
|
||||
continue;
|
||||
}
|
||||
val = paramInfo.getValue(splitValue);
|
||||
m_asset->set(paramInfo.destName.toUtf8().constData(), val);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -619,6 +703,21 @@ QVariant AssetParameterModel::data(const QModelIndex &index, int role) const
|
||||
return !element.hasAttribute(QStringLiteral("notintimeline"));
|
||||
case AlphaRole:
|
||||
return element.attribute(QStringLiteral("alpha")) == QLatin1String("1");
|
||||
case FakeRectRole: {
|
||||
QVariantMap mappedParams;
|
||||
QDomNodeList children = element.elementsByTagName(QStringLiteral("parammap"));
|
||||
for (int i = 0; i < children.count(); ++i) {
|
||||
QDomElement currentParameter = children.item(i).toElement();
|
||||
const QString position = currentParameter.attribute(QStringLiteral("position"));
|
||||
AssetRectInfo paramInfo(currentParameter.attribute(QStringLiteral("src")), position.toInt(), currentParameter.attribute(QStringLiteral("default")),
|
||||
currentParameter.attribute(QStringLiteral("min")), currentParameter.attribute(QStringLiteral("max")),
|
||||
currentParameter.attribute(QStringLiteral("factor")),
|
||||
currentParameter.attribute(QStringLiteral("fromborder")) == QLatin1String("1"),
|
||||
currentParameter.attribute(QStringLiteral("from")));
|
||||
mappedParams.insert(position, QVariant::fromValue(paramInfo));
|
||||
}
|
||||
return mappedParams;
|
||||
}
|
||||
case ValueRole: {
|
||||
if (m_params.at(paramName).type == ParamType::MultiSwitch) {
|
||||
// Multi params concatenate param names with a '\n' and param values with a space
|
||||
@@ -752,6 +851,10 @@ ParamType AssetParameterModel::paramTypeFromStr(const QString &type)
|
||||
return ParamType::KeyframeParam;
|
||||
} else if (type == QLatin1String("animatedrect") || type == QLatin1String("rect")) {
|
||||
return ParamType::AnimatedRect;
|
||||
} else if (type == QLatin1String("animatedfakerect")) {
|
||||
return ParamType::AnimatedFakeRect;
|
||||
} else if (type == QLatin1String("fakerect")) {
|
||||
return ParamType::FakeRect;
|
||||
} else if (type == QLatin1String("geometry")) {
|
||||
return ParamType::Geometry;
|
||||
} else if (type == QLatin1String("keyframe") || type == QLatin1String("animated")) {
|
||||
@@ -792,8 +895,8 @@ ParamType AssetParameterModel::paramTypeFromStr(const QString &type)
|
||||
// static
|
||||
bool AssetParameterModel::isAnimated(ParamType type)
|
||||
{
|
||||
return type == ParamType::KeyframeParam || type == ParamType::AnimatedRect || type == ParamType::ColorWheel || type == ParamType::Roto_spline ||
|
||||
type == ParamType::Color;
|
||||
return type == ParamType::KeyframeParam || type == ParamType::AnimatedRect || type == ParamType::AnimatedFakeRect || type == ParamType::ColorWheel ||
|
||||
type == ParamType::Roto_spline || type == ParamType::Color;
|
||||
}
|
||||
|
||||
// static
|
||||
@@ -875,7 +978,7 @@ QVariant AssetParameterModel::parseAttribute(const QString &attribute, const QDo
|
||||
if (frameSize.isEmpty()) {
|
||||
frameSize = profileSize;
|
||||
}
|
||||
if (type == ParamType::AnimatedRect && content == "adjustcenter") {
|
||||
if ((type == ParamType::AnimatedRect || type == ParamType::AnimatedFakeRect || type == ParamType::FakeRect) && content == "adjustcenter") {
|
||||
int contentHeight = profileSize.height();
|
||||
int contentWidth = profileSize.width();
|
||||
double sourceDar = frameSize.width() / frameSize.height();
|
||||
@@ -925,7 +1028,8 @@ QVariant AssetParameterModel::parseAttribute(const QString &attribute, const QDo
|
||||
.replace(QLatin1String("%fittedContentHeight"), QString::number(frameSize.height() * fitScale))
|
||||
.replace(QLatin1String("%out"), QString::number(out))
|
||||
.replace(QLatin1String("%fade"), QString::number(frame_duration));
|
||||
if ((type == ParamType::AnimatedRect || type == ParamType::Geometry) && attribute == QLatin1String("default")) {
|
||||
if ((type == ParamType::AnimatedRect || type == ParamType::AnimatedFakeRect || type == ParamType::FakeRect || type == ParamType::Geometry) &&
|
||||
attribute == QLatin1String("default")) {
|
||||
if (content.contains(QLatin1Char('%'))) {
|
||||
// This is a generic default like: "25% 0% 50% 100%". Parse values
|
||||
QStringList numbers = content.split(QLatin1Char(' '));
|
||||
@@ -1129,7 +1233,7 @@ QJsonDocument AssetParameterModel::toJson(QVector<int> selection, bool includeFi
|
||||
QModelIndex ix = index(m_rows.indexOf(fixed.first), 0);
|
||||
currentParam.insert(QLatin1String("name"), QJsonValue(fixed.first));
|
||||
ParamType type = data(ix, AssetParameterModel::TypeRole).value<ParamType>();
|
||||
if (percentageExport && type == ParamType::AnimatedRect) {
|
||||
if (percentageExport && (type == ParamType::AnimatedRect || type == ParamType::AnimatedFakeRect || type == ParamType::FakeRect)) {
|
||||
// Convert values to percents
|
||||
const QString percentVal = animationToPercentage(fixed.second.toString());
|
||||
if (percentVal.isEmpty()) {
|
||||
@@ -1190,7 +1294,7 @@ QJsonDocument AssetParameterModel::toJson(QVector<int> selection, bool includeFi
|
||||
} else {
|
||||
QString resultValue = param.second.value.toString();
|
||||
ParamType type = data(ix, AssetParameterModel::TypeRole).value<ParamType>();
|
||||
if (percentageExport && type == ParamType::AnimatedRect) {
|
||||
if (percentageExport && (type == ParamType::AnimatedRect || type == ParamType::AnimatedFakeRect || type == ParamType::FakeRect)) {
|
||||
// Convert values to percents
|
||||
const QString percentVal = animationToPercentage(resultValue);
|
||||
if (!percentVal.isEmpty()) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core.h"
|
||||
#include "definitions.h"
|
||||
#include "klocalizedstring.h"
|
||||
#include <QAbstractListModel>
|
||||
@@ -29,6 +30,8 @@ enum class ParamType {
|
||||
EffectButtons,
|
||||
MultiSwitch,
|
||||
AnimatedRect, // Animated rects have X, Y, width, height, and opacity (in [0,1])
|
||||
AnimatedFakeRect, // Contains 4 parameters that make a rect, animated
|
||||
FakeRect, // Contains 4 parameters that make a rect
|
||||
Geometry,
|
||||
KeyframeParam,
|
||||
Color,
|
||||
@@ -44,10 +47,138 @@ enum class ParamType {
|
||||
Fontfamily,
|
||||
Filterjob,
|
||||
Readonly,
|
||||
Hidden
|
||||
Hidden,
|
||||
Unknown
|
||||
};
|
||||
Q_DECLARE_METATYPE(ParamType)
|
||||
|
||||
struct AssetRectInfo
|
||||
{
|
||||
QString destName;
|
||||
int position{0};
|
||||
double defaultValue;
|
||||
double minimum;
|
||||
double maximum;
|
||||
double factor{1};
|
||||
double from{0};
|
||||
bool fromBorder;
|
||||
explicit AssetRectInfo(const QString &name, int pos, const QString &value, const QString &min, const QString &max, const QString &fac = QString(),
|
||||
bool border = false, const QString &fr = QString())
|
||||
: destName(name)
|
||||
, position(pos)
|
||||
, fromBorder(border)
|
||||
{
|
||||
if (value == QLatin1String("%width")) {
|
||||
defaultValue = pCore->getCurrentFrameSize().width();
|
||||
} else if (value == QLatin1String("%height")) {
|
||||
defaultValue = pCore->getCurrentFrameSize().height();
|
||||
} else {
|
||||
defaultValue = value.toDouble();
|
||||
}
|
||||
if (min == QLatin1String("%width")) {
|
||||
minimum = pCore->getCurrentFrameSize().width();
|
||||
} else if (min == QLatin1String("%height")) {
|
||||
minimum = pCore->getCurrentFrameSize().height();
|
||||
} else {
|
||||
minimum = min.toDouble();
|
||||
}
|
||||
if (max == QLatin1String("%width")) {
|
||||
maximum = pCore->getCurrentFrameSize().width();
|
||||
} else if (max == QLatin1String("%height")) {
|
||||
maximum = pCore->getCurrentFrameSize().height();
|
||||
} else {
|
||||
maximum = max.toDouble();
|
||||
}
|
||||
if (fac == QLatin1String("%width")) {
|
||||
factor = pCore->getCurrentFrameSize().width();
|
||||
} else if (fac == QLatin1String("%height")) {
|
||||
factor = pCore->getCurrentFrameSize().height();
|
||||
} else {
|
||||
if (fac.isEmpty()) {
|
||||
factor = 1.0;
|
||||
} else {
|
||||
factor = fac.toDouble();
|
||||
}
|
||||
}
|
||||
if (fr == QLatin1String("%width")) {
|
||||
from = pCore->getCurrentFrameSize().width();
|
||||
} else if (fr == QLatin1String("%height")) {
|
||||
from = pCore->getCurrentFrameSize().height();
|
||||
} else {
|
||||
from = fr.toDouble();
|
||||
}
|
||||
}
|
||||
explicit AssetRectInfo() {}
|
||||
double getValue(double val) const
|
||||
{
|
||||
if (from != 0) {
|
||||
val = from - val;
|
||||
}
|
||||
if (factor != 1.) {
|
||||
val /= factor;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
double getValue(const QStringList &vals) const
|
||||
{
|
||||
double val = vals.at(position).toDouble();
|
||||
if (fromBorder) {
|
||||
if (position == 2) {
|
||||
// Width
|
||||
val += vals.at(0).toDouble();
|
||||
} else if (position == 3) {
|
||||
// Width
|
||||
val += vals.at(1).toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
if (from != 0) {
|
||||
val = from - val;
|
||||
}
|
||||
if (factor != 1.) {
|
||||
val /= factor;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
double getValue(const mlt_rect rect) const
|
||||
{
|
||||
double val = 0.;
|
||||
switch (position) {
|
||||
case 0:
|
||||
val = rect.x;
|
||||
break;
|
||||
case 1:
|
||||
val = rect.y;
|
||||
break;
|
||||
case 2:
|
||||
val = rect.w;
|
||||
break;
|
||||
case 3:
|
||||
val = rect.h;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (fromBorder) {
|
||||
if (position == 2) {
|
||||
// Width
|
||||
val += rect.x;
|
||||
} else if (position == 3) {
|
||||
// Width
|
||||
val += rect.y;
|
||||
}
|
||||
}
|
||||
|
||||
if (from != 0) {
|
||||
val = from - val;
|
||||
}
|
||||
if (factor != 1.) {
|
||||
val /= factor;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
/** @class AssetParameterModel
|
||||
@brief This class is the model for a list of parameters.
|
||||
The behaviour of a transition or an effect is typically controlled by several parameters. This class exposes this parameters as a list that can be rendered
|
||||
@@ -116,6 +247,8 @@ public:
|
||||
ParentPositionRole,
|
||||
ParentDurationRole,
|
||||
HideKeyframesFirstRole,
|
||||
// Obtain the real (distinct) parameters for a fake rect (x, y, w, h)
|
||||
FakeRectRole,
|
||||
List1Role,
|
||||
List2Role,
|
||||
Enum1Role,
|
||||
|
||||
@@ -289,13 +289,17 @@ int AssetParameterView::contentHeight() const
|
||||
|
||||
MonitorSceneType AssetParameterView::needsMonitorEffectScene() const
|
||||
{
|
||||
MonitorSceneType requestedType = MonitorSceneDefault;
|
||||
if (m_mainKeyframeWidget) {
|
||||
return m_mainKeyframeWidget->requiredScene();
|
||||
requestedType = m_mainKeyframeWidget->requiredScene();
|
||||
}
|
||||
if (requestedType != MonitorSceneDefault) {
|
||||
return requestedType;
|
||||
}
|
||||
for (int i = 0; i < m_model->rowCount(); ++i) {
|
||||
QModelIndex index = m_model->index(i, 0);
|
||||
auto type = m_model->data(index, AssetParameterModel::TypeRole).value<ParamType>();
|
||||
if (type == ParamType::Geometry) {
|
||||
if (type == ParamType::Geometry || type == ParamType::FakeRect) {
|
||||
return MonitorSceneGeometry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ std::pair<AbstractParamWidget *, KeyframeContainer *> AbstractParamWidget::const
|
||||
widget = new BoolParamWidget(model, index, parent);
|
||||
break;
|
||||
case ParamType::Geometry:
|
||||
case ParamType::FakeRect:
|
||||
widget = new GeometryEditWidget(model, index, frameSize, parent, layout);
|
||||
break;
|
||||
case ParamType::Position:
|
||||
|
||||
@@ -273,7 +273,13 @@ KeyframeContainer::KeyframeContainer(std::shared_ptr<AssetParameterModel> model,
|
||||
QList<QPersistentModelIndex> rectParams;
|
||||
for (const auto &w : m_parameters) {
|
||||
auto type = m_model->data(w.first, AssetParameterModel::TypeRole).value<ParamType>();
|
||||
if (type == ParamType::AnimatedRect) {
|
||||
if (type == ParamType::AnimatedFakeRect) {
|
||||
paramList.insert(w.first, i18n("Height"));
|
||||
paramList.insert(w.first, i18n("Width"));
|
||||
paramList.insert(w.first, i18n("Y position"));
|
||||
paramList.insert(w.first, i18n("X position"));
|
||||
rectParams << w.first;
|
||||
} else if (type == ParamType::AnimatedRect) {
|
||||
if (m_model->data(w.first, AssetParameterModel::OpacityRole).toBool()) {
|
||||
paramList.insert(w.first, i18n("Opacity"));
|
||||
}
|
||||
@@ -437,7 +443,7 @@ void KeyframeContainer::slotRefreshParams()
|
||||
auto type = m_model->data(w.first, AssetParameterModel::TypeRole).value<ParamType>();
|
||||
if (type == ParamType::KeyframeParam) {
|
||||
(static_cast<DoubleWidget *>(w.second))->setValue(m_keyframes->getInterpolatedValue(pos, w.first).toDouble());
|
||||
} else if (type == ParamType::AnimatedRect) {
|
||||
} else if (type == ParamType::AnimatedRect || type == ParamType::AnimatedFakeRect) {
|
||||
const QString val = m_keyframes->getInterpolatedValue(pos, w.first).toString();
|
||||
const QStringList vals = val.split(QLatin1Char(' '));
|
||||
QRect rect;
|
||||
@@ -596,7 +602,7 @@ void KeyframeContainer::initNeededSceneAndHelper()
|
||||
m_neededScene = MonitorSceneType::MonitorSceneRoto;
|
||||
m_monitorHelper = new RotoHelper(pCore->getMonitor(m_model->monitorId), m_model, m_parent);
|
||||
break;
|
||||
} else if (type == ParamType::AnimatedRect) {
|
||||
} else if (type == ParamType::AnimatedRect || type == ParamType::AnimatedFakeRect) {
|
||||
m_neededScene = MonitorSceneType::MonitorSceneGeometry;
|
||||
m_monitorHelper = new KeyframeMonitorHelper(pCore->getMonitor(m_model->monitorId), m_model, m_neededScene, m_parent);
|
||||
break;
|
||||
@@ -625,7 +631,7 @@ void KeyframeContainer::addParameter(const QPersistentModelIndex &index)
|
||||
|
||||
qDebug() << "::::::PARAM ADDED:" << name << static_cast<int>(type) << comment << suffix;
|
||||
// create KeyframeCurveEditor(s) which controls the current parameter
|
||||
if (type == ParamType::AnimatedRect) {
|
||||
if (type == ParamType::AnimatedRect || type == ParamType::AnimatedFakeRect) {
|
||||
QVector<QString> tabname = QVector<QString>() << i18n("X position") << i18n("Y position") << i18n("Width") << i18n("Height");
|
||||
if (m_model->data(index, AssetParameterModel::OpacityRole).toBool()) {
|
||||
tabname.append(i18n("Opacity"));
|
||||
@@ -641,7 +647,7 @@ void KeyframeContainer::addParameter(const QPersistentModelIndex &index)
|
||||
QLabel *labelWidget = nullptr;
|
||||
QWidget *paramWidget = nullptr;
|
||||
QString paramName = m_model->data(index, AssetParameterModel::NameRole).toString();
|
||||
if (type == ParamType::AnimatedRect) {
|
||||
if (type == ParamType::AnimatedRect || type == ParamType::AnimatedFakeRect) {
|
||||
int inPos = m_model->data(index, AssetParameterModel::ParentInRole).toInt();
|
||||
QPair<int, int> range(inPos, inPos + m_model->data(index, AssetParameterModel::ParentDurationRole).toInt());
|
||||
const QString value = m_keyframes->getInterpolatedValue(getPosition(), index).toString();
|
||||
|
||||
@@ -10,6 +10,47 @@
|
||||
#include "effectstackmodel.hpp"
|
||||
#include <utility>
|
||||
|
||||
static QMap<mlt_keyframe_type, QString> typeMap = {
|
||||
// Map keyframe type to any single character except numeric values.
|
||||
{mlt_keyframe_discrete, "|"},
|
||||
{mlt_keyframe_discrete, "!"},
|
||||
{mlt_keyframe_linear, ""},
|
||||
{mlt_keyframe_smooth, "~"},
|
||||
{mlt_keyframe_smooth_loose, "~"},
|
||||
{mlt_keyframe_smooth_natural, "$"},
|
||||
{mlt_keyframe_smooth_tight, "-"},
|
||||
{mlt_keyframe_sinusoidal_in, "a"},
|
||||
{mlt_keyframe_sinusoidal_out, "b"},
|
||||
{mlt_keyframe_sinusoidal_in_out, "c"},
|
||||
{mlt_keyframe_quadratic_in, "d"},
|
||||
{mlt_keyframe_quadratic_out, "e"},
|
||||
{mlt_keyframe_quadratic_in_out, "f"},
|
||||
{mlt_keyframe_cubic_in, "g"},
|
||||
{mlt_keyframe_cubic_out, "h"},
|
||||
{mlt_keyframe_cubic_in_out, "i"},
|
||||
{mlt_keyframe_quartic_in, "j"},
|
||||
{mlt_keyframe_quartic_out, "k"},
|
||||
{mlt_keyframe_quartic_in_out, "l"},
|
||||
{mlt_keyframe_quintic_in, "m"},
|
||||
{mlt_keyframe_quintic_out, "n"},
|
||||
{mlt_keyframe_quintic_in_out, "o"},
|
||||
{mlt_keyframe_exponential_in, "p"},
|
||||
{mlt_keyframe_exponential_out, "q"},
|
||||
{mlt_keyframe_exponential_in_out, "r"},
|
||||
{mlt_keyframe_circular_in, "s"},
|
||||
{mlt_keyframe_circular_out, "t"},
|
||||
{mlt_keyframe_circular_in_out, "u"},
|
||||
{mlt_keyframe_back_in, "v"},
|
||||
{mlt_keyframe_back_out, "w"},
|
||||
{mlt_keyframe_back_in_out, "x"},
|
||||
{mlt_keyframe_elastic_in, "y"},
|
||||
{mlt_keyframe_elastic_out, "z"},
|
||||
{mlt_keyframe_elastic_in_out, "A"},
|
||||
{mlt_keyframe_bounce_in, "B"},
|
||||
{mlt_keyframe_bounce_out, "C"},
|
||||
{mlt_keyframe_bounce_in_out, "D"},
|
||||
};
|
||||
|
||||
EffectItemModel::EffectItemModel(const QList<QVariant> &effectData, std::unique_ptr<Mlt::Properties> effect, const QDomElement &xml, const QString &effectId,
|
||||
const std::shared_ptr<AbstractTreeModel> &stack, bool isEnabled, QString originalDecimalPoint)
|
||||
: AbstractEffectItem(EffectItemType::Effect, effectData, stack, false, isEnabled)
|
||||
@@ -80,7 +121,100 @@ std::shared_ptr<EffectItemModel> EffectItemModel::construct(std::unique_ptr<Mlt:
|
||||
continue;
|
||||
}
|
||||
QString paramValue = effect->get(paramName.toUtf8().constData());
|
||||
qDebug() << effectId << ": Setting parameter " << paramName << " to " << paramValue;
|
||||
if (paramValue.isEmpty() && paramType == QLatin1String("animatedfakerect")) {
|
||||
// Check if we have existing values for the fake rect individual components
|
||||
QDomNodeList children = currentParameter.elementsByTagName(QLatin1String("parammap"));
|
||||
qDebug() << ":::: FOUND FAKE RECT PARAM WITH EMPTY VALUES: " << paramName;
|
||||
QMap<int, QRectF> rectValues;
|
||||
QMap<int, mlt_keyframe_type> keyframeMap;
|
||||
QMap<QString, std::pair<bool, int>> paramsFrom;
|
||||
// Sort parameters
|
||||
QMap<int, QString> sortedParams;
|
||||
for (int c = 0; c < children.count(); ++c) {
|
||||
QDomElement currentParameter = children.item(c).toElement();
|
||||
int position = currentParameter.attribute(QStringLiteral("position")).toInt();
|
||||
const QString childName = currentParameter.attribute(QStringLiteral("src"));
|
||||
bool fromBorder = currentParameter.attribute(QStringLiteral("fromborder")).toInt() == 1;
|
||||
int from = 0;
|
||||
if (currentParameter.hasAttribute(QStringLiteral("from"))) {
|
||||
const QString fromString = currentParameter.attribute(QStringLiteral("from"));
|
||||
if (fromString == QLatin1String("%width")) {
|
||||
from = pCore->getCurrentFrameSize().width();
|
||||
} else if (fromString == QLatin1String("%height")) {
|
||||
from = pCore->getCurrentFrameSize().height();
|
||||
}
|
||||
}
|
||||
paramsFrom.insert(childName, {fromBorder, from});
|
||||
sortedParams.insert(position, childName);
|
||||
}
|
||||
|
||||
for (auto param = sortedParams.cbegin(), end = sortedParams.cend(); param != end; ++param) {
|
||||
if (!effect->property_exists(param.value().toUtf8().constData())) {
|
||||
// Missing parameter, use default values
|
||||
break;
|
||||
}
|
||||
// Force animation parsing
|
||||
(void)effect->anim_get_double(param.value().toUtf8().constData(), 0);
|
||||
Mlt::Animation anim = effect->get_animation(param.value().toUtf8().constData());
|
||||
qDebug() << "Found" << anim.key_count() << " Keyframes in " << param.value();
|
||||
int frame;
|
||||
if (param.key() == 0) {
|
||||
// First param, parse keyframes
|
||||
for (int j = 0; j < anim.key_count(); ++j) {
|
||||
mlt_keyframe_type type;
|
||||
anim.key_get(j, frame, type);
|
||||
keyframeMap.insert(frame, type);
|
||||
}
|
||||
if (keyframeMap.isEmpty()) {
|
||||
// No keyframes found, use default value
|
||||
break;
|
||||
}
|
||||
for (auto &k : keyframeMap.keys()) {
|
||||
QRectF rect(effect->anim_get_double(param.value().toUtf8().constData(), k), 0, 0, 0);
|
||||
rectValues.insert(k, rect);
|
||||
}
|
||||
} else {
|
||||
for (auto &k : keyframeMap.keys()) {
|
||||
QRectF rect = rectValues.value(k);
|
||||
bool fromBorder = paramsFrom.value(param.value()).first;
|
||||
int from = paramsFrom.value(param.value()).second;
|
||||
double value = effect->anim_get_double(param.value().toUtf8().constData(), k);
|
||||
switch (param.key()) {
|
||||
case 1:
|
||||
rect.setY(value);
|
||||
break;
|
||||
case 2:
|
||||
if (fromBorder) {
|
||||
value += rect.x();
|
||||
}
|
||||
if (from > 0) {
|
||||
value = from - value;
|
||||
}
|
||||
rect.setWidth(value);
|
||||
break;
|
||||
case 3:
|
||||
if (fromBorder) {
|
||||
value += rect.y();
|
||||
}
|
||||
if (from > 0) {
|
||||
value = from - value;
|
||||
}
|
||||
rect.setHeight(value);
|
||||
break;
|
||||
default:
|
||||
qDebug() << ":::::: UNEXPECTED FAKERECT POSITION: " << param.key();
|
||||
break;
|
||||
}
|
||||
rectValues.insert(k, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto vals = rectValues.cbegin(), end = rectValues.cend(); vals != end; ++vals) {
|
||||
paramValue.append(QString::number(vals.key()));
|
||||
paramValue.append(typeMap.value(keyframeMap.value(vals.key())));
|
||||
paramValue.append(QString("%1 %2 %3 %4;").arg(vals.value().x()).arg(vals.value().y()).arg(vals.value().width()).arg(vals.value().height()));
|
||||
}
|
||||
}
|
||||
currentParameter.setAttribute(QStringLiteral("value"), paramValue);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user