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

Slideshow animations now use external config file instead of hardcoded, remember last image type

parent af7f1689
......@@ -19,6 +19,7 @@ install(FILES
kdenliveeffectscategory.rc
kdenlivetranscodingrc
kdenlivedefaultlayouts.rc
slideanimations.rc
DESTINATION ${DATA_INSTALL_DIR}${DATA_INSTALL_PREFIX})
install(FILES kdenlive.notifyrc DESTINATION ${KNOTIFYRC_INSTALL_DIR})
......
[slides]
Pan=0=-5%% -5%% 107%% 107%%;%d=0%% 0%% 102%% 102%%;%d=0%% 0%% 106%% 106%%;%d=0%% -4%% 108%% 108%%;%d=0%% -4%% 108%% 108%%;%d=-5%% -5%% 108%% 108%%;%d=0%% -5%% 108%% 108%%;%d=-5%% 0%% 108%% 108%%
Pan and zoom=0=0%% 0%% 100%% 100%%;%d=-8%% -8%% 107%% 107%%;%d=-5%% -5%% 110%% 110%%;%d=0%% 0%% 100%% 100%%;%d=2%% 5%% 108%% 108%%;%d=-5%% 0%% 105%% 105%%
Zoom=0=0%% 0%% 100%% 100%%;%d=-6%% -6%% 112%% 112%%
......@@ -4160,6 +4160,7 @@ void Bin::showSlideshowWidget(const std::shared_ptr<ProjectClip> &clip)
properties.insert(QStringLiteral("luma_file"), dia->lumaFile());
properties.insert(QStringLiteral("softness"), QString::number(dia->softness()));
properties.insert(QStringLiteral("animation"), dia->animation());
properties.insert(QStringLiteral("low-pass"), QString::number(dia->lowPass()));
QMap<QString, QString> oldProperties;
oldProperties.insert(QStringLiteral("out"), clip->getProducerProperty(QStringLiteral("out")));
......@@ -4173,6 +4174,8 @@ void Bin::showSlideshowWidget(const std::shared_ptr<ProjectClip> &clip)
oldProperties.insert(QStringLiteral("luma_file"), clip->getProducerProperty(QStringLiteral("luma_file")));
oldProperties.insert(QStringLiteral("softness"), clip->getProducerProperty(QStringLiteral("softness")));
oldProperties.insert(QStringLiteral("animation"), clip->getProducerProperty(QStringLiteral("animation")));
oldProperties.insert(QStringLiteral("low-pass"), clip->getProducerProperty(QStringLiteral("low-pass")));
slotEditClipCommand(clip->AbstractProjectItem::clipId(), oldProperties, properties);
}
delete dia;
......
......@@ -1277,7 +1277,7 @@ void ProjectClip::setProperties(const QMap<QString, QString> &properties, bool r
QStringLiteral("force_colorspace"), QStringLiteral("force_tff"), QStringLiteral("force_progressive"), QStringLiteral("video_delay")
};
QStringList forceReloadProperties{QStringLiteral("rotate"),QStringLiteral("autorotate"), QStringLiteral("templatetext"), QStringLiteral("resource"), QStringLiteral("force_fps"), QStringLiteral("set.test_image"), QStringLiteral("video_index"), QStringLiteral("disable_exif")};
QStringList keys{QStringLiteral("luma_duration"), QStringLiteral("luma_file"), QStringLiteral("fade"), QStringLiteral("ttl"), QStringLiteral("softness"), QStringLiteral("crop"), QStringLiteral("animation")};
QStringList keys{QStringLiteral("luma_duration"), QStringLiteral("luma_file"), QStringLiteral("fade"), QStringLiteral("ttl"), QStringLiteral("softness"), QStringLiteral("crop"), QStringLiteral("animation"), QStringLiteral("low-pass")};
QVector<int> updateRoles;
while (i.hasNext()) {
i.next();
......
......@@ -237,6 +237,7 @@ void ClipCreationDialog::createSlideshowClip(KdenliveDoc *doc, const QString &pa
if (dia->exec() == QDialog::Accepted) {
// Ready, create xml
KRecentDirs::add(QStringLiteral(":KdenliveSlideShowFolder"), QUrl::fromLocalFile(dia->selectedPath()).adjusted(QUrl::RemoveFilename).toLocalFile());
KdenliveSettings::setSlideshowmimeextension(dia->extension());
std::unordered_map<QString, QString> properties;
properties[QStringLiteral("ttl")] = QString::number(doc->getFramePos(dia->clipDuration()));
properties[QStringLiteral("loop")] = QString::number(static_cast<int>(dia->loop()));
......@@ -246,6 +247,7 @@ void ClipCreationDialog::createSlideshowClip(KdenliveDoc *doc, const QString &pa
properties[QStringLiteral("luma_file")] = dia->lumaFile();
properties[QStringLiteral("softness")] = QString::number(dia->softness());
properties[QStringLiteral("animation")] = dia->animation();
properties[QStringLiteral("low-pass")] = QString::number(dia->lowPass());
int duration = doc->getFramePos(dia->clipDuration()) * dia->imageCount();
ClipCreator::createSlideshowClip(dia->selectedPath(), duration, dia->clipName(), parentId, properties, std::move(model));
......
......@@ -167,18 +167,23 @@ void ClipLoadTask::processSlideShow(std::shared_ptr<Mlt::Producer> producer)
{
int ttl = Xml::getXmlProperty(m_xml, QStringLiteral("ttl")).toInt();
QString anim = Xml::getXmlProperty(m_xml, QStringLiteral("animation"));
bool lowPass = Xml::getXmlProperty(m_xml, QStringLiteral("low-pass"), QStringLiteral("0")).toInt() == 1;
if (lowPass) {
auto *blur = new Mlt::Filter(*pCore->getProjectProfile(), "avfilter.avgblur");
if ((blur == nullptr) || !blur->is_valid()) {
delete blur;
blur = new Mlt::Filter(*pCore->getProjectProfile(), "boxblur");
}
if ((blur != nullptr) && blur->is_valid()) {
producer->attach(*blur);
}
}
if (!anim.isEmpty()) {
auto *filter = new Mlt::Filter(*pCore->getProjectProfile(), "affine");
if ((filter != nullptr) && filter->is_valid()) {
int cycle = ttl;
QString geometry = SlideshowClip::animationToGeometry(anim, cycle);
if (!geometry.isEmpty()) {
if (anim.contains(QStringLiteral("low-pass"))) {
auto *blur = new Mlt::Filter(*pCore->getProjectProfile(), "boxblur");
if ((blur != nullptr) && blur->is_valid()) {
producer->attach(*blur);
}
}
filter->set("transition.rect", geometry.toUtf8().data());
filter->set("transition.cycle", cycle);
filter->set("transition.mirror_off", 1);
......
......@@ -1049,6 +1049,11 @@
<default>true</default>
</entry>
<entry name="slideshowmimeextension" type="String">
<label>The default image extension for slideshows.</label>
<default></default>
</entry>
<entry name="monitorscene_directupdate" type="Bool">
<label>Update parameters while monitor scene changes.</label>
<default>false</default>
......
......@@ -57,12 +57,21 @@ SlideshowClip::SlideshowClip(const Timecode &tc, QString clipFolder, ProjectClip
m_view.image_type->addItem(i18n("Preview from CR2 (*.cr2)"), QStringLiteral("cr2"));
m_view.image_type->addItem(i18n("Preview from ARW (*.arw)"), QStringLiteral("arw"));
m_view.animation->addItem(i18n("None"), QString());
m_view.animation->addItem(i18nc("Image Pan", "Pan"), QStringLiteral("Pan"));
m_view.animation->addItem(i18n("Pan, low-pass"), QStringLiteral("Pan, low-pass"));
m_view.animation->addItem(i18n("Pan and zoom"), QStringLiteral("Pan and zoom"));
m_view.animation->addItem(i18n("Pan and zoom, low-pass"), QStringLiteral("Pan and zoom, low-pass"));
m_view.animation->addItem(i18n("Zoom"), QStringLiteral("Zoom"));
m_view.animation->addItem(i18n("Zoom, low-pass"), QStringLiteral("Zoom, low-pass"));
KConfig conf(QStringLiteral("slideanimations.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
KConfigGroup group(&conf, "slides");
QMap<QString,QString>slideTranslations;
slideTranslations.insert(QStringLiteral("Pan"), i18nc("Image Pan", "Pan"));
slideTranslations.insert(QStringLiteral("Pan and zoom"), i18n("Pan and zoom"));
slideTranslations.insert(QStringLiteral("Zoom"), i18n("Zoom"));
QStringList animValues = group.keyList();
for (const auto &val : animValues) {
if (slideTranslations.contains(val)) {
m_view.animation->addItem(slideTranslations.value(val), val);
} else {
m_view.animation->addItem(val, val);
}
}
m_view.clip_duration->setInputMask(m_timecode.mask());
m_view.luma_duration->setInputMask(m_timecode.mask());
......@@ -86,11 +95,9 @@ SlideshowClip::SlideshowClip(const Timecode &tc, QString clipFolder, ProjectClip
m_view.folder_url->setText(QFileInfo(url).absolutePath());
QString filter = QFileInfo(url).fileName();
QString ext = filter.section(QLatin1Char('.'), -1);
for (int i = 0; i < m_view.image_type->count(); ++i) {
if (m_view.image_type->itemData(i).toString() == ext) {
m_view.image_type->setCurrentIndex(i);
break;
}
int ix = m_view.image_type->findData(ext);
if (ix > -1) {
m_view.image_type->setCurrentIndex(ix);
}
} else {
// the image sequence is defined by pattern
......@@ -98,6 +105,12 @@ SlideshowClip::SlideshowClip(const Timecode &tc, QString clipFolder, ProjectClip
m_view.pattern_url->setText(url);
}
} else {
if (!KdenliveSettings::slideshowmimeextension().isEmpty()) {
int ix = m_view.image_type->findData(KdenliveSettings::slideshowmimeextension());
if (ix > -1) {
m_view.image_type->setCurrentIndex(ix);
}
}
m_view.method_mime->setChecked(KdenliveSettings::slideshowbymime());
slotMethodChanged(m_view.method_mime->isChecked());
}
......@@ -155,6 +168,7 @@ SlideshowClip::SlideshowClip(const Timecode &tc, QString clipFolder, ProjectClip
} else {
m_view.animation->setCurrentIndex(0);
}
m_view.low_pass->setChecked(clip->getProducerProperty(QStringLiteral("low-pass")) == QLatin1String("1"));
int ttl = clip->getProducerIntProperty(QStringLiteral("ttl"));
m_view.clip_duration->setText(tc.getTimecodeFromFrames(ttl));
m_view.clip_duration_frames->setValue(ttl);
......@@ -170,6 +184,10 @@ SlideshowClip::SlideshowClip(const Timecode &tc, QString clipFolder, ProjectClip
slotEnableLumaFile(m_view.luma_fade->checkState());
parseFolder();
}
m_view.low_pass->setEnabled(!m_view.animation->currentData().isNull());
connect(m_view.animation, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, [&]() {
m_view.low_pass->setEnabled(!m_view.animation->currentData().isNull());
});
// adjustSize();
}
......@@ -242,7 +260,7 @@ void SlideshowClip::parseFolder()
QString filter;
if (isMime) {
// TODO: improve jpeg image detection with extension like jpeg, requires change in MLT image producers
filter = m_view.image_type->itemData(m_view.image_type->currentIndex()).toString();
filter = m_view.image_type->currentData().toString();
filters << QStringLiteral("*.") + filter;
dir.setNameFilters(filters);
result = dir.entryList(QDir::Files);
......@@ -355,7 +373,7 @@ QString SlideshowClip::selectedPath()
url = m_view.pattern_url->url();
}
QString path = selectedPath(url, m_view.method_mime->isChecked(),
QStringLiteral(".all.") + m_view.image_type->itemData(m_view.image_type->currentIndex()).toString(), &list);
QStringLiteral(".all.") + m_view.image_type->currentData().toString(), &list);
m_count = list.count();
// qCDebug(KDENLIVE_LOG)<<"// SELECTED PATH: "<<path;
return path;
......@@ -524,22 +542,33 @@ void SlideshowClip::slotMethodChanged(bool active)
parseFolder();
}
int SlideshowClip::lowPass() const
{
return m_view.low_pass->isEnabled() && m_view.low_pass->isChecked() ? 1 : 0;
}
// static
QString SlideshowClip::animationToGeometry(const QString &animation, int &ttl)
{
// load animation profiles
KConfig conf(QStringLiteral("slideanimations.rc"), KConfig::CascadeConfig, QStandardPaths::AppDataLocation);
KConfigGroup group(&conf, "slides");
QString geometry;
if (animation.startsWith(QLatin1String("Pan and zoom"))) {
geometry = QString::asprintf(
"0=0%% 0%% 100%% 100%%;%d=-8%% -8%% 107%% 107%%;%d=-5%% -5%% 110%% 110%%;%d=0%% 0%% 100%% 100%%;%d=2%% 5%% 108%% 108%%;%d=-5%% 0%% 105%% 105%%", ttl - 1,
ttl, ttl * 2 - 1, ttl * 2, ttl * 3 - 1);
ttl *= 3;
} else if (animation.startsWith(QLatin1String("Pan"))) {
geometry = QString::asprintf("0=-5%% -5%% 107%% 107%%;%d=0%% 0%% 102%% 102%%;%d=0%% 0%% 106%% 106%%;%d=0%% -4%% 108%% 108%%;%d=0%% -4%% 108%% 108%%;%d=-5%%/"
"-5%% 108%% 108%%;%d=0%% -5%% 108%% 108%%;%d=-5%% 0%% 108%% 108%%",
ttl - 1, ttl, ttl * 2 - 1, ttl * 2, ttl * 3 - 1, ttl * 3, ttl * 4 - 1);
ttl *= 4;
} else if (animation.startsWith(QLatin1String("Zoom"))) {
geometry = QString::asprintf("0=0%% 0%% 100%% 100%%;%d=-6%% -6%% 112%% 112%%", ttl - 1);
if (group.hasKey(animation)) {
geometry = group.readEntry(animation);
}
int frames = geometry.count(QLatin1String("%d="));
int frameNumber = ttl - 1;
QString str = QStringLiteral("%d");
for (int i = 0; i < frames; i++) {
geometry.replace(geometry.indexOf(str), 2, QString::number(frameNumber));
frameNumber = qFloor((i + 3)/2) * ttl;
frameNumber -= (i % 2);
}
return geometry;
}
const QString SlideshowClip::extension() const
{
return m_view.image_type->currentData().toString();
}
......@@ -34,6 +34,10 @@ public:
QString lumaFile() const;
int softness() const;
QString animation() const;
/** @brief Should we add a low-pass (blur) filter */
int lowPass() const;
/** @brief The selected image extension */
const QString extension() const;
/** @brief Get the image frame number from a file path, for example image_047.jpg will return 47. */
static int getFrameNumberFromPath(const QUrl &path);
......
......@@ -6,119 +6,42 @@
<rect>
<x>0</x>
<y>0</y>
<width>330</width>
<height>582</height>
<width>354</width>
<height>631</height>
</rect>
</property>
<property name="windowTitle">
<string>Slideshow Clip</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Name:</string>
<property name="verticalSpacing">
<number>6</number>
</property>
<item row="12" column="0" colspan="4">
<widget class="QListWidget" name="icon_list"/>
</item>
<item row="10" column="2" colspan="2">
<widget class="KComboBox" name="animation">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="clip_name"/>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="slide_crop">
<property name="text">
<string>Center crop</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="4">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Image Selection Method</string>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Name:</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QRadioButton" name="method_mime">
<property name="text">
<string>&amp;MIME type</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="method_pattern">
<property name="text">
<string>Fi&amp;lename pattern</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Folder:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KUrlRequester" name="folder_url"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Image type:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="KComboBox" name="image_type"/>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>First frame</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="KUrlRequester" name="pattern_url"/>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0" colspan="2">
......@@ -151,20 +74,16 @@
</item>
</layout>
</item>
<item row="2" column="3">
<widget class="KComboBox" name="clip_duration_format"/>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="slide_loop">
<property name="text">
<string>Loop</string>
<item row="8" column="2" colspan="2">
<widget class="QSlider" name="luma_softness">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QCheckBox" name="slide_fade">
<property name="text">
<string>Dissolve:</string>
<property name="maximum">
<number>100</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
......@@ -194,7 +113,7 @@
</item>
</layout>
</item>
<item row="6" column="0">
<item row="7" column="0">
<widget class="QCheckBox" name="luma_fade">
<property name="enabled">
<bool>false</bool>
......@@ -204,14 +123,21 @@
</property>
</widget>
</item>
<item row="6" column="2" colspan="2">
<item row="5" column="0">
<widget class="QCheckBox" name="slide_fade">
<property name="text">
<string>Dissolve:</string>
</property>
</widget>
</item>
<item row="7" column="2" colspan="2">
<widget class="KComboBox" name="luma_file">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="7" column="0">
<item row="8" column="0">
<widget class="QLabel" name="label_softness">
<property name="enabled">
<bool>false</bool>
......@@ -221,23 +147,127 @@
</property>
</widget>
</item>
<item row="7" column="2" colspan="2">
<widget class="QSlider" name="luma_softness">
<property name="enabled">
<bool>false</bool>
</property>
<property name="maximum">
<number>100</number>
<item row="1" column="0" colspan="4">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Image Selection Method</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="verticalSpacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="method_mime">
<property name="text">
<string>&amp;MIME type</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QRadioButton" name="method_pattern">
<property name="text">
<string>Fi&amp;lename pattern</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Folder:</string>
</property>
</widget>
</item>
<item row="2" column="2">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Image type:</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="2">
<widget class="KUrlRequester" name="folder_url"/>
</item>
<item row="1" column="1" colspan="2">
<widget class="KComboBox" name="image_type"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_2">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>First frame</string>
</property>
</widget>
</item>
<item row="1" column="2">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1" colspan="2">
<widget class="KUrlRequester" name="pattern_url"/>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
<item row="14" column="2" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="9" column="0" colspan="4">
<widget class="QListWidget" name="icon_list"/>
<item row="3" column="0">
<widget class="QCheckBox" name="slide_loop">
<property name="text">
<string>Loop</string>
</property>
</widget>
</item>
<item row="10" column="0" colspan="4">
<item row="0" column="1" colspan="3">
<widget class="QLineEdit" name="clip_name"/>
</item>
<item row="13" column="0" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="show_thumbs">
......@@ -268,37 +298,23 @@
</item>
</layout>
</item>
<item row="11" column="2" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>