aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Baptiste Mardelle <[email protected]>2015-05-22 22:59:49 +0200
committerJean-Baptiste Mardelle <[email protected]>2015-05-22 22:59:49 +0200
commit601d93f07202cfaacbc0c76cced6381ff8c514f9 (patch)
tree4c9147f38d58a020d44191ba2bd5e30bbd1e18f1
parent571b63c546407ea96e810b68c55b68325da1e2b9 (diff)
Load subclip thumbnails, sort by in point, fix bin display
-rw-r--r--src/bin/abstractprojectitem.cpp4
-rw-r--r--src/bin/abstractprojectitem.h1
-rw-r--r--src/bin/bin.cpp14
-rw-r--r--src/bin/bin.h110
-rw-r--r--src/bin/projectclip.cpp57
-rw-r--r--src/bin/projectclip.h3
-rw-r--r--src/bin/projectsortproxymodel.cpp1
-rw-r--r--src/bin/projectsubclip.cpp12
-rw-r--r--src/bin/projectsubclip.h2
-rw-r--r--src/doc/documentchecker.cpp2
10 files changed, 150 insertions, 56 deletions
diff --git a/src/bin/abstractprojectitem.cpp b/src/bin/abstractprojectitem.cpp
index 9accf3c..e1631b8 100644
--- a/src/bin/abstractprojectitem.cpp
+++ b/src/bin/abstractprojectitem.cpp
@@ -168,6 +168,10 @@ QVariant AbstractProjectItem::data(DataType type) const
{
QVariant data;
switch (type) {
+ case SortRole:
+ if (m_itemType == SubClipItem) data = QVariant(m_duration);
+ else data = QVariant(m_name);
+ break;
case DataName:
data = QVariant(m_name);
break;
diff --git a/src/bin/abstractprojectitem.h b/src/bin/abstractprojectitem.h
index 4f7e2a6..6dc5dab 100644
--- a/src/bin/abstractprojectitem.h
+++ b/src/bin/abstractprojectitem.h
@@ -120,6 +120,7 @@ public:
DataName = Qt::DisplayRole,
DataThumbnail = Qt::DecorationRole,
DataId = Qt::UserRole,
+ SortRole,
DataDescription,
DataDate,
IconOverlay,
diff --git a/src/bin/bin.cpp b/src/bin/bin.cpp
index 3a52f0a..4051d2b 100644
--- a/src/bin/bin.cpp
+++ b/src/bin/bin.cpp
@@ -55,6 +55,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QSlider>
#include <QMenu>
#include <QDebug>
+#include <QtConcurrent>
#include <QUndoCommand>
@@ -1746,15 +1747,23 @@ void Bin::loadSubClips(const QString&id, const QMap <QString,QString> data)
ProjectClip *clip = getBinClip(id);
if (!clip) return;
QMapIterator<QString, QString> i(data);
+ QDir thumbsFolder(projectFolder().path() + "/thumbs/");
+ QList <int> missingThumbs;
while (i.hasNext()) {
i.next();
if (!i.value().contains(";")) {
// Problem, the zone has no in/out points
continue;
}
+ QImage img;
int in = i.value().section(";", 0, 0).toInt();
int out = i.value().section(";", 1, 1).toInt();
- new ProjectSubClip(clip, in, out, i.key());
+ missingThumbs << in;
+ new ProjectSubClip(clip, in, out, m_doc->timecode().getDisplayTimecodeFromFrames(in, KdenliveSettings::frametimecode()), i.key());
+ }
+ if (!missingThumbs.isEmpty()) {
+ // generate missing subclip thumbnails
+ QtConcurrent::run(clip, &ProjectClip::slotExtractSubImage, missingThumbs);
}
}
@@ -1768,7 +1777,8 @@ void Bin::addClipCut(const QString&id, int in, int out)
// A subclip with same zone already exists
return;
}
- sub = new ProjectSubClip(clip, in, out);
+ sub = new ProjectSubClip(clip, in, out, m_doc->timecode().getDisplayTimecodeFromFrames(in, KdenliveSettings::frametimecode()));
+ QtConcurrent::run(clip, &ProjectClip::slotExtractSubImage, QList <int>() << in);
}
void Bin::removeClipCut(const QString&id, int in, int out)
diff --git a/src/bin/bin.h b/src/bin/bin.h
index 0a38ffd..d026df1 100644
--- a/src/bin/bin.h
+++ b/src/bin/bin.h
@@ -130,7 +130,7 @@ public:
return QSize(hint.width(), qMax(option.fontMetrics.lineSpacing() * 2 + 4, qMax(hint.height(), option.decorationSize.height())));
}
if (type == AbstractProjectItem::SubClipItem) {
- return QSize(hint.width(), qMin((int) (option.fontMetrics.lineSpacing() * 1.5) + 4, hint.height()));
+ return QSize(hint.width(), qMax(option.fontMetrics.lineSpacing() * 2 + 4, qMin(hint.height(), (int) (option.decorationSize.height() / 1.5))));
}
QIcon icon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
QString line1 = index.data(Qt::DisplayRole).toString();
@@ -138,7 +138,6 @@ public:
int textW = qMax(option.fontMetrics.width(line1), option.fontMetrics.width(line2));
QSize iconSize = icon.actualSize(option.decorationSize);
-
return QSize(qMax(textW, iconSize.width()) + 4, option.fontMetrics.lineSpacing() * 2 + 4);
}
@@ -158,16 +157,20 @@ public:
if (option.state & QStyle::State_Selected) {
painter->setPen(option.palette.highlightedText().color());
}
+ else painter->setPen(option.palette.text().color());
QRect r = r1;
- r.setWidth(opt.decorationSize.width());
- // Draw thumbnail
- opt.icon.paint(painter, r);
-
- int decoWidth = r.width() + 2 * textMargin;
QFont font = painter->font();
font.setBold(true);
painter->setFont(font);
- if (type == AbstractProjectItem::ClipItem) {
+ if (type == AbstractProjectItem::ClipItem || type == AbstractProjectItem::SubClipItem) {
+ double factor = (double) opt.decorationSize.height() / r1.height();
+ int decoWidth = 2 * textMargin;
+ if (factor != 0) {
+ r.setWidth(opt.decorationSize.width() / factor);
+ // Draw thumbnail
+ opt.icon.paint(painter, r);
+ decoWidth += r.width();
+ }
int mid = (int)((r1.height() / 2));
r1.adjust(decoWidth, 0, 0, -mid);
QRect r2 = option.rect;
@@ -186,52 +189,63 @@ public:
painter->setPen(subTextColor);
painter->drawText(r2, Qt::AlignLeft | Qt::AlignTop , subText, &bounding);
- // Overlay icon if necessary
- QVariant v = index.data(AbstractProjectItem::IconOverlay);
- if (!v.isNull()) {
- QIcon reload = QIcon::fromTheme(v.toString());
- r.setTop(r.bottom() - bounding.height());
- r.setWidth(bounding.height());
- reload.paint(painter, r);
- }
-
- int jobProgress = index.data(AbstractProjectItem::JobProgress).toInt();
- if (jobProgress != 0 && jobProgress != JobDone && jobProgress != JobAborted) {
- if (jobProgress != JobCrashed) {
- // Draw job progress bar
- QColor color = option.palette.alternateBase().color();
- painter->setPen(Qt::NoPen);
- color.setAlpha(180);
- painter->setBrush(QBrush(color));
- QRect progress(r1.x() + 1, opt.rect.bottom() - 12, r1.width() / 2, 8);
- painter->drawRect(progress);
- painter->setBrush(option.palette.text());
- if (jobProgress > 0) {
- progress.adjust(1, 1, 0, -1);
- progress.setWidth((progress.width() - 4) * jobProgress / 100);
- painter->drawRect(progress);
- } else if (jobProgress == JobWaiting) {
- // Draw kind of a pause icon
- progress.adjust(1, 1, 0, -1);
- progress.setWidth(2);
- painter->drawRect(progress);
- progress.moveLeft(progress.right() + 2);
- painter->drawRect(progress);
- }
- } else if (jobProgress == JobCrashed) {
- QString jobText = index.data(AbstractProjectItem::JobMessage).toString();
- if (!jobText.isEmpty()) {
- QRectF txtBounding = painter->boundingRect(r2, Qt::AlignRight | Qt::AlignVCenter, " " + jobText + " ");
+ if (type == AbstractProjectItem::ClipItem) {
+ // Overlay icon if necessary
+ QVariant v = index.data(AbstractProjectItem::IconOverlay);
+ if (!v.isNull()) {
+ QIcon reload = QIcon::fromTheme(v.toString());
+ r.setTop(r.bottom() - bounding.height());
+ r.setWidth(bounding.height());
+ reload.paint(painter, r);
+ }
+
+ int jobProgress = index.data(AbstractProjectItem::JobProgress).toInt();
+ if (jobProgress != 0 && jobProgress != JobDone && jobProgress != JobAborted) {
+ if (jobProgress != JobCrashed) {
+ // Draw job progress bar
+ QColor color = option.palette.alternateBase().color();
painter->setPen(Qt::NoPen);
- painter->setBrush(option.palette.highlight());
- painter->drawRoundedRect(txtBounding, 2, 2);
- painter->setPen(option.palette.highlightedText().color());
- painter->drawText(txtBounding, Qt::AlignCenter, jobText);
+ color.setAlpha(180);
+ painter->setBrush(QBrush(color));
+ QRect progress(r1.x() + 1, opt.rect.bottom() - 12, r1.width() / 2, 8);
+ painter->drawRect(progress);
+ painter->setBrush(option.palette.text());
+ if (jobProgress > 0) {
+ progress.adjust(1, 1, 0, -1);
+ progress.setWidth((progress.width() - 4) * jobProgress / 100);
+ painter->drawRect(progress);
+ } else if (jobProgress == JobWaiting) {
+ // Draw kind of a pause icon
+ progress.adjust(1, 1, 0, -1);
+ progress.setWidth(2);
+ painter->drawRect(progress);
+ progress.moveLeft(progress.right() + 2);
+ painter->drawRect(progress);
+ }
+ } else if (jobProgress == JobCrashed) {
+ QString jobText = index.data(AbstractProjectItem::JobMessage).toString();
+ if (!jobText.isEmpty()) {
+ QRectF txtBounding = painter->boundingRect(r2, Qt::AlignRight | Qt::AlignVCenter, " " + jobText + " ");
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(option.palette.highlight());
+ painter->drawRoundedRect(txtBounding, 2, 2);
+ painter->setPen(option.palette.highlightedText().color());
+ painter->drawText(txtBounding, Qt::AlignCenter, jobText);
+ }
}
}
}
}
else {
+ // Folder or Folder Up items
+ double factor = (double) opt.decorationSize.height() / r1.height();
+ int decoWidth = 2 * textMargin;
+ if (factor != 0) {
+ r.setWidth(opt.decorationSize.width() / factor);
+ // Draw thumbnail
+ opt.icon.paint(painter, r);
+ decoWidth += r.width();
+ }
r1.adjust(decoWidth, 0, 0, 0);
QRectF bounding;
painter->drawText(r1, Qt::AlignLeft | Qt::AlignTop, index.data(AbstractProjectItem::DataName).toString(), &bounding);
diff --git a/src/bin/projectclip.cpp b/src/bin/projectclip.cpp
index 686af3b..bc2b5b7 100644
--- a/src/bin/projectclip.cpp
+++ b/src/bin/projectclip.cpp
@@ -661,8 +661,8 @@ void ProjectClip::slotExtractImage(QList <int> frames)
QDir thumbFolder(bin()->projectFolder().path() + "/thumbs/");
for (int i = 0; i < frames.count(); i++) {
int pos = frames.at(i);
- if (thumbFolder.exists(hash() + '_' + QString::number(pos) + ".png")) {
- emit thumbReady(pos, QImage(thumbFolder.absoluteFilePath(hash() + '_' + QString::number(pos) + ".png")));
+ if (thumbFolder.exists(hash() + '#' + QString::number(pos) + ".png")) {
+ emit thumbReady(pos, QImage(thumbFolder.absoluteFilePath(hash() + '#' + QString::number(pos) + ".png")));
continue;
}
int max = prod->get_out();
@@ -677,6 +677,59 @@ void ProjectClip::slotExtractImage(QList <int> frames)
}
}
+void ProjectClip::slotExtractSubImage(QList <int> frames)
+{
+ Mlt::Producer *prod = producer();
+ if (prod == NULL) return;
+ // Check if we are using GPU accel, then we need to use alternate producer
+ if (KdenliveSettings::gpu_accel()) {
+ if (m_gpuProducer == NULL) {
+ QString service = prod->get("mlt_service");
+ m_gpuProducer = new Mlt::Producer(*prod->profile(), service.toUtf8().constData(), prod->get("resource"));
+ Mlt::Filter scaler(*prod->profile(), "swscale");
+ Mlt::Filter converter(*prod->profile(), "avcolor_space");
+ m_gpuProducer->attach(scaler);
+ m_gpuProducer->attach(converter);
+ }
+ prod = m_gpuProducer;
+ }
+ int imageWidth = (int)((double) 150 * prod->profile()->width() / prod->profile()->height() + 0.5);
+ int fullWidth = (int)((double) 150 * prod->profile()->dar() + 0.5);
+ QDir thumbFolder(bin()->projectFolder().path() + "/thumbs/");
+ for (int i = 0; i < frames.count(); i++) {
+ int pos = frames.at(i);
+ QImage img(thumbFolder.absolutePath() + '#' + QString::number(pos) + ".png");
+ if (!img.isNull()) {
+ ProjectSubClip *clip;
+ for (int i = 0; i < count(); ++i) {
+ clip = static_cast<ProjectSubClip *>(at(i));
+ if (clip && clip->zone().x() == pos) {
+ clip->setThumbnail(img);
+ }
+ }
+ continue;
+ }
+ int max = prod->get_out();
+ if (pos >= max) pos = max - 1;
+ prod->seek(pos);
+ Mlt::Frame *frame = prod->get_frame();
+ if (frame && frame->is_valid()) {
+ QImage img = KThumb::getFrame(frame, imageWidth, fullWidth, 150);
+ if (!img.isNull()) {
+ img.save(thumbFolder.absoluteFilePath(hash() + '#' + QString::number(pos) + ".png"));
+ ProjectSubClip *clip;
+ for (int i = 0; i < count(); ++i) {
+ clip = static_cast<ProjectSubClip *>(at(i));
+ if (clip && clip->zone().x() == pos) {
+ clip->setThumbnail(img);
+ }
+ }
+ }
+ }
+ delete frame;
+ }
+}
+
int ProjectClip::audioChannels() const
{
if (!m_controller || !m_controller->audioInfo()) return 0;
diff --git a/src/bin/projectclip.h b/src/bin/projectclip.h
index 66b63af..1a4edb9 100644
--- a/src/bin/projectclip.h
+++ b/src/bin/projectclip.h
@@ -201,7 +201,10 @@ public:
public slots:
void updateAudioThumbnail(QVariantList* audioLevels);
+ /** @brief Extract image thumbnails for timeline. */
void slotExtractImage(QList <int> frames);
+ /** @brief Extract image thumbnails for clip's subclips. */
+ void slotExtractSubImage(QList <int> frames);
void slotCreateAudioThumbs();
protected:
diff --git a/src/bin/projectsortproxymodel.cpp b/src/bin/projectsortproxymodel.cpp
index 220543d..9d02840 100644
--- a/src/bin/projectsortproxymodel.cpp
+++ b/src/bin/projectsortproxymodel.cpp
@@ -30,6 +30,7 @@ ProjectSortProxyModel::ProjectSortProxyModel(QObject *parent)
{
m_selection = new QItemSelectionModel(this);
connect(m_selection, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(onCurrentRowChanged(QItemSelection,QItemSelection)));
+ setSortRole(AbstractProjectItem::SortRole);
setDynamicSortFilter(true);
}
diff --git a/src/bin/projectsubclip.cpp b/src/bin/projectsubclip.cpp
index 98eca51..9798dac 100644
--- a/src/bin/projectsubclip.cpp
+++ b/src/bin/projectsubclip.cpp
@@ -28,7 +28,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
class ClipController;
-ProjectSubClip::ProjectSubClip(ProjectClip *parent, int in, int out, const QString &name) :
+ProjectSubClip::ProjectSubClip(ProjectClip *parent, int in, int out, const QString &timecode, const QString &name) :
AbstractProjectItem(AbstractProjectItem::SubClipItem, parent->clipId(), parent)
, m_masterClip(parent)
, m_in(in)
@@ -38,13 +38,14 @@ ProjectSubClip::ProjectSubClip(ProjectClip *parent, int in, int out, const QStri
pix.fill(Qt::lightGray);
m_thumbnail = QIcon(pix);
if (name.isEmpty()) {
- m_name = i18n("Zone %1-%2", in, out);
+ m_name = i18n("Zone %1", parent->count() + 1);
}
else {
m_name = name;
}
m_clipStatus = StatusReady;
setParent(parent);
+ m_duration = timecode;
// Save subclip in MLT
parent->setProducerProperty("kdenlive:clipzone." + m_name, QString::number(in) + ";" + QString::number(out));
}
@@ -113,6 +114,13 @@ void ProjectSubClip::setCurrent(bool current, bool notify)
}
}
+void ProjectSubClip::setThumbnail(QImage img)
+{
+ QPixmap thumb = roundedPixmap(QPixmap::fromImage(img));
+ m_thumbnail = QIcon(thumb);
+ bin()->emitItemUpdated(this);
+}
+
bool ProjectSubClip::rename(const QString &name)
{
if (m_name == name) return false;
diff --git a/src/bin/projectsubclip.h b/src/bin/projectsubclip.h
index b0799e7..763ea09 100644
--- a/src/bin/projectsubclip.h
+++ b/src/bin/projectsubclip.h
@@ -53,7 +53,7 @@ public:
/**
* @brief Constructor; used when loading a project and the producer is already available.
*/
- ProjectSubClip(ProjectClip *parent, int in, int out, const QString &name = QString());
+ ProjectSubClip(ProjectClip *parent, int in, int out, const QString &timecode, const QString &name = QString());
virtual ~ProjectSubClip();
ProjectClip *clip(const QString &id);
diff --git a/src/doc/documentchecker.cpp b/src/doc/documentchecker.cpp
index c33c164..5080ba6 100644
--- a/src/doc/documentchecker.cpp
+++ b/src/doc/documentchecker.cpp
@@ -107,7 +107,7 @@ bool DocumentChecker::hasErrorInClips()
}
// Check for slideshows
if ((service == "qimage" || service == "pixbuf") && QUrl::fromLocalFile(resource).fileName().contains("*")) resource = QUrl::fromLocalFile(resource).adjusted(QUrl::RemoveFilename).path();
- if (!QFile::exists(resource)) {
+ if (!QFile::exists(resource) && !EffectsList::property(e, "kdenlive:file_size").isEmpty()) {
// Missing clip found
m_missingClips.append(e);
} else {