#include "abstractclipitem.h"
#include "abstractgroupitem.h"
#include "titledocument.h"
+#include "subprojectitem.h"
#include "kthumb.h"
#include <mlt++/Mlt.h>
QMap<QString, int>::const_iterator i;
int max;
int done = 0;
+ int thumbType = 0; // 0 = timeline thumb, 1 = project clip zone thumb, 2 = clip properties thumb
+
while (!m_requestedThumbs.isEmpty() && !m_abortThumb) {
m_thumbsMutex.lock();
i = m_requestedThumbs.constBegin();
m_processingThumbId = i.key();
QList<int> values = m_requestedThumbs.values(m_processingThumbId);
m_requestedThumbs.remove(m_processingThumbId);
+ if (m_processingThumbId.startsWith("?")) {
+ // if id starts with ?, it means the request comes from a clip property widget
+ thumbType = 2;
+ m_processingThumbId.remove(0, 1);
+ }
+ if (m_processingThumbId.startsWith("#")) {
+ // if id starts with #, it means the request comes from project tree
+ thumbType = 1;
+ m_processingThumbId.remove(0, 1);
+ }
m_thumbsMutex.unlock();
qSort(values);
DocClipBase *clip = getClipById(m_processingThumbId);
if (!clip) continue;
max = m_requestedThumbs.size() + values.count();
+ int pos;
while (!values.isEmpty() && clip->thumbProducer() && !m_abortThumb) {
- clip->thumbProducer()->getThumb(values.takeFirst());
+ pos = values.takeFirst();
+ switch (thumbType) {
+ case 1:
+ clip->thumbProducer()->getGenericThumb(pos, SubProjectItem::itemDefaultHeight(), thumbType);
+ break;
+ case 2:
+ clip->thumbProducer()->getGenericThumb(pos, 180, thumbType);
+ break;
+ default:
+ clip->thumbProducer()->getThumb(pos);
+ }
done++;
if (max > 3) emit displayMessage(i18n("Loading thumbnails"), 100 * done / max);
}
return volumeMatch;
}
-
+void ClipManager::projectTreeThumbReady(const QString &id, int frame, QImage img, int type)
+{
+ switch (type) {
+ case 2:
+ emit gotClipPropertyThumbnail(id, img);
+ break;
+ default:
+ emit thumbReady(id, frame, img);
+ }
+}
void requestThumbs(const QString id, QList <int> frames);
/** @brief remove a clip id from the queue list. */
void stopThumbs(const QString &id);
+ void projectTreeThumbReady(const QString &id, int frame, QImage img, int type);
#if KDE_IS_VERSION(4,5,0)
KImageCache* pixmapCache;
QString m_processingAudioThumbId;
/** @brief The list of removable drives. */
QList<SolidVolumeInfo> m_removableVolumes;
+
+ QPoint m_projectTreeThumbSize;
/** @brief Get a list of drives, to check if we have files on removable media. */
void listRemovableVolumes();
void availableClip(const QString &);
void checkAllClips(bool displayRatioChanged, bool fpsChanged, QStringList brokenClips);
void displayMessage(const QString &, int);
+ void thumbReady(const QString &id, int, QImage);
+ void gotClipPropertyThumbnail(const QString &id, QImage);
};
#endif
if (props.contains("colorspace"))
new QTreeWidgetItem(m_view.clip_vproperties, QStringList() << i18n("Colorspace") << ProfilesDialog::getColorspaceDescription(props.value("colorspace").toInt()));
-
- int width = 180.0 * KdenliveSettings::project_display_ratio();
- if (width % 2 == 1) width++;
- QPixmap pix = m_clip->thumbProducer()->getImage(url, m_clip->getClipThumbFrame(), width, 180);
- QPixmap framedPix(pix.width(), pix.height());
- framedPix.fill(Qt::transparent);
- QPainter p(&framedPix);
- p.setRenderHint(QPainter::Antialiasing, true);
- QPainterPath path;
- path.addRoundedRect(0.5, 0.5, framedPix.width() - 1, framedPix.height() - 1, 4, 4);
- p.setClipPath(path);
- p.drawPixmap(0, 0, pix);
- p.end();
-
- m_view.clip_thumb->setPixmap(framedPix);
+ m_view.clip_thumb->setMinimumSize(180 * KdenliveSettings::project_display_ratio(), 180);
+
if (t == IMAGE || t == VIDEO || t == PLAYLIST) m_view.tabWidget->removeTab(AUDIOTAB);
} else {
m_view.tabWidget->removeTab(IMAGETAB);
if (del2) delete del2;
}
+void ClipProperties::slotGotThumbnail(const QString &id, QImage img)
+{
+ if (id != m_clip->getId()) return;
+ QPixmap framedPix(img.width(), img.height());
+ framedPix.fill(Qt::transparent);
+ QPainter p(&framedPix);
+ p.setRenderHint(QPainter::Antialiasing, true);
+ QPainterPath path;
+ path.addRoundedRect(0.5, 0.5, framedPix.width() - 1, framedPix.height() - 1, 4, 4);
+ p.setClipPath(path);
+ p.drawImage(0, 0, img);
+ p.end();
+ m_view.clip_thumb->setPixmap(framedPix);
+}
+
void ClipProperties::slotApplyProperties()
{
if (m_clip != NULL) {
void slotSaveMarkers();
void slotLoadMarkers();
void slotDeleteAnalysis();
+ void slotGotThumbnail(const QString &id, QImage img);
private:
Ui::ClipProperties_UI m_view;
if (m_producer) m_clipManager->stopThumbs(m_id);
m_intraFramesQueue.clear();
m_intra.waitForFinished();
- m_mutex.lock();
+ QMutexLocker lock(&m_mutex);
m_producer = producer;
// FIXME: the profile() call leaks an object, but trying to free
// it leads to a double-free in Profile::~Profile()
m_dar = profile->dar();
m_ratio = (double) profile->width() / profile->height();
}
- m_mutex.unlock();
}
void KThumb::clearProducer()
const int theight = KdenliveSettings::trackheight();
const int swidth = (int)(theight * m_ratio + 0.5);
const int dwidth = (int)(theight * m_dar + 0.5);
-
QImage img = getProducerFrame(frame, swidth, dwidth, theight);
emit thumbReady(frame, img);
}
+void KThumb::getGenericThumb(int frame, int height, int type)
+{
+ const int swidth = (int)(height * m_ratio + 0.5);
+ const int dwidth = (int)(height * m_dar + 0.5);
+ QImage img = getProducerFrame(frame, swidth, dwidth, height);
+ m_clipManager->projectTreeThumbReady(m_id, frame, img, type);
+}
+
QImage KThumb::extractImage(int frame, int width, int height)
{
if (m_producer == NULL) {
Mlt::Profile profile(KdenliveSettings::current_profile().toUtf8().constData());
QPixmap pix(width, height);
if (url.isEmpty()) return pix;
-
- //"<mlt><playlist><producer resource=\"" + url.path() + "\" /></playlist></mlt>");
- //Mlt::Producer producer(profile, "xml-string", tmp);
Mlt::Producer *producer = new Mlt::Producer(profile, url.path().toUtf8().constData());
double swidth = (double) profile.width() / profile.height();
pix = QPixmap::fromImage(getFrame(producer, frame, (int) (height * swidth + 0.5), width, height));
p.fill(QColor(Qt::black).rgb());
return p;
}
- m_mutex.lock();
+ QMutexLocker lock(&m_mutex);
m_producer->seek(framepos);
Mlt::Frame *frame = m_producer->get_frame();
+ frame->set("rescale.interp", "nearest");
+ frame->set("deinterlace_method", "onefield");
+ frame->set("top_field_first", -1 );
QImage p = getFrame(frame, frameWidth, displayWidth, height);
delete frame;
- m_mutex.unlock();
return p;
}
producer->seek(framepos);
Mlt::Frame *frame = producer->get_frame();
+ frame->set("rescale.interp", "nearest");
+ frame->set("deinterlace_method", "onefield");
+ frame->set("top_field_first", -1 );
QImage p = getFrame(frame, frameWidth, displayWidth, height);
delete frame;
return p;
p.fill(QColor(Qt::red).rgb());
return p;
}
-
+
int ow = frameWidth;
int oh = height;
mlt_image_format format = mlt_image_rgb24a;
- frame->set("rescale.interp", "nearest");
- frame->set("deinterlace_method", "onefield");
- frame->set("top_field_first", -1 );
//frame->set("progressive", "1");
if (ow % 2 == 1) ow++;
- const uchar* imagedata = frame->get_image(format, ow, oh);
QImage image(ow, oh, QImage::Format_ARGB32_Premultiplied);
+ const uchar* imagedata = frame->get_image(format, ow, oh);
memcpy(image.bits(), imagedata, ow * oh * 4);//.byteCount());
//const uchar* imagedata = frame->get_image(format, ow, oh);
QImage findCachedThumb(const QString &path);
#endif
void getThumb(int frame);
+ void getGenericThumb(int frame, int height, int type);
public slots:
void updateClipUrl(KUrl url, const QString &hash);
// any type of clip but a title
ClipProperties *dia = new ClipProperties(clip, m_activeDocument->timecode(), m_activeDocument->fps(), this);
+
+ if (clip->clipType() == AV || clip->clipType() == VIDEO || clip->clipType() == PLAYLIST) {
+ // request clip thumbnails
+ m_activeDocument->clipManager()->requestThumbs(QString('?' + clip->getId()), QList<int>() << clip->getClipThumbFrame());
+ connect(m_activeDocument->clipManager(), SIGNAL(gotClipPropertyThumbnail(const QString&,QImage)), dia, SLOT(slotGotThumbnail(const QString&,QImage)));
+ }
+
connect(dia, SIGNAL(addMarkers(const QString &, QList <CommentedTime>)), m_activeTimeline->projectView(), SLOT(slotAddClipMarker(const QString &, QList <CommentedTime>)));
connect(dia, SIGNAL(deleteAnalysis(QString,QString)), m_activeTimeline->projectView(), SLOT(slotAddClipExtraData(QString,QString)));
connect(m_activeTimeline->projectView(), SIGNAL(updateClipMarkers(DocClipBase *)), dia, SLOT(slotFillMarkersList(DocClipBase *)));
QList <CutZoneInfo> cuts = clip->cutZones();
if (!cuts.isEmpty()) {
for (int i = 0; i < cuts.count(); i++) {
- SubProjectItem *sub = new SubProjectItem(item, cuts.at(i).zone.x(), cuts.at(i).zone.y(), cuts.at(i).description);
+ SubProjectItem *sub = new SubProjectItem(m_render->dar(), item, cuts.at(i).zone.x(), cuts.at(i).zone.y(), cuts.at(i).description);
if (!clip->getClipHash().isEmpty()) {
QString cachedPixmap = m_doc->projectFolder().path(KUrl::AddTrailingSlash) + "thumbs/" + clip->getClipHash() + '#' + QString::number(cuts.at(i).zone.x()) + ".png";
if (QFile::exists(cachedPixmap)) {
connect(m_doc->clipManager(), SIGNAL(missingClip(const QString &)), this, SLOT(slotMissingClip(const QString &)));
connect(m_doc->clipManager(), SIGNAL(availableClip(const QString &)), this, SLOT(slotAvailableClip(const QString &)));
connect(m_doc->clipManager(), SIGNAL(checkAllClips(bool, bool, QStringList)), this, SLOT(updateAllClips(bool, bool, QStringList)));
+ connect(m_doc->clipManager(), SIGNAL(thumbReady(const QString &, int, QImage)), this, SLOT(slotSetThumbnail(const QString &, int, QImage)));
+}
+
+void ProjectList::slotSetThumbnail(const QString &id, int framePos, QImage img)
+{
+ QString fullid = id + '#' + QString::number(framePos);
+ ProjectItem *pItem = NULL;
+ QTreeWidgetItem *item = getAnyItemById(fullid);
+ if (item && item->parent()) pItem = static_cast <ProjectItem *>(item->parent());
+ if (!item && framePos == 0) pItem = getItemById(id);
+ if (!item && !pItem) return;
+ if (item) item->setData(0, Qt::DecorationRole, QPixmap::fromImage(img));
+ else if (pItem) pItem->setData(0, Qt::DecorationRole, QPixmap::fromImage(img));
+ if (pItem) {
+ QString hash = pItem->getClipHash();
+ if (!hash.isEmpty()) m_doc->cacheImage(hash + '#' + QString::number(framePos), img);
+ }
}
QList <DocClipBase*> ProjectList::documentClipList() const
DocClipBase *base = clip->referencedClip();
base->addCutZone(in, out);
monitorItemEditing(false);
- SubProjectItem *sub = new SubProjectItem(clip, in, out, desc);
+ SubProjectItem *sub = new SubProjectItem(m_render->dar(), clip, in, out, desc);
if (newItem && desc.isEmpty() && !m_listView->isColumnHidden(1)) {
if (!clip->isExpanded())
clip->setExpanded(true);
m_listView->scrollToItem(sub);
m_listView->editItem(sub, 1);
}
- QImage img = clip->referencedClip()->extractImage(in, (int)(sub->sizeHint(0).height() * m_render->dar()), sub->sizeHint(0).height() - 2);
- sub->setData(0, Qt::DecorationRole, QPixmap::fromImage(img));
- QString hash = clip->getClipHash();
- if (!hash.isEmpty()) m_doc->cacheImage(hash + '#' + QString::number(in), img);
+ m_doc->clipManager()->requestThumbs(QString('#' + id), QList <int>() << in);
monitorItemEditing(true);
}
emit projectModified();
}
}
+
#include "projectlist.moc"
void slotTranscodeClipJob(const QString &condition, QString params, QString desc);
/** @brief Start an MLT process job. */
void slotStartFilterJob(ItemInfo, const QString&,const QString&,const QString&,const QString&,const QString&,const QString&,const QStringList&);
+ void slotSetThumbnail(const QString &id, int framePos, QImage img);
private:
render_process_args << "consumer:" + (scriptExport ? "$SOURCE" : playlistPath);
else
render_process_args << (scriptExport ? "$SOURCE" : playlistPath);
+
render_process_args << (scriptExport ? "$TARGET" : KUrl(dest).url());
render_process_args << paramsList;
#include "kdenlivesettings.h"
#include "docclipbase.h"
+
#include <KDebug>
#include <KLocale>
#include <KIcon>
const int DurationRole = Qt::UserRole + 1;
+const int itemHeight = 30;
-SubProjectItem::SubProjectItem(QTreeWidgetItem * parent, int in, int out, QString description) :
+SubProjectItem::SubProjectItem(double display_ratio, QTreeWidgetItem * parent, int in, int out, QString description) :
QTreeWidgetItem(parent, PROJECTSUBCLIPTYPE), m_in(in), m_out(out), m_description(description)
{
- setSizeHint(0, QSize(65, 30));
+ setSizeHint(0, QSize((int) (itemHeight * display_ratio) + 2, itemHeight + 2));
setFlags(Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled);
QString name = Timecode::getStringTimecode(in, KdenliveSettings::project_fps());
setText(0, name);
setText(1, description);
GenTime duration = GenTime(out - in, KdenliveSettings::project_fps());
if (duration != GenTime()) setData(0, DurationRole, Timecode::getEasyTimecode(duration, KdenliveSettings::project_fps()));
+ QPixmap pix((int) (itemHeight * display_ratio), itemHeight);
+ pix.fill(Qt::gray);
+ setData(0, Qt::DecorationRole, pix);
//setFlags(Qt::NoItemFlags);
//kDebug() << "Constructed with clipId: " << m_clipId;
}
return 0;
}
+//static
+int SubProjectItem::itemDefaultHeight()
+{
+ return itemHeight;
+}
+
QDomElement SubProjectItem::toXml() const
{
//return m_clip->toXML();
class SubProjectItem : public QTreeWidgetItem
{
public:
- SubProjectItem(QTreeWidgetItem * parent, int in, int out, QString description = QString());
+ SubProjectItem(double display_ratio, QTreeWidgetItem * parent, int in, int out, QString description = QString());
virtual ~SubProjectItem();
QDomElement toXml() const;
int numReferences() const;
void setZone(QPoint p);
QString description() const;
void setDescription(QString desc);
+ static int itemDefaultHeight();
/** Make sure folders appear on top of the tree widget */
virtual bool operator<(const QTreeWidgetItem &other)const {
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0" colspan="4">
<widget class="QLabel" name="clip_thumb">
- <property name="text">
- <string>Image preview</string>
- </property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>