diff --git a/app/core.cpp b/app/core.cpp index 16cab3c1e..18b077042 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -34,11 +34,12 @@ #include "dialog/about/about.h" #include "dialog/sequence/sequence.h" #include "dialog/preferences/preferences.h" +#include "dialog/projectproperties/projectproperties.h" #include "panel/panelmanager.h" #include "panel/project/project.h" #include "project/item/footage/footage.h" #include "project/item/sequence/sequence.h" -#include "render/colorservice.h" +#include "render/colormanager.h" #include "task/import/import.h" #include "task/taskmanager.h" #include "ui/style/style.h" @@ -92,6 +93,9 @@ void Core::Start() // Load application config Config::Load(); + // Set up color manager + ColorManager::CreateInstance(); + // // Start GUI (FIXME CLI mode) @@ -108,6 +112,8 @@ void Core::Stop() { AudioManager::DestroyInstance(); + ColorManager::DestroyInstance(); + delete main_window_; } @@ -208,6 +214,12 @@ void Core::DialogPreferencesShow() pd.exec(); } +void Core::DialogProjectPropertiesShow() +{ + ProjectPropertiesDialog ppd(main_window_); + ppd.exec(); +} + void Core::CreateNewFolder() { // Locate the most recently focused Project panel (assume that's the panel the user wants to import into) @@ -334,12 +346,8 @@ void Core::StartGUI(bool full_screen) // When a new project is opened, update the mainwindow connect(this, SIGNAL(ProjectOpened(Project*)), main_window_, SLOT(ProjectOpen(Project*))); - // Initialize color service - ColorService::Init(); - // Initialize audio service AudioManager::CreateInstance(); - } Project *Core::GetActiveProject() diff --git a/app/core.h b/app/core.h index 88e1e0593..d723c5731 100644 --- a/app/core.h +++ b/app/core.h @@ -129,6 +129,11 @@ public slots: */ void DialogPreferencesShow(); + /** + * @brief Show Project Properties dialog + */ + void DialogProjectPropertiesShow(); + /** * @brief Create a new folder in the currently active project */ diff --git a/app/dialog/CMakeLists.txt b/app/dialog/CMakeLists.txt index 4c13cbc0c..0e8af7660 100644 --- a/app/dialog/CMakeLists.txt +++ b/app/dialog/CMakeLists.txt @@ -16,7 +16,9 @@ add_subdirectory(about) add_subdirectory(actionsearch) +add_subdirectory(footageproperties) add_subdirectory(preferences) +add_subdirectory(projectproperties) add_subdirectory(sequence) set(OLIVE_SOURCES diff --git a/app/dialog/footageproperties/CMakeLists.txt b/app/dialog/footageproperties/CMakeLists.txt new file mode 100644 index 000000000..3c4ee65c3 --- /dev/null +++ b/app/dialog/footageproperties/CMakeLists.txt @@ -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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + dialog/footageproperties/footageproperties.h + dialog/footageproperties/footageproperties.cpp + PARENT_SCOPE +) diff --git a/app/dialog/footageproperties/footageproperties.cpp b/app/dialog/footageproperties/footageproperties.cpp new file mode 100644 index 000000000..d4ea2a2e1 --- /dev/null +++ b/app/dialog/footageproperties/footageproperties.cpp @@ -0,0 +1,209 @@ +/*** + + 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 . + +***/ + +#include "footageproperties.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +namespace OCIO = OCIO_NAMESPACE::v1; + +#include "undo/undostack.h" + +FootagePropertiesDialog::FootagePropertiesDialog(QWidget *parent, FootagePtr footage) : + QDialog(parent), + footage_(footage) +{ + QGridLayout* layout = new QGridLayout(this); + + setWindowTitle(tr("\"%1\" Properties").arg(footage_->name())); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + int row = 0; + + layout->addWidget(new QLabel(tr("Tracks:"), this), row, 0, 1, 2); + row++; + + track_list = new QListWidget(this); + + foreach (StreamPtr stream, footage_->streams()) { + QListWidgetItem* item = new QListWidgetItem(stream->description(), track_list); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(stream->enabled() ? Qt::Checked : Qt::Unchecked); + track_list->addItem(item); + } + + layout->addWidget(track_list, row, 0, 1, 2); + row++; + + /*if (f->video_tracks.size() > 0) { + // frame conforming + if (!f->video_tracks.at(0).infinite_length) { + layout->addWidget(new QLabel(tr("Conform to Frame Rate:"), this), row, 0); + conform_fr = new QDoubleSpinBox(this); + conform_fr->setMinimum(0.01); + conform_fr->setValue(f->video_tracks.at(0).video_frame_rate * f->speed); + layout->addWidget(conform_fr, row, 1); + } + + row++; + + // premultiplied alpha mode + premultiply_alpha_setting = new QCheckBox(tr("Alpha is Premultiplied"), this); + premultiply_alpha_setting->setChecked(f->alpha_is_associated); + layout->addWidget(premultiply_alpha_setting, row, 0); + + row++; + + // deinterlacing mode + interlacing_box = new QComboBox(this); + interlacing_box->addItem( + tr("Auto (%1)").arg( + Footage::get_interlacing_name(f->video_tracks.at(0).video_auto_interlacing) + ) + ); + interlacing_box->addItem(Footage::get_interlacing_name(VIDEO_PROGRESSIVE)); + interlacing_box->addItem(Footage::get_interlacing_name(VIDEO_TOP_FIELD_FIRST)); + interlacing_box->addItem(Footage::get_interlacing_name(VIDEO_BOTTOM_FIELD_FIRST)); + + interlacing_box->setCurrentIndex( + (f->video_tracks.at(0).video_auto_interlacing == f->video_tracks.at(0).video_interlacing) + ? 0 + : f->video_tracks.at(0).video_interlacing + 1); + + layout->addWidget(new QLabel(tr("Interlacing:"), this), row, 0); + layout->addWidget(interlacing_box, row, 1); + + row++; + + input_color_space = new QComboBox(this); + + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + + QString footage_colorspace = f->Colorspace(); + + for (int i=0;igetNumColorSpaces();i++) { + QString colorspace = config->getColorSpaceNameByIndex(i); + + input_color_space->addItem(colorspace); + + if (colorspace == footage_colorspace) { + input_color_space->setCurrentIndex(i); + } + } + + layout->addWidget(new QLabel(tr("Color Space:")), row, 0); + layout->addWidget(input_color_space, row, 1); + + row++; + + } + */ + + name_box = new QLineEdit(footage_->name(), this); + layout->addWidget(new QLabel(tr("Name:"), this), row, 0); + layout->addWidget(name_box, row, 1); + row++; + + QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + buttons->setCenterButtons(true); + layout->addWidget(buttons, row, 0, 1, 2); + + connect(buttons, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttons, SIGNAL(rejected()), this, SLOT(reject())); +} + +void FootagePropertiesDialog::accept() { + /*Footage* f = item->to_footage(); + + ComboAction* ca = new ComboAction(); + + // set track enable + for (int i=0;icount();i++) { + QListWidgetItem* item = track_list->item(i); + const QVariant& data = item->data(Qt::UserRole+1); + if (!data.isNull()) { + int index = data.toInt(); + bool found = false; + for (int j=0;jvideo_tracks.size();j++) { + if (f->video_tracks.at(j).file_index == index) { + f->video_tracks[j].enabled = (item->checkState() == Qt::Checked); + found = true; + break; + } + } + if (!found) { + for (int j=0;jaudio_tracks.size();j++) { + if (f->audio_tracks.at(j).file_index == index) { + f->audio_tracks[j].enabled = (item->checkState() == Qt::Checked); + break; + } + } + } + } + } + + bool refresh_clips = false; + + // set interlacing + if (f->video_tracks.size() > 0) { + if (interlacing_box->currentIndex() > 0) { + ca->append(new SetInt(&f->video_tracks[0].video_interlacing, interlacing_box->currentIndex() - 1)); + } else { + ca->append(new SetInt(&f->video_tracks[0].video_interlacing, f->video_tracks.at(0).video_auto_interlacing)); + } + + // set frame rate conform + if (!f->video_tracks.at(0).infinite_length) { + if (!qFuzzyCompare(conform_fr->value(), f->video_tracks.at(0).video_frame_rate)) { + ca->append(new SetDouble(&f->speed, f->speed, conform_fr->value()/f->video_tracks.at(0).video_frame_rate)); + refresh_clips = true; + } + } + + // set premultiplied alpha + f->alpha_is_associated = premultiply_alpha_setting->isChecked(); + } + + f->SetColorspace(input_color_space->currentText()); + + // set name + MediaRename* mr = new MediaRename(item, name_box->text()); + + ca->append(mr); + ca->appendPost(new CloseAllClipsCommand()); + ca->appendPost(new UpdateFootageTooltip(item)); + if (refresh_clips) { + ca->appendPost(new RefreshClips(item)); + } + ca->appendPost(new UpdateViewer()); + + olive::undo_stack.push(ca);*/ + + QDialog::accept(); +} diff --git a/app/dialog/footageproperties/footageproperties.h b/app/dialog/footageproperties/footageproperties.h new file mode 100644 index 000000000..157dbf305 --- /dev/null +++ b/app/dialog/footageproperties/footageproperties.h @@ -0,0 +1,96 @@ +/*** + + 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 . + +***/ + +#ifndef MEDIAPROPERTIESDIALOG_H +#define MEDIAPROPERTIESDIALOG_H + +#include +#include +#include +#include +#include +#include + +#include "project/item/footage/footage.h" + +/** + * @brief The MediaPropertiesDialog class + * + * A dialog for setting properties on Media. This can be loaded from any part of the application provided it's given + * a valid Media object. + */ +class FootagePropertiesDialog : public QDialog { + Q_OBJECT +public: + /** + * @brief MediaPropertiesDialog Constructor + * + * @param parent + * + * QWidget parent. Usually MainWindow or Project panel. + * + * @param i + * + * Media object to set properties for. + */ + FootagePropertiesDialog(QWidget *parent, FootagePtr footage); +private: + /** + * @brief ComboBox for interlacing setting + */ + QComboBox* interlacing_box; + + /** + * @brief Media name text field + */ + QLineEdit* name_box; + + /** + * @brief Internal pointer to Media object (set in constructor) + */ + FootagePtr footage_; + + /** + * @brief A list widget for listing the tracks in Media + */ + QListWidget* track_list; + + /** + * @brief Frame rate to conform to + */ + QDoubleSpinBox* conform_fr; + + /** + * @brief Setting for associated/premultiplied alpha + */ + QCheckBox* premultiply_alpha_setting; + + /** + * @brief Setting for this media's color space + */ + QComboBox* input_color_space; +private slots: + /** + * @brief Overridden accept function for saving the properties back to the Media class + */ + void accept(); +}; + +#endif // MEDIAPROPERTIESDIALOG_H diff --git a/app/dialog/projectproperties/CMakeLists.txt b/app/dialog/projectproperties/CMakeLists.txt new file mode 100644 index 000000000..7763f8c34 --- /dev/null +++ b/app/dialog/projectproperties/CMakeLists.txt @@ -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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + dialog/projectproperties/projectproperties.h + dialog/projectproperties/projectproperties.cpp + PARENT_SCOPE +) diff --git a/app/dialog/projectproperties/projectproperties.cpp b/app/dialog/projectproperties/projectproperties.cpp new file mode 100644 index 000000000..ae31a7966 --- /dev/null +++ b/app/dialog/projectproperties/projectproperties.cpp @@ -0,0 +1,78 @@ +/*** + + 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 . + +***/ + +#include "projectproperties.h" + +#include +#include +#include +#include +#include +#include +#include +namespace OCIO = OCIO_NAMESPACE::v1; + +#include "render/colormanager.h" + +ProjectPropertiesDialog::ProjectPropertiesDialog(QWidget *parent) : + QDialog(parent) +{ + QGridLayout* layout = new QGridLayout(this); + + setWindowTitle(tr("Project Properties")); + + layout->addWidget(new QLabel(tr("OpenColorIO Configuration:")), 0, 0); + + ocio_filename_ = new QLineEdit(); + layout->addWidget(ocio_filename_, 0, 1); + + QPushButton* browse_btn = new QPushButton(tr("Browse")); + layout->addWidget(browse_btn, 0, 2); + connect(browse_btn, SIGNAL(clicked(bool)), this, SLOT(BrowseForOCIOConfig())); + + QDialogButtonBox* dialog_btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); + layout->addWidget(dialog_btns, 1, 0, 1, 3); + connect(dialog_btns, SIGNAL(accepted()), this, SLOT(accept())); + connect(dialog_btns, SIGNAL(rejected()), this, SLOT(reject())); +} + +void ProjectPropertiesDialog::accept() +{ + try { + OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromFile(ocio_filename_->text().toUtf8()); + + ColorManager::instance()->SetConfig(config); + + QDialog::accept(); + } catch (OCIO::Exception& e) { + QMessageBox::critical(this, + tr("OpenColorIO Config Error"), + tr("Failed to set OpenColorIO configuration: %1").arg(e.what()), + QMessageBox::Ok); + } +} + +void ProjectPropertiesDialog::BrowseForOCIOConfig() +{ + QString fn = QFileDialog::getOpenFileName(this, tr("Browse for OpenColorIO configuration")); + if (!fn.isEmpty()) { + ocio_filename_->setText(fn); + } +} diff --git a/app/dialog/projectproperties/projectproperties.h b/app/dialog/projectproperties/projectproperties.h new file mode 100644 index 000000000..6700e9c50 --- /dev/null +++ b/app/dialog/projectproperties/projectproperties.h @@ -0,0 +1,44 @@ +/*** + + 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 . + +***/ + +#ifndef PROJECTPROPERTIESDIALOG_H +#define PROJECTPROPERTIESDIALOG_H + +#include +#include + +class ProjectPropertiesDialog : public QDialog +{ + Q_OBJECT +public: + ProjectPropertiesDialog(QWidget* parent); + +public slots: + virtual void accept() override; + +private: + QLineEdit* ocio_filename_; + +private slots: + void BrowseForOCIOConfig(); + +}; + +#endif // PROJECTPROPERTIESDIALOG_H diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 7a94db510..3d769be86 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -154,7 +154,7 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &time) if (color_service_ == nullptr) { // FIXME: Hardcoded values for testing - color_service_ = std::make_shared("srgb", OCIO::ROLE_SCENE_LINEAR); + color_service_ = ColorProcessor::Create("srgb", OCIO::ROLE_SCENE_LINEAR); } // OpenColorIO v1's color transforms can be done on GPU, which improves performance but reduces accuracy. When @@ -166,7 +166,7 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &time) if (alpha_is_associated) { // Unassociate alpha here if associated - ColorService::DisassociateAlpha(frame_); + ColorManager::DisassociateAlpha(frame_); } // Transform color to reference space @@ -174,10 +174,10 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &time) if (alpha_is_associated) { // If alpha was associated, reassociate here - ColorService::ReassociateAlpha(frame_); + ColorManager::ReassociateAlpha(frame_); } else { // If alpha was not associated, associate here - ColorService::AssociateAlpha(frame_); + ColorManager::AssociateAlpha(frame_); } } diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index c1dede3d0..23a7c28de 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -25,7 +25,7 @@ #include "decoder/decoder.h" #include "node/node.h" -#include "render/colorservice.h" +#include "render/colormanager.h" #include "render/rendertexture.h" #include "render/gl/shadergenerators.h" @@ -69,7 +69,7 @@ private: DecoderPtr decoder_; - ColorServicePtr color_service_; + ColorProcessorPtr color_service_; ShaderPtr pipeline_; diff --git a/app/project/item/footage/stream.cpp b/app/project/item/footage/stream.cpp index c1b89c9eb..eb364d1f0 100644 --- a/app/project/item/footage/stream.cpp +++ b/app/project/item/footage/stream.cpp @@ -24,7 +24,8 @@ Stream::Stream() : footage_(nullptr), - type_(kUnknown) + type_(kUnknown), + enabled_(true) { } @@ -88,6 +89,16 @@ void Stream::set_duration(const int64_t &duration) duration_ = duration; } +bool Stream::enabled() +{ + return enabled_; +} + +void Stream::set_enabled(bool e) +{ + enabled_ = e; +} + QIcon Stream::IconFromType(const Stream::Type &type) { switch (type) { diff --git a/app/project/item/footage/stream.h b/app/project/item/footage/stream.h index 23106d8e2..f187d0613 100644 --- a/app/project/item/footage/stream.h +++ b/app/project/item/footage/stream.h @@ -78,6 +78,9 @@ public: const int64_t& duration() const; void set_duration(const int64_t& duration); + bool enabled(); + void set_enabled(bool e); + static QIcon IconFromType(const Type& type); private: @@ -91,6 +94,8 @@ private: Type type_; + bool enabled_; + }; using StreamPtr = std::shared_ptr; diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt index 3a0a6a23b..5a26e69d0 100644 --- a/app/render/CMakeLists.txt +++ b/app/render/CMakeLists.txt @@ -18,8 +18,10 @@ add_subdirectory(gl) set(OLIVE_SOURCES ${OLIVE_SOURCES} - render/colorservice.h - render/colorservice.cpp + render/colormanager.h + render/colormanager.cpp + render/colorprocessor.h + render/colorprocessor.cpp render/pixelformat.h render/pixelformat.cpp render/pixelservice.h diff --git a/app/render/colormanager.cpp b/app/render/colormanager.cpp new file mode 100644 index 000000000..aab542b72 --- /dev/null +++ b/app/render/colormanager.cpp @@ -0,0 +1,155 @@ +#include "colormanager.h" + +#include + +#include "common/define.h" + +ColorManager* ColorManager::instance_ = nullptr; + +void ColorManager::SetConfig(const QString &filename) +{ + SetConfig(OCIO::Config::CreateFromFile(filename.toUtf8())); +} + +void ColorManager::SetConfig(OpenColorIO::v1::ConstConfigRcPtr config) +{ + OCIO::SetCurrentConfig(config); + + emit ConfigChanged(); +} + +void ColorManager::CreateInstance() +{ + if (instance_ == nullptr) { + instance_ = new ColorManager(); + } +} + +ColorManager *ColorManager::instance() +{ + return instance_; +} + +void ColorManager::DestroyInstance() +{ + delete instance_; + instance_ = nullptr; +} + +void ColorManager::DisassociateAlpha(FramePtr f) +{ + AssociateAlphaPixFmtFilter(kDisassociate, f); +} + +void ColorManager::AssociateAlpha(FramePtr f) +{ + AssociateAlphaPixFmtFilter(kAssociate, f); +} + +void ColorManager::ReassociateAlpha(FramePtr f) +{ + AssociateAlphaPixFmtFilter(kReassociate, f); +} + +QStringList ColorManager::ListAvailableDisplays() +{ + QStringList displays; + + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + + int number_of_displays = config->getNumDisplays(); + + for (int i=0;igetDisplay(i)); + } + + return displays; +} + +QString ColorManager::GetDefaultDisplay() +{ + return OCIO::GetCurrentConfig()->getDefaultDisplay(); +} + +QStringList ColorManager::ListAvailableViews(QString display) +{ + QStringList views; + + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + + int number_of_views = config->getNumViews(display.toUtf8()); + + for (int i=0;igetView(display.toUtf8(), i)); + } + + return views; +} + +QString ColorManager::GetDefaultView(const QString &display) +{ + return OCIO::GetCurrentConfig()->getDefaultView(display.toUtf8()); +} + +QStringList ColorManager::ListAvailableLooks() +{ + QStringList looks; + + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + + int number_of_looks = config->getNumLooks(); + + for (int i=0;igetLookNameByIndex(i)); + } + + return looks; +} + +ColorManager::ColorManager() +{ +} + +void ColorManager::AssociateAlphaPixFmtFilter(ColorManager::AlphaAction action, FramePtr f) +{ + int pixel_count = f->width() * f->height() * kRGBAChannels; + + switch (static_cast(f->format())) { + case olive::PIX_FMT_INVALID: + case olive::PIX_FMT_COUNT: + qWarning() << "Alpha association functions received an invalid pixel format"; + break; + case olive::PIX_FMT_RGBA8: + case olive::PIX_FMT_RGBA16U: + qWarning() << "Alpha association functions only works on float-based pixel formats at this time"; + break; + case olive::PIX_FMT_RGBA16F: + { + AssociateAlphaInternal(action, reinterpret_cast(f->data()), pixel_count); + break; + } + case olive::PIX_FMT_RGBA32F: + { + AssociateAlphaInternal(action, reinterpret_cast(f->data()), pixel_count); + break; + } + } +} + +template +void ColorManager::AssociateAlphaInternal(ColorManager::AlphaAction action, T *data, int pix_count) +{ + for (int i=0;i 0) { + for (int j=0;j + +#include "colorprocessor.h" +#include "decoder/frame.h" +#include "render/gl/shadergenerators.h" + +class ColorManager : public QObject +{ + Q_OBJECT +public: + void SetConfig(const QString& filename); + + void SetConfig(OCIO::ConstConfigRcPtr config); + + static void CreateInstance(); + + static ColorManager* instance(); + + static void DestroyInstance(); + + static void DisassociateAlpha(FramePtr f); + + static void AssociateAlpha(FramePtr f); + + static void ReassociateAlpha(FramePtr f); + + static QStringList ListAvailableDisplays(); + + static QString GetDefaultDisplay(); + + static QStringList ListAvailableViews(QString display); + + static QString GetDefaultView(const QString& display); + + static QStringList ListAvailableLooks(); + +signals: + void ConfigChanged(); + +private: + ColorManager(); + + static ColorManager* instance_; + + enum AlphaAction { + kAssociate, + kDisassociate, + kReassociate + }; + + static void AssociateAlphaPixFmtFilter(AlphaAction action, FramePtr f); + + template + static void AssociateAlphaInternal(AlphaAction action, T* data, int pix_count); +}; + +#endif // COLORSERVICE_H diff --git a/app/render/colorprocessor.cpp b/app/render/colorprocessor.cpp new file mode 100644 index 000000000..eeb334ea4 --- /dev/null +++ b/app/render/colorprocessor.cpp @@ -0,0 +1,62 @@ +#include "colorprocessor.h" + +#include "common/define.h" + +ColorProcessor::ColorProcessor(const QString& source_space, const QString& dest_space) +{ + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + + processor = config->getProcessor(source_space.toUtf8(), + dest_space.toUtf8()); +} + +ColorProcessor::ColorProcessor(const QString& source_space, + QString display, + QString view, + const QString& look) +{ + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + + if (display.isEmpty()) { + display = config->getDefaultDisplay(); + } + + if (view.isEmpty()) { + view = config->getDefaultView(display.toUtf8()); + } + + // Get current display stats + OCIO::DisplayTransformRcPtr transform = OCIO::DisplayTransform::Create(); + transform->setInputColorSpaceName(source_space.toUtf8()); + transform->setDisplay(display.toUtf8()); + transform->setView(view.toUtf8()); + + if (!look.isEmpty()) { + transform->setLooksOverride(look.toUtf8()); + transform->setLooksOverrideEnabled(true); + } + + processor = config->getProcessor(transform); +} + +void ColorProcessor::ConvertFrame(FramePtr f) +{ + OCIO::PackedImageDesc img(reinterpret_cast(f->data()), f->width(), f->height(), kRGBAChannels); + + processor->apply(img); +} + +ColorProcessorPtr ColorProcessor::Create(const QString& source_space, const QString& dest_space) +{ + return std::make_shared(source_space, dest_space); +} + +ColorProcessorPtr ColorProcessor::Create(const QString &source_space, const QString &display, const QString &view, const QString &look) +{ + return std::make_shared(source_space, display, view, look); +} + +OpenColorIO::v1::ConstProcessorRcPtr ColorProcessor::GetProcessor() +{ + return processor; +} diff --git a/app/render/colorprocessor.h b/app/render/colorprocessor.h new file mode 100644 index 000000000..e575e2574 --- /dev/null +++ b/app/render/colorprocessor.h @@ -0,0 +1,38 @@ +#ifndef COLORPROCESSOR_H +#define COLORPROCESSOR_H + +#include +namespace OCIO = OCIO_NAMESPACE::v1; + +#include "decoder/frame.h" + +class ColorProcessor; +using ColorProcessorPtr = std::shared_ptr; + +class ColorProcessor +{ +public: + ColorProcessor(const QString &source_space, const QString &dest_space); + + ColorProcessor(const QString& source_space, + QString display, + QString view, + const QString& look); + + static ColorProcessorPtr Create(const QString& source_space, const QString& dest_space); + + static ColorProcessorPtr Create(const QString& source_space, + const QString& display, + const QString& view, + const QString& look); + + OCIO::ConstProcessorRcPtr GetProcessor(); + + void ConvertFrame(FramePtr f); + +private: + OCIO::ConstProcessorRcPtr processor; + +}; + +#endif // COLORPROCESSOR_H diff --git a/app/render/colorservice.cpp b/app/render/colorservice.cpp deleted file mode 100644 index bd3335c00..000000000 --- a/app/render/colorservice.cpp +++ /dev/null @@ -1,102 +0,0 @@ -#include "colorservice.h" - -#include - -#include "common/define.h" - -ColorService::ColorService(const char* source_space, const char* dest_space) -{ - OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); - - processor = config->getProcessor(source_space, - dest_space); -} - -void ColorService::Init() -{ - // FIXME: Load configured config file for this session - /*try { - // FIXME: Hardcoded values for testing purposes - OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromFile("/run/media/matt/Home/OpenColorIO/ocio.configs.0.7v4/nuke-default/config.ocio"); - - OCIO::SetCurrentConfig(config); - } catch (OCIO::Exception& exception) { - qWarning() << "OpenColorIO Error:" << exception.what(); - }*/ -} - -ColorServicePtr ColorService::Create(const char *source_space, const char *dest_space) -{ - return std::make_shared(source_space, dest_space); -} - -void ColorService::ConvertFrame(FramePtr f) -{ - OCIO::PackedImageDesc img(reinterpret_cast(f->data()), f->width(), f->height(), kRGBAChannels); - - processor->apply(img); -} - -void ColorService::DisassociateAlpha(FramePtr f) -{ - AssociateAlphaPixFmtFilter(kDisassociate, f); -} - -void ColorService::AssociateAlpha(FramePtr f) -{ - AssociateAlphaPixFmtFilter(kAssociate, f); -} - -void ColorService::ReassociateAlpha(FramePtr f) -{ - AssociateAlphaPixFmtFilter(kReassociate, f); -} - -OpenColorIO::v1::ConstProcessorRcPtr ColorService::GetProcessor() -{ - return processor; -} - -void ColorService::AssociateAlphaPixFmtFilter(ColorService::AlphaAction action, FramePtr f) -{ - int pixel_count = f->width() * f->height() * kRGBAChannels; - - switch (static_cast(f->format())) { - case olive::PIX_FMT_INVALID: - case olive::PIX_FMT_COUNT: - qWarning() << "Alpha association functions received an invalid pixel format"; - break; - case olive::PIX_FMT_RGBA8: - case olive::PIX_FMT_RGBA16U: - qWarning() << "Alpha association functions only works on float-based pixel formats at this time"; - break; - case olive::PIX_FMT_RGBA16F: - { - AssociateAlphaInternal(action, reinterpret_cast(f->data()), pixel_count); - break; - } - case olive::PIX_FMT_RGBA32F: - { - AssociateAlphaInternal(action, reinterpret_cast(f->data()), pixel_count); - break; - } - } -} - -template -void ColorService::AssociateAlphaInternal(ColorService::AlphaAction action, T *data, int pix_count) -{ - for (int i=0;i 0) { - for (int j=0;j -#include -namespace OCIO = OCIO_NAMESPACE::v1; - -#include "decoder/frame.h" -#include "render/gl/shadergenerators.h" - -class ColorService; -using ColorServicePtr = std::shared_ptr; - -class ColorService -{ -public: - ColorService(const char *source_space, const char *dest_space); - - static void Init(); - - static ColorServicePtr Create(const char *source_space, const char *dest_space); - - void ConvertFrame(FramePtr f); - - static void DisassociateAlpha(FramePtr f); - - static void AssociateAlpha(FramePtr f); - - static void ReassociateAlpha(FramePtr f); - - OCIO::ConstProcessorRcPtr GetProcessor(); - -private: - OCIO::ConstProcessorRcPtr processor; - - enum AlphaAction { - kAssociate, - kDisassociate, - kReassociate - }; - - static void AssociateAlphaPixFmtFilter(AlphaAction action, FramePtr f); - - template - static void AssociateAlphaInternal(AlphaAction action, T* data, int pix_count); -}; - -#endif // COLORSERVICE_H diff --git a/app/widget/viewer/viewerglwidget.cpp b/app/widget/viewer/viewerglwidget.cpp index 751fb62cd..a13585546 100644 --- a/app/widget/viewer/viewerglwidget.cpp +++ b/app/widget/viewer/viewerglwidget.cpp @@ -20,6 +20,7 @@ #include "viewerglwidget.h" +#include #include #include #include @@ -32,8 +33,17 @@ ViewerGLWidget::ViewerGLWidget(QWidget *parent) : texture_(0), ocio_lut_(0) { - // FIXME: Hardcoded values for testing - color_service_ = ColorService::Create(OCIO::ROLE_SCENE_LINEAR, "srgb"); + connect(ColorManager::instance(), SIGNAL(ConfigChanged()), this, SLOT(ColorConfigChangedSlot())); + + RefreshColorSettings(); + + setContextMenuPolicy(Qt::CustomContextMenu); + connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(ShowContextMenu(const QPoint&))); +} + +ViewerGLWidget::~ViewerGLWidget() +{ + ContextCleanup(); } void ViewerGLWidget::SetTexture(GLuint tex) @@ -47,11 +57,7 @@ void ViewerGLWidget::SetTexture(GLuint tex) void ViewerGLWidget::initializeGL() { - // Re-retrieve pipeline pertaining to this context - pipeline_ = olive::ShaderGenerator::OCIOPipeline(context(), - ocio_lut_, - color_service_->GetProcessor(), - true); + SetupPipeline(); connect(context(), SIGNAL(aboutToBeDestroyed()), this, SLOT(ContextCleanup()), Qt::DirectConnection); } @@ -78,6 +84,31 @@ void ViewerGLWidget::paintGL() } } +void ViewerGLWidget::SetupPipeline() +{ + // Re-retrieve pipeline pertaining to this context + pipeline_ = olive::ShaderGenerator::OCIOPipeline(context(), + ocio_lut_, + color_service_->GetProcessor(), + true); +} + +void ViewerGLWidget::RefreshColorSettings() +{ + // FIXME: Should probably check first whether the new config has the existing settings + + ocio_display_ = ColorManager::GetDefaultDisplay(); + ocio_view_ = ColorManager::GetDefaultView(ocio_display_); + ocio_look_.clear(); + + SetupColorProcessor(); +} + +void ViewerGLWidget::SetupColorProcessor() +{ + color_service_ = ColorProcessor::Create(OCIO::ROLE_SCENE_LINEAR, ocio_display_, ocio_view_, ocio_look_); +} + void ViewerGLWidget::ContextCleanup() { makeCurrent(); @@ -91,3 +122,40 @@ void ViewerGLWidget::ContextCleanup() doneCurrent(); } + +void ViewerGLWidget::ShowContextMenu(const QPoint &pos) +{ + QMenu menu; + + QStringList displays = ColorManager::ListAvailableDisplays(); + QMenu* ocio_display_menu = menu.addMenu(tr("OCIO Display")); + foreach (QString d, displays) { + QAction* action = ocio_display_menu->addAction(d); + action->setChecked(ocio_display_ == d); + } + + QStringList views = ColorManager::ListAvailableViews(ocio_display_); + QMenu* ocio_view_menu = menu.addMenu(tr("OCIO View")); + foreach (QString v, views) { + QAction* action = ocio_view_menu->addAction(v); + action->setChecked(ocio_view_ == v); + } + + QStringList looks = ColorManager::ListAvailableLooks(); + QMenu* ocio_look_menu = menu.addMenu(tr("OCIO Look")); + foreach (QString l, looks) { + QAction* action = ocio_look_menu->addAction(l); + action->setChecked(ocio_look_ == l); + } + + menu.exec(mapToGlobal(pos)); +} + +void ViewerGLWidget::ColorConfigChangedSlot() +{ + RefreshColorSettings(); + + if (pipeline_ != nullptr) { + SetupPipeline(); + } +} diff --git a/app/widget/viewer/viewerglwidget.h b/app/widget/viewer/viewerglwidget.h index 8dee3c379..17c4d2548 100644 --- a/app/widget/viewer/viewerglwidget.h +++ b/app/widget/viewer/viewerglwidget.h @@ -23,7 +23,7 @@ #include -#include "render/colorservice.h" +#include "render/colormanager.h" #include "render/gl/shaderptr.h" /** @@ -54,6 +54,28 @@ public: */ ViewerGLWidget(QWidget* parent); + virtual ~ViewerGLWidget() override; + + /** + * @brief Deleted copy constructor + */ + ViewerGLWidget(const ViewerGLWidget& other) = delete; + + /** + * @brief Deleted move constructor + */ + ViewerGLWidget(ViewerGLWidget&& other) = delete; + + /** + * @brief Deleted copy assignment + */ + ViewerGLWidget& operator=(const ViewerGLWidget& other) = delete; + + /** + * @brief Deleted move assignment + */ + ViewerGLWidget& operator=(ViewerGLWidget&& other) = delete; + public slots: /** * @brief Set the texture to draw and draw it @@ -79,6 +101,38 @@ protected: */ virtual void paintGL() override; private: + /** + * @brief Creates the render pipeline shader + * + * If it already exists, it will be deleted. + */ + void SetupPipeline(); + + /** + * @brief Sets all color settings to the defaults pertaining to this configuration + */ + void RefreshColorSettings(); + + /** + * @brief Call this if this user has selected a different display/view/look to recreate the processor + */ + void SetupColorProcessor(); + + /** + * @brief Internal variable to set color space to + */ + QString ocio_display_; + + /** + * @brief Internal variable to set color space to + */ + QString ocio_view_; + + /** + * @brief Internal variable to set color space to + */ + QString ocio_look_; + /** * @brief Internal reference to the OpenGL texture to draw. Set in SetTexture() and used in paintGL(). */ @@ -99,10 +153,23 @@ private: /** * @brief Color management service */ - ColorServicePtr color_service_; + ColorProcessorPtr color_service_; private slots: + /** + * @brief Slot to connect just before the OpenGL context is destroyed to clean up resources + */ void ContextCleanup(); + + /** + * @brief Show context menu + */ + void ShowContextMenu(const QPoint& pos); + + /** + * @brief Slot called whenever the color configuration changes + */ + void ColorConfigChangedSlot(); }; #endif // VIEWERGLWIDGET_H diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index 415be884a..40061c2bf 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -49,6 +49,8 @@ MainMenu::MainMenu(QMainWindow *parent) : file_menu_->addSeparator(); file_export_item_ = file_menu_->AddItem("export", nullptr, nullptr, "Ctrl+M"); file_menu_->addSeparator(); + file_project_properties_item_ = file_menu_->AddItem("projectproperties", &olive::core, SLOT(DialogProjectPropertiesShow())); + file_menu_->addSeparator(); file_exit_item_ = file_menu_->AddItem("exit", parent, SLOT(close()), "Ctrl+Q"); // @@ -477,12 +479,13 @@ void MainMenu::Retranslate() file_menu_->setTitle(tr("&File")); file_new_menu_->setTitle(tr("&New")); file_open_item_->setText(tr("&Open Project")); - file_open_recent_menu_->setTitle(tr("Open Recent")); - file_open_recent_clear_item_->setText(tr("Clear Recent List")); + file_open_recent_menu_->setTitle(tr("Open &Recent")); + file_open_recent_clear_item_->setText(tr("&Clear Recent List")); file_save_item_->setText(tr("&Save Project")); file_save_as_item_->setText(tr("Save Project &As")); file_import_item_->setText(tr("&Import...")); file_export_item_->setText(tr("&Export...")); + file_project_properties_item_->setText(tr("&Project Properties...")); file_exit_item_->setText(tr("E&xit")); // Edit menu diff --git a/app/window/mainwindow/mainmenu.h b/app/window/mainwindow/mainmenu.h index 913133473..82fbe4668 100644 --- a/app/window/mainwindow/mainmenu.h +++ b/app/window/mainwindow/mainmenu.h @@ -136,6 +136,7 @@ private: QAction* file_save_as_item_; QAction* file_import_item_; QAction* file_export_item_; + QAction* file_project_properties_item_; QAction* file_exit_item_; Menu* edit_menu_;