From 30c6bf74ebfcfe1fd05c8345d98f13bc1226a36f Mon Sep 17 00:00:00 2001 From: "Simon A. Eugster" Date: Thu, 22 Jul 2010 18:48:11 +0000 Subject: [PATCH] Levels added. svn path=/trunk/kdenlive/; revision=4623 --- src/CMakeLists.txt | 13 +- src/abstractscopewidget.cpp | 8 +- src/abstractscopewidget.h | 4 +- src/colorcorrection/levelsgenerator.cpp | 172 ++++++++++++++++++++++++ src/colorcorrection/levelsgenerator.h | 27 ++++ src/levels.cpp | 78 +++++++++++ src/levels.h | 42 ++++++ src/mainwindow.cpp | 8 ++ src/mainwindow.h | 4 + src/widgets/levels_ui.ui | 62 +++++++++ 10 files changed, 409 insertions(+), 9 deletions(-) create mode 100644 src/colorcorrection/levelsgenerator.cpp create mode 100644 src/colorcorrection/levelsgenerator.h create mode 100644 src/levels.cpp create mode 100644 src/levels.h create mode 100644 src/widgets/levels_ui.ui diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 40732b5b..84177849 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -94,6 +94,7 @@ kde4_add_ui_files(kdenlive_UI widgets/colorplaneexport_ui.ui widgets/waveform_ui.ui widgets/rgbparade_ui.ui + widgets/levels_ui.ui ) set(kdenlive_SRCS @@ -196,15 +197,17 @@ set(kdenlive_SRCS tracksconfigdialog.cpp configtrackscommand.cpp abstractscopewidget.cpp + rebuildgroupcommand.cpp + levels.cpp + rgbparade.cpp vectorscope.cpp + waveform.cpp colorplaneexport.cpp colortools.cpp - rebuildgroupcommand.cpp - waveform.cpp - rgbparade.cpp - colorcorrection/waveformgenerator.cpp - colorcorrection/vectorscopegenerator.cpp + colorcorrection/levelsgenerator.cpp colorcorrection/rgbparadegenerator.cpp + colorcorrection/vectorscopegenerator.cpp + colorcorrection/waveformgenerator.cpp razorgroupcommand.cpp ) diff --git a/src/abstractscopewidget.cpp b/src/abstractscopewidget.cpp index 1d419116..998cc725 100644 --- a/src/abstractscopewidget.cpp +++ b/src/abstractscopewidget.cpp @@ -216,10 +216,12 @@ void AbstractScopeWidget::paintEvent(QPaintEvent *) initialDimensionUpdateDone = true; } + qDebug() << "Drawing top/left at " << m_scopeRect.topLeft().x() << "/" << m_scopeRect.topLeft().y(); + QPainter davinci(this); - davinci.drawImage(scopeRect().topLeft(), m_imgBackground); - davinci.drawImage(scopeRect().topLeft(), m_imgScope); - davinci.drawImage(scopeRect().topLeft(), m_imgHUD); + davinci.drawImage(m_scopeRect.topLeft(), m_imgBackground); + davinci.drawImage(m_scopeRect.topLeft(), m_imgScope); + davinci.drawImage(m_scopeRect.topLeft(), m_imgHUD); } void AbstractScopeWidget::customContextMenuRequested(const QPoint &pos) diff --git a/src/abstractscopewidget.h b/src/abstractscopewidget.h index 65cde138..fba34962 100644 --- a/src/abstractscopewidget.h +++ b/src/abstractscopewidget.h @@ -97,7 +97,9 @@ protected: /** Offset from the widget's borders */ const uchar offset; - /** The rect on the widget we're painting in. */ + /** The rect on the widget we're painting in. + Can be used by the implementing widget, e.g. in the render methods. + Is updated when necessary (size changes). */ QRect m_scopeRect; /** Images storing the calculated layers. Will be used on repaint events. */ diff --git a/src/colorcorrection/levelsgenerator.cpp b/src/colorcorrection/levelsgenerator.cpp new file mode 100644 index 00000000..7a216aaa --- /dev/null +++ b/src/colorcorrection/levelsgenerator.cpp @@ -0,0 +1,172 @@ +/*************************************************************************** + * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) * + * This file is part of kdenlive. See www.kdenlive.org. * + * * + * This program 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. * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include "levelsgenerator.h" + +LevelsGenerator::LevelsGenerator() +{ +} + +QImage LevelsGenerator::calculateLevels(const QSize ¶deSize, const QImage &image, const int &components, const uint &accelFactor) const +{ + qDebug() << "Levels rect size is: " << paradeSize.width() << "/" << paradeSize.height(); + if (paradeSize.height() <= 0 || paradeSize.width() <= 0) { + return QImage(); + } + + bool drawY = (components & LevelsGenerator::ComponentY) != 0; + bool drawR = (components & LevelsGenerator::ComponentR) != 0; + bool drawG = (components & LevelsGenerator::ComponentG) != 0; + bool drawB = (components & LevelsGenerator::ComponentB) != 0; + + int r[256], g[256], b[256], y[256]; + // Initialize the values to zero + std::fill(r, r+255, 0); + std::fill(g, g+255, 0); + std::fill(b, b+255, 0); + std::fill(y, y+255, 0); + + const uint iw = image.bytesPerLine(); + const uint ih = image.height(); + const uint ww = paradeSize.width(); + const uint wh = paradeSize.height(); + const uint byteCount = iw*ih; + const uint stepsize = 4*accelFactor; + + const uchar *bits = image.bits(); + QRgb *col; + + + // Read the stats from the input image + for (uint i = 0; i < byteCount; i += stepsize) { + col = (QRgb *)bits; + + r[qRed(*col)]++; + g[qGreen(*col)]++; + b[qBlue(*col)]++; + y[(int)floor(.299*qRed(*col) + .587*qGreen(*col) + .114*qBlue(*col))]++; + + bits += stepsize; + } + + + const int nParts = (drawY ? 1 : 0) + (drawR ? 1 : 0) + (drawG ? 1 : 0) + (drawB ? 1 : 0); + if (nParts == 0) { + // Nothing to draw + return QImage(); + } + + const int d = 20; // Distance for text + const int partH = (wh-4*d)/nParts; + const float scaling = (float)partH/(byteCount >> 7); + + int wy = 0; // Drawing position + int partY; // Vertical position for the dots + + QImage levels(paradeSize, QImage::Format_ARGB32); + QImage component(256, partH, QImage::Format_ARGB32); + QPainter davinci(&levels); + levels.fill(qRgba(0, 0, 0, 0)); + + if (drawY) { + qDebug() << "Drawing Y at " << wy << " with height " << partH; + component.fill(qRgba(0, 0, 0, 0)); + + for (int x = 0; x < 256; x++) { + // Calculate the height of the curve at position x + partY = scaling*y[x]; + + // Invert the y axis + if (partY > partH-1) { partY = partH-1; } + partY = partH-1 - partY; + + for (int k = partH-1; k >= partY; k--) { + component.setPixel(x, k, qRgba(220, 220, 210, 255)); + } + } + + davinci.drawImage(0, wy, component.scaled(ww, component.height(), Qt::IgnoreAspectRatio, Qt::FastTransformation)); + + wy += partH + d; + } + + if (drawR) { + qDebug() << "Drawing R at " << wy << " with height " << partH; + component.fill(qRgba(0, 0, 0, 0)); + + for (int x = 0; x < 256; x++) { + // Calculate the height of the curve at position x + partY = scaling*r[x]; + + // Invert the y axis + if (partY > partH-1) { partY = partH-1; } + partY = partH-1 - partY; + + for (int k = partH-1; k >= partY; k--) { + component.setPixel(x, k, qRgba(255, 128, 0, 255)); + } + } + + davinci.drawImage(0, wy, component.scaled(ww, component.height(), Qt::IgnoreAspectRatio, Qt::FastTransformation)); + + wy += partH + d; + } + + if (drawG) { + qDebug() << "Drawing G at " << wy << " with height " << partH; + component.fill(qRgba(0, 0, 0, 0)); + + for (int x = 0; x < 256; x++) { + // Calculate the height of the curve at position x + partY = scaling*g[x]; + + // Invert the y axis + if (partY > partH-1) { partY = partH-1; } + partY = partH-1 - partY; + + for (int k = partH-1; k >= partY; k--) { + component.setPixel(x, k, qRgba(128, 255, 0, 255)); + } + } + + davinci.drawImage(0, wy, component.scaled(ww, component.height(), Qt::IgnoreAspectRatio, Qt::FastTransformation)); + + wy += partH + d; + } + + if (drawB) { + qDebug() << "Drawing B at " << wy << " with height " << partH; + component.fill(qRgba(0, 0, 0, 0)); + + for (int x = 0; x < 256; x++) { + // Calculate the height of the curve at position x + partY = scaling*b[x]; + + // Invert the y axis + if (partY > partH-1) { partY = partH-1; } + partY = partH-1 - partY; + + for (int k = partH-1; k >= partY; k--) { + component.setPixel(x, k, qRgba(0, 128, 255, 255)); + } + } + + davinci.drawImage(0, wy, component.scaled(ww, component.height(), Qt::IgnoreAspectRatio, Qt::FastTransformation)); + + wy += partH + d; + } + + return levels; +} diff --git a/src/colorcorrection/levelsgenerator.h b/src/colorcorrection/levelsgenerator.h new file mode 100644 index 00000000..3f77c4cc --- /dev/null +++ b/src/colorcorrection/levelsgenerator.h @@ -0,0 +1,27 @@ +/*************************************************************************** + * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) * + * This file is part of kdenlive. See www.kdenlive.org. * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef LEVELSGENERATOR_H +#define LEVELSGENERATOR_H + +#include + +class QImage; +class QSize; + +class LevelsGenerator : public QObject +{ +public: + LevelsGenerator(); + QImage calculateLevels(const QSize ¶deSize, const QImage &image, const int &components, const uint &accelFactor = 1) const; + enum Components { ComponentY = 1<<0, ComponentR = 1<<1, ComponentG = 1<<2, ComponentB = 1<<3 }; +}; + +#endif // LEVELSGENERATOR_H diff --git a/src/levels.cpp b/src/levels.cpp new file mode 100644 index 00000000..84ab527b --- /dev/null +++ b/src/levels.cpp @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) * + * This file is part of kdenlive. See www.kdenlive.org. * + * * + * This program 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. * + ***************************************************************************/ + +#include +#include "levelsgenerator.h" +#include "levels.h" +#include "renderer.h" + +Levels::Levels(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent) : + AbstractScopeWidget(projMonitor, clipMonitor, parent) +{ + ui = new Ui::Levels_UI(); + ui->setupUi(this); + + ui->cbY->setChecked(true); + ui->cbR->setChecked(true); + ui->cbG->setChecked(true); + ui->cbB->setChecked(true); + + bool b = true; + b &= connect(ui->cbY, SIGNAL(toggled(bool)), this, SLOT(forceUpdateScope())); + b &= connect(ui->cbR, SIGNAL(toggled(bool)), this, SLOT(forceUpdateScope())); + b &= connect(ui->cbG, SIGNAL(toggled(bool)), this, SLOT(forceUpdateScope())); + b &= connect(ui->cbB, SIGNAL(toggled(bool)), this, SLOT(forceUpdateScope())); + Q_ASSERT(b); +} + +Levels::~Levels() +{ + delete ui; +} + +QString Levels::widgetName() const { return QString("Levels"); } + +bool Levels::isHUDDependingOnInput() const { return false; } +bool Levels::isScopeDependingOnInput() const { return true; } +bool Levels::isBackgroundDependingOnInput() const { return false; } + +QRect Levels::scopeRect() +{ + qDebug() << "According to the spacer, the top left point is " << ui->verticalSpacer->geometry().x() << "/" << ui->verticalSpacer->geometry().y(); + QPoint topleft(offset, offset+ ui->verticalSpacer->geometry().y()); + return QRect(topleft, this->rect().size() - QSize(topleft.x() + offset, topleft.y() + offset)); +} + +QImage Levels::renderHUD(uint) +{ + emit signalHUDRenderingFinished(0, 1); + return QImage(); +} +QImage Levels::renderScope(uint accelFactor) +{ + QTime start = QTime::currentTime(); + start.start(); + + const int componentFlags = (ui->cbY->isChecked() ? 1 : 0) * LevelsGenerator::ComponentY + | (ui->cbR->isChecked() ? 1 : 0) * LevelsGenerator::ComponentR + | (ui->cbG->isChecked() ? 1 : 0) * LevelsGenerator::ComponentG + | (ui->cbB->isChecked() ? 1 : 0) * LevelsGenerator::ComponentB; + + QImage levels = m_levelsGenerator->calculateLevels(m_scopeRect.size(), m_activeRender->extractFrame(m_activeRender->seekFramePosition()), + componentFlags, accelFactor); + + emit signalScopeRenderingFinished(0, 1); + return levels; +} +QImage Levels::renderBackground(uint) +{ + emit signalBackgroundRenderingFinished(0, 1); + return QImage(); +} diff --git a/src/levels.h b/src/levels.h new file mode 100644 index 00000000..43df14e6 --- /dev/null +++ b/src/levels.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) * + * This file is part of kdenlive. See www.kdenlive.org. * + * * + * This program 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. * + ***************************************************************************/ + +#ifndef LEVELS_H +#define LEVELS_H + +#include "abstractscopewidget.h" +#include "ui_levels_ui.h" + +class LevelsGenerator; + +class Levels : public AbstractScopeWidget { + Q_OBJECT + +public: + Levels(Monitor *projMonitor, Monitor *clipMonitor, QWidget *parent = 0); + ~Levels(); + QString widgetName() const; + + +private: + LevelsGenerator *m_levelsGenerator; + + QRect scopeRect(); + bool isHUDDependingOnInput() const; + bool isScopeDependingOnInput() const; + bool isBackgroundDependingOnInput() const; + QImage renderHUD(uint accelerationFactor); + QImage renderScope(uint accelerationFactor); + QImage renderBackground(uint accelerationFactor); + Ui::Levels_UI *ui; + +}; + +#endif // LEVELS_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 836fa7fb..b59826ed 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -55,6 +55,7 @@ #include "vectorscope.h" #include "waveform.h" #include "rgbparade.h" +#include "levels.h" #include #include @@ -233,6 +234,12 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, QWidget *parent m_RGBParadeDock->setWidget(m_RGBParade); addDockWidget(Qt::TopDockWidgetArea, m_RGBParadeDock); + m_levels = new Levels(m_projectMonitor, m_clipMonitor, this); + m_levelsDock = new QDockWidget("Levels", this); + m_levelsDock->setObjectName(m_levels->widgetName()); + m_levelsDock->setWidget(m_levels); + addDockWidget(Qt::TopDockWidgetArea, m_levelsDock); + m_undoViewDock = new QDockWidget(i18n("Undo History"), this); m_undoViewDock->setObjectName("undo_history"); @@ -264,6 +271,7 @@ MainWindow::MainWindow(const QString &MltPath, const KUrl & Url, QWidget *parent tabifyDockWidget(m_vectorscopeDock, m_waveformDock); tabifyDockWidget(m_vectorscopeDock, m_RGBParadeDock); + tabifyDockWidget(m_vectorscopeDock, m_levelsDock); tabifyDockWidget(m_vectorscopeDock, m_undoViewDock); tabifyDockWidget(m_vectorscopeDock, m_effectListDock); diff --git a/src/mainwindow.h b/src/mainwindow.h index 983d4867..ff754134 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -61,6 +61,7 @@ class JogShuttle; class DocClipBase; class Render; class Transition; +class Levels; class Vectorscope; class Waveform; class RGBParade; @@ -162,6 +163,9 @@ private: QDockWidget *m_RGBParadeDock; RGBParade *m_RGBParade; + QDockWidget *m_levelsDock; + Levels *m_levels; + QDockWidget *m_undoViewDock; QUndoView *m_undoView; QUndoGroup *m_commandStack; diff --git a/src/widgets/levels_ui.ui b/src/widgets/levels_ui.ui new file mode 100644 index 00000000..5bd67cd7 --- /dev/null +++ b/src/widgets/levels_ui.ui @@ -0,0 +1,62 @@ + + + Levels_UI + + + + 0 + 0 + 400 + 300 + + + + Form + + + + + + Y + + + + + + + R + + + + + + + G + + + + + + + B + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + -- 2.39.2