From: Till Theato Date: Fri, 31 Dec 2010 14:06:34 +0000 (+0000) Subject: Add bézier color curves GUI (WIP) X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=60d9d6db7d92c37de9e20f10c137552aa0cafbce;p=kdenlive Add bézier color curves GUI (WIP) svn path=/trunk/kdenlive/; revision=5225 --- diff --git a/data/kdenliveeffectscategory.rc b/data/kdenliveeffectscategory.rc index 6cad2c7b..eee3fddc 100644 --- a/data/kdenliveeffectscategory.rc +++ b/data/kdenliveeffectscategory.rc @@ -1,7 +1,7 @@ - + Colour correction diff --git a/effects/CMakeLists.txt b/effects/CMakeLists.txt index 8a3c6b88..643c51cc 100644 --- a/effects/CMakeLists.txt +++ b/effects/CMakeLists.txt @@ -51,6 +51,7 @@ frei0r_alpha0ps.xml frei0r_alphagrad.xml frei0r_alphaspot.xml frei0r_balanc0r.xml +frei0r_bezier_curves.xml frei0r_brightness.xml frei0r_coloradj_rgb.xml frei0r_colordistance.xml diff --git a/effects/frei0r_bezier_curves.xml b/effects/frei0r_bezier_curves.xml new file mode 100644 index 00000000..07d9a692 --- /dev/null +++ b/effects/frei0r_bezier_curves.xml @@ -0,0 +1,18 @@ + + + Bézier Curves + Color curves adjustment + Maksim Golovkin, Till Theato + + + Channel + + + + Luma formula + + + + Bézier Curve(Spline) Widget + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a48f6cbf..9952bd0c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,9 @@ -add_subdirectory(widgets) -add_subdirectory(mimetypes) +add_subdirectory(beziercurve) +add_subdirectory(colorcorrection) add_subdirectory(kiss_fft) +add_subdirectory(mimetypes) +add_subdirectory(onmonitoritems) +add_subdirectory(widgets) macro_optional_find_package(Nepomuk) include(FindQImageBlitz) @@ -14,11 +17,12 @@ include_directories( ${QDBUS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} - ${CMAKE_SOURCE_DIR}/src/widgets + ${CMAKE_SOURCE_DIR}/src/audioscopes + ${CMAKE_SOURCE_DIR}/src/beziercurve ${CMAKE_SOURCE_DIR}/src/colorcorrection - ${CMAKE_SOURCE_DIR}/src/onmonitoritems ${CMAKE_SOURCE_DIR}/src/kiss_fft - ${CMAKE_SOURCE_DIR}/src/audioscopes + ${CMAKE_SOURCE_DIR}/src/onmonitoritems + ${CMAKE_SOURCE_DIR}/src/widgets ) @@ -258,11 +262,10 @@ set(kdenlive_SRCS kiss_fft/_kiss_fft_guts.h kiss_fft/kiss_fft.c kiss_fft/tools/kiss_fftr.c + beziercurve/cubicbezierspline.cpp + beziercurve/beziersplinewidget.cpp ) -add_subdirectory(${CMAKE_SOURCE_DIR}/src/colorcorrection) -add_subdirectory(${CMAKE_SOURCE_DIR}/src/onmonitoritems) - add_definitions(${KDE4_DEFINITIONS}) if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR NO_JOGSHUTTLE) diff --git a/src/beziercurve/CMakeLists.txt b/src/beziercurve/CMakeLists.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/beziercurve/CMakeLists.txt @@ -0,0 +1 @@ + diff --git a/src/beziercurve/beziersplinewidget.cpp b/src/beziercurve/beziersplinewidget.cpp new file mode 100644 index 00000000..3ec2bc2a --- /dev/null +++ b/src/beziercurve/beziersplinewidget.cpp @@ -0,0 +1,338 @@ +/*************************************************************************** + * Copyright (C) 2010 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#include "beziersplinewidget.h" + +#include +#include + +#include + +BezierSplineWidget::BezierSplineWidget(QWidget* parent) : + QWidget(parent), + m_mode(ModeNormal), + m_currentPointIndex(-1) +{ + //m_spline.setPrecision(width()); + setMouseTracking(true); + setAutoFillBackground(false); + setAttribute(Qt::WA_OpaquePaintEvent); + setMinimumSize(150, 150); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +CubicBezierSpline BezierSplineWidget::spline() +{ + return m_spline; +} + +void BezierSplineWidget::setSpline(const CubicBezierSpline& spline) +{ + // TODO: cleanup + m_spline.fromString(spline.toString()); +} + +void BezierSplineWidget::paintEvent(QPaintEvent* event) +{ + Q_UNUSED(event); + + QPainter p(this); + + p.fillRect(rect(), palette().background()); + + int wWidth = width() - 1; + int wHeight = height() - 1; + + // Draw curve. + double prevY = wHeight - m_spline.value(0.) * wHeight; + double prevX = 0.; + double curY; + double normalizedX = -1; + int x; + + p.setPen(QPen(Qt::black, 1, Qt::SolidLine)); + for (x = 0 ; x < wWidth ; ++x) { + normalizedX = x / (double)wWidth; + curY = wHeight - m_spline.value(normalizedX, true) * wHeight; + + /** + * Keep in mind that QLineF rounds doubles + * to ints mathematically, not just rounds down + * like in C + */ + p.drawLine(QLineF(prevX, prevY, + x, curY)); + prevX = x; + prevY = curY; + } + p.drawLine(QLineF(prevX, prevY , + x, wHeight - m_spline.value(1.0, true) * wHeight)); + + // Drawing curve handles. + p.setPen(QPen(Qt::red, 1, Qt::SolidLine)); + BPoint point; + for (int i = 0; i < m_spline.points().count(); ++i) { + point = m_spline.points().at(i); + p.drawEllipse(QRectF(point.h1.x() * wWidth - 3, + wHeight - 3 - point.h1.y() * wHeight, 6, 6)); + p.drawEllipse(QRectF(point.p.x() * wWidth - 3, + wHeight - 3 - point.p.y() * wHeight, 6, 6)); + p.drawEllipse(QRectF(point.h2.x() * wWidth - 3, + wHeight - 3 - point.h2.y() * wHeight, 6, 6)); + } +} + +void BezierSplineWidget::resizeEvent(QResizeEvent* event) +{ + m_spline.setPrecision(width()); + QWidget::resizeEvent(event); +} + +void BezierSplineWidget::mousePressEvent(QMouseEvent* event) +{ + double x = event->pos().x() / (double)(width() - 1); + double y = 1.0 - event->pos().y() / (double)(height() - 1); + + point_types selectedPoint; + int closest_point_index = nearestPointInRange(QPointF(x, y), width(), height(), &selectedPoint); + + /*if (event->button() == Qt::RightButton && closest_point_index > 0 && closest_point_index < d->m_curve.points().count() - 1) { + d->m_curve.removePoint(closest_point_index); + setCursor(Qt::ArrowCursor); + d->setState(ST_NORMAL); + if (closest_point_index < d->m_grab_point_index) + --d->m_grab_point_index; + d->setCurveModified(); + return; + } else*/ if (event->button() != Qt::LeftButton) return; + + if (closest_point_index < 0) { + BPoint po; + po.p = QPointF(x, y); + po.h1 = QPointF(x-0.05, y-0.05); + po.h2 = QPointF(x+0.05, y+0.05); + m_currentPointIndex = m_spline.addPoint(po); + m_currentPointType = PTypeP; + if (m_currentPointIndex == -1) // x already in use + return; + /*if (!d->jumpOverExistingPoints(newPoint, -1)) return;*/ + } else { + m_currentPointIndex = closest_point_index; + m_currentPointType = selectedPoint; + } + + BPoint point = m_spline.points()[m_currentPointIndex]; + QPointF p; + switch (m_currentPointType) { + case PTypeH1: + p = point.h1; + break; + case PTypeP: + p = point.p; + break; + case PTypeH2: + p = point.h2; + } + + m_grabOriginalX = p.x(); + m_grabOriginalY = p.y(); + m_grabOffsetX = p.x() - x; + m_grabOffsetY = p.y() - y; + + switch (m_currentPointType) { + case PTypeH1: + point.h1 = QPointF(x + m_grabOffsetX, y + m_grabOffsetY); + break; + case PTypeP: + point.p = QPointF(x + m_grabOffsetX, y + m_grabOffsetY); + break; + case PTypeH2: + point.h2 = QPointF(x + m_grabOffsetX, y + m_grabOffsetY); + } + m_spline.setPoint(m_currentPointIndex, point); + + //d->m_draggedAwayPointIndex = -1; + + m_mode = ModeDrag; + + update(); +} + +void BezierSplineWidget::mouseReleaseEvent(QMouseEvent* event) +{ + if (event->button() != Qt::LeftButton) + return; + + setCursor(Qt::ArrowCursor); + m_mode = ModeNormal; + + emit modified(); +} + +void BezierSplineWidget::mouseMoveEvent(QMouseEvent* event) +{ + double x = event->pos().x() / (double)(width() - 1); + double y = 1.0 - event->pos().y() / (double)(height() - 1); + + if (m_mode == ModeNormal) { // If no point is selected set the the cursor shape if on top + point_types type; + int nearestPointIndex = nearestPointInRange(QPointF(x, y), width(), height(), &type); + + if (nearestPointIndex < 0) + setCursor(Qt::ArrowCursor); + else + setCursor(Qt::CrossCursor); + } else { // Else, drag the selected point + /*bool crossedHoriz = event->pos().x() - width() > MOUSE_AWAY_THRES || + event->pos().x() < -MOUSE_AWAY_THRES; + bool crossedVert = event->pos().y() - height() > MOUSE_AWAY_THRES || + event->pos().y() < -MOUSE_AWAY_THRES; + + bool removePoint = (crossedHoriz || crossedVert); + + if (!removePoint && d->m_draggedAwayPointIndex >= 0) { + // point is no longer dragged away so reinsert it + QPointF newPoint(d->m_draggedAwayPoint); + d->m_grab_point_index = d->m_curve.addPoint(newPoint); + d->m_draggedAwayPointIndex = -1; + } + + if (removePoint && + (d->m_draggedAwayPointIndex >= 0)) + return; + */ + + setCursor(Qt::CrossCursor); + + x += m_grabOffsetX; + y += m_grabOffsetY; + + double leftX, rightX; + BPoint point = m_spline.points()[m_currentPointIndex]; + switch (m_currentPointType) { + case PTypeH1: + rightX = point.p.x(); + if (m_currentPointIndex == 0) + leftX = -1000; + else + leftX = m_spline.points()[m_currentPointIndex - 1].p.x(); + x = qBound(leftX, x, rightX); + point.h1 = QPointF(x, y); + break; + case PTypeP: + if (m_currentPointIndex == 0) { + leftX = 0.0; + rightX = 0.0; + /*if (d->m_curve.points().count() > 1) + * rightX = d->m_curve.points()[d->m_grab_point_index + 1].x() - POINT_AREA; + * else + * rightX = 1.0;*/ + } else if (m_currentPointIndex == m_spline.points().count() - 1) { + leftX = 1.0;//m_spline.points()[m_currentPointIndex - 1].p.x(); + rightX = 1.0; + } else { + //// the 1E-4 addition so we can grab the dot later. + leftX = m_spline.points()[m_currentPointIndex - 1].p.x();// + POINT_AREA; + rightX = m_spline.points()[m_currentPointIndex + 1].p.x();// - POINT_AREA; + } + x = qBound(leftX, x, rightX); + y = qBound(0., y, 1.); + point.p = QPointF(x, y); + break; + case PTypeH2: + leftX = point.p.x(); + if (m_currentPointIndex == m_spline.points().count() - 1) + rightX = 1001; + else + rightX = m_spline.points()[m_currentPointIndex + 1].p.x(); + x = qBound(leftX, x, rightX); + point.h2 = QPointF(x, y); + }; + + m_spline.setPoint(m_currentPointIndex, point); + + /*if (removePoint && d->m_curve.points().count() > 2) { + d->m_draggedAwayPoint = d->m_curve.points()[d->m_grab_point_index]; + d->m_draggedAwayPointIndex = d->m_grab_point_index; + d->m_curve.removePoint(d->m_grab_point_index); + d->m_grab_point_index = bounds(d->m_grab_point_index, 0, d->m_curve.points().count() - 1); + } + + d->setCurveModified();*/ + update(); + } +} + +void BezierSplineWidget::leaveEvent(QEvent* event) +{ + QWidget::leaveEvent(event); +} + +int BezierSplineWidget::nearestPointInRange(QPointF p, int wWidth, int wHeight, BezierSplineWidget::point_types* sel) +{ + double nearestDistanceSquared = 1000; + point_types selectedPoint; + int nearestIndex = -1; + int i = 0; + + double distanceSquared; + foreach(const BPoint & point, m_spline.points()) { + distanceSquared = pow(point.h1.x() - p.x(), 2) + pow(point.h1.y() - p.y(), 2); + if (distanceSquared < nearestDistanceSquared) { + nearestIndex = i; + nearestDistanceSquared = distanceSquared; + selectedPoint = PTypeH1; + } + distanceSquared = pow(point.p.x() - p.x(), 2) + pow(point.p.y() - p.y(), 2); + if (distanceSquared < nearestDistanceSquared) { + nearestIndex = i; + nearestDistanceSquared = distanceSquared; + selectedPoint = PTypeP; + } + distanceSquared = pow(point.h2.x() - p.x(), 2) + pow(point.h2.y() - p.y(), 2); + if (distanceSquared < nearestDistanceSquared) { + nearestIndex = i; + nearestDistanceSquared = distanceSquared; + selectedPoint = PTypeH2; + } + ++i; + } + + if (nearestIndex >= 0) { + BPoint point = m_spline.points()[nearestIndex]; + QPointF p2; + switch (selectedPoint) { + case PTypeH1: + p2 = point.h1; + break; + case PTypeP: + p2 = point.p; + break; + case PTypeH2: + p2 = point.h2; + } + if (qAbs(p.x() - p2.x()) * (wWidth - 1) < 5 && qAbs(p.y() - p2.y()) * (wHeight - 1) < 5) { + *sel = selectedPoint; + return nearestIndex; + } + } + + return -1; +} + +#include "beziersplinewidget.moc" diff --git a/src/beziercurve/beziersplinewidget.h b/src/beziercurve/beziersplinewidget.h new file mode 100644 index 00000000..245db968 --- /dev/null +++ b/src/beziercurve/beziersplinewidget.h @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright (C) 2010 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#ifndef BEZIERSPLINEWIDGET_H +#define BEZIERSPLINEWIDGET_H + +#include "cubicbezierspline.h" + +#include +#include + +class BezierSplineWidget : public QWidget +{ + Q_OBJECT + +public: + BezierSplineWidget(QWidget* parent = 0); + + CubicBezierSpline spline(); + void setSpline(const CubicBezierSpline &spline); + +protected: + //void keyPressEvent(QKeyEvent *event); + void paintEvent(QPaintEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent * event); + void mouseMoveEvent(QMouseEvent * event); + void leaveEvent(QEvent *event); + void resizeEvent(QResizeEvent *event); + +private: + CubicBezierSpline m_spline; + enum modes { ModeDrag, ModeNormal }; + enum point_types { PTypeH1, PTypeP, PTypeH2 }; + modes m_mode; + int m_currentPointIndex; + point_types m_currentPointType; + double m_grabOffsetX; + double m_grabOffsetY; + double m_grabOriginalX; + double m_grabOriginalY; + //QPointF m_draggedAwayPoint; + //int m_draggedAwayPointIndex; + + //inline void drawGrid(QPainter &p, int width, int height); + int nearestPointInRange(QPointF p, int wWidth, int wHeight, point_types *sel); + +signals: + void modified(); +}; + +#endif diff --git a/src/beziercurve/cubicbezierspline.cpp b/src/beziercurve/cubicbezierspline.cpp new file mode 100644 index 00000000..c6d031f8 --- /dev/null +++ b/src/beziercurve/cubicbezierspline.cpp @@ -0,0 +1,239 @@ +/*************************************************************************** + * Copyright (C) 2010 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#include "cubicbezierspline.h" + +#include + +static bool pointLessThan(const BPoint &a, const BPoint &b) +{ + return a.p.x() < b.p.x(); +} + +CubicBezierSpline::CubicBezierSpline(QObject* parent) : + QObject(parent), + m_validSpline(false), + m_precision(100) +{ + BPoint start; + start.p.setX(0); + start.p.setY(0); + start.h1.setX(0); + start.h1.setY(0); + start.h2.setX(0.1); + start.h2.setY(0.1); + m_points.append(start); + + BPoint end; + end.p.setX(1); + end.p.setY(1); + end.h1.setX(0.9); + end.h1.setY(0.9); + end.h2.setX(1); + end.h2.setY(1); + m_points.append(end); +} + +CubicBezierSpline::CubicBezierSpline(const CubicBezierSpline& spline, QObject* parent) : + QObject(parent) +{ + m_precision = spline.m_precision; + m_points = spline.m_points; + m_validSpline = false; +} + +CubicBezierSpline& CubicBezierSpline::operator=(const CubicBezierSpline& spline) +{ + m_precision = spline.m_precision; + m_points = spline.m_points; + return *this; +} + +void CubicBezierSpline::fromString(const QString& spline) +{ + m_points.clear(); + m_validSpline = false; + + QStringList bpoints = spline.split('|'); + foreach(const QString &bpoint, bpoints) { + QStringList points = bpoint.split('#'); + QList values; + foreach(const QString &point, points) { + QStringList xy = point.split(';'); + if (xy.count() == 2) + values.append(QPointF(xy.at(0).toDouble(), xy.at(1).toDouble())); + } + if (values.count() == 3) { + BPoint bp; + bp.h1 = values.at(0); + bp.p = values.at(1); + bp.h2 = values.at(2); + m_points.append(bp); + } + } + + keepSorted(); + validatePoints(); +} + +QString CubicBezierSpline::toString() const +{ + QStringList spline; + foreach(const BPoint &p, m_points) { + spline << (QString::number(p.h1.x()) + ";" + QString::number(p.h1.y()) + + "#" + QString::number(p.p.x()) + ";" + QString::number(p.p.y()) + + "#" + QString::number(p.h2.x()) + ";" + QString::number(p.h2.y())); + } + return spline.join("|"); +} + +int CubicBezierSpline::setPoint(int ix, const BPoint& point) +{ + m_points[ix] = point; + keepSorted(); + validatePoints(); + m_validSpline = false; + return m_points.indexOf(point); // in case it changed +} + +QList CubicBezierSpline::points() +{ + return m_points; +} + +void CubicBezierSpline::removePoint(int ix) +{ + m_points.removeAt(ix); + m_validSpline = false; +} + +int CubicBezierSpline::addPoint(const BPoint& point) +{ + m_points.append(point); + keepSorted(); + validatePoints(); + m_validSpline = false; + return m_points.indexOf(point); +} + +void CubicBezierSpline::setPrecision(int pre) +{ + if (pre != m_precision) { + m_precision = pre; + m_validSpline = false; + } +} + +int CubicBezierSpline::getPrecision() +{ + return m_precision; +} + +qreal CubicBezierSpline::value(qreal x, bool cont) +{ + update(); + + //x = qBound(m_spline.constBegin().key(), x, m_spline.constEnd().key()); + //kDebug() << "....x" << x<<"bounddown"< diff) + break; + + diff = qAbs(x - m_i.key()); + y = m_i.value(); + ++m_i; + } + return qBound((qreal)0.0, y, (qreal)1.0); +} + +void CubicBezierSpline::validatePoints() +{ + BPoint p1, p2; + for (int i = 0; i < m_points.count() - 1; ++i) { + p1 = m_points.at(i); + p2 = m_points.at(i+1); + p1.h2.setX(qBound(p1.p.x(), p1.h2.x(), p2.p.x())); + p2.h1.setX(qBound(p1.p.x(), p2.h1.x(), p2.p.x())); + m_points[i] = p1; + m_points[i+1] = p2; + } +} + +void CubicBezierSpline::keepSorted() +{ + qSort(m_points.begin(), m_points.end(), pointLessThan); +} + +QPointF CubicBezierSpline::point(double t, const QList< QPointF >& points) +{ + // coefficients from Bernstein basis polynomial of degree 3 + double c1 = (1-t) * (1-t) * (1-t); + double c2 = 3 * t * (1-t) * (1-t); + double c3 = 3 * t * t * (1-t); + double c4 = t * t * t; + + return QPointF(points[0].x()*c1 + points[1].x()*c2 + points[2].x()*c3 + points[3].x()*c4, + points[0].y()*c1 + points[1].y()*c2 + points[2].y()*c3 + points[3].y()*c4); +} + +void CubicBezierSpline::update() +{ + if (m_validSpline) + return; + + m_validSpline = true; + m_spline.clear(); + + QList points; + QPointF p; + for (int i = 0; i < m_points.count() - 1; ++i) { + points.clear(); + points << m_points.at(i).p + << m_points.at(i).h2 + << m_points.at(i+1).h1 + << m_points.at(i+1).p; + + int numberOfValues = (int)((points[3].x() - points[0].x()) * m_precision); + if (numberOfValues == 0) + numberOfValues = 1; + double step = 1 / (double)numberOfValues; + double t = 0; + while (t <= 1) { + p = point(t, points); + m_spline.insert(p.x(), p.y()); + t += step; + } + } + /*QMap::const_iterator i = m_spline.constBegin(); + kDebug()<<"////////////spline/////////////"; + while (i != m_spline.constEnd()) { + kDebug() << i.key() << i.value(); + ++i; + } + kDebug()<<"////////////spline/////////////end";*/ +} + +#include "cubicbezierspline.moc" diff --git a/src/beziercurve/cubicbezierspline.h b/src/beziercurve/cubicbezierspline.h new file mode 100644 index 00000000..078066e2 --- /dev/null +++ b/src/beziercurve/cubicbezierspline.h @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2010 by Till Theato (root@ttill.de) * + * This file is part of Kdenlive (www.kdenlive.org). * + * * + * Kdenlive is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 2 of the License, or * + * (at your option) any later version. * + * * + * Kdenlive is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with Kdenlive. If not, see . * + ***************************************************************************/ + +#ifndef CUBICBEZIERSPLINE_H +#define CUBICBEZIERSPLINE_H + + +#include + +class BPoint +{ +public: + QPointF h1; // handle 1 + QPointF p; // point + QPointF h2; // handle 2 + + bool operator==(const BPoint &point) const { return point.h1 == h1 && point.p == p && point.h2 == h2; } +}; + +class CubicBezierSpline : public QObject +{ + Q_OBJECT + +public: + CubicBezierSpline(QObject* parent = 0); + CubicBezierSpline(const CubicBezierSpline &spline, QObject* parent = 0); + CubicBezierSpline& operator=(const CubicBezierSpline &spline); + + void fromString(const QString &spline); + QString toString() const; + + QList points(); + qreal value(qreal x, bool cont = false); + + int setPoint(int ix, const BPoint &point); + int addPoint(const BPoint &point); + void removePoint(int ix); + void setPrecision(int pre); + int getPrecision(); + +private: + QPointF point(double t, const QList &points); + void validatePoints(); + void keepSorted(); + void update(); + + QList m_points; + QMap m_spline; + QMap ::const_iterator m_i; + bool m_validSpline; + int m_precision; + +}; + +#endif diff --git a/src/effectstackedit.cpp b/src/effectstackedit.cpp index f3184f29..f2ce5b1e 100644 --- a/src/effectstackedit.cpp +++ b/src/effectstackedit.cpp @@ -34,6 +34,8 @@ #include "colortools.h" #include "doubleparameterwidget.h" #include "cornerswidget.h" +#include "beziercurve/beziersplinewidget.h" +#include "beziercurve/cubicbezierspline.h" #include #include @@ -379,6 +381,14 @@ void EffectStackEdit::transferParamDesc(const QDomElement d, int pos, int in, in QString depends = pa.attribute("depends"); if (!depends.isEmpty()) meetDependency(paramName, type, EffectsList::parameter(e, depends)); + } else if (type == "bezier_spline") { + BezierSplineWidget *widget = new BezierSplineWidget(this); + CubicBezierSpline spline; + spline.fromString(value); + widget->setSpline(spline); + m_vbox->addWidget(widget); + m_valueItems[paramName] = widget; + connect(widget, SIGNAL(modified()), this, SLOT(collectAllParameters())); } else if (type == "corners") { CornersWidget *corners = new CornersWidget(m_monitor, pos, isEffect, pa.attribute("factor").toInt(), this); connect(corners, SIGNAL(checkMonitorPosition(int)), this, SIGNAL(checkMonitorPosition(int))); @@ -658,6 +668,9 @@ void EffectStackEdit::collectAllParameters() QString depends = pa.attributes().namedItem("depends").nodeValue(); if (!depends.isEmpty()) meetDependency(paramName, type, EffectsList::parameter(newparam, depends)); + } else if (type == "bezier_spline") { + BezierSplineWidget *widget = (BezierSplineWidget*)m_valueItems.value(paramName); + setValue = widget->spline().toString(); } else if (type == "corners") { CornersWidget *corners = ((CornersWidget*)m_valueItems.value(paramName)); QString xName = pa.attributes().namedItem("xpoints").nodeValue();