pixelsampler: added pixel sampler panel

When visible, this panel will show the current reference and display transformed
color under the cursor of any viewer.
This commit is contained in:
itsmattkc
2020-04-01 16:57:10 +11:00
parent 76f260cb3a
commit 6ea6dd810e
25 changed files with 390 additions and 48 deletions
+18
View File
@@ -59,6 +59,24 @@ const PixelFormat::Format &Frame::format() const
return params_.format();
}
Color Frame::get_pixel(int x, int y) const
{
if (!contains_pixel(x, y)) {
return Color();
}
int pixel_index = y * width() + x;
int byte_offset = PixelFormat::GetBufferSize(video_params().format(), pixel_index, 1);
return Color(data_.data() + byte_offset, video_params().format());
}
bool Frame::contains_pixel(int x, int y) const
{
return (is_allocated() && x >= 0 && x < width() && y >= 0 && y < height());
}
const rational &Frame::sample_aspect_ratio() const
{
return sample_aspect_ratio_;
+4
View File
@@ -25,6 +25,7 @@
#include <QVector>
#include "common/rational.h"
#include "render/color.h"
#include "render/pixelformat.h"
#include "render/videoparams.h"
@@ -48,6 +49,9 @@ public:
const int& height() const;
const PixelFormat::Format& format() const;
Color get_pixel(int x, int y) const;
bool contains_pixel(int x, int y) const;
const rational& sample_aspect_ratio() const;
void set_sample_aspect_ratio(const rational& sample_aspect_ratio);
+1
View File
@@ -19,6 +19,7 @@ add_subdirectory(curve)
add_subdirectory(footageviewer)
add_subdirectory(node)
add_subdirectory(param)
add_subdirectory(pixelsampler)
add_subdirectory(project)
add_subdirectory(sequenceviewer)
add_subdirectory(taskmanager)
+22
View File
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2019 Olive Team
#
# 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 3 of the License, or
# (at your option) any later version.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
panel/pixelsampler/pixelsamplerpanel.h
panel/pixelsampler/pixelsamplerpanel.cpp
PARENT_SCOPE
)
@@ -0,0 +1,23 @@
#include "pixelsamplerpanel.h"
PixelSamplerPanel::PixelSamplerPanel(QWidget *parent) :
PanelWidget(parent)
{
// FIXME: This won't work if there's ever more than one of this panel
setObjectName("ProjectPanel");
sampler_widget_ = new ManagedPixelSamplerWidget();
setWidget(sampler_widget_);
Retranslate();
}
void PixelSamplerPanel::SetValues(const Color &reference, const Color &display)
{
sampler_widget_->SetValues(reference, display);
}
void PixelSamplerPanel::Retranslate()
{
SetTitle(tr("Pixel Sampler"));
}
@@ -0,0 +1,23 @@
#ifndef PIXELSAMPLERPANEL_H
#define PIXELSAMPLERPANEL_H
#include "widget/panel/panel.h"
#include "widget/viewer/pixelsamplerwidget.h"
class PixelSamplerPanel : public PanelWidget
{
Q_OBJECT
public:
PixelSamplerPanel(QWidget* parent = nullptr);
public slots:
void SetValues(const Color& reference, const Color& display);
private:
virtual void Retranslate() override;
ManagedPixelSamplerWidget* sampler_widget_;
};
#endif // PIXELSAMPLERPANEL_H
+8
View File
@@ -45,3 +45,11 @@ VideoRenderBackend *ViewerPanelBase::video_renderer() const
{
return static_cast<ViewerWidget*>(GetTimeBasedWidget())->video_renderer();
}
void ViewerPanelBase::ConnectPixelSamplerPanel(PixelSamplerPanel *psp)
{
ViewerWidget* vw = static_cast<ViewerWidget*>(GetTimeBasedWidget());
connect(psp, &PixelSamplerPanel::visibilityChanged, vw, &ViewerWidget::SetSignalCursorColorEnabled);
connect(vw, &ViewerWidget::CursorColor, psp, &PixelSamplerPanel::SetValues);
}
+3
View File
@@ -1,6 +1,7 @@
#ifndef VIEWERPANELBASE_H
#define VIEWERPANELBASE_H
#include "panel/pixelsampler/pixelsamplerpanel.h"
#include "panel/timebased/timebased.h"
#include "widget/viewer/viewer.h"
@@ -24,6 +25,8 @@ public:
VideoRenderBackend* video_renderer() const;
void ConnectPixelSamplerPanel(PixelSamplerPanel *psp);
};
#endif // VIEWERPANELBASE_H
+2
View File
@@ -20,6 +20,8 @@ set(OLIVE_SOURCES
${OLIVE_SOURCES}
render/audioparams.h
render/audioparams.cpp
render/color.h
render/color.cpp
render/colormanager.h
render/colormanager.cpp
render/colorprocessor.h
@@ -87,7 +87,7 @@ void OpenGLFramebuffer::Release()
context_->functions()->glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void OpenGLFramebuffer::Attach(OpenGLTexturePtr texture, bool clear)
void OpenGLFramebuffer::Attach(OpenGLTexture *texture, bool clear)
{
if (context_ == nullptr) {
return;
@@ -115,6 +115,11 @@ void OpenGLFramebuffer::Attach(OpenGLTexturePtr texture, bool clear)
f->glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void OpenGLFramebuffer::Attach(OpenGLTexturePtr texture, bool clear)
{
Attach(texture.get(), clear);
}
void OpenGLFramebuffer::Detach()
{
if (context_ == nullptr) {
@@ -43,6 +43,7 @@ public:
void Release();
void Attach(OpenGLTexture* texture, bool clear = false);
void Attach(OpenGLTexturePtr texture, bool clear = false);
void Detach();
@@ -57,7 +58,7 @@ private:
GLuint buffer_;
OpenGLTexturePtr texture_;
OpenGLTexture* texture_;
};
#endif // OPENGLFRAMEBUFFER_H
+14
View File
@@ -0,0 +1,14 @@
#include "color.h"
Color::Color(const char *data, const PixelFormat::Format &format)
{
OIIO::convert_type(PixelFormat::GetOIIOTypeDesc(format),
data,
PixelFormat::GetOIIOTypeDesc(PixelFormat::PIX_FMT_RGB32F),
data_,
PixelFormat::FormatHasAlphaChannel(format) ? kRGBAChannels : kRGBChannels);
if (!PixelFormat::FormatHasAlphaChannel(format)) {
set_alpha(1.0f);
}
}
+50
View File
@@ -0,0 +1,50 @@
#ifndef COLOR_H
#define COLOR_H
#include "common/define.h"
#include "render/pixelformat.h"
/**
* @brief High precision 32-bit float based RGBA color value
*/
class Color
{
public:
Color()
{
for (int i=0;i<kRGBAChannels;i++) {
data_[i] = 0;
}
}
Color(const float& r, const float& g, const float& b, const float& a = 1.0f)
{
data_[0] = r;
data_[1] = g;
data_[2] = b;
data_[3] = a;
}
Color(const char *data, const PixelFormat::Format &format);
const float& red() const {return data_[0];}
const float& green() const {return data_[1];}
const float& blue() const {return data_[2];}
const float& alpha() const {return data_[3];}
void set_red(const float& red) {data_[0] = red;}
void set_green(const float& green) {data_[1] = green;}
void set_blue(const float& blue) {data_[2] = blue;}
void set_alpha(const float& alpha) {data_[3] = alpha;}
float* data() {return data_;}
const float* data() const {return data_;}
static Color fromData(const char* data, const PixelFormat::Format& format);
private:
float data_[kRGBAChannels];
};
#endif // COLOR_H
+6
View File
@@ -43,6 +43,12 @@ void ColorProcessor::ConvertFrame(FramePtr f)
processor->apply(img);
}
Color ColorProcessor::ConvertColor(Color in)
{
processor->applyRGBA(in.data());
return in;
}
ColorProcessorPtr ColorProcessor::Create(OCIO::ConstConfigRcPtr config, const QString& source_space, const QString& dest_space)
{
return std::make_shared<ColorProcessor>(config, source_space, dest_space);
+3 -1
View File
@@ -6,7 +6,7 @@ namespace OCIO = OCIO_NAMESPACE::v1;
#include "codec/frame.h"
#include "common/constructors.h"
#include "render/color.h"
class ColorProcessor;
using ColorProcessorPtr = std::shared_ptr<ColorProcessor>;
@@ -35,6 +35,8 @@ public:
void ConvertFrame(FramePtr f);
Color ConvertColor(Color in);
private:
OCIO::ConstProcessorRcPtr processor;
-44
View File
@@ -140,50 +140,6 @@ PixelFormat::Format PixelFormat::OIIOFormatToOliveFormat(OIIO::TypeDesc desc, bo
return PixelFormat::PIX_FMT_INVALID;
}
/*PixelFormat::Info PixelFormat::GetPixelFormatInfo(const PixelFormat::Format &format)
{
PixelFormat::Info info;
switch (format) {
case PixelFormat::PIX_FMT_RGB8:
case PixelFormat::PIX_FMT_RGBA8:
info.name = tr("8-bit");
info.internal_format = (format == PixelFormat::PIX_FMT_RGB8) ? GL_RGB8 : GL_RGBA8;
info.gl_pixel_type = GL_UNSIGNED_BYTE;
info.oiio_desc = OIIO::TypeDesc::UINT8;
break;
case PixelFormat::PIX_FMT_RGB16U:
case PixelFormat::PIX_FMT_RGBA16U:
info.name = tr("16-bit Integer");
info.internal_format = (format == PixelFormat::PIX_FMT_RGB16U) ? GL_RGB8 : GL_RGBA16;
info.gl_pixel_type = GL_UNSIGNED_SHORT;
info.oiio_desc = OIIO::TypeDesc::UINT16;
break;
case PixelFormat::PIX_FMT_RGB16F:
case PixelFormat::PIX_FMT_RGBA16F:
info.name = tr("Half-Float (16-bit)");
info.internal_format = (format == PixelFormat::PIX_FMT_RGB8) ? GL_RGB8 : GL_RGBA16F;
info.gl_pixel_type = GL_HALF_FLOAT;
info.oiio_desc = OIIO::TypeDesc::HALF;
break;
case PixelFormat::PIX_FMT_RGB32F:
case PixelFormat::PIX_FMT_RGBA32F:
info.name = tr("Full-Float (32-bit)");
info.internal_format = (format == PixelFormat::PIX_FMT_RGB8) ? GL_RGB8 : GL_RGBA32F;
info.gl_pixel_type = GL_FLOAT;
info.oiio_desc = OIIO::TypeDesc::FLOAT;
break;
case PixelFormat::PIX_FMT_INVALID:
case PixelFormat::PIX_FMT_COUNT:
qFatal("Invalid pixel format requested");
}
info.pixel_format = GL_RGBA;
info.bytes_per_pixel = BytesPerPixel(format);
return info;
}*/
int PixelFormat::GetBufferSize(const PixelFormat::Format &format, const int &width, const int &height)
{
return BytesPerPixel(format) * width * height;
+2
View File
@@ -20,6 +20,8 @@ set(OLIVE_SOURCES
widget/viewer/audiowaveformview.cpp
widget/viewer/footageviewer.h
widget/viewer/footageviewer.cpp
widget/viewer/pixelsamplerwidget.h
widget/viewer/pixelsamplerwidget.cpp
widget/viewer/viewer.h
widget/viewer/viewer.cpp
widget/viewer/viewerglwidget.h
+55
View File
@@ -0,0 +1,55 @@
#include "pixelsamplerwidget.h"
#include <QVBoxLayout>
PixelSamplerWidget::PixelSamplerWidget(QWidget *parent) :
QGroupBox(parent)
{
QVBoxLayout* layout = new QVBoxLayout(this);
label_ = new QLabel();
layout->addWidget(label_);
setTitle(tr("Color"));
UpdateLabelInternal();
}
void PixelSamplerWidget::SetValues(const Color &color)
{
color_ = color;
UpdateLabelInternal();
}
void PixelSamplerWidget::UpdateLabelInternal()
{
label_->setText(tr("<html>"
"<font color='#FF8080'>R: %1</font><br>"
"<font color='#80FF80'>G: %2</font><br>"
"<font color='#8080FF'>B: %3</font><br>"
"A: %4"
"</html>").arg(QString::number(color_.red()),
QString::number(color_.green()),
QString::number(color_.blue()),
QString::number(color_.alpha())));
}
ManagedPixelSamplerWidget::ManagedPixelSamplerWidget(QWidget *parent) :
QWidget(parent)
{
QVBoxLayout* layout = new QVBoxLayout(this);
display_view_ = new PixelSamplerWidget();
display_view_->setTitle(tr("Display"));
layout->addWidget(display_view_);
reference_view_ = new PixelSamplerWidget();
reference_view_->setTitle(tr("Reference"));
layout->addWidget(reference_view_);
}
void ManagedPixelSamplerWidget::SetValues(const Color &reference, const Color &display)
{
reference_view_->SetValues(reference);
display_view_->SetValues(display);
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef PIXELSAMPLERWIDGET_H
#define PIXELSAMPLERWIDGET_H
#include <QLabel>
#include <QGroupBox>
#include <QWidget>
#include "render/color.h"
class PixelSamplerWidget : public QGroupBox
{
Q_OBJECT
public:
PixelSamplerWidget(QWidget* parent = nullptr);
public slots:
void SetValues(const Color& color);
private:
void UpdateLabelInternal();
Color color_;
QLabel* label_;
};
class ManagedPixelSamplerWidget : public QWidget
{
Q_OBJECT
public:
ManagedPixelSamplerWidget(QWidget* parent = nullptr);
public slots:
void SetValues(const Color& reference, const Color& display);
private:
PixelSamplerWidget* reference_view_;
PixelSamplerWidget* display_view_;
};
#endif // PIXELSAMPLERWIDGET_H
+6
View File
@@ -57,6 +57,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
gl_widget_ = new ViewerGLWidget();
connect(gl_widget_, &ViewerGLWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu);
connect(gl_widget_, &ViewerGLWidget::CursorColor, this, &ViewerWidget::CursorColor);
connect(sizer_, &ViewerSizer::RequestMatrix, gl_widget_, &ViewerGLWidget::SetMatrix);
sizer_->SetWidget(gl_widget_);
@@ -531,6 +532,11 @@ void ViewerWidget::SetOCIOLook(const QString &look)
gl_widget_->SetOCIOLook(look);
}
void ViewerWidget::SetSignalCursorColorEnabled(bool e)
{
gl_widget_->SetSignalCursorColorEnabled(e);
}
void ViewerWidget::TimebaseChangedEvent(const rational &timebase)
{
TimeBasedWidget::TimebaseChangedEvent(timebase);
+11
View File
@@ -106,6 +106,17 @@ public slots:
*/
void SetOCIOLook(const QString& look);
/**
* @brief Wrapper for ViewerGLWidget::SetSignalCursorColorEnabled()
*/
void SetSignalCursorColorEnabled(bool e);
signals:
/**
* @brief Wrapper for ViewerGLWidget::CursorColor()
*/
void CursorColor(const Color& reference, const Color& display);
protected:
virtual void TimebaseChangedEvent(const rational &) override;
virtual void TimeChangedEvent(const int64_t &) override;
+47 -1
View File
@@ -23,6 +23,7 @@
#include <OpenImageIO/imagebuf.h>
#include <QFileInfo>
#include <QMessageBox>
#include <QMouseEvent>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
@@ -39,7 +40,8 @@ bool ViewerGLWidget::nouveau_check_done_ = false;
ViewerGLWidget::ViewerGLWidget(QWidget *parent) :
QOpenGLWidget(parent),
color_manager_(nullptr),
has_image_(false)
has_image_(false),
signal_cursor_color_(false)
{
setContextMenuPolicy(Qt::CustomContextMenu);
}
@@ -128,6 +130,25 @@ void ViewerGLWidget::SetImage(const QString &fn)
update();
}
void ViewerGLWidget::SetSignalCursorColorEnabled(bool e)
{
qDebug() << e;
signal_cursor_color_ = e;
setMouseTracking(e);
// Create frame buffer for reading from texture
if (e != frame_buffer_.IsCreated()) {
makeCurrent();
if (e) {
frame_buffer_.Create(context());
} else {
frame_buffer_.Destroy();
}
doneCurrent();
}
}
void ViewerGLWidget::SetOCIODisplay(const QString &display)
{
ocio_display_ = display;
@@ -184,6 +205,31 @@ void ViewerGLWidget::mousePressEvent(QMouseEvent *event)
emit DragStarted();
}
void ViewerGLWidget::mouseMoveEvent(QMouseEvent *event)
{
QOpenGLWidget::mouseMoveEvent(event);
if (signal_cursor_color_) {
Color reference, display;
if (has_image_) {
QVector3D pixel_pos(static_cast<float>(event->x()) / static_cast<float>(width()) * 2.0f - 1.0f,
static_cast<float>(event->y()) / static_cast<float>(height()) * 2.0f - 1.0f,
0);
pixel_pos = pixel_pos * matrix_.inverted();
int frame_x = qRound((pixel_pos.x() + 1.0f) * 0.5f * load_buffer_.width());
int frame_y = qRound((pixel_pos.y() + 1.0f) * 0.5f * load_buffer_.height());
reference = load_buffer_.get_pixel(frame_x, frame_y);
display = color_service_->ConvertColor(reference);
}
emit CursorColor(reference, display);
}
}
void ViewerGLWidget::SetOCIOParameters(const QString &display, const QString &view, const QString &look)
{
ocio_display_ = display;
+31
View File
@@ -24,8 +24,10 @@
#include <QOpenGLWidget>
#include "render/backend/opengl/openglcolorprocessor.h"
#include "render/backend/opengl/openglframebuffer.h"
#include "render/backend/opengl/openglshader.h"
#include "render/backend/opengl/opengltexture.h"
#include "render/color.h"
#include "render/colormanager.h"
/**
@@ -119,15 +121,37 @@ public slots:
*/
void SetMatrix(const QMatrix4x4& mat);
/**
* @brief Enables or disables whether this color at the cursor should be emitted
*
* Since tracking the mouse every movement, reading pixels, and doing color transforms are processor intensive, we
* have an option for it. Ideally, this should be connected to a PixelSamplerPanel::visibilityChanged signal so that
* it can automatically be enabled when the user is pixel sampling and disabled for optimization when they're not.
*/
void SetSignalCursorColorEnabled(bool e);
signals:
/**
* @brief Signal emitted when the user starts dragging from the viewer
*/
void DragStarted();
/**
* @brief Signal emitted when cursor color is enabled and the user's mouse position changes
*/
void CursorColor(const Color& reference, const Color& display);
protected:
/**
* @brief Override the mouse press event simply to emit the DragStarted() signal
*/
virtual void mousePressEvent(QMouseEvent* event) override;
/**
* @brief Override mouse move to provide functionality for
*/
virtual void mouseMoveEvent(QMouseEvent* event) override;
/**
* @brief Initialize function to set up the OpenGL context upon its construction
*
@@ -183,6 +207,11 @@ private:
*/
OpenGLColorProcessorPtr color_service_;
/**
* @brief Framebuffer used for reading reference space pixels from the texture
*/
OpenGLFramebuffer frame_buffer_;
/**
* @brief Drawing matrix (defaults to identity)
*/
@@ -199,6 +228,8 @@ private:
bool has_image_;
bool signal_cursor_color_;
private slots:
/**
* @brief Slot to connect just before the OpenGL context is destroyed to clean up resources
+7
View File
@@ -68,6 +68,8 @@ MainWindow::MainWindow(QWidget *parent) :
footage_viewer_panel_->raise();
sequence_viewer_panel_ = PanelManager::instance()->CreatePanel<SequenceViewerPanel>(this);
addDockWidget(Qt::TopDockWidgetArea, sequence_viewer_panel_);
pixel_sampler_panel_ = PanelManager::instance()->CreatePanel<PixelSamplerPanel>(this);
addDockWidget(Qt::TopDockWidgetArea, pixel_sampler_panel_);
project_panel_ = PanelManager::instance()->CreatePanel<ProjectPanel>(this);
addDockWidget(Qt::BottomDockWidgetArea, project_panel_);
tool_panel_ = PanelManager::instance()->CreatePanel<ToolPanel>(this);
@@ -97,6 +99,9 @@ MainWindow::MainWindow(QWidget *parent) :
sequence_viewer_panel_->ConnectTimeBasedPanel(param_panel_);
sequence_viewer_panel_->ConnectTimeBasedPanel(curve_panel_);
footage_viewer_panel_->ConnectPixelSamplerPanel(pixel_sampler_panel_);
sequence_viewer_panel_->ConnectPixelSamplerPanel(pixel_sampler_panel_);
connect(project_panel_, &ProjectPanel::ProjectNameChanged, this, &MainWindow::UpdateTitle);
UpdateTitle();
}
@@ -303,6 +308,8 @@ void MainWindow::SetDefaultLayout()
task_man_panel_->setFloating(true);
curve_panel_->close();
curve_panel_->setFloating(true);
pixel_sampler_panel_->close();
pixel_sampler_panel_->setFloating(true);
resizeDocks({node_panel_, param_panel_, sequence_viewer_panel_},
{width()/3, width()/3, width()/3},
+2
View File
@@ -34,6 +34,7 @@
#include "panel/tool/tool.h"
#include "panel/footageviewer/footageviewer.h"
#include "panel/sequenceviewer/sequenceviewer.h"
#include "panel/pixelsampler/pixelsamplerpanel.h"
#include "project/project.h"
/**
@@ -76,6 +77,7 @@ private:
AudioMonitorPanel* audio_monitor_panel_;
TaskManagerPanel* task_man_panel_;
CurvePanel* curve_panel_;
PixelSamplerPanel* pixel_sampler_panel_;
private slots:
void FocusedPanelChanged(PanelWidget* panel);