diff --git a/App/CMakeLists.txt b/App/CMakeLists.txt index 06d530c2ff..c89d01fd29 100644 --- a/App/CMakeLists.txt +++ b/App/CMakeLists.txt @@ -18,10 +18,10 @@ # ***** END LICENSE BLOCK ***** set(Natron_SOURCES NatronApp_main.cpp) -if(WINDOWS) - list(APPEND Natron_SOURCES ../Natron.rc) -endif() add_executable(Natron ${Natron_SOURCES}) +if(WIN32) + target_sources(Natron PRIVATE ../Natron.rc) +endif() if(APPLE) set_target_properties(Natron PROPERTIES MACOSX_BUNDLE TRUE @@ -59,3 +59,40 @@ install(FILES ../Gui/Resources/Images/natronIcon256_linux.png DESTINATION "${CMAKE_INSTALL_DATADIR}/pixmaps") install(FILES ../Gui/Resources/Images/natronProjectIcon_linux.png DESTINATION "${CMAKE_INSTALL_DATADIR}/pixmaps") + +IF(WIN32 AND DEPLOYQT_FOUND) + SET(CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/install_deps_${CMAKE_BUILD_TYPE}.cmake) + + # Find and install needed DLLs + file(GENERATE OUTPUT + "${CONFIG_FILE}" CONTENT + [[ + EXECUTE_PROCESS(COMMAND del ${INSTALL_BINPATH}/*.dll) + SET(TARGET_APP $) + FILE(GET_RUNTIME_DEPENDENCIES + RESOLVED_DEPENDENCIES_VAR deps_resolved + UNRESOLVED_DEPENDENCIES_VAR deps_unresolved + EXECUTABLES ${TARGET_APP} + DIRECTORIES ${CMAKELIBPATH} + PRE_EXCLUDE_REGEXES "api-ms-*" "ext-ms-*" + POST_EXCLUDE_REGEXES ".*system32/.*\\.dll" + ) + MESSAGE(STATUS "Resolving runtime dependencies for ${TARGET_APP}") + FOREACH(dep ${deps_resolved}) + FILE(INSTALL ${dep} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + MESSAGE(STATUS "Installing ${dep}") + ENDFOREACH() + FOREACH(dep ${deps_unresolved}) + MESSAGE(WARNING "Runtime dependency ${dep} could not be resolved.") + ENDFOREACH() + EXECUTE_PROCESS(COMMAND ${DEPLOYQT_EXE} --no-angle --compiler-runtime ${INSTALL_BINPATH}/Natron.exe) + ]]) + + INSTALL(CODE "SET(INSTALL_BINPATH \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}\")") + INSTALL(CODE "SET(DEPLOYQT_EXE \"${DEPLOYQT_EXE}\")") + INSTALL(CODE "SET(CMAKELIBPATH \"${CMAKE_SYSTEM_LIBRARY_PATH};${CMAKE_MINGW_SYSTEM_LIBRARY_PATH};${MINGWPATH}\")") + INSTALL(SCRIPT ${CONFIG_FILE}) + INSTALL(DIRECTORY ${Python3_LIBRARY_DIRS}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} + DESTINATION lib + PATTERN "*.pyc" EXCLUDE) +ENDIF() \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 48dd73e220..71693613a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,18 @@ if(WIN32) endif() find_package(Python3 COMPONENTS Interpreter Development) +if(WIN32) + # Search qtdeploy executable + find_program(DEPLOYQT_EXE windeployqt.exe) + if(NOT ${DEPLOYQT_EXE} STREQUAL "") + set(DEPLOYQT_FOUND 1) + message(STATUS "windeployqt.exe found : ${DEPLOYQT_EXE}") + else() + set(DEPLOYQT_FOUND 0) + message(STATUS "windeployqt.exe not found, will not deploy Qt dependencies") + endif() +endif() + if(IS_DEBUG_BUILD AND WIN32) # Explicitly setting SHIBOKEN_PYTHON_LIBRARIES variable to avoid PYTHON_DEBUG_LIBRARY-NOTFOUND # link errors on Windows debug builds. @@ -84,7 +96,7 @@ if(NATRON_QT6) set(SHIBOKEN_LIB Shiboken6::libshiboken) set(PYSIDE_LIB PySide6::pyside6) else() - find_package(Qt5 5.15 CONFIG REQUIRED COMPONENTS ${QT_COMPONENTS}) + find_package(Qt5 5.15 CONFIG REQUIRED COMPONENTS ${QT_COMPONENTS} NO_SYSTEM_ENVIRONMENT_PATH) find_package(Shiboken2 5.15 CONFIG REQUIRED COMPONENTS libshiboken2 shiboken2) find_package(PySide2 5.15 CONFIG REQUIRED COMPONENTS pyside2) @@ -101,6 +113,8 @@ else() set(PYSIDE_LIB PySide2::pyside2) endif() +message(STATUS ">>> qt dirs ${Qt5Gui_INCLUDE_DIRS}") + if(IS_DEBUG_BUILD AND WIN32) # Remove NDEBUG from Shiboken INTERFACE_COMPILE_DEFINITIONS so it is not inherited in debug builds. get_property(SHIBOKEN_DEFS TARGET ${SHIBOKEN_LIB} PROPERTY INTERFACE_COMPILE_DEFINITIONS) diff --git a/Engine/AppInstance.h b/Engine/AppInstance.h index ab3561194c..383f635644 100644 --- a/Engine/AppInstance.h +++ b/Engine/AppInstance.h @@ -218,10 +218,18 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON { } + virtual void loadProjectGui(bool /*isAutosave*/, boost::archive::binary_iarchive & /*archive*/) const + { + } + virtual void saveProjectGui(boost::archive::xml_oarchive & /*archive*/) { } + virtual void saveProjectGui(boost::archive::binary_oarchive & /*archive*/) + { + } + virtual void setupViewersForViews(const std::vector& /*viewNames*/) { } diff --git a/Engine/AppManager.cpp b/Engine/AppManager.cpp index 071f46b2d5..a8f742fb4a 100644 --- a/Engine/AppManager.cpp +++ b/Engine/AppManager.cpp @@ -314,6 +314,9 @@ AppManager::loadFromArgs(const CLArgs& cl) std::cout << "argv[" << i << "] = " << StrUtils::utf16_to_utf8( std::wstring(_imp->commandLineArgsWide[i]) ) << std::endl; } #endif + // This should fix GL widgets when undocked + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); // This needs to be done BEFORE creating qApp because // on Linux, X11 will create a context that would corrupt @@ -322,6 +325,7 @@ AppManager::loadFromArgs(const CLArgs& cl) _imp->renderingContextPool.reset( new GPUContextPool() ); initializeOpenGLFunctionsOnce(true); + // QCoreApplication will hold a reference to that appManagerArgc integer until it dies. // Thus ensure that the QCoreApplication is destroyed when returning this function. initializeQApp(_imp->nArgs, &_imp->commandLineArgsUtf8.front()); // calls QCoreApplication::QCoreApplication(), which calls setlocale() @@ -476,6 +480,9 @@ AppManager::~AppManager() // After this line, everything is cleaned-up (should be) and the process may resume in the main and could in theory be able to re-create a new AppManager _imp->_qApp.reset(); + + // Fast cleanup + TerminateProcess(GetCurrentProcess(), 0); } class QuitInstanceArgs @@ -3182,7 +3189,7 @@ void AppManager::registerUNCPath(const QString& path, const QChar& driveLetter) { - assert( QThread::currentThread() == qApp->thread() ); + //assert( QThread::currentThread() == qApp->thread() ); _imp->uncPathMapping[driveLetter] = path; } @@ -4189,7 +4196,7 @@ NATRON_PYTHON_NAMESPACE::getFunctionArguments(const std::string& pyFunc, #endif std::stringstream ss; ss << "import inspect\n"; - ss << "args_spec = inspect.getargspec(" << pyFunc << ")\n"; + ss << "args_spec = inspect.getfullargspec(" << pyFunc << ")\n"; std::string script = ss.str(); std::string output; bool ok = NATRON_PYTHON_NAMESPACE::interpretPythonScript(script, error, &output); diff --git a/Engine/EngineFwd.h b/Engine/EngineFwd.h index 55f1bf6fbb..28d73dccf7 100644 --- a/Engine/EngineFwd.h +++ b/Engine/EngineFwd.h @@ -39,6 +39,8 @@ namespace boost { namespace archive { class xml_iarchive; class xml_oarchive; +class binary_iarchive; +class binary_oarchive; } namespace serialization { class access; diff --git a/Engine/Project.cpp b/Engine/Project.cpp index cc6c75cf9d..b852190d6c 100644 --- a/Engine/Project.cpp +++ b/Engine/Project.cpp @@ -287,11 +287,6 @@ Project::loadProjectInternal(const QString & path, } bool ret = false; - FStreamsSupport::ifstream ifile; - FStreamsSupport::open( &ifile, filePath.toStdString() ); - if (!ifile) { - throw std::runtime_error( tr("Failed to open %1").arg(filePath).toStdString() ); - } if ( (NATRON_VERSION_MAJOR == 1) && (NATRON_VERSION_MINOR == 0) && (NATRON_VERSION_REVISION == 0) ) { ///Try to determine if the project was made during Natron v1.0.0 - RC2 or RC3 to detect a bug we introduced at that time @@ -315,21 +310,28 @@ Project::loadProjectInternal(const QString & path, } LoadProjectSplashScreen_RAII __raii_splashscreen__(getApp(), name); + bool xml_loaded = false; try { - bool bgProject; - boost::archive::xml_iarchive iArchive(ifile); - { - FlagSetter __raii_loadingProjectInternal__(true, &_imp->isLoadingProjectInternal, &_imp->isLoadingProjectMutex); + // XML loading tests first, then binary loading + FStreamsSupport::ifstream ifile; + FStreamsSupport::open( &ifile, filePath.toStdString()); + if (ifile) { + bool bgProject; + boost::archive::xml_iarchive iArchive(ifile); + { + FlagSetter __raii_loadingProjectInternal__(true, &_imp->isLoadingProjectInternal, &_imp->isLoadingProjectMutex); + + iArchive >> boost::serialization::make_nvp("Background_project", bgProject); + ProjectSerialization projectSerializationObj( getApp() ); + iArchive >> boost::serialization::make_nvp("Project", projectSerializationObj); + ret = load(projectSerializationObj, name, path, mustSave); + } // __raii_loadingProjectInternal__ - iArchive >> boost::serialization::make_nvp("Background_project", bgProject); - ProjectSerialization projectSerializationObj( getApp() ); - iArchive >> boost::serialization::make_nvp("Project", projectSerializationObj); - ret = load(projectSerializationObj, name, path, mustSave); - } // __raii_loadingProjectInternal__ - - if (!bgProject) { - getApp()->loadProjectGui(isAutoSave, iArchive); + if (!bgProject) { + getApp()->loadProjectGui(isAutoSave, iArchive); + } + xml_loaded = true; } } catch (const std::exception &e) { const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo(); @@ -339,7 +341,6 @@ Project::loadProjectInternal(const QString & path, QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME)); throw std::runtime_error(message.toStdString()); } - throw std::runtime_error( tr("Unrecognized or damaged project file:").toStdString() + ' ' + e.what()); } catch (...) { const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo(); if (pInfo.vMajor > NATRON_VERSION_MAJOR || @@ -348,7 +349,48 @@ Project::loadProjectInternal(const QString & path, QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME)); throw std::runtime_error(message.toStdString()); } - throw std::runtime_error( tr("Unrecognized or damaged project file").toStdString() ); + } + + if (!xml_loaded){ + try { + FStreamsSupport::ifstream ifile; + FStreamsSupport::open( &ifile, filePath.toStdString(), std::ios::in | std::ios::binary ); + if (!ifile) { + throw std::runtime_error( tr("Failed to open %1").arg(filePath).toStdString() ); + } + bool bgProject; + boost::archive::binary_iarchive iArchive(ifile); + { + FlagSetter __raii_loadingProjectInternal__(true, &_imp->isLoadingProjectInternal, &_imp->isLoadingProjectMutex); + + iArchive >> boost::serialization::make_nvp("Background_project", bgProject); + ProjectSerialization projectSerializationObj( getApp() ); + iArchive >> boost::serialization::make_nvp("Project", projectSerializationObj); + ret = load(projectSerializationObj, name, path, mustSave); + } // __raii_loadingProjectInternal__ + + if (!bgProject) { + getApp()->loadProjectGui(isAutoSave, iArchive); + } + } catch (const std::exception &e) { + const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo(); + if (pInfo.vMajor > NATRON_VERSION_MAJOR || + (pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor > NATRON_VERSION_MINOR) || + (pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor == NATRON_VERSION_MINOR && pInfo.vRev > NATRON_VERSION_REVISION)) { + QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME)); + throw std::runtime_error(message.toStdString()); + } + throw std::runtime_error( tr("Unrecognized or damaged project file:").toStdString() + ' ' + e.what()); + } catch (...) { + const ProjectBeingLoadedInfo& pInfo = getApp()->getProjectBeingLoadedInfo(); + if (pInfo.vMajor > NATRON_VERSION_MAJOR || + (pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor > NATRON_VERSION_MINOR) || + (pInfo.vMajor == NATRON_VERSION_MAJOR && pInfo.vMinor == NATRON_VERSION_MINOR && pInfo.vRev > NATRON_VERSION_REVISION)) { + QString message = tr("This project was saved with a more recent version (%1.%2.%3) of %4. Projects are not forward compatible and may only be opened in a version of %4 equal or more recent than the version that saved it.").arg(pInfo.vMajor).arg(pInfo.vMinor).arg(pInfo.vRev).arg(QString::fromUtf8(NATRON_APPLICATION_NAME)); + throw std::runtime_error(message.toStdString()); + } + throw std::runtime_error( tr("Unrecognized or damaged project file").toStdString() ); + } } Format f; @@ -599,12 +641,6 @@ Project::saveProjectInternal(const QString & path, tmpFilename.append( QString::number( time.toMSecsSinceEpoch() ) ); { - FStreamsSupport::ofstream ofile; - FStreamsSupport::open( &ofile, tmpFilename.toStdString() ); - if (!ofile) { - throw std::runtime_error( tr("Failed to open file ").toStdString() + tmpFilename.toStdString() ); - } - ///Fix file paths before saving. QString oldProjectPath = QString::fromUtf8( _imp->getProjectPath().c_str() ); @@ -615,27 +651,62 @@ Project::saveProjectInternal(const QString & path, _imp->natronVersion->setValue( generateUserFriendlyNatronVersionName() ); } - try { - boost::archive::xml_oarchive oArchive(ofile); - bool bgProject = getApp()->isBackground(); - oArchive << boost::serialization::make_nvp("Background_project", bgProject); - ProjectSerialization projectSerializationObj( getApp() ); - save(&projectSerializationObj); - oArchive << boost::serialization::make_nvp("Project", projectSerializationObj); - if (!bgProject) { - AppInstancePtr app = getApp(); - if (app) { - app->saveProjectGui(oArchive); + if (!appPTR->getCurrentSettings()->saveAsBinary()){ + try { + FStreamsSupport::ofstream ofile; + FStreamsSupport::open( &ofile, tmpFilename.toStdString(), std::ios::out); + if (!ofile) { + throw std::runtime_error( tr("Failed to open file ").toStdString() + tmpFilename.toStdString() ); + } + boost::archive::xml_oarchive oArchive(ofile); + bool bgProject = getApp()->isBackground(); + oArchive << boost::serialization::make_nvp("Background_project", bgProject); + ProjectSerialization projectSerializationObj( getApp() ); + save(&projectSerializationObj); + oArchive << boost::serialization::make_nvp("Project", projectSerializationObj); + if (!bgProject) { + AppInstancePtr app = getApp(); + if (app) { + app->saveProjectGui(oArchive); + } + } + } catch (...) { + if (!autoSave && updateProjectProperties) { + ///Reset the old project path in case of failure. + _imp->autoSetProjectDirectory(oldProjectPath); } + throw; } - } catch (...) { - if (!autoSave && updateProjectProperties) { - ///Reset the old project path in case of failure. - _imp->autoSetProjectDirectory(oldProjectPath); + } + else + { + try { + FStreamsSupport::ofstream ofile; + FStreamsSupport::open( &ofile, tmpFilename.toStdString(), std::ios::out | std::ios::binary); + if (!ofile) { + throw std::runtime_error( tr("Failed to open file ").toStdString() + tmpFilename.toStdString() ); + } + boost::archive::binary_oarchive oArchive(ofile); + bool bgProject = getApp()->isBackground(); + oArchive << boost::serialization::make_nvp("Background_project", bgProject); + ProjectSerialization projectSerializationObj( getApp() ); + save(&projectSerializationObj); + oArchive << boost::serialization::make_nvp("Project", projectSerializationObj); + if (!bgProject) { + AppInstancePtr app = getApp(); + if (app) { + app->saveProjectGui(oArchive); + } + } + } catch (...) { + if (!autoSave && updateProjectProperties) { + ///Reset the old project path in case of failure. + _imp->autoSetProjectDirectory(oldProjectPath); + } + throw; } - throw; } - } // ofile + } if (!autoSave) { // rotate backups diff --git a/Engine/Settings.cpp b/Engine/Settings.cpp index 64b63b4c7b..31d297c202 100644 --- a/Engine/Settings.cpp +++ b/Engine/Settings.cpp @@ -207,6 +207,12 @@ Settings::initializeKnobsGeneral() "last saved), *.~2~ (third last saved).") ); _generalTab->addKnob(_saveVersions); + _saveAsBinary = AppManager::createKnob( this, tr("Save projects as binary") ); + _saveAsBinary->setName("saveAsBinary"); + _saveAsBinary->setHintToolTip( tr("When checked, %1 will save projects in a binary format which is faster to load and save. " + "When unchecked, projects will be saved in a human-readable text format. " + "Note that the text format is not guaranteed to be stable across versions of %1.").arg( QString::fromUtf8(NATRON_APPLICATION_NAME) ) ); + _hostName = AppManager::createKnob( this, tr("Appear to plug-ins as") ); _hostName->setName("pluginHostName"); _hostName->setHintToolTip( tr("%1 will appear with the name of the selected application to the OpenFX plug-ins. " @@ -1469,6 +1475,7 @@ Settings::setDefaultValues() _autoSaveUnSavedProjects->setDefaultValue(true); _autoSaveDelay->setDefaultValue(5, 0); _saveVersions->setDefaultValue(1); + _saveAsBinary->setDefaultValue(false); _hostName->setDefaultValue(0); _customHostName->setDefaultValue(NATRON_ORGANIZATION_DOMAIN_TOPLEVEL "." NATRON_ORGANIZATION_DOMAIN_SUB "." NATRON_APPLICATION_NAME); @@ -2912,6 +2919,12 @@ Settings::saveVersions() const return _saveVersions->getValue(); } +bool +Settings::saveAsBinary() const +{ + return _saveAsBinary->getValue(); +} + bool Settings::isSnapToNodeEnabled() const diff --git a/Engine/Settings.h b/Engine/Settings.h index 61fa7b582b..43b761d9dc 100644 --- a/Engine/Settings.h +++ b/Engine/Settings.h @@ -181,6 +181,8 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON int saveVersions() const; + bool saveAsBinary() const; + bool isSnapToNodeEnabled() const; bool isCheckForUpdatesEnabled() const; @@ -434,6 +436,7 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON KnobBoolPtr _autoSaveUnSavedProjects; KnobIntPtr _autoSaveDelay; KnobIntPtr _saveVersions; + KnobBoolPtr _saveAsBinary; KnobChoicePtr _hostName; KnobStringPtr _customHostName; diff --git a/Global/Enums.h b/Global/Enums.h index 37f75d66ce..acd1bab91a 100644 --- a/Global/Enums.h +++ b/Global/Enums.h @@ -299,6 +299,8 @@ enum PixmapEnum NATRON_PIXMAP_VIEWER_GAMMA_DISABLED, NATRON_PIXMAP_VIEWER_GAIN_ENABLED, NATRON_PIXMAP_VIEWER_GAIN_DISABLED, + NATRON_PIXMAP_VIEWER_DITHER_ENABLED, + NATRON_PIXMAP_VIEWER_DITHER_DISABLED, NATRON_PIXMAP_SCRIPT_CLEAR_OUTPUT, NATRON_PIXMAP_SCRIPT_EXEC_SCRIPT, diff --git a/Gui/CurveWidget.cpp b/Gui/CurveWidget.cpp index a52108e629..4fc6ed2987 100644 --- a/Gui/CurveWidget.cpp +++ b/Gui/CurveWidget.cpp @@ -164,6 +164,7 @@ CurveWidget::initializeGL() // always running in the main thread assert( qApp && qApp->thread() == QThread::currentThread() ); appPTR->initializeOpenGLFunctionsOnce(); + makeCurrent(); } void diff --git a/Gui/DopeSheetHierarchyView.cpp b/Gui/DopeSheetHierarchyView.cpp index a65f46b37b..27f66d6032 100644 --- a/Gui/DopeSheetHierarchyView.cpp +++ b/Gui/DopeSheetHierarchyView.cpp @@ -130,7 +130,7 @@ HierarchyViewSelectionModel::selectChildren(const QModelIndex &index, QItemSelection *selection) const { int row = 0; - QModelIndex childIndex = index.model()->index(row, 0); + QModelIndex childIndex = index.model()->index(row, 0, index); while ( childIndex.isValid() ) { if ( !selection->contains(childIndex) ) { @@ -143,7 +143,7 @@ HierarchyViewSelectionModel::selectChildren(const QModelIndex &index, } ++row; - childIndex = index.model()->index(row, 0); + childIndex = index.model()->index(row, 0, index); } } diff --git a/Gui/DopeSheetView.cpp b/Gui/DopeSheetView.cpp index b0c7bc0164..7bccaed593 100644 --- a/Gui/DopeSheetView.cpp +++ b/Gui/DopeSheetView.cpp @@ -3171,6 +3171,8 @@ DopeSheetView::initializeGL() return; } + makeCurrent(); + _imp->generateKeyframeTextures(); } diff --git a/Gui/Gui.h b/Gui/Gui.h index 8c0a6b52a9..310b559a6f 100644 --- a/Gui/Gui.h +++ b/Gui/Gui.h @@ -305,8 +305,10 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON static QPixmap screenShot(QWidget* w); void loadProjectGui(bool isAutosave, boost::archive::xml_iarchive & obj) const; + void loadProjectGui(bool isAutosave, boost::archive::binary_iarchive & obj) const; void saveProjectGui(boost::archive::xml_oarchive & archive); + void saveProjectGui(boost::archive::binary_oarchive & archive); void setColorPickersColor(double r, double g, double b, double a); diff --git a/Gui/Gui20.cpp b/Gui/Gui20.cpp index 1ccbb77d17..e61470c6b7 100644 --- a/Gui/Gui20.cpp +++ b/Gui/Gui20.cpp @@ -1465,6 +1465,13 @@ Gui::loadProjectGui(bool isAutosave, boost::archive::xml_iarchive & obj) const _imp->_projectGui->load(isAutosave, obj); } +void +Gui::loadProjectGui(bool isAutosave, boost::archive::binary_iarchive & obj) const +{ + assert(_imp->_projectGui); + _imp->_projectGui->load(isAutosave, obj); +} + void Gui::saveProjectGui(boost::archive::xml_oarchive & archive) { @@ -1472,6 +1479,13 @@ Gui::saveProjectGui(boost::archive::xml_oarchive & archive) _imp->_projectGui->save(archive); } +void +Gui::saveProjectGui(boost::archive::binary_oarchive & archive) +{ + assert(_imp->_projectGui); + _imp->_projectGui->save(archive); +} + bool Gui::isAboutToClose() const { diff --git a/Gui/GuiAppInstance.cpp b/Gui/GuiAppInstance.cpp index a4eddc6798..ee378b30d4 100644 --- a/Gui/GuiAppInstance.cpp +++ b/Gui/GuiAppInstance.cpp @@ -788,6 +788,12 @@ GuiAppInstance::loadProjectGui(bool isAutosave, boost::archive::xml_iarchive & a _imp->_gui->loadProjectGui(isAutosave, archive); } +void +GuiAppInstance::loadProjectGui(bool isAutosave, boost::archive::binary_iarchive & archive) const +{ + _imp->_gui->loadProjectGui(isAutosave, archive); +} + void GuiAppInstance::saveProjectGui(boost::archive::xml_oarchive & archive) { @@ -796,6 +802,14 @@ GuiAppInstance::saveProjectGui(boost::archive::xml_oarchive & archive) } } +void +GuiAppInstance::saveProjectGui(boost::archive::binary_oarchive & archive) +{ + if (_imp->_gui) { + _imp->_gui->saveProjectGui(archive); + } +} + void GuiAppInstance::setupViewersForViews(const std::vector& viewNames) { diff --git a/Gui/GuiAppInstance.h b/Gui/GuiAppInstance.h index e542fb6659..d274e81928 100644 --- a/Gui/GuiAppInstance.h +++ b/Gui/GuiAppInstance.h @@ -132,7 +132,9 @@ GCC_DIAG_SUGGEST_OVERRIDE_ON Natron::StandardButtonEnum defaultButton, bool* stopAsking) OVERRIDE FINAL WARN_UNUSED_RETURN; virtual void loadProjectGui(bool isAutosave, boost::archive::xml_iarchive & archive) const OVERRIDE FINAL; + virtual void loadProjectGui(bool isAutosave, boost::archive::binary_iarchive & archive) const OVERRIDE FINAL; virtual void saveProjectGui(boost::archive::xml_oarchive & archive) OVERRIDE FINAL; + virtual void saveProjectGui(boost::archive::binary_oarchive & archive) OVERRIDE FINAL; virtual void notifyRenderStarted(const QString & sequenceName, int firstFrame, int lastFrame, int frameStep, bool canPause, diff --git a/Gui/GuiApplicationManager.cpp b/Gui/GuiApplicationManager.cpp index 25df689ca0..54049f28a2 100644 --- a/Gui/GuiApplicationManager.cpp +++ b/Gui/GuiApplicationManager.cpp @@ -790,6 +790,12 @@ GuiApplicationManager::getIcon(PixmapEnum e, path = NATRON_IMAGES_PATH "interp_curve_z.png"; break; // DON'T add a default: case here + case NATRON_PIXMAP_VIEWER_DITHER_ENABLED: + path = NATRON_IMAGES_PATH "dither_enabled.png"; + break; + case NATRON_PIXMAP_VIEWER_DITHER_DISABLED: + path = NATRON_IMAGES_PATH "dither_disabled.png"; + break; } // switch if ( path.empty() ) { assert(!"Missing image."); diff --git a/Gui/GuiResources.qrc b/Gui/GuiResources.qrc index 3d9ac34815..6218b77fd9 100644 --- a/Gui/GuiResources.qrc +++ b/Gui/GuiResources.qrc @@ -159,6 +159,8 @@ Resources/Images/curve.png Resources/Images/cuspPoints.png Resources/Images/diskcache_icon.png + Resources/Images/dither_enabled.png + Resources/Images/dither_disabled.png Resources/Images/dot_icon.png Resources/Images/ellipse.png Resources/Images/enter_group.png diff --git a/Gui/ProjectGui.cpp b/Gui/ProjectGui.cpp index 0eddd2e6fd..b7e4bae636 100644 --- a/Gui/ProjectGui.cpp +++ b/Gui/ProjectGui.cpp @@ -261,10 +261,18 @@ AddFormatDialog::getFormat() const return Format(0, 0, w, h, name.toStdString(), pa); } +template +void +ProjectGui::save(boost::archive::xml_oarchive & archive) const; + +template +void +ProjectGui::save(boost::archive::binary_oarchive & archive) const; + // Version is handled in ProjectGuiSerialization -template<> +template void -ProjectGui::save(boost::archive::xml_oarchive & archive) const +ProjectGui::save(Archive & archive) const { ProjectGuiSerialization projectGuiSerializationObj; @@ -430,9 +438,15 @@ loadNodeGuiSerialization(Gui* gui, } } // loadNodeGuiSerialization -template<> +template void +ProjectGui::load(bool isAutosave, boost::archive::xml_iarchive & archive); + +template void +ProjectGui::load(bool isAutosave, boost::archive::binary_iarchive & archive); + +template void -ProjectGui::load(bool isAutosave, boost::archive::xml_iarchive & archive) +ProjectGui::load(bool isAutosave, Archive & archive) { ProjectGuiSerialization obj; diff --git a/Gui/ProjectGuiSerialization.h b/Gui/ProjectGuiSerialization.h index 5354c41ce5..ebe645455e 100644 --- a/Gui/ProjectGuiSerialization.h +++ b/Gui/ProjectGuiSerialization.h @@ -38,6 +38,8 @@ GCC_DIAG_OFF(unused-parameter) // /opt/local/include/boost/serialization/smart_cast.hpp:254:25: warning: unused parameter 'u' [-Wunused-parameter] #include #include +#include +#include #include #include // /usr/local/include/boost/serialization/shared_ptr.hpp:112:5: warning: unused typedef 'boost_static_assert_typedef_112' [-Wunused-local-typedef] diff --git a/Gui/Resources/Images/dither_disabled.png b/Gui/Resources/Images/dither_disabled.png new file mode 100644 index 0000000000..536fdfbfe9 Binary files /dev/null and b/Gui/Resources/Images/dither_disabled.png differ diff --git a/Gui/Resources/Images/dither_enabled.png b/Gui/Resources/Images/dither_enabled.png new file mode 100644 index 0000000000..294cfb9536 Binary files /dev/null and b/Gui/Resources/Images/dither_enabled.png differ diff --git a/Gui/Shaders.cpp b/Gui/Shaders.cpp index 5fe11d0c62..d36e532898 100644 --- a/Gui/Shaders.cpp +++ b/Gui/Shaders.cpp @@ -29,202 +29,242 @@ NATRON_NAMESPACE_ENTER -const char* fragRGB = - "uniform sampler2D Tex;\n" - "uniform float gain;\n" - "uniform float offset;\n" - "uniform int lut;\n" - "uniform float gamma;\n" - "\n" - "float linear_to_srgb(float c) {\n" - " return (c<=0.0031308) ? (12.92*c) : (((1.0+0.055)*pow(c,1.0/2.4))-0.055);\n" - "}\n" - "float linear_to_rec709(float c) {" - " return (c<0.018) ? (4.500*c) : (1.099*pow(c,0.45) - 0.099);\n" - "}\n" - "float linear_to_bt1886(float c) {" - " return pow(c,1.0/2.4);\n" - "}\n" - "void main() {\n" - " vec4 color_tmp = texture2D(Tex,gl_TexCoord[0].st);\n" - " color_tmp.rgb = (color_tmp.rgb * gain) + offset;\n" - " if (lut == 0) { // srgb\n" -// << TO SRGB - " color_tmp.r = linear_to_srgb(color_tmp.r);\n" - " color_tmp.g = linear_to_srgb(color_tmp.g);\n" - " color_tmp.b = linear_to_srgb(color_tmp.b);\n" -// << END TO SRGB - " } else if (lut == 2) { // Rec 709\n" -// << TO REC 709 - " color_tmp.r = linear_to_rec709(color_tmp.r);\n" - " color_tmp.g = linear_to_rec709(color_tmp.g);\n" - " color_tmp.b = linear_to_rec709(color_tmp.b);\n" -// << END TO REC 709 - " } else if (lut == 3) { // BT1886\n" -// << TO BT1886 - " color_tmp.r = linear_to_bt1886(color_tmp.r);\n" - " color_tmp.g = linear_to_bt1886(color_tmp.g);\n" - " color_tmp.b = linear_to_bt1886(color_tmp.b);\n" -// << END TO BT1886 - " }\n" - " if (gamma <= 0.) {\n" - " color_tmp.r = (color_tmp.r >= 1.) ? 1. : 0.;\n" - " color_tmp.g = (color_tmp.g >= 1.) ? 1. : 0.;\n" - " color_tmp.b = (color_tmp.b >= 1.) ? 1. : 0.;\n" - " } else {\n" - " color_tmp.r = pow(color_tmp.r, 1./gamma);\n" - " color_tmp.g = pow(color_tmp.g, 1./gamma);\n" - " color_tmp.b = pow(color_tmp.b, 1./gamma);\n" - " }\n" - " gl_FragColor = color_tmp;\n" - "}\n" -; -const char* vertRGB = - "void main()\n" - "{\n" - " gl_TexCoord[0] = gl_MultiTexCoord0;" - " gl_Position = ftransform();\n" - "}\n" - "\n" -; +const char* fragRGB = R"( + uniform sampler2D Tex; + uniform float gain; + uniform float offset; + uniform int lut; + uniform float gamma; + uniform int dither; + + #if __VERSION__ < 150 + float rnd(vec2 p) + { + return 1.0 - 2.0*fract(sin(dot(p.xy ,vec2(12.9898,78.233))) * 43758.5453); + } + #else + float rnd(vec2 p) + { + int n = int(p.x * 40.0 + p.y * 6400.0); + n = (n << 13) ^ n; + return 1.0 - float( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0; + } + #endif + + float linear_to_srgb(float c) { + return (c<=0.0031308) ? (12.92*c) : (((1.0+0.055)*pow(c,1.0/2.4))-0.055); + } + float linear_to_rec709(float c) { + return (c<0.018) ? (4.500*c) : (1.099*pow(c,0.45) - 0.099); + } + float linear_to_bt1886(float c) { + return pow(c,1.0/2.4); + } + void main() { + vec4 color_tmp = texture2D(Tex,gl_TexCoord[0].st); + color_tmp.rgb = (color_tmp.rgb * gain) + offset; + if (lut == 0) { // srgb + // << TO SRGB + color_tmp.r = linear_to_srgb(color_tmp.r); + color_tmp.g = linear_to_srgb(color_tmp.g); + color_tmp.b = linear_to_srgb(color_tmp.b); + // << END TO SRGB + } else if (lut == 2) { // Rec 709 + // << TO REC 709 + color_tmp.r = linear_to_rec709(color_tmp.r); + color_tmp.g = linear_to_rec709(color_tmp.g); + color_tmp.b = linear_to_rec709(color_tmp.b); + // << END TO REC 709 + } else if (lut == 3) { // BT1886 + // << TO BT1886 + color_tmp.r = linear_to_bt1886(color_tmp.r); + color_tmp.g = linear_to_bt1886(color_tmp.g); + color_tmp.b = linear_to_bt1886(color_tmp.b); + // << END TO BT1886 + } + if (gamma <= 0.) { + color_tmp.r = (color_tmp.r >= 1.) ? 1. : 0.; + color_tmp.g = (color_tmp.g >= 1.) ? 1. : 0.; + color_tmp.b = (color_tmp.b >= 1.) ? 1. : 0.; + } else { + color_tmp.r = pow(color_tmp.r, 1./gamma); + color_tmp.g = pow(color_tmp.g, 1./gamma); + color_tmp.b = pow(color_tmp.b, 1./gamma); + } + + if (dither){ + //dithering + ivec2 texsize = textureSize2D(Tex, 0); + vec2 coord = gl_TexCoord[0].st / texsize; + + vec3 c = color_tmp.rgb; + float a = color_tmp.a; + float scale = 255.0; + float seed = 32; + + vec2 pr = (0.9 + 0.1 * seed) * coord.xy * 1000.1; + vec2 pg = (0.9 + 0.1 * seed) * coord.xy * 1000.2; + vec2 pb = (0.9 + 0.1 * seed) * coord.xy * 1000.3; + + + gl_FragColor = vec4(c.rgb + vec3(rnd(pr), rnd(pg), rnd(pb)) * vec3(0.5) / vec3(scale), a); + } else { + gl_FragColor = vec4(color_tmp.rgb, color_tmp.a); + } + } +)"; +const char* vertRGB = R"( + void main() + { + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = ftransform(); + } + )"; /*There's a black texture used for when the user disconnect the viewer It's not just a shader,because we still need coordinates feedback. */ -const char* blackFrag = - "uniform sampler2D Tex;\n" - "void main()\n" - "{\n" - " gl_FragColor = texture2D(Tex,gl_TexCoord[0].st);\n" - "}\n"; -const char *histogramComputation_frag = - "#extension GL_ARB_texture_rectangle : enable\n" - "uniform sampler2DRect Tex;\n" - "uniform int channel;\n" - "void main()\n" - "{\n" - " gl_FragColor = vec4(1.0,0.0,0.0,1.0);\n" - "}\n" -; -const char *histogramComputationVertex_vert = - "#extension GL_ARB_texture_rectangle : enable\n" - "uniform sampler2DRect Tex;\n" - "uniform int channel;\n" - "attribute vec2 TexCoord;\n" - "void main()\n" - "{\n" - "\n" - " vec4 c = texture2DRect(Tex, TexCoord.xy );\n" - "\n" - " float sel = 0.0;\n" - " if(channel == 0){ // luminance\n" - " sel = 0.299*c.r + 0.587*c.g +0.114*c.b;\n" - " }else if(channel == 1){ // red\n" - " sel = c.r;\n" - " }else if(channel == 2){ //green\n" - " sel = c.g;\n" - " }else if(channel == 3){ // blue\n" - " sel = c.b;\n" - " }else if(channel == 4){ // alpha\n" - " sel = c.a;\n" - " }\n" - " clamp(sel, 0.0, 1.0);\n" - "// set new point position to the color intensity in [-1.0,1.0] interval\n" - "// as this is homogeneous coord. clip space\n" - " gl_Position.x =(2.0-4.0/257.0)*sel-1.0+2.0/257.0;\n" - " gl_Position.y = 0.0;\n" - " gl_Position.z = 0.0;\n" - "}\n" -; -const char *histogramRendering_frag = - "#extension GL_ARB_texture_rectangle : enable\n" - "uniform sampler2DRect HistogramTex;\n" - "uniform sampler2DRect MaximumRedTex;\n" - "uniform sampler2DRect MaximumGreenTex;\n" - "uniform sampler2DRect MaximumBlueTex;\n" - "uniform int channel;\n" - "void main()\n" - "{\n" - " if(channel == 0){\n" - " gl_FragColor =vec4(0.8,0.8,0.8,0.8);\n" - " }else if(channel == 1){\n" - " gl_FragColor =vec4(1.0,0.0,0.0,0.8);\n" - " }else if(channel == 2){\n" - " gl_FragColor =vec4(0.0,1.0,0.0,0.8);\n" - " }else if(channel == 3){\n" - " gl_FragColor =vec4(0.0,0.0,1.0,0.8);\n" - " }\n" - " \n" - "}\n" -; -const char *histogramRenderingVertex_vert = - "#extension GL_ARB_texture_rectangle : enable\n" - "uniform sampler2DRect HistogramTex;\n" - "uniform sampler2DRect MaximumRedTex;\n" - "uniform sampler2DRect MaximumGreenTex;\n" - "uniform sampler2DRect MaximumBlueTex;\n" - "uniform int channel;\n" - "attribute vec3 TexCoord;\n" - "void main()\n" - "{\n" - "\n" - " vec4 c = texture2DRect(HistogramTex, TexCoord.xy );\n" - " float bottom = TexCoord.z;\n" - " float maximum = 0.0;\n" - " float maximumRed = texture2DRect(MaximumRedTex,vec2(0.0,0.0)).r;\n" - " float maximumGreen = texture2DRect(MaximumGreenTex,vec2(0.0,0.0)).r;\n" - " float maximumBlue = texture2DRect(MaximumBlueTex,vec2(0.0,0.0)).r;\n" - " maximum = max(max(maximumRed,maximumGreen),maximumBlue);\n" - " if(maximum == 0.0){\n" - " maximum = 1000000.0;\n" - " }\n" - "// set new point position to the color intensity in [-1.0,1.0] interval\n" - "// as this is homogeneous coord. clip space\n" - " gl_Position.x =(2.0-4.0/257.0)*(TexCoord.x/255.0)-1.0+2.0/257.0;\n" - " if(bottom == 1.0){\n" - " gl_Position.y = -1.0;\n" - " }else{\n" - " float y = c.r/maximum;\n" - " gl_Position.y = 2.0*y-1.0;\n" - " }\n" - " gl_Position.z = 0.0;\n" - "}\n" -; -const char* minimal_vert = - "#extension GL_ARB_texture_rectangle : enable\n" - "gl_TexCoord[0]=gl_MultiTexCoord0;" - "gl_Position = ftransform();" +const char* blackFrag = R"( + uniform sampler2D Tex; + void main() + { + gl_FragColor = texture2D(Tex,gl_TexCoord[0].st); + }; +)"; + +const char *histogramComputation_frag = R"( + #extension GL_ARB_texture_rectangle : enable + uniform sampler2DRect Tex; + uniform int channel; + void main() + { + gl_FragColor = vec4(1.0,0.0,0.0,1.0); + } +)"; + +const char *histogramComputationVertex_vert = R"( + #extension GL_ARB_texture_rectangle : enable + uniform sampler2DRect Tex; + uniform int channel; + attribute vec2 TexCoord; + void main() + { + + vec4 c = texture2DRect(Tex, TexCoord.xy ); + + float sel = 0.0; + if(channel == 0){ // luminance + sel = 0.299*c.r + 0.587*c.g +0.114*c.b; + }else if(channel == 1){ // red + sel = c.r; + }else if(channel == 2){ //green + sel = c.g; + }else if(channel == 3){ // blue + sel = c.b; + }else if(channel == 4){ // alpha + sel = c.a; + } + clamp(sel, 0.0, 1.0); + // set new point position to the color intensity in [-1.0,1.0] interval + // as this is homogeneous coord. clip space + gl_Position.x =(2.0-4.0/257.0)*sel-1.0+2.0/257.0; + gl_Position.y = 0.0; + gl_Position.z = 0.0; + } +)"; + +const char *histogramRendering_frag = R"( + #extension GL_ARB_texture_rectangle : enable + uniform sampler2DRect HistogramTex; + uniform sampler2DRect MaximumRedTex; + uniform sampler2DRect MaximumGreenTex; + uniform sampler2DRect MaximumBlueTex; + uniform int channel; + void main() + { + if(channel == 0){ + gl_FragColor =vec4(0.8,0.8,0.8,0.8); + }else if(channel == 1){ + gl_FragColor =vec4(1.0,0.0,0.0,0.8); + }else if(channel == 2){ + gl_FragColor =vec4(0.0,1.0,0.0,0.8); + }else if(channel == 3){ + gl_FragColor =vec4(0.0,0.0,1.0,0.8); + } + + } +)"; + +const char *histogramRenderingVertex_vert = R"( + #extension GL_ARB_texture_rectangle : enable + uniform sampler2DRect HistogramTex; + uniform sampler2DRect MaximumRedTex; + uniform sampler2DRect MaximumGreenTex; + uniform sampler2DRect MaximumBlueTex; + uniform int channel; + attribute vec3 TexCoord; + void main() + { + + vec4 c = texture2DRect(HistogramTex, TexCoord.xy ); + float bottom = TexCoord.z; + float maximum = 0.0; + float maximumRed = texture2DRect(MaximumRedTex,vec2(0.0,0.0)).r; + float maximumGreen = texture2DRect(MaximumGreenTex,vec2(0.0,0.0)).r; + float maximumBlue = texture2DRect(MaximumBlueTex,vec2(0.0,0.0)).r; + maximum = max(max(maximumRed,maximumGreen),maximumBlue); + if(maximum == 0.0){ + maximum = 1000000.0; + } + // set new point position to the color intensity in [-1.0,1.0] interval + // as this is homogeneous coord. clip space + gl_Position.x =(2.0-4.0/257.0)*(TexCoord.x/255.0)-1.0+2.0/257.0; + if(bottom == 1.0){ + gl_Position.y = -1.0; + }else{ + float y = c.r/maximum; + gl_Position.y = 2.0*y-1.0; + } + gl_Position.z = 0.0; + } +)"; + +const char* minimal_vert = R"( + #extension GL_ARB_texture_rectangle : enable + gl_TexCoord[0]=gl_MultiTexCoord0; + gl_Position = ftransform(); ; const char *histogramMaximum_frag = - "#extension GL_ARB_texture_rectangle : enable\n" - "uniform sampler2DRect Tex;\n" - "void main()\n" - "{\n" - " vec4 a,b,c,d;\n" - " vec2 texCoord = gl_TexCoord[0].st;\n" - " texCoord.s = (texCoord.s-0.5) * 4.0 + 0.5;\n" - " a = texture2DRect(Tex,texCoord.st);\n" - " vec2 texCoord1,texCoord2,texCoord3;\n" - " texCoord1 = texCoord.st + vec2(1.0,0.0);\n" - " texCoord2 = texCoord.st + vec2(2.0,0.0);\n" - " texCoord3 = texCoord.st + vec2(3.0,0.0);\n" - " if(texCoord1.s <= 256.0){\n" - " b = texture2DRect(Tex,texCoord1);\n" - " }else{\n" - " b = a;\n" - " }\n" - " if(texCoord2.s <= 256.0){\n" - " c = texture2DRect(Tex,texCoord2);\n" - " }else{\n" - " c = a;\n" - " }\n" - " if(texCoord3.s <= 256.0){\n" - " d = texture2DRect(Tex,texCoord3);\n" - " }else{\n" - " d = a;\n" - " }\n" - " gl_FragColor = vec4(max(max(a.r,b.r),max(c.r,d.r)),0.0,0.0,1.0);\n" - "}\n" -; + #extension GL_ARB_texture_rectangle : enable + uniform sampler2DRect Tex; + void main() + { + vec4 a,b,c,d; + vec2 texCoord = gl_TexCoord[0].st; + texCoord.s = (texCoord.s-0.5) * 4.0 + 0.5; + a = texture2DRect(Tex,texCoord.st); + vec2 texCoord1,texCoord2,texCoord3; + texCoord1 = texCoord.st + vec2(1.0,0.0); + texCoord2 = texCoord.st + vec2(2.0,0.0); + texCoord3 = texCoord.st + vec2(3.0,0.0); + if(texCoord1.s <= 256.0){ + b = texture2DRect(Tex,texCoord1); + }else{ + b = a; + } + if(texCoord2.s <= 256.0){ + c = texture2DRect(Tex,texCoord2); + }else{ + c = a; + } + if(texCoord3.s <= 256.0){ + d = texture2DRect(Tex,texCoord3); + }else{ + d = a; + } + gl_FragColor = vec4(max(max(a.r,b.r),max(c.r,d.r)),0.0,0.0,1.0); + } +)"; + NATRON_NAMESPACE_EXIT diff --git a/Gui/Shaders.h b/Gui/Shaders.h index 7d8835d1e6..a3575b7bad 100644 --- a/Gui/Shaders.h +++ b/Gui/Shaders.h @@ -32,6 +32,7 @@ NATRON_NAMESPACE_ENTER extern const char* fragRGB; extern const char* vertRGB; +extern const char* fragDither; /*There's a black texture used for when the user disconnect the viewer It's not just a shader,because we still need coordinates feedback. diff --git a/Gui/ViewerGL.cpp b/Gui/ViewerGL.cpp index ff31696571..81327072f0 100644 --- a/Gui/ViewerGL.cpp +++ b/Gui/ViewerGL.cpp @@ -260,12 +260,11 @@ ViewerGL::paintGL() } glCheckError(); - { double screenPixelRatio = getScreenPixelRatio(); if (screenPixelRatio != _imp->_screenPixelRatio) { _imp->_screenPixelRatio = screenPixelRatio; - _imp->_textFont.reset(new QFont(appFont, appFontSize * screenPixelRatio)); + _imp->_textFont.reset(new QFont(appFont, appFontSize * screenPixelRatio, QFont::Bold)); } } assert(_imp->_textFont); @@ -1720,6 +1719,14 @@ ViewerGL::setLut(int lut) _imp->displayingImageLut = (ViewerColorSpaceEnum)lut; } +void +ViewerGL::setDither(bool dither) +{ + // always running in the main thread + assert( qApp && qApp->thread() == QThread::currentThread() ); + _imp->enableDisplayDither = dither; +} + #define QMouseEventLocalPos(e) ( e->localPos() ) void diff --git a/Gui/ViewerGL.h b/Gui/ViewerGL.h index 9015efa5b4..d98b85db6e 100644 --- a/Gui/ViewerGL.h +++ b/Gui/ViewerGL.h @@ -396,6 +396,8 @@ public Q_SLOTS: void setLut(int lut); + void setDither(bool dither); + bool isWipeHandleVisible() const; void setZoomOrPannedSinceLastFit(bool enabled); diff --git a/Gui/ViewerGLPrivate.cpp b/Gui/ViewerGLPrivate.cpp index 957837e3de..159d501256 100644 --- a/Gui/ViewerGLPrivate.cpp +++ b/Gui/ViewerGLPrivate.cpp @@ -80,6 +80,7 @@ ViewerGL::Implementation::Implementation(ViewerGL* this_, , zoomOrPannedSinceLastFit(false) , oldClick() , displayingImageLut(eViewerColorSpaceSRGB) + , enableDisplayDither(false) , ms(eMouseStateUndefined) , hs(eHoverStateNothing) , textRenderingColor(200, 200, 200, 255) @@ -856,6 +857,7 @@ ViewerGL::Implementation::activateShaderRGB(int texIndex) shaderRGB->setUniformValue("lut", (GLint)displayingImageLut); float gamma = displayTextures[texIndex].gamma; shaderRGB->setUniformValue("gamma", gamma); + shaderRGB->setUniformValue("dither", (GLint)enableDisplayDither); } bool diff --git a/Gui/ViewerGLPrivate.h b/Gui/ViewerGLPrivate.h index c3d66782d8..dafc7deebe 100644 --- a/Gui/ViewerGLPrivate.h +++ b/Gui/ViewerGLPrivate.h @@ -166,6 +166,7 @@ struct ViewerGL::Implementation bool zoomOrPannedSinceLastFit; //< true if the user zoomed or panned the image since the last call to fitToRoD QPoint oldClick; ViewerColorSpaceEnum displayingImageLut; + bool enableDisplayDither; MouseStateEnum ms; /*!< Holds the mouse state*/ HoverStateEnum hs; const QColor textRenderingColor; diff --git a/Gui/ViewerTab.cpp b/Gui/ViewerTab.cpp index 59d026a065..774219a94b 100644 --- a/Gui/ViewerTab.cpp +++ b/Gui/ViewerTab.cpp @@ -524,6 +524,24 @@ ViewerTab::ViewerTab(const std::list & existingNodesContext, QObject::connect( _imp->checkerboardButton, SIGNAL(clicked(bool)), this, SLOT(onCheckerboardButtonClicked()) ); _imp->secondRowLayout->addWidget(_imp->checkerboardButton); + QPixmap ditheringEnabled, ditheringDisabled; + appPTR->getIcon(NATRON_PIXMAP_VIEWER_DITHER_ENABLED, pixmapIconSize, &ditheringEnabled); + appPTR->getIcon(NATRON_PIXMAP_VIEWER_DITHER_DISABLED, pixmapIconSize, &ditheringDisabled); + QIcon icDthr; + icDthr.addPixmap(ditheringEnabled, QIcon::Normal, QIcon::On); + icDthr.addPixmap(ditheringDisabled, QIcon::Normal, QIcon::Off); + _imp->ditherButton = new Button(icDthr, QString(), _imp->secondSettingsRow); + _imp->ditherButton->setFocusPolicy(Qt::NoFocus); + _imp->ditherButton->setCheckable(true); + _imp->ditherButton->setChecked(false); + _imp->ditherButton->setDown(false); + _imp->ditherButton->setToolTip( NATRON_NAMESPACE::convertFromPlainText(tr("If checked, it will activate a dithering filter to avoid banding artifacts (needs 32 bits floating point rendering)"), NATRON_NAMESPACE::WhiteSpaceNormal) ); + _imp->ditherButton->setFixedSize(buttonSize); + _imp->ditherButton->setIconSize(buttonIconSize); + QObject::connect( _imp->ditherButton, SIGNAL(clicked(bool)), this, SLOT(onDitherButtonClicked()) ); + _imp->secondRowLayout->addWidget(_imp->ditherButton); + + _imp->viewsComboBox = new ComboBox(_imp->secondSettingsRow); _imp->viewsComboBox->setToolTip( QString::fromUtf8("

") + tr("Active view:") + QString::fromUtf8("

") + tr( "Tells the viewer what view should be displayed.") ); diff --git a/Gui/ViewerTab.h b/Gui/ViewerTab.h index 686e7ad512..291e9c16aa 100644 --- a/Gui/ViewerTab.h +++ b/Gui/ViewerTab.h @@ -397,6 +397,7 @@ public Q_SLOTS: void hideAllToolbars(); void onCheckerboardButtonClicked(); + void onDitherButtonClicked(); void onPickerButtonClicked(bool); diff --git a/Gui/ViewerTab40.cpp b/Gui/ViewerTab40.cpp index 76529f87b8..64dbb14bc1 100644 --- a/Gui/ViewerTab40.cpp +++ b/Gui/ViewerTab40.cpp @@ -675,6 +675,15 @@ ViewerTab::onCheckerboardButtonClicked() _imp->viewer->redraw(); } +void +ViewerTab::onDitherButtonClicked() +{ + _imp->ditherEnabled = !_imp->ditherEnabled; + _imp->ditherButton->setDown(_imp->ditherEnabled); + _imp->viewer->setDither(_imp->ditherEnabled); + _imp->viewer->redraw(); +} + bool ViewerTab::isCheckerboardEnabled() const { diff --git a/Gui/ViewerTabPrivate.cpp b/Gui/ViewerTabPrivate.cpp index a99997976d..caa27eca63 100644 --- a/Gui/ViewerTabPrivate.cpp +++ b/Gui/ViewerTabPrivate.cpp @@ -97,6 +97,7 @@ ViewerTabPrivate::ViewerTabPrivate(ViewerTab* publicInterface, , gammaSlider(NULL) , viewerColorSpace(NULL) , checkerboardButton(NULL) + , ditherButton(NULL) , pickerButton(NULL) , viewsComboBox(NULL) , currentViewIndex(0) diff --git a/Gui/ViewerTabPrivate.h b/Gui/ViewerTabPrivate.h index c2a0df662b..402d31c9fd 100644 --- a/Gui/ViewerTabPrivate.h +++ b/Gui/ViewerTabPrivate.h @@ -115,6 +115,7 @@ struct ViewerTabPrivate ScaleSliderQWidget* gammaSlider; ComboBox* viewerColorSpace; Button* checkerboardButton; + Button* ditherButton; Button* pickerButton; ComboBox* viewsComboBox; ViewIdx currentViewIndex; @@ -192,6 +193,7 @@ struct ViewerTabPrivate bool isFileDialogViewer; mutable QMutex checkerboardMutex; bool checkerboardEnabled; + bool ditherEnabled; mutable QMutex fpsMutex; double fps; diff --git a/Renderer/CMakeLists.txt b/Renderer/CMakeLists.txt index b54e1f254c..0b29c2d848 100644 --- a/Renderer/CMakeLists.txt +++ b/Renderer/CMakeLists.txt @@ -18,7 +18,7 @@ # ***** END LICENSE BLOCK ***** set(NatronRenderer_SOURCES NatronRenderer_main.cpp) -if(WINDOWS) +if(WIN32) list(APPEND NatronRenderer_SOURCES ../Natron.rc) endif() add_executable(NatronRenderer ${NatronRenderer_SOURCES}) diff --git a/libs/gflags/CMakeLists.txt b/libs/gflags/CMakeLists.txt index 01397809aa..0bc3f2af5c 100644 --- a/libs/gflags/CMakeLists.txt +++ b/libs/gflags/CMakeLists.txt @@ -30,7 +30,7 @@ set(gflags_SOURCES src/gflags_completions.cc src/gflags_reporting.cc ) -if(WINDOWS) +if(WIN32) set(gflags_HEADERS ${gflags_HEADERS} src/windows_port.h) set(gflags_SOURCES ${gflags_SOURCES} src/windows_port.cc) endif() diff --git a/tools/utils/sourceList.py b/tools/utils/sourceList.py index 13d236f4d9..b1e5871026 100644 --- a/tools/utils/sourceList.py +++ b/tools/utils/sourceList.py @@ -15,7 +15,7 @@ def list_typesystem_cpp_sources(typesystem, out): sources = [f"{package.lower()}_module_wrapper.cpp"] sources.extend([f"{typename.lower()}_wrapper.cpp" for typename in types]) - return [os.path.normpath(os.path.join(out, package, f)) for f in sources] + return [os.path.normpath(os.path.join(out, package, f)).replace("\\", "/") for f in sources] if __name__ == "__main__":