//m_hover(false),
m_speed(speed),
m_strobe(strobe),
- m_framePixelWidth(0)
+ m_framePixelWidth(0),
+ m_limitedKeyFrames(false)
{
setZValue(2);
- setRect(0, 0, (info.endPos - info.startPos).frames(fps) - 0.02, (double)(KdenliveSettings::trackheight() - 2));
- setPos(info.startPos.frames(fps), (double)(info.track * KdenliveSettings::trackheight()) + 1);
+ setRect(0, 0, (info.endPos - info.startPos).frames(fps) - 0.02, (double) itemHeight());
+ setPos(info.startPos.frames(fps), (double)(info.track * KdenliveSettings::trackheight()) + 1 + itemOffset());
// set speed independant info
if (m_speed <= 0 && m_speed > -1)
m_endThumbTimer.setSingleShot(true);
connect(&m_endThumbTimer, SIGNAL(timeout()), this, SLOT(slotGetEndThumb()));
- connect(this, SIGNAL(getThumb(int, int)), m_clip->thumbProducer(), SLOT(extractImage(int, int)));
+ connect(this, SIGNAL(getThumb(int, int)), m_clip, SLOT(slotExtractImage(int, int)));
connect(m_clip->thumbProducer(), SIGNAL(thumbReady(int, QImage)), this, SLOT(slotThumbReady(int, QImage)));
connect(m_clip, SIGNAL(gotAudioData()), this, SLOT(slotGotAudioData()));
} else if (m_clipType == IMAGE || m_clipType == TEXT) {
m_baseColor = QColor(141, 166, 215);
if (m_clipType == TEXT) {
- connect(this, SIGNAL(getThumb(int, int)), m_clip->thumbProducer(), SLOT(extractImage(int, int)));
+ connect(this, SIGNAL(getThumb(int, int)), m_clip, SLOT(slotExtractImage(int, int)));
connect(m_clip->thumbProducer(), SIGNAL(thumbReady(int, QImage)), this, SLOT(slotThumbReady(int, QImage)));
}
//m_startPix = KThumb::getImage(KUrl(clip->getProperty("resource")), (int)(KdenliveSettings::trackheight() * KdenliveSettings::project_display_ratio()), KdenliveSettings::trackheight());
continue;
// Check if this effect has a variable parameter
- if (e.attribute("default").startsWith('%')) {
+ if (e.attribute("default").contains('%')) {
double evaluatedValue = ProfilesDialog::getStringEval(projectScene()->profile(), e.attribute("default"));
e.setAttribute("default", evaluatedValue);
if (e.hasAttribute("value") && e.attribute("value").startsWith('%')) {
}
}
+ if (effect.attribute("id") == "crop") {
+ // default use_profile to 1 for clips with proxies to avoid problems when rendering
+ if (e.attribute("name") == "use_profile" && !(m_clip->getProperty("proxy").isEmpty() || m_clip->getProperty("proxy") == "-"))
+ e.setAttribute("value", "1");
+ }
+
if ((e.attribute("type") == "keyframe" || e.attribute("type") == "simplekeyframe") && e.attribute("keyframes").isEmpty()) {
// Effect has a keyframe type parameter, we need to set the values
e.setAttribute("keyframes", QString::number(cropStart().frames(m_fps)) + ':' + e.attribute("default"));
QDomElement e = params.item(i).toElement();
if (!e.isNull() && (e.attribute("type") == "keyframe" || e.attribute("type") == "simplekeyframe") && e.attribute("intimeline") == "1") {
m_keyframes.clear();
+ m_limitedKeyFrames = e.attribute("type") == "keyframe";
m_visibleParam = i;
double max = e.attribute("max").toDouble();
double min = e.attribute("min").toDouble();
m_endPix = QPixmap();
m_audioThumbCachePic.clear();
}
+ kDebug()<<"........... RESET THMBS";
slotFetchThumbs();
}
QLineF l2(mapped.left() + m_startPix.width(), mapped.top(), mapped.left() + m_startPix.width(), mapped.bottom());
painter->drawLine(l2);
}
- if (painter->matrix().m11() == FRAME_SIZE) {
+ if (painter->matrix().m11() == FRAME_SIZE && m_clip->thumbProducer() && clipType() != COLOR && clipType() != AUDIO && !m_audioOnly) {
int offset = (m_info.startPos - m_info.cropStart).frames(m_fps);
- int left = qMax((int) m_info.startPos.frames(m_fps) + 1, (int) mapToScene(exposed.left(), 0).x());
- int right = qMin((int)(m_info.startPos + m_info.cropDuration).frames(m_fps) - 1, (int) mapToScene(exposed.right(), 0).x());
- doGetIntraThumbs(painter, mapped.topLeft(), m_info.cropStart.frames(m_fps), left - offset, right - offset);
+ int left = qMax((int) m_info.startPos.frames(m_fps) + 1, (int) mapToScene(exposed.left(), 0).x()) - offset;
+ int right = qMin((int)(m_info.startPos + m_info.cropDuration).frames(m_fps) - 1, (int) mapToScene(exposed.right(), 0).x()) - offset;
+ QPointF startPos = mapped.topLeft();
+ int twidth = FRAME_SIZE;
+ int startOffset = m_info.cropStart.frames(m_fps);
+ if (clipType() == IMAGE || clipType() == TEXT) {
+ for (int i = left; i <= right; i++) {
+ painter->drawPixmap(startPos + QPointF(twidth *(i - startOffset), 0), m_startPix);
+ }
+ }
+ else {
+#if KDE_IS_VERSION(4,5,0)
+ if (m_clip && m_clip->thumbProducer()) {
+ m_clip->thumbProducer()->queryIntraThumbs(left, right);
+ connect(m_clip->thumbProducer(), SIGNAL(thumbsCached()), this, SLOT(slotGotThumbsCache()));
+ QString path = m_clip->fileURL().path() + "_";
+ for (int i = left; i <= right; i++) {
+ painter->drawImage(startPos + QPointF(twidth *(i - startOffset), 0), m_clip->thumbProducer()->findCachedThumb(path + QString::number(i)));
+ }
+ }
+#endif
+ }
}
painter->setPen(Qt::black);
}
painter->setPen(QPen(Qt::lightGray));
// draw effect or transition keyframes
- if (mapped.width() > 20) drawKeyFrames(painter, exposed);
+ if (mapped.width() > 20) drawKeyFrames(painter, m_limitedKeyFrames);
//painter->setMatrixEnabled(true);
return MOVE;
}
+int ClipItem::itemHeight()
+{
+ return KdenliveSettings::trackheight() - 2;
+}
+
QList <GenTime> ClipItem::snapMarkers() const
{
QList < GenTime > snaps;
parameters.addParam("id", effectId);
// special case: the affine effect needs in / out points
- if (effectId == "pan_zoom") {
- parameters.addParam("in", QString::number(cropStart().frames(m_fps)));
- parameters.addParam("out", QString::number((cropStart() + cropDuration()).frames(m_fps)));
- }
QDomNodeList params = effect.elementsByTagName("parameter");
int fade = 0;
+ bool needInOutSync = false;
for (int i = 0; i < params.count(); i++) {
QDomElement e = params.item(i).toElement();
if (!e.isNull()) {
+ if (e.attribute("type") == "geometry" && !e.hasAttribute("fixed")) {
+ // Effects with a geometry parameter need to sync in / out with parent clip
+ needInOutSync = true;
+ }
if (e.attribute("type") == "simplekeyframe") {
QStringList values = e.attribute("keyframes").split(";", QString::SkipEmptyParts);
double factor = e.attribute("factor", "1").toDouble();
}
} else {
double fact;
- if (e.attribute("factor").startsWith('%')) {
+ if (e.attribute("factor").contains('%')) {
fact = ProfilesDialog::getStringEval(projectScene()->profile(), e.attribute("factor"));
} else fact = e.attribute("factor", "1").toDouble();
parameters.addParam(e.attribute("name"), QString::number(e.attribute("value").toDouble() / fact));
}
}
}
+ if (needInOutSync) {
+ parameters.addParam("in", QString::number(cropStart().frames(m_fps)));
+ parameters.addParam("out", QString::number((cropStart() + cropDuration()).frames(m_fps) - 1));
+ parameters.addParam("_sync_in_out", "1");
+ }
m_effectNames = m_effectList.effectNames().join(" / ");
if (fade > 0) m_startFade = fade;
else if (fade < 0) m_endFade = -fade;
setSelectedEffect(m_selectedEffect);
return;
}
+ m_limitedKeyFrames = e.attribute("type") == "keyframe";
const QStringList keyframes = e.attribute("keyframes").split(';', QString::SkipEmptyParts);
foreach(const QString &str, keyframes) {
int pos = str.section(':', 0, 0).toInt();
if (!m_keyframes.contains(m_selectedKeyframe)) m_selectedKeyframe = -1;
}
-void ClipItem::doGetIntraThumbs(QPainter *painter, const QPointF startPos, int offset, int start, int end)
-{
- if (!m_clip->thumbProducer() || clipType() == COLOR) return;
- if (scene() && scene()->views().isEmpty()) return;
- CustomTrackView *view = (CustomTrackView *) scene()->views()[0];
- if (view == NULL) return;
- const int theight = KdenliveSettings::trackheight();
- const int twidth = FRAME_SIZE;
-
- if (clipType() == IMAGE || clipType() == TEXT) {
- for (int i = start; i <= end; i++)
- painter->drawPixmap(startPos + QPointF(twidth *(i - offset), 0), m_startPix);
- }
- QPixmap p;
- for (int i = start; i <= end; i++) {
- if (!view->m_pixmapCache->find(m_clip->fileURL().path() + "%" + QString::number(i), p)) {
- p = m_clip->thumbProducer()->extractImage(i, twidth, theight);
- view->m_pixmapCache->insert(m_clip->fileURL().path() + "%" + QString::number(i), p);
- }
- painter->drawPixmap(startPos + QPointF(twidth *(i - offset), 0), p);
- }
-}
-
-QList <int> ClipItem::updatePanZoom(int width, int height, int cut)
-{
- QList <int> effectPositions;
- for (int i = 0; i < m_effectList.count(); i++) {
- QDomElement effect = m_effectList.at(i);
- QDomNodeList params = effect.elementsByTagName("parameter");
- for (int j = 0; j < params.count(); j++) {
- QDomElement e = params.item(j).toElement();
- if (e.isNull())
- continue;
- if (e.attribute("type") == "geometry" && !e.hasAttribute("fixed")) {
- effectPositions << i;
- updateGeometryKeyframes(effect, j, width, height, cut);
- }
- }
- }
-
- return effectPositions;
-}
-
Mlt::Producer *ClipItem::getProducer(int track, bool trackSpecific)
{
if (isAudioOnly())
return m_clip->producer(trackSpecific ? track : -1);
}
-QMap<int, QDomElement> ClipItem::adjustEffectsToDuration(int width, int height, int previous, int current, bool fromStart)
+QMap<int, QDomElement> ClipItem::adjustEffectsToDuration(int width, int height, ItemInfo oldInfo)
{
QMap<int, QDomElement> effects;
for (int i = 0; i < m_effectList.count(); i++) {
QString id = effect.attribute("id");
int in = EffectsList::parameter(effect, "in").toInt();
int out = EffectsList::parameter(effect, "out").toInt();
- int clipEnd = (cropStart() + cropDuration()).frames(m_fps);
+ int clipEnd = (cropStart() + cropDuration()).frames(m_fps) - 1;
if (id == "fade_from_black" || id == "fadein") {
if (in != cropStart().frames(m_fps)) {
effects[i] = effect.cloneNode().toElement();
if (out != clipEnd) {
effects[i] = effect.cloneNode().toElement();
int diff = out - clipEnd;
- in -= diff;
+ in = qMax(in - diff, (int) cropStart().frames(m_fps));
out -= diff;
EffectsList::setParameter(effect, "in", QString::number(in));
EffectsList::setParameter(effect, "out", QString::number(out));
setFadeOut(out - in);
}
continue;
- } else if (fromStart && effect.attribute("id") == "freeze") {
+ } else if (effect.attribute("id") == "freeze" && cropStart() != oldInfo.cropStart) {
effects[i] = effect.cloneNode().toElement();
- int diff = previous - current;
+ int diff = (oldInfo.cropStart - cropStart()).frames(m_fps);
int frame = EffectsList::parameter(effect, "frame").toInt();
EffectsList::setParameter(effect, "frame", QString::number(frame - diff));
continue;
if (type == "geometry" && !param.hasAttribute("fixed")) {
if (!effects.contains(i))
effects[i] = effect.cloneNode().toElement();
- updateGeometryKeyframes(effect, j, width, height, 0);
+ updateGeometryKeyframes(effect, j, width, height, oldInfo);
} else if (type == "simplekeyframe" || type == "keyframe") {
if (!effects.contains(i))
effects[i] = effect.cloneNode().toElement();
return false;
}
-void ClipItem::updateGeometryKeyframes(QDomElement effect, int paramIndex, int width, int height, int cut)
+void ClipItem::updateGeometryKeyframes(QDomElement effect, int paramIndex, int width, int height, ItemInfo oldInfo)
{
- // TODO: properly update when clip resized from start
QDomElement param = effect.elementsByTagName("parameter").item(paramIndex).toElement();
-
- int in = cropStart().frames(fps());
- int out = in + cropDuration().frames(fps());
- int dur = out - in - 1;
-
- effect.setAttribute("in", in);
- effect.setAttribute("out", out);
-
- Mlt::Geometry geometry(param.attribute("value").toUtf8().data(), dur, width, height);
- Mlt::GeometryItem item;
- bool endFrameAdded = false;
- if (cut == 0) {
- while (!geometry.next_key(&item, dur)) {
- if (!endFrameAdded) {
- // add keyframe at the end with interpolated value
-
- // but only once ;)
- endFrameAdded = true;
-
- Mlt::GeometryItem endItem;
- Mlt::GeometryItem interp;
- geometry.fetch(&interp, dur - 1);
- endItem.frame(dur - 1);
- endItem.x(interp.x());
- endItem.y(interp.y());
- endItem.w(interp.w());
- endItem.h(interp.h());
- endItem.mix(interp.mix());
- geometry.insert(&endItem);
+ int offset = oldInfo.cropStart.frames(m_fps);
+ QString data = param.attribute("value");
+ if (offset > 0) {
+ QStringList kfrs = data.split(';');
+ data.clear();
+ foreach (QString keyframe, kfrs) {
+ if (keyframe.contains('=')) {
+ int pos = keyframe.section('=', 0, 0).toInt();
+ pos += offset;
+ data.append(QString::number(pos) + "=" + keyframe.section('=', 1) + ";");
}
- geometry.remove(item.frame());
- }
- } else {
- Mlt::Geometry origGeometry(param.attribute("value").toUtf8().data(), dur, width, height);
- // remove keyframes before cut point
- while (!geometry.prev_key(&item, cut - 1) && item.frame() < cut)
- geometry.remove(item.frame());
-
- // add a keyframe at new pos 0
- origGeometry.fetch(&item, cut);
- item.frame(0);
- geometry.insert(&item);
-
- // move exisiting keyframes by -cut
- while (!origGeometry.next_key(&item, cut)) {
- geometry.remove(item.frame());
- origGeometry.remove(item.frame());
- item.frame(item.frame() - cut);
- geometry.insert(&item);
+ else data.append(keyframe + ";");
}
}
+ Mlt::Geometry geometry(data.toUtf8().data(), oldInfo.cropDuration.frames(m_fps), width, height);
+ param.setAttribute("value", geometry.serialise(cropStart().frames(m_fps), (cropStart() + cropDuration()).frames(m_fps) - 1));
+}
- param.setAttribute("value", geometry.serialise());
+void ClipItem::slotGotThumbsCache()
+{
+ disconnect(m_clip->thumbProducer(), SIGNAL(thumbsCached()), this, SLOT(slotGotThumbsCache()));
+ update();
}
#include "clipitem.moc"