diff --git a/app/core.h b/app/core.h index d723c5731..d14bb2fa7 100644 --- a/app/core.h +++ b/app/core.h @@ -101,6 +101,18 @@ public: */ void StartModalTask(Task* t); + /** + * @brief Get the currently active project + * + * Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project + * that the user wishes to work on. + * + * @return + * + * The active Project file, or nullptr if the heuristic couldn't find one. + */ + Project* GetActiveProject(); + public slots: /** * @brief Set the current application-wide tool @@ -186,18 +198,6 @@ private: */ void StartGUI(bool full_screen); - /** - * @brief Get the currently active project - * - * Uses the UI/Panel system to determine which Project was the last focused on and assumes this is the active Project - * that the user wishes to work on. - * - * @return - * - * The active Project file, or nullptr if the heuristic couldn't find one. - */ - Project* GetActiveProject(); - /** * @brief Internal main window object */ diff --git a/app/dialog/projectproperties/projectproperties.cpp b/app/dialog/projectproperties/projectproperties.cpp index ae31a7966..ddd90a88e 100644 --- a/app/dialog/projectproperties/projectproperties.cpp +++ b/app/dialog/projectproperties/projectproperties.cpp @@ -23,34 +23,68 @@ #include #include #include +#include #include #include #include #include namespace OCIO = OCIO_NAMESPACE::v1; +#include "config/config.h" +#include "core.h" #include "render/colormanager.h" ProjectPropertiesDialog::ProjectPropertiesDialog(QWidget *parent) : - QDialog(parent) + QDialog(parent), + working_project_(olive::core.GetActiveProject()) { - QGridLayout* layout = new QGridLayout(this); + QVBoxLayout* layout = new QVBoxLayout(this); setWindowTitle(tr("Project Properties")); - layout->addWidget(new QLabel(tr("OpenColorIO Configuration:")), 0, 0); + QGroupBox* color_group = new QGroupBox(); + color_group->setTitle(tr("Color Management")); + + QGridLayout* color_layout = new QGridLayout(color_group); + + int row = 0; + + color_layout->addWidget(new QLabel(tr("OpenColorIO Configuration:")), row, 0); ocio_filename_ = new QLineEdit(); - layout->addWidget(ocio_filename_, 0, 1); + color_layout->addWidget(ocio_filename_, row, 1); + + row++; + + color_layout->addWidget(new QLabel(tr("Default Input Color Space:")), row, 0); + + default_input_colorspace_ = new QComboBox(); + color_layout->addWidget(default_input_colorspace_, row, 1, 1, 2); + + row++; QPushButton* browse_btn = new QPushButton(tr("Browse")); - layout->addWidget(browse_btn, 0, 2); + color_layout->addWidget(browse_btn, 0, 2); connect(browse_btn, SIGNAL(clicked(bool)), this, SLOT(BrowseForOCIOConfig())); + layout->addWidget(color_group); + QDialogButtonBox* dialog_btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); - layout->addWidget(dialog_btns, 1, 0, 1, 3); + layout->addWidget(dialog_btns); connect(dialog_btns, SIGNAL(accepted()), this, SLOT(accept())); connect(dialog_btns, SIGNAL(rejected()), this, SLOT(reject())); + + if (working_project_ == nullptr) { + QMessageBox::critical(this, + tr("No Active Project"), + tr("No project is currently open to set the properties for"), + QMessageBox::Ok); + reject(); + return; + } + + ocio_filename_->setText(working_project_->ocio_config()); + ListPossibleInputSpaces(working_project_->ocio_config()); } void ProjectPropertiesDialog::accept() @@ -58,6 +92,11 @@ void ProjectPropertiesDialog::accept() try { OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromFile(ocio_filename_->text().toUtf8()); + working_project_->set_default_input_colorspace(default_input_colorspace_->currentText()); + + working_project_->set_ocio_config(ocio_filename_->text()); + + // This should ripple changes throughout the program that the color config has changed, therefore must be done last ColorManager::instance()->SetConfig(config); QDialog::accept(); @@ -69,10 +108,48 @@ void ProjectPropertiesDialog::accept() } } +bool ProjectPropertiesDialog::VerifyOCIOConfig(const QString &fn) +{ + try { + OCIO::Config::CreateFromFile(fn.toUtf8()); + + return true; + } catch (OCIO::Exception& e) { + QMessageBox::critical(this, + tr("OpenColorIO Config Error"), + tr("Failed to set OpenColorIO configuration: %1").arg(e.what()), + QMessageBox::Ok); + + return false; + } +} + +void ProjectPropertiesDialog::ListPossibleInputSpaces(const QString& fn) +{ + try { + default_input_colorspace_->clear(); + + QStringList input_cs = ColorManager::ListAvailableInputColorspaces(OCIO::Config::CreateFromFile(fn.toUtf8())); + + foreach (QString cs, input_cs) { + default_input_colorspace_->addItem(cs); + + if (cs == working_project_->default_input_colorspace()) { + default_input_colorspace_->setCurrentIndex(default_input_colorspace_->count()-1); + } + } + } catch (OCIO::Exception&) { + } +} + void ProjectPropertiesDialog::BrowseForOCIOConfig() { QString fn = QFileDialog::getOpenFileName(this, tr("Browse for OpenColorIO configuration")); if (!fn.isEmpty()) { - ocio_filename_->setText(fn); + if (VerifyOCIOConfig(fn)) { + ocio_filename_->setText(fn); + + ListPossibleInputSpaces(fn); + } } } diff --git a/app/dialog/projectproperties/projectproperties.h b/app/dialog/projectproperties/projectproperties.h index 6700e9c50..7573d30b8 100644 --- a/app/dialog/projectproperties/projectproperties.h +++ b/app/dialog/projectproperties/projectproperties.h @@ -21,9 +21,12 @@ #ifndef PROJECTPROPERTIESDIALOG_H #define PROJECTPROPERTIESDIALOG_H +#include #include #include +#include "project/project.h" + class ProjectPropertiesDialog : public QDialog { Q_OBJECT @@ -34,8 +37,16 @@ public slots: virtual void accept() override; private: + bool VerifyOCIOConfig(const QString& fn); + + void ListPossibleInputSpaces(const QString &fn); + + Project* working_project_; + QLineEdit* ocio_filename_; + QComboBox* default_input_colorspace_; + private slots: void BrowseForOCIOConfig(); diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 3d769be86..c5d62d207 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -23,6 +23,7 @@ #include #include +#include "core.h" #include "decoder/ffmpeg/ffmpegdecoder.h" #include "node/processor/renderer/renderer.h" #include "project/item/footage/footage.h" @@ -32,7 +33,7 @@ MediaInput::MediaInput() : decoder_(nullptr), - color_service_(nullptr), + color_processor_(nullptr), pipeline_(nullptr), ocio_texture_(0), frame_(nullptr) @@ -77,7 +78,7 @@ void MediaInput::Release() frame_ = nullptr; decoder_ = nullptr; - color_service_ = nullptr; + color_processor_ = nullptr; pipeline_ = nullptr; if (ocio_texture_ != 0) { @@ -95,6 +96,11 @@ NodeOutput *MediaInput::texture_output() return texture_output_; } +StreamPtr MediaInput::Footage() +{ + return footage_input_->get_value(0).value(); +} + void MediaInput::SetFootage(StreamPtr f) { footage_input_->set_value(QVariant::fromValue(f)); @@ -152,9 +158,14 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &time) return 0; } - if (color_service_ == nullptr) { - // FIXME: Hardcoded values for testing - color_service_ = ColorProcessor::Create("srgb", OCIO::ROLE_SCENE_LINEAR); + if (color_processor_ == nullptr) { + QString colorspace = std::static_pointer_cast(Footage())->colorspace(); + if (colorspace.isEmpty()) { + // FIXME: Should use Footage() to find the Project* it belongs to instead of this + colorspace = olive::core.GetActiveProject()->default_input_colorspace(); + } + + color_processor_ = ColorProcessor::Create(colorspace, OCIO::ROLE_SCENE_LINEAR); } // OpenColorIO v1's color transforms can be done on GPU, which improves performance but reduces accuracy. When @@ -170,7 +181,7 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &time) } // Transform color to reference space - color_service_->ConvertFrame(frame_); + color_processor_->ConvertFrame(frame_); if (alpha_is_associated) { // If alpha was associated, reassociate here @@ -223,7 +234,7 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &time) if (pipeline_ == nullptr) { pipeline_ = olive::ShaderGenerator::OCIOPipeline(renderer->context(), ocio_texture_, // FIXME: A raw GLuint texture, should wrap this up - color_service_->GetProcessor(), + color_processor_->GetProcessor(), alpha_is_associated); // Used for cleanup later @@ -284,7 +295,7 @@ bool MediaInput::SetupDecoder() } // Get currently selected Footage - StreamPtr stream = footage_input_->get_value(0).value(); + StreamPtr stream = Footage(); // If no footage is selected, return nothing if (stream == nullptr) { diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index 23a7c28de..dccb3e77d 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -49,6 +49,7 @@ public: NodeOutput* texture_output(); + StreamPtr Footage(); void SetFootage(StreamPtr f); virtual void Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time) override; @@ -69,7 +70,7 @@ private: DecoderPtr decoder_; - ColorProcessorPtr color_service_; + ColorProcessorPtr color_processor_; ShaderPtr pipeline_; diff --git a/app/project/item/footage/imagestream.h b/app/project/item/footage/imagestream.h index e1ee1131a..05a198fa8 100644 --- a/app/project/item/footage/imagestream.h +++ b/app/project/item/footage/imagestream.h @@ -26,8 +26,9 @@ /** * @brief A Stream derivative containing video-specific information */ -class ImageStream : public Stream, public QObject +class ImageStream : public Stream { + Q_OBJECT public: ImageStream(); diff --git a/app/project/item/footage/stream.h b/app/project/item/footage/stream.h index f187d0613..2fcca5075 100644 --- a/app/project/item/footage/stream.h +++ b/app/project/item/footage/stream.h @@ -38,8 +38,9 @@ class Footage; * The Stream class is fairly simple and is intended to be subclassed for data that pertains specifically to one * Stream::Type. \see VideoStream and \see AudioStream. */ -class Stream +class Stream : public QObject { + Q_OBJECT public: enum Type { kUnknown, diff --git a/app/project/project.cpp b/app/project/project.cpp index 402983cac..a811acd2b 100644 --- a/app/project/project.cpp +++ b/app/project/project.cpp @@ -39,3 +39,23 @@ void Project::set_name(const QString &s) { name_ = s; } + +const QString &Project::ocio_config() +{ + return ocio_config_; +} + +void Project::set_ocio_config(const QString &ocio_config) +{ + ocio_config_ = ocio_config; +} + +const QString &Project::default_input_colorspace() +{ + return default_input_colorspace_; +} + +void Project::set_default_input_colorspace(const QString &colorspace) +{ + default_input_colorspace_ = colorspace; +} diff --git a/app/project/project.h b/app/project/project.h index 08d3c0f48..fce841658 100644 --- a/app/project/project.h +++ b/app/project/project.h @@ -48,10 +48,19 @@ public: const QString& name(); void set_name(const QString& s); + const QString& ocio_config(); + void set_ocio_config(const QString& ocio_config); + + const QString& default_input_colorspace(); + void set_default_input_colorspace(const QString& colorspace); + private: Folder root_; QString name_; + + QString ocio_config_; + QString default_input_colorspace_; }; using ProjectPtr = std::shared_ptr; diff --git a/app/render/colormanager.cpp b/app/render/colormanager.cpp index aab542b72..848f3ae15 100644 --- a/app/render/colormanager.cpp +++ b/app/render/colormanager.cpp @@ -3,6 +3,7 @@ #include #include "common/define.h" +#include "config/config.h" ColorManager* ColorManager::instance_ = nullptr; @@ -106,6 +107,19 @@ QStringList ColorManager::ListAvailableLooks() return looks; } +QStringList ColorManager::ListAvailableInputColorspaces(OpenColorIO::v1::ConstConfigRcPtr config) +{ + QStringList spaces; + + int number_of_colorspaces = config->getNumColorSpaces(); + + for (int i=0;igetColorSpaceNameByIndex(i)); + } + + return spaces; +} + ColorManager::ColorManager() { } diff --git a/app/render/colormanager.h b/app/render/colormanager.h index 9447a5765..649355db7 100644 --- a/app/render/colormanager.h +++ b/app/render/colormanager.h @@ -37,6 +37,8 @@ public: static QStringList ListAvailableLooks(); + static QStringList ListAvailableInputColorspaces(OCIO::ConstConfigRcPtr config); + signals: void ConfigChanged(); diff --git a/app/window/mainwindow/mainmenu.cpp b/app/window/mainwindow/mainmenu.cpp index 40061c2bf..3a2b59cdc 100644 --- a/app/window/mainwindow/mainmenu.cpp +++ b/app/window/mainwindow/mainmenu.cpp @@ -36,7 +36,7 @@ MainMenu::MainMenu(QMainWindow *parent) : // // FILE MENU // - file_menu_ = new Menu(this); + file_menu_ = new Menu(this, this, SLOT(FileMenuAboutToShow())); file_new_menu_ = new Menu(file_menu_); olive::menu_shared.AddItemsForNewMenu(file_new_menu_); file_open_item_ = file_menu_->AddItem("openproj", nullptr, nullptr, "Ctrl+O"); @@ -327,6 +327,11 @@ void MainMenu::ToolItemTriggered() olive::core.SetTool(tool); } +void MainMenu::FileMenuAboutToShow() +{ + file_project_properties_item_->setEnabled(olive::core.GetActiveProject() != nullptr); +} + void MainMenu::ViewMenuAboutToShow() { // Parent is QMainWindow diff --git a/app/window/mainwindow/mainmenu.h b/app/window/mainwindow/mainmenu.h index 82fbe4668..0cd1847dc 100644 --- a/app/window/mainwindow/mainmenu.h +++ b/app/window/mainwindow/mainmenu.h @@ -57,6 +57,11 @@ private slots: */ void ToolItemTriggered(); + /** + * @brief Slot triggered just before the File menu shows + */ + void FileMenuAboutToShow(); + /** * @brief Slot triggered just before the View menu shows */