//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"));
bool ClipItem::checkKeyFrames()
{
bool clipEffectsModified = false;
+ QLocale locale;
// go through all effects this clip has
for (int ix = 0; ix < m_effectList.count(); ++ix) {
QStringList keyframeParams = keyframes(ix);
// go through all keyframes for one param
foreach(const QString &str, keyframes) {
int pos = str.section(':', 0, 0).toInt();
- double val = str.section(':', 1, 1).toDouble();
+ double val = locale.toDouble(str.section(':', 1, 1));
if (pos - start < 0) {
// a keyframe is defined before the start of the clip
cutKeyFrame = true;
int diff = pos - lastPos;
double ratio = (double)(start - lastPos) / diff;
double newValue = lastValue + (val - lastValue) * ratio;
- newKeyFrames.append(QString::number(start) + ':' + QString::number(newValue));
+ newKeyFrames.append(QString::number(start) + ':' + locale.toString(newValue));
modified = true;
}
cutKeyFrame = false;
if (diff != 0) {
double ratio = (double)(end - lastPos) / diff;
double newValue = lastValue + (val - lastValue) * ratio;
- newKeyFrames.append(QString::number(end) + ':' + QString::number(newValue));
+ newKeyFrames.append(QString::number(end) + ':' + locale.toString(newValue));
modified = true;
}
break;
} else {
- newKeyFrames.append(QString::number(pos) + ':' + QString::number(val));
+ newKeyFrames.append(QString::number(pos) + ':' + locale.toString(val));
}
}
lastPos = pos;
{
QDomElement effect = getEffectAt(ix);
if (effect.attribute("disable") == "1") return;
+ QLocale locale;
QDomNodeList params = effect.elementsByTagName("parameter");
int keyframeParams = 0;
for (int i = 0; i < params.count(); i++) {
if (ix == m_selectedEffect && keyframeParams == 0) {
m_keyframes.clear();
m_visibleParam = i;
- double max = e.attribute("max").toDouble();
- double min = e.attribute("min").toDouble();
+ double max = locale.toDouble(e.attribute("max"));
+ double min = locale.toDouble(e.attribute("min"));
m_keyframeFactor = 100.0 / (max - min);
m_keyframeOffset = min;
- m_keyframeDefault = e.attribute("default").toDouble();
+ m_keyframeDefault = locale.toDouble(e.attribute("default"));
m_selectedKeyframe = 0;
// parse keyframes
const QStringList keyframes = e.attribute("keyframes").split(';', QString::SkipEmptyParts);
foreach(const QString &str, keyframes) {
int pos = str.section(':', 0, 0).toInt();
- double val = str.section(':', 1, 1).toDouble();
+ double val = locale.toDouble(str.section(':', 1, 1));
m_keyframes[pos] = val;
}
if (m_keyframes.find(m_editedKeyframe) == m_keyframes.end()) m_editedKeyframe = -1;
void ClipItem::setSelectedEffect(const int ix)
{
m_selectedEffect = ix;
+ QLocale locale;
QDomElement effect = effectAt(m_selectedEffect);
if (!effect.isNull() && effect.attribute("disable") != "1") {
QDomNodeList params = effect.elementsByTagName("parameter");
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();
+ double max = locale.toDouble(e.attribute("max"));
+ double min = locale.toDouble(e.attribute("min"));
m_keyframeFactor = 100.0 / (max - min);
m_keyframeOffset = min;
- m_keyframeDefault = e.attribute("default").toDouble();
+ m_keyframeDefault = locale.toDouble(e.attribute("default"));
m_selectedKeyframe = 0;
// parse keyframes
const QStringList keyframes = e.attribute("keyframes").split(';', QString::SkipEmptyParts);
foreach(const QString &str, keyframes) {
int pos = str.section(':', 0, 0).toInt();
- double val = str.section(':', 1, 1).toDouble();
+ double val = locale.toDouble(str.section(':', 1, 1));
m_keyframes[pos] = val;
}
if (m_keyframes.find(m_editedKeyframe) == m_keyframes.end())
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;
EffectsParameterList ClipItem::addEffect(const QDomElement effect, bool /*animate*/)
{
bool needRepaint = false;
+ QLocale locale;
int ix;
if (!effect.hasAttribute("kdenlive_ix")) {
ix = effectsCounter();
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();
+ double factor = locale.toDouble(e.attribute("factor", "1"));
if (factor != 1) {
for (int j = 0; j < values.count(); j++) {
QString pos = values.at(j).section(':', 0, 0);
- double val = values.at(j).section(':', 1, 1).toDouble() / factor;
- values[j] = pos + "=" + QString::number(val);
+ double val = locale.toDouble(values.at(j).section(':', 1, 1)) / factor;
+ values[j] = pos + "=" + locale.toString(val);
}
}
parameters.addParam(e.attribute("name"), values.join(";"));
}
} 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));
+ } else fact = locale.toDouble(e.attribute("factor", "1"));
+ parameters.addParam(e.attribute("name"), locale.toString(locale.toDouble(e.attribute("value")) / 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;
void ClipItem::insertKeyframe(QDomElement effect, int pos, int val)
{
if (effect.attribute("disable") == "1") return;
+ QLocale locale;
effect.setAttribute("active_keyframe", pos);
m_editedKeyframe = pos;
QDomNodeList params = effect.elementsByTagName("parameter");
bool added = false;
foreach(const QString &str, keyframes) {
int kpos = str.section(':', 0, 0).toInt();
- double newval = str.section(':', 1, 1).toDouble();
+ double newval = locale.toDouble(str.section(':', 1, 1));
if (kpos < pos) {
newkfr.append(str);
} else if (!added) {
if (i == m_visibleParam)
newkfr.append(QString::number(pos) + ":" + QString::number(val));
else
- newkfr.append(QString::number(pos) + ":" + QString::number(newval));
+ newkfr.append(QString::number(pos) + ":" + locale.toString(newval));
if (kpos > pos) newkfr.append(str);
added = true;
} else newkfr.append(str);
void ClipItem::movedKeyframe(QDomElement effect, int oldpos, int newpos, double value)
{
if (effect.attribute("disable") == "1") return;
+ QLocale locale;
effect.setAttribute("active_keyframe", newpos);
QDomNodeList params = effect.elementsByTagName("parameter");
int start = cropStart().frames(m_fps);
newpos = qMax(newpos, start);
newpos = qMin(newpos, end);
if (i == m_visibleParam)
- newkfr.append(QString::number(newpos) + ":" + QString::number(value));
+ newkfr.append(QString::number(newpos) + ":" + locale.toString(value));
else
newkfr.append(QString::number(newpos) + ":" + str.section(':', 1, 1));
}
void ClipItem::updateKeyframes(QDomElement effect)
{
m_keyframes.clear();
+ QLocale locale;
// parse keyframes
QDomNodeList params = effect.elementsByTagName("parameter");
QDomElement e = params.item(m_visibleParam).toElement();
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();
- double val = str.section(':', 1, 1).toDouble();
+ double val = locale.toDouble(str.section(':', 1, 1));
m_keyframes[pos] = val;
}
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();
{
int in = cropStart().frames(m_fps);
int out = (cropStart() + cropDuration()).frames(m_fps) - 1;
+ QLocale locale;
const QStringList data = parameter.attribute("keyframes").split(';', QString::SkipEmptyParts);
QMap <int, double> keyframes;
foreach (QString keyframe, data)
- keyframes[keyframe.section(':', 0, 0).toInt()] = keyframe.section(':', 1, 1).toDouble();
+ keyframes[keyframe.section(':', 0, 0).toInt()] = locale.toDouble(keyframe.section(':', 1, 1));
QMap<int, double>::iterator i = keyframes.end();
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"
+