From 07592c9d784fb6e70a8c1308e634dc947e47d4e0 Mon Sep 17 00:00:00 2001 From: mfeemster Date: Sat, 21 Mar 2015 15:27:37 -0700 Subject: [PATCH] Remove ReadMe.txt from all project files. Add Curves.h, and CurvesGraphicsView.h/cpp to support bezier color curves. Add Curves member to Ember. Add curves capability to EmberCL. Remove some unused variables in the kernel created in RendererCL::CreateFinalAccumKernelString(). Use glm namespace for vec classes if GLM_VERSION >= 96, else use glm::detail. As a result of using glm namespace, all instances of min and max had to be qualified with std:: Split ComputeCamera into that and ComputeQuality(). Reduce the amount of ComputeCamera() and MakeDmap() calls on each incremental iter that doesn't use temporal samples. Fix clamping bug with DE filter widths. Provide functions to return the kernels from RendererCL to assist with diagnostics and debugging. Prevent extra newline in EmberRender when only rendering a single image. Add the ability to delete an ember at a given index in EmberFile. Allow deleting/focusing ember in library tab with delete and enter keys. Reorder some code in Fractorium.h to match the tabs order. Add and call ClearFinalImages() to clear buffers in controller to fix bug where previous CPU render would be shown for a split second when switching from OpenCL back to CPU. Refactor ember library pointer syncing to a function SyncPointers(). Add the ability to save ember Xmls to an unique automatically generated name after the first time the user has specified a name. --- Builds/MSVC/Installer/Product.wxs | 2 +- Builds/MSVC/VS2013/Ember.vcxproj | 4 +- Builds/MSVC/VS2013/Ember.vcxproj.filters | 6 +- Builds/MSVC/VS2013/EmberCL.vcxproj | 3 - Builds/MSVC/VS2013/EmberCL.vcxproj.filters | 3 - Builds/MSVC/VS2013/EmberGenome.vcxproj | 1 - .../MSVC/VS2013/EmberGenome.vcxproj.filters | 1 - Builds/MSVC/VS2013/EmberRender.vcxproj | 1 - .../MSVC/VS2013/EmberRender.vcxproj.filters | 1 - Builds/MSVC/VS2013/EmberTester.vcxproj | 3 - .../MSVC/VS2013/EmberTester.vcxproj.filters | 3 - Builds/MSVC/VS2013/Fractorium.vcxproj | 45 +++ Builds/MSVC/VS2013/Fractorium.vcxproj.filters | 15 + Data/Version History.txt | 2 +- Source/Ember/Curves.h | 284 ++++++++++++++++++ Source/Ember/Ember.cpp | 2 + Source/Ember/Ember.h | 28 +- Source/Ember/EmberDefines.h | 26 +- Source/Ember/EmberPch.h | 2 + Source/Ember/EmberToXml.h | 16 +- Source/Ember/Palette.h | 2 +- Source/Ember/Renderer.cpp | 206 ++++++++----- Source/Ember/Renderer.h | 34 ++- Source/Ember/RendererBase.cpp | 3 +- Source/Ember/RendererBase.h | 2 + Source/Ember/SheepTools.h | 1 + Source/Ember/Variation.h | 8 +- Source/Ember/Variations01.h | 4 +- Source/Ember/Variations02.h | 2 +- Source/Ember/Variations04.h | 2 +- Source/Ember/Variations05.h | 12 +- Source/Ember/XmlToEmber.h | 18 +- Source/EmberAnimate/EmberAnimate.rc | 8 +- Source/EmberCL/EmberCLFunctions.h | 15 + .../EmberCL/FinalAccumOpenCLKernelCreator.cpp | 16 +- Source/EmberCL/RendererCL.cpp | 47 ++- Source/EmberCL/RendererCL.h | 3 + Source/EmberCommon/JpegUtils.h | 2 +- Source/EmberGenome/EmberGenome.rc | 8 +- Source/EmberRender/EmberRender.cpp | 2 +- Source/EmberRender/EmberRender.rc | 8 +- Source/EmberTester/EmberTester.cpp | 79 ++++- Source/Fractorium/AboutDialog.ui | 2 +- Source/Fractorium/CurvesGraphicsView.cpp | 213 +++++++++++++ Source/Fractorium/CurvesGraphicsView.h | 150 +++++++++ Source/Fractorium/EmberFile.h | 18 ++ Source/Fractorium/FinalRenderDialog.cpp | 2 +- .../Fractorium/FinalRenderEmberController.cpp | 9 +- Source/Fractorium/Fractorium.cpp | 18 +- Source/Fractorium/Fractorium.h | 23 +- Source/Fractorium/Fractorium.rc | Bin 4574 -> 4574 bytes Source/Fractorium/Fractorium.ui | 151 +++++++++- .../Fractorium/FractoriumEmberController.cpp | 4 +- Source/Fractorium/FractoriumEmberController.h | 50 +-- Source/Fractorium/FractoriumLibrary.cpp | 105 ++++++- Source/Fractorium/FractoriumMenus.cpp | 22 +- Source/Fractorium/FractoriumPch.h | 2 + Source/Fractorium/FractoriumRender.cpp | 31 +- Source/Fractorium/FractoriumSettings.cpp | 9 +- Source/Fractorium/FractoriumSettings.h | 4 + Source/Fractorium/FractoriumXformsColor.cpp | 83 ++++- Source/Fractorium/GLEmberController.cpp | 4 +- Source/Fractorium/GLWidget.cpp | 2 +- Source/Fractorium/OptionsDialog.cpp | 6 +- Source/Fractorium/OptionsDialog.h | 1 + Source/Fractorium/OptionsDialog.ui | 7 + Source/Fractorium/SpinBox.cpp | 2 +- 67 files changed, 1585 insertions(+), 263 deletions(-) create mode 100644 Source/Ember/Curves.h create mode 100644 Source/Fractorium/CurvesGraphicsView.cpp create mode 100644 Source/Fractorium/CurvesGraphicsView.h diff --git a/Builds/MSVC/Installer/Product.wxs b/Builds/MSVC/Installer/Product.wxs index 184aa10..457244e 100644 --- a/Builds/MSVC/Installer/Product.wxs +++ b/Builds/MSVC/Installer/Product.wxs @@ -1,6 +1,6 @@ - + diff --git a/Builds/MSVC/VS2013/Ember.vcxproj b/Builds/MSVC/VS2013/Ember.vcxproj index ed07e31..4016a78 100644 --- a/Builds/MSVC/VS2013/Ember.vcxproj +++ b/Builds/MSVC/VS2013/Ember.vcxproj @@ -266,12 +266,10 @@ true - - - + diff --git a/Builds/MSVC/VS2013/Ember.vcxproj.filters b/Builds/MSVC/VS2013/Ember.vcxproj.filters index 32450ef..59e3bf5 100644 --- a/Builds/MSVC/VS2013/Ember.vcxproj.filters +++ b/Builds/MSVC/VS2013/Ember.vcxproj.filters @@ -19,9 +19,6 @@ {1ae77918-b5ee-4186-9fec-802fed55144e} - - - Header Files @@ -113,6 +110,9 @@ Header Files + + Header Files + diff --git a/Builds/MSVC/VS2013/EmberCL.vcxproj b/Builds/MSVC/VS2013/EmberCL.vcxproj index 9917da6..6787428 100644 --- a/Builds/MSVC/VS2013/EmberCL.vcxproj +++ b/Builds/MSVC/VS2013/EmberCL.vcxproj @@ -273,9 +273,6 @@ $(CUDA_PATH)lib\$(PlatformName) - - - {f62787dd-1327-448b-9818-030062bcfaa5} diff --git a/Builds/MSVC/VS2013/EmberCL.vcxproj.filters b/Builds/MSVC/VS2013/EmberCL.vcxproj.filters index bb0f844..7db175f 100644 --- a/Builds/MSVC/VS2013/EmberCL.vcxproj.filters +++ b/Builds/MSVC/VS2013/EmberCL.vcxproj.filters @@ -17,9 +17,6 @@ {d66f35ca-a4cd-470a-9c56-653b0665b598} - - - Source Files diff --git a/Builds/MSVC/VS2013/EmberGenome.vcxproj b/Builds/MSVC/VS2013/EmberGenome.vcxproj index 9f65e21..92d2585 100644 --- a/Builds/MSVC/VS2013/EmberGenome.vcxproj +++ b/Builds/MSVC/VS2013/EmberGenome.vcxproj @@ -299,7 +299,6 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" - diff --git a/Builds/MSVC/VS2013/EmberGenome.vcxproj.filters b/Builds/MSVC/VS2013/EmberGenome.vcxproj.filters index 5ae7193..267f436 100644 --- a/Builds/MSVC/VS2013/EmberGenome.vcxproj.filters +++ b/Builds/MSVC/VS2013/EmberGenome.vcxproj.filters @@ -15,7 +15,6 @@ - Resource Files diff --git a/Builds/MSVC/VS2013/EmberRender.vcxproj b/Builds/MSVC/VS2013/EmberRender.vcxproj index 30be90f..7b32cf8 100644 --- a/Builds/MSVC/VS2013/EmberRender.vcxproj +++ b/Builds/MSVC/VS2013/EmberRender.vcxproj @@ -301,7 +301,6 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" - diff --git a/Builds/MSVC/VS2013/EmberRender.vcxproj.filters b/Builds/MSVC/VS2013/EmberRender.vcxproj.filters index 38a8460..03ab99e 100644 --- a/Builds/MSVC/VS2013/EmberRender.vcxproj.filters +++ b/Builds/MSVC/VS2013/EmberRender.vcxproj.filters @@ -15,7 +15,6 @@ - Resource Files diff --git a/Builds/MSVC/VS2013/EmberTester.vcxproj b/Builds/MSVC/VS2013/EmberTester.vcxproj index 084c6f6..b51d7c5 100644 --- a/Builds/MSVC/VS2013/EmberTester.vcxproj +++ b/Builds/MSVC/VS2013/EmberTester.vcxproj @@ -297,9 +297,6 @@ xcopy /F /Y /R /D "$(SolutionDir)intel64\$(Configuration)\tbb.pdb" "$(OutDir)" xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" - - - {1d6039f6-5078-416f-a3af-a36efc7e6a1c} diff --git a/Builds/MSVC/VS2013/EmberTester.vcxproj.filters b/Builds/MSVC/VS2013/EmberTester.vcxproj.filters index f5ad239..be5b41b 100644 --- a/Builds/MSVC/VS2013/EmberTester.vcxproj.filters +++ b/Builds/MSVC/VS2013/EmberTester.vcxproj.filters @@ -14,9 +14,6 @@ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - Header Files diff --git a/Builds/MSVC/VS2013/Fractorium.vcxproj b/Builds/MSVC/VS2013/Fractorium.vcxproj index 95d8b18..77b6dd9 100644 --- a/Builds/MSVC/VS2013/Fractorium.vcxproj +++ b/Builds/MSVC/VS2013/Fractorium.vcxproj @@ -303,6 +303,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"Use Use + @@ -347,6 +348,12 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"true true + + true + true + true + true + true true @@ -427,6 +434,12 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"true true + + true + true + true + true + true true @@ -493,6 +506,12 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"true true + + true + true + true + true + true true @@ -730,6 +749,32 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)""$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fFractoriumPch.h" "-f../../../../../Source/Fractorium/TwoButtonComboWidget.h" -DUNICODE -DWIN32 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_MULTIMEDIA_LIB -DQT_HELP_LIB -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_XML_LIB -D_MBCS "-I." "-I$(QTDIR)\include" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName" "-I$(QTDIR)\..\qtmultimedia\include\QtMultimedia" "-I$(QTDIR)\..\qtmultimedia\include" "-I$(QTDIR)\..\qttools\include" "-I$(QTDIR)\..\qttools\include\QtHelp" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtXml" "-I.\GeneratedFiles" "-I$(ProjectDir)..\..\..\Source\Ember" "-I$(ProjectDir)..\..\..\Source\EmberCL" "-I$(ProjectDir)..\..\..\Source\EmberCommon" "-I$(ProjectDir)..\..\..\..\glm" "-I$(ProjectDir)..\..\..\..\tbb\include" "-I$(ProjectDir)..\..\..\..\libjpeg" "-I$(ProjectDir)..\..\..\..\libpng" "-I$(ProjectDir)..\..\..\..\libxml2\include" "-I$(ProjectDir)..\..\..\..\glew\include" "-I$(CUDA_PATH)include" "-I.\GeneratedFiles\$(ConfigurationName)\." + + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing CurvesGraphicsView.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fFractoriumPch.h" "-f../../../../../Source/Fractorium/CurvesGraphicsView.h" -DUNICODE -DWIN32 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_MULTIMEDIA_LIB -DQT_HELP_LIB -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_XML_LIB -D_MBCS "-I." "-I$(QTDIR)\include" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName" "-I$(QTDIR)\..\qtmultimedia\include\QtMultimedia" "-I$(QTDIR)\..\qtmultimedia\include" "-I$(QTDIR)\..\qttools\include" "-I$(QTDIR)\..\qttools\include\QtHelp" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtXml" "-I.\GeneratedFiles" "-I$(ProjectDir)..\..\..\Source\Ember" "-I$(ProjectDir)..\..\..\Source\EmberCL" "-I$(ProjectDir)..\..\..\Source\EmberCommon" "-I$(ProjectDir)..\..\..\..\glm" "-I$(ProjectDir)..\..\..\..\tbb\include" "-I$(ProjectDir)..\..\..\..\libjpeg" "-I$(ProjectDir)..\..\..\..\libpng" "-I$(ProjectDir)..\..\..\..\libxml2\include" "-I$(ProjectDir)..\..\..\..\glew\include" "-I$(AMDAPPSDKROOT)\include" "-I$(CUDA_PATH)include" "-I.\GeneratedFiles\$(ConfigurationName)\." + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing CurvesGraphicsView.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fFractoriumPch.h" "-f../../../../../Source/Fractorium/CurvesGraphicsView.h" -DUNICODE -DWIN32 -DQT_DLL -DQT_CORE_LIB -DQT_GUI_LIB -DQT_MULTIMEDIA_LIB -DQT_HELP_LIB -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_XML_LIB -D_MBCS "-I." "-I$(QTDIR)\include" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName" "-I$(QTDIR)\..\qtmultimedia\include\QtMultimedia" "-I$(QTDIR)\..\qtmultimedia\include" "-I$(QTDIR)\..\qttools\include" "-I$(QTDIR)\..\qttools\include\QtHelp" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtXml" "-I.\GeneratedFiles" "-I$(ProjectDir)..\..\..\Source\Ember" "-I$(ProjectDir)..\..\..\Source\EmberCL" "-I$(ProjectDir)..\..\..\Source\EmberCommon" "-I$(ProjectDir)..\..\..\..\glm" "-I$(ProjectDir)..\..\..\..\tbb\include" "-I$(ProjectDir)..\..\..\..\libjpeg" "-I$(ProjectDir)..\..\..\..\libpng" "-I$(ProjectDir)..\..\..\..\libxml2\include" "-I$(ProjectDir)..\..\..\..\glew\include" "-I$(AMDAPPSDKROOT)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing CurvesGraphicsView.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fFractoriumPch.h" "-f../../../../../Source/Fractorium/CurvesGraphicsView.h" -DUNICODE -DWIN32 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_MULTIMEDIA_LIB -DQT_HELP_LIB -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_XML_LIB -D_MBCS "-I." "-I$(QTDIR)\include" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName" "-I$(QTDIR)\..\qtmultimedia\include\QtMultimedia" "-I$(QTDIR)\..\qtmultimedia\include" "-I$(QTDIR)\..\qttools\include" "-I$(QTDIR)\..\qttools\include\QtHelp" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtXml" "-I.\GeneratedFiles" "-I$(ProjectDir)..\..\..\Source\Ember" "-I$(ProjectDir)..\..\..\Source\EmberCL" "-I$(ProjectDir)..\..\..\Source\EmberCommon" "-I$(ProjectDir)..\..\..\..\glm" "-I$(ProjectDir)..\..\..\..\tbb\include" "-I$(ProjectDir)..\..\..\..\libjpeg" "-I$(ProjectDir)..\..\..\..\libpng" "-I$(ProjectDir)..\..\..\..\libxml2\include" "-I$(ProjectDir)..\..\..\..\glew\include" "-I$(AMDAPPSDKROOT)\include" "-I$(CUDA_PATH)include" "-I.\GeneratedFiles\$(ConfigurationName)\." + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing CurvesGraphicsView.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fFractoriumPch.h" "-f../../../../../Source/Fractorium/CurvesGraphicsView.h" -DUNICODE -DWIN32 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_MULTIMEDIA_LIB -DQT_HELP_LIB -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_XML_LIB -D_MBCS "-I." "-I$(QTDIR)\include" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName" "-I$(QTDIR)\..\qtmultimedia\include\QtMultimedia" "-I$(QTDIR)\..\qtmultimedia\include" "-I$(QTDIR)\..\qttools\include" "-I$(QTDIR)\..\qttools\include\QtHelp" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtXml" "-I.\GeneratedFiles" "-I$(ProjectDir)..\..\..\Source\Ember" "-I$(ProjectDir)..\..\..\Source\EmberCL" "-I$(ProjectDir)..\..\..\Source\EmberCommon" "-I$(ProjectDir)..\..\..\..\glm" "-I$(ProjectDir)..\..\..\..\tbb\include" "-I$(ProjectDir)..\..\..\..\libjpeg" "-I$(ProjectDir)..\..\..\..\libpng" "-I$(ProjectDir)..\..\..\..\libxml2\include" "-I$(ProjectDir)..\..\..\..\glew\include" "-I$(AMDAPPSDKROOT)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing CurvesGraphicsView.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fFractoriumPch.h" "-f../../../../../Source/Fractorium/CurvesGraphicsView.h" -DUNICODE -DWIN32 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_MULTIMEDIA_LIB -DQT_HELP_LIB -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_XML_LIB -D_MBCS "-I." "-I$(QTDIR)\include" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName" "-I$(QTDIR)\..\qtmultimedia\include\QtMultimedia" "-I$(QTDIR)\..\qtmultimedia\include" "-I$(QTDIR)\..\qttools\include" "-I$(QTDIR)\..\qttools\include\QtHelp" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtXml" "-I.\GeneratedFiles" "-I$(ProjectDir)..\..\..\Source\Ember" "-I$(ProjectDir)..\..\..\Source\EmberCL" "-I$(ProjectDir)..\..\..\Source\EmberCommon" "-I$(ProjectDir)..\..\..\..\glm" "-I$(ProjectDir)..\..\..\..\tbb\include" "-I$(ProjectDir)..\..\..\..\libjpeg" "-I$(ProjectDir)..\..\..\..\libpng" "-I$(ProjectDir)..\..\..\..\libxml2\include" "-I$(ProjectDir)..\..\..\..\glew\include" "-I$(CUDA_PATH)include" "-I.\GeneratedFiles\$(ConfigurationName)\." + $(QTDIR)\bin\moc.exe;%(FullPath) + Moc%27ing CurvesGraphicsView.h... + .\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp + "$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fFractoriumPch.h" "-f../../../../../Source/Fractorium/CurvesGraphicsView.h" -DUNICODE -DWIN32 -DQT_DLL -DQT_NO_DEBUG -DNDEBUG -DQT_CORE_LIB -DQT_GUI_LIB -DQT_MULTIMEDIA_LIB -DQT_HELP_LIB -DQT_OPENGL_LIB -DQT_WIDGETS_LIB -DQT_XML_LIB -D_MBCS "-I." "-I$(QTDIR)\include" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles" "-I$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName" "-I$(QTDIR)\..\qtmultimedia\include\QtMultimedia" "-I$(QTDIR)\..\qtmultimedia\include" "-I$(QTDIR)\..\qttools\include" "-I$(QTDIR)\..\qttools\include\QtHelp" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtCore" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtOpenGL" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtXml" "-I.\GeneratedFiles" "-I$(ProjectDir)..\..\..\Source\Ember" "-I$(ProjectDir)..\..\..\Source\EmberCL" "-I$(ProjectDir)..\..\..\Source\EmberCommon" "-I$(ProjectDir)..\..\..\..\glm" "-I$(ProjectDir)..\..\..\..\tbb\include" "-I$(ProjectDir)..\..\..\..\libjpeg" "-I$(ProjectDir)..\..\..\..\libpng" "-I$(ProjectDir)..\..\..\..\libxml2\include" "-I$(ProjectDir)..\..\..\..\glew\include" "-I$(AMDAPPSDKROOT)\include" "-I$(CUDA_PATH)include" "-I.\GeneratedFiles\$(ConfigurationName)\." + diff --git a/Builds/MSVC/VS2013/Fractorium.vcxproj.filters b/Builds/MSVC/VS2013/Fractorium.vcxproj.filters index ac10fe2..c3986fe 100644 --- a/Builds/MSVC/VS2013/Fractorium.vcxproj.filters +++ b/Builds/MSVC/VS2013/Fractorium.vcxproj.filters @@ -232,6 +232,18 @@ Generated Files\ReleaseNvidia + + Widgets + + + Generated Files\Debug + + + Generated Files\Release + + + Generated Files\ReleaseNvidia + @@ -329,6 +341,9 @@ Widgets + + Widgets + diff --git a/Data/Version History.txt b/Data/Version History.txt index f0bc409..08ce724 100644 --- a/Data/Version History.txt +++ b/Data/Version History.txt @@ -1,4 +1,4 @@ -0.4.1.8 Beta +0.4.1.9 Beta --User changes Thread image writing in EmberAnimate and when doing animation sequence in final render dialog. Add total time output for verbose mode in EmberAnimate to match EmberRender. diff --git a/Source/Ember/Curves.h b/Source/Ember/Curves.h new file mode 100644 index 0000000..1838dae --- /dev/null +++ b/Source/Ember/Curves.h @@ -0,0 +1,284 @@ +#pragma once + +#include "Utils.h" +#include "Isaac.h" + +/// +/// Curves class. +/// + +namespace EmberNs +{ +/// +/// The Bezier curves used to adjust the colors during final accumulation. +/// This functionality was gotten directly from Apophysis. +/// +template +class EMBER_API Curves +{ +public: + /// + /// Constructor which sets the curve and weight values to their defaults. + /// + Curves(bool init = false) + { + if (init) + Init(); + else + Clear(); + } + + /// + /// Default copy constructor. + /// + /// The Curves object to copy + Curves(const Curves& curves) + { + Curves::operator=(curves); + } + + /// + /// Copy constructor to copy a Curves object of type U. + /// Special case that must be here in the header because it has + /// a second template parameter. + /// + /// The Curves object to copy + template + Curves(const Curves& curves) + { + Curves::operator=(curves); + } + + /// + /// Default assignment operator. + /// + /// The Curves object to copy + Curves& operator = (const Curves& curves) + { + if (this != &curves) + Curves::operator=(curves); + + return *this; + } + + /// + /// Assignment operator to assign a Curves object of type U. + /// + /// The Curves object to copy + /// Reference to updated self + template + Curves& operator = (const Curves& curves) + { + for (uint i = 0; i < 4; i++) + { + m_Points[i][0].x = T(curves.m_Points[i][0].x); m_Points[i][0].y = T(curves.m_Points[i][0].y); m_Weights[i].x = T(curves.m_Weights[i].x); + m_Points[i][1].x = T(curves.m_Points[i][1].x); m_Points[i][1].y = T(curves.m_Points[i][1].y); m_Weights[i].y = T(curves.m_Weights[i].y); + m_Points[i][2].x = T(curves.m_Points[i][2].x); m_Points[i][2].y = T(curves.m_Points[i][2].y); m_Weights[i].z = T(curves.m_Weights[i].z); + m_Points[i][3].x = T(curves.m_Points[i][3].x); m_Points[i][3].y = T(curves.m_Points[i][3].y); m_Weights[i].w = T(curves.m_Weights[i].w); + } + + return *this; + } + + /// + /// Unary addition operator to add a Curves object to this one. + /// + /// The Curves object to add + /// Reference to updated self + Curves& operator += (const Curves& curves) + { + for (uint i = 0; i < 4; i++) + { + m_Points[i][0] += curves.m_Points[i][0]; + m_Points[i][1] += curves.m_Points[i][1]; + m_Points[i][2] += curves.m_Points[i][2]; + m_Points[i][3] += curves.m_Points[i][3]; + + m_Weights[i] += curves.m_Weights[i]; + } + + return *this; + } + + /// + /// Unary multiplication operator to multiply this object by another Curves object. + /// + /// The Curves object to multiply this one by + /// Reference to updated self + Curves& operator *= (const Curves& curves) + { + for (uint i = 0; i < 4; i++) + { + m_Points[i][0] *= curves.m_Points[i][0]; + m_Points[i][1] *= curves.m_Points[i][1]; + m_Points[i][2] *= curves.m_Points[i][2]; + m_Points[i][3] *= curves.m_Points[i][3]; + + m_Weights[i] *= curves.m_Weights[i]; + } + + return *this; + } + + /// + /// Unary multiplication operator to multiply this object by a scalar of type T. + /// + /// The scalar to multiply this object by + /// Reference to updated self + Curves& operator *= (const T& t) + { + for (uint i = 0; i < 4; i++) + { + m_Points[i][0] *= t; + m_Points[i][1] *= t; + m_Points[i][2] *= t; + m_Points[i][3] *= t; + + m_Weights[i] *= t; + } + + return *this; + } + + /// + /// Set the curve and weight values to their default state. + /// + void Init() + { + for (uint i = 0; i < 4; i++) + { + m_Points[i][0] = v2T(0);//0,0 -> 0,0 -> 1,1 -> 1,1. + m_Points[i][1] = v2T(0); + m_Points[i][2] = v2T(1); + m_Points[i][3] = v2T(1); + + m_Weights[i] = v4T(1); + } + } + + /// + /// Set the curve and weight values to an empty state. + /// + void Clear() + { + memset(&m_Points, 0, sizeof(m_Points)); + memset(&m_Weights, 0, sizeof(m_Weights)); + } + + /// + /// Whether any points are not the default. + /// + /// True if any point has been set to a value other than the default, else false. + bool CurvesSet() + { + bool set = false; + + for (uint i = 0; i < 4; i++) + { + if ((m_Points[i][0] != v2T(0)) || + (m_Points[i][1] != v2T(0)) || + (m_Points[i][2] != v2T(1)) || + (m_Points[i][3] != v2T(1))) + { + set = true; + break; + } + } + + return set; + } + + /// + /// Wrapper around calling BezierSolve() on each of the 4 weight and point vectors. + /// + /// The position to apply + /// vec4 that contains the y component of the solution for each vector in each component + v4T BezierFunc(const T& t) + { + v4T result; + v2T solution(0, 0); + + BezierSolve(t, m_Points[0], &m_Weights[0], solution); result.x = solution.y; + BezierSolve(t, m_Points[1], &m_Weights[1], solution); result.y = solution.y; + BezierSolve(t, m_Points[2], &m_Weights[2], solution); result.z = solution.y; + BezierSolve(t, m_Points[3], &m_Weights[3], solution); result.w = solution.y; + + return result; + } + +private: + /// + /// Solve the given point and weight vectors for the given position and store + /// the output in the solution vec2 passed in. + /// + /// The position to apply + /// A pointer to an array of 4 vec2 + /// A pointer to an array of 4 weights + /// The vec2 to store the solution in + void BezierSolve(const T& t, v2T* src, v4T* w, v2T& solution) + { + T s, s2, s3, t2, t3, nom_x, nom_y, denom; + + s = 1 - t; + s2 = s * s; + s3 = s * s * s; + t2 = t * t; + t3 = t * t * t; + + nom_x = (w->x * s3 * src->x) + (w->y * s2 * 3 * t * src[1].x) + (w->z * s * 3 * t2 * src[2].x) + (w->w * t3 * src[3].x); + + nom_y = (w->x * s3 * src->y) + (w->y * s2 * 3 * t * src[1].y) + (w->z * s * 3 * t2 * src[2].y) + (w->w * t3 * src[3].y); + + denom = (w->x * s3) + (w->y * s2 * 3 * t) + (w->z * s * 3 * t2) + (w->w * t3); + + + if (isnan(nom_x) || isnan(nom_y) || isnan(denom) || denom == 0) + return; + + solution.x = nom_x / denom; + solution.y = nom_y / denom; + } + +public: + v2T m_Points[4][4]; + v4T m_Weights[4]; +}; + +//Must declare this outside of the class to provide for both orders of parameters. + +/// +/// Multiplication operator to multiply a Curves object by a scalar of type T. +/// +/// The curves object to multiply +/// The scalar to multiply curves by by +/// Copy of new Curves +template +Curves operator * (const Curves& curves, const T& t) +{ + Curves c(curves); + + for (uint i = 0; i < 4; i++) + { + c.m_Points[i][0] *= t; + c.m_Points[i][1] *= t; + c.m_Points[i][2] *= t; + c.m_Points[i][3] *= t; + + c.m_Weights[i] *= t; + } + + return c; +} + +/// +/// Multiplication operator for reverse order. +/// +/// The scalar to multiply curves by by +/// The curves object to multiply +/// Copy of new Curves +template +Curves operator * (const T& t, const Curves& curves) +{ + return curves * t; +} +} \ No newline at end of file diff --git a/Source/Ember/Ember.cpp b/Source/Ember/Ember.cpp index 9748997..f0aa11a 100644 --- a/Source/Ember/Ember.cpp +++ b/Source/Ember/Ember.cpp @@ -1,6 +1,7 @@ #include "EmberPch.h" #include "EmberDefines.h" #include "Isaac.h" +#include "Curves.h" #include "Ember.h" #include "Utils.h" #include "Iterator.h" @@ -390,6 +391,7 @@ template<> unique_ptr> QTIsaac; \ /*template EMBER_API class RenderCallback;*/ \ template EMBER_API class CarToRas; \ + template EMBER_API class Curves; \ template EMBER_API class XmlToEmber; \ template EMBER_API class EmberToXml; diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index 6de5d4f..95b211e 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -1,5 +1,6 @@ #pragma once +#include "Curves.h" #include "Xform.h" #include "PaletteList.h" #include "SpatialFilter.h" @@ -150,6 +151,7 @@ public: m_Index = ember.m_Index; m_ScaleType = ember.ScaleType(); m_Palette = ember.m_Palette; + m_Curves = ember.m_Curves; m_Xforms.clear(); @@ -243,6 +245,9 @@ public: m_PaletteMode = PALETTE_STEP; m_PaletteInterp = INTERP_HSV; + //Curves. + m_Curves.Init(); + m_Name = "No name"; m_ParentFilename = "No parent"; @@ -795,6 +800,7 @@ public: InterpT<&Ember::m_MinRadDE>(embers, coefs, size); InterpT<&Ember::m_CurveDE>(embers, coefs, size); InterpT<&Ember::m_SpatialFilterRadius>(embers, coefs, size); + InterpX, &Ember::m_Curves>(embers, coefs, size); //An extra step needed here due to the OOD that was not needed in the original. //A small price to pay for the conveniences it affords us elsewhere. @@ -1362,6 +1368,7 @@ public: m_Xforms.clear(); m_FinalXform.Clear(); + m_Curves.Init(); ClearEdit(); } @@ -1651,6 +1658,9 @@ public: //Xml field: "color" or "colors" or "palette" . Palette m_Palette; + //Curves used to adjust the color during final accumulation. + Curves m_Curves; + //Strings. //The name of this ember. @@ -1674,6 +1684,15 @@ private: /// eScaleType m_ScaleType; + //The vector containing all of the xforms. + //Xml field: "xform". + vector> m_Xforms; + + //Optional final xform. Default is empty. + //Discussed in section 3.2 of the paper, page 6. + //Xml field: "finalxform". + Xform m_FinalXform; + /// /// Interpolation function that takes the address of a member variable of type T as a template parameter. /// This is an alternative to using macros. @@ -1739,15 +1758,6 @@ private: for (size_t k = 0; k < size; k++) xform->*m += coefs[k] * embers[k].GetTotalXform(i)->*m; } - - //The vector containing all of the xforms. - //Xml field: "xform". - vector> m_Xforms; - - //Optional final xform. Default is empty. - //Discussed in section 3.2 of the paper, page 6. - //Xml field: "finalxform". - Xform m_FinalXform; }; /// diff --git a/Source/Ember/EmberDefines.h b/Source/Ember/EmberDefines.h index 7bb35f6..d18279e 100644 --- a/Source/Ember/EmberDefines.h +++ b/Source/Ember/EmberDefines.h @@ -42,7 +42,7 @@ namespace EmberNs { -#define EMBER_VERSION "0.4.1.8" +#define EMBER_VERSION "0.4.1.9" #define EPS6 T(1e-6) #define EPS std::numeric_limits::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way. #define ISAAC_SIZE 4 @@ -83,13 +83,23 @@ typedef std::chrono::high_resolution_clock Clock; #define DO_DOUBLE 1//Comment this out for shorter build times during development. Always uncomment for release. //#define ISAAC_FLAM3_DEBUG 1//This is almost never needed, but is very useful when troubleshooting difficult bugs. Enable it to do a side by side comparison with flam3. -#define v2T glm::detail::tvec2 -#define v3T glm::detail::tvec3 -#define v4T glm::detail::tvec4 -#define m2T glm::detail::tmat2x2 -#define m3T glm::detail::tmat3x3 -#define m4T glm::detail::tmat4x4 -#define m23T glm::detail::tmat2x3 +#if GLM_VERSION >= 96 + #define v2T glm::tvec2 + #define v3T glm::tvec3 + #define v4T glm::tvec4 + #define m2T glm::tmat2x2 + #define m3T glm::tmat3x3 + #define m4T glm::tmat4x4 + #define m23T glm::tmat2x3 +#else + #define v2T glm::detail::tvec2 + #define v3T glm::detail::tvec3 + #define v4T glm::detail::tvec4 + #define m2T glm::detail::tmat2x2 + #define m3T glm::detail::tmat3x3 + #define m4T glm::detail::tmat4x4 + #define m23T glm::detail::tmat2x3 +#endif enum eInterp : uint { EMBER_INTERP_LINEAR = 0, EMBER_INTERP_SMOOTH = 1 }; enum eAffineInterp : uint { INTERP_LINEAR = 0, INTERP_LOG = 1, INTERP_COMPAT = 2, INTERP_OLDER = 3 }; diff --git a/Source/Ember/EmberPch.h b/Source/Ember/EmberPch.h index c05af4b..3005fba 100644 --- a/Source/Ember/EmberPch.h +++ b/Source/Ember/EmberPch.h @@ -78,5 +78,7 @@ using namespace tbb; using namespace std; using namespace std::chrono; +using namespace glm; +using namespace glm::detail; using glm::uint; using glm::uint16; diff --git a/Source/Ember/EmberToXml.h b/Source/Ember/EmberToXml.h index 7e0b24a..e765e51 100644 --- a/Source/Ember/EmberToXml.h +++ b/Source/Ember/EmberToXml.h @@ -145,7 +145,7 @@ public: os << " zoom=\"" << ember.m_Zoom << "\""; os << " rotate=\"" << ember.m_Rotate << "\""; - os << " supersample=\"" << max(1, ember.m_Supersample) << "\""; + os << " supersample=\"" << std::max(1, ember.m_Supersample) << "\""; os << " filter=\"" << ember.m_SpatialFilterRadius << "\""; os << " filter_shape=\"" << ToLower(SpatialFilterCreator::ToString(ember.m_SpatialFilterType)) << "\""; @@ -207,7 +207,19 @@ public: os << "\""; os << " new_linear=\"1\""; - os << ">\n"; + os << " curves=\""; + + for (glm::length_t ci = 0; ci < 4; ci++) + { + for (glm::length_t cj = 0; cj < 4; cj++) + { + os << ember.m_Curves.m_Points[ci][cj].x << " "; + os << ember.m_Curves.m_Points[ci][cj].y << " "; + os << ember.m_Curves.m_Weights[ci][cj] << " "; + } + } + + os << "\">\n"; //This is a grey area, what to do about symmetry to avoid duplicating the symmetry xforms when reading back?//TODO//BUG. //if (ember.m_Symmetry) diff --git a/Source/Ember/Palette.h b/Source/Ember/Palette.h index 3a87213..87915f6 100644 --- a/Source/Ember/Palette.h +++ b/Source/Ember/Palette.h @@ -533,7 +533,7 @@ public: } //Identify the most saturated channel. - maxc = max(max(cBuf[0], cBuf[1]), cBuf[2]); + maxc = std::max(std::max(cBuf[0], cBuf[1]), cBuf[2]); maxa = ls * maxc; //If a channel is saturated and highlight power is non-negative diff --git a/Source/Ember/Renderer.cpp b/Source/Ember/Renderer.cpp index 0fcf7e9..f063260 100644 --- a/Source/Ember/Renderer.cpp +++ b/Source/Ember/Renderer.cpp @@ -28,41 +28,6 @@ Renderer::~Renderer() /// Non-virtual processing functions. /// -/// -/// Compute the camera. -/// This sets up the bounds of the cartesian plane that the raster bounds correspond to. -/// This must be called after ComputeBounds() which sets up the raster bounds. -/// -template -void Renderer::ComputeCamera() -{ - m_Scale = pow(T(2.0), Zoom()); - m_ScaledQuality = Quality() * m_Scale * m_Scale; - - m_PixelsPerUnitX = PixelsPerUnit() * m_Scale; - m_PixelsPerUnitY = m_PixelsPerUnitX; - m_PixelsPerUnitX /= PixelAspectRatio(); - - T shift = 0; - T t0 = T(m_GutterWidth) / (Supersample() * m_PixelsPerUnitX); - T t1 = T(m_GutterWidth) / (Supersample() * m_PixelsPerUnitY); - - //These go from ll to ur, moving from negative to positive. - m_LowerLeftX = CenterX() - FinalRasW() / m_PixelsPerUnitX / T(2.0); - m_LowerLeftY = CenterY() - FinalRasH() / m_PixelsPerUnitY / T(2.0); - m_UpperRightX = m_LowerLeftX + FinalRasW() / m_PixelsPerUnitX; - m_UpperRightY = m_LowerLeftY + FinalRasH() / m_PixelsPerUnitY; - - T carLlX = m_LowerLeftX - t0; - T carLlY = m_LowerLeftY - t1 + shift; - T carUrX = m_UpperRightX + t0; - T carUrY = m_UpperRightY + t1 + shift; - - m_RotMat.MakeID(); - m_RotMat.Rotate(-Rotate()); - m_CarToRas.Init(carLlX, carLlY, carUrX, carUrY, m_SuperRasW, m_SuperRasH, PixelAspectRatio()); -} - /// /// Add an ember to the end of the embers vector and reset the rendering process. /// Reset the rendering process. @@ -124,7 +89,7 @@ void Renderer::ComputeBounds() //If the radius of the density estimation filter is greater than the //gutter width, have to pad with more. Otherwise, use the same value. for (size_t i = 0; i < m_Embers.size(); i++) - maxDEFilterWidth = max(size_t(ceil(m_Embers[i].m_MaxRadDE) * m_Ember.m_Supersample), maxDEFilterWidth); + maxDEFilterWidth = std::max(size_t(ceil(m_Embers[i].m_MaxRadDE) * m_Ember.m_Supersample), maxDEFilterWidth); //Need an extra ss = (int)floor(m_Supersample / 2.0) of pixels so that a local iteration count for DE can be determined.//SMOULDER if (maxDEFilterWidth > 0) @@ -140,6 +105,50 @@ void Renderer::ComputeBounds() m_SuperSize = m_SuperRasW * m_SuperRasH; } +/// +/// Compute the scale based on the zoom, then the quality based on the computed scale. +/// This sets up the bounds of the cartesian plane that the raster bounds correspond to. +/// This must be called before ComputeCamera() which will use scale. +/// +template +void Renderer::ComputeQuality() +{ + m_Scale = pow(T(2.0), Zoom()); + m_ScaledQuality = Quality() * m_Scale * m_Scale; +} + +/// +/// Compute the camera. +/// This sets up the bounds of the cartesian plane that the raster bounds correspond to. +/// This must be called after ComputeBounds() which sets up the raster bounds. +/// +template +void Renderer::ComputeCamera() +{ + m_PixelsPerUnitX = PixelsPerUnit() * m_Scale; + m_PixelsPerUnitY = m_PixelsPerUnitX; + m_PixelsPerUnitX /= PixelAspectRatio(); + + T shift = 0; + T t0 = T(m_GutterWidth) / (Supersample() * m_PixelsPerUnitX); + T t1 = T(m_GutterWidth) / (Supersample() * m_PixelsPerUnitY); + + //These go from ll to ur, moving from negative to positive. + m_LowerLeftX = CenterX() - FinalRasW() / m_PixelsPerUnitX / T(2.0); + m_LowerLeftY = CenterY() - FinalRasH() / m_PixelsPerUnitY / T(2.0); + m_UpperRightX = m_LowerLeftX + FinalRasW() / m_PixelsPerUnitX; + m_UpperRightY = m_LowerLeftY + FinalRasH() / m_PixelsPerUnitY; + + T carLlX = m_LowerLeftX - t0; + T carLlY = m_LowerLeftY - t1 + shift; + T carUrX = m_UpperRightX + t0; + T carUrY = m_UpperRightY + t1 + shift; + + m_RotMat.MakeID(); + m_RotMat.Rotate(-Rotate()); + m_CarToRas.Init(carLlX, carLlY, carUrX, carUrY, m_SuperRasW, m_SuperRasH, PixelAspectRatio()); +} + /// /// Set the current ember. /// This will also populate the vector of embers with a single element copy @@ -327,7 +336,7 @@ eRenderStatus Renderer::Run(vector& finalImage, double time, s bool accumOnly = m_ProcessAction == ACCUM_ONLY; bool resume = m_ProcessState != NONE; bool newFilterAlloc; - size_t temporalSample = 0; + size_t i, temporalSample = 0; T deTime; eRenderStatus success = RENDER_OK; //double iterationTime = 0; @@ -353,6 +362,7 @@ eRenderStatus Renderer::Run(vector& finalImage, double time, s m_Gamma = 0; m_Vibrancy = 0;//Accumulate these after each temporal sample. m_VibGamCount = 0; + m_CurvesSet = false; m_Background.Clear(); } //User requested an increase in quality after finishing. @@ -365,6 +375,7 @@ eRenderStatus Renderer::Run(vector& finalImage, double time, s m_Vibrancy = 0; m_VibGamCount = 0; m_Background.Clear(); + ComputeQuality();//Must recompute quality when doing a quality increase. } //Make sure values are within valid range. @@ -427,8 +438,9 @@ eRenderStatus Renderer::Run(vector& finalImage, double time, s Interpolater::Interpolate(m_Embers, deTime, 0, m_Ember); //it.Toc("Interp 2"); - ClampGte(m_Ember.m_MinRadDE, 0); - ClampGte(m_Ember.m_MaxRadDE, 0); + ClampGteRef(m_Ember.m_MinRadDE, 0); + ClampGteRef(m_Ember.m_MaxRadDE, 0); + ClampGteRef(m_Ember.m_MaxRadDE, m_Ember.m_MinRadDE); if (!CreateDEFilter(newFilterAlloc)) { @@ -446,7 +458,7 @@ eRenderStatus Renderer::Run(vector& finalImage, double time, s //Interpolate again. //it.Tic(); - if (m_Embers.size() > 1) + if (TemporalSamples() > 1 && m_Embers.size() > 1) Interpolater::Interpolate(m_Embers, temporalTime, 0, m_Ember);//This will perform all necessary precalcs via the ember/xform/variation assignment operators. //it.Toc("Interp 3"); @@ -458,13 +470,16 @@ eRenderStatus Renderer::Run(vector& finalImage, double time, s goto Finish; } - ComputeCamera(); - - //For each temporal sample, the palette m_Dmap needs to be re-created with color scalar. 1 if no temporal samples. - MakeDmap(colorScalar); + //Don't need to do this every time through for a single image. + if (TemporalSamples() > 1 || !resume) + { + ComputeQuality(); + ComputeCamera(); + MakeDmap(colorScalar);//For each temporal sample, the palette m_Dmap needs to be re-created with color scalar. 1 if no temporal samples. + } //The actual number of times to iterate. Each thread will get (totalIters / ThreadCount) iters to do. - //This is based on zoom and scale calculated in ComputeCamera(). + //This is based on zoom and scale calculated in ComputeQuality(). //Note that the iter count is based on the final image dimensions, and not the super sampled dimensions. size_t itersPerTemporalSample = ItersPerTemporalSample();//The total number of iterations for this temporal sample without overrides. size_t sampleItersToDo;//The number of iterations to actually do in this sample, considering overrides. @@ -474,7 +489,7 @@ eRenderStatus Renderer::Run(vector& finalImage, double time, s else sampleItersToDo = itersPerTemporalSample;//Run as many iters as specified to complete this temporal sample. - sampleItersToDo = min(sampleItersToDo, itersPerTemporalSample - m_LastIter); + sampleItersToDo = std::min(sampleItersToDo, itersPerTemporalSample - m_LastIter); EmberStats stats = Iterate(sampleItersToDo, temporalSample);//The heavy work is done here. //If no iters were executed, something went catastrophically wrong. @@ -602,6 +617,13 @@ AccumOnly: //Make sure a filter has been created. CreateSpatialFilter(newFilterAlloc); + m_CurvesSet = m_Ember.m_Curves.CurvesSet(); + + //Color curves must be re-calculated as well. + if (m_CurvesSet) + for (i = 0; i < COLORMAP_LENGTH; i++) + m_Csa[i] = m_Ember.m_Curves.BezierFunc(i / T(COLORMAP_LENGTH_MINUS_1)) * T(COLORMAP_LENGTH_MINUS_1); + if (AccumulatorToFinalImage(finalImage, finalOffset) == RENDER_OK) { m_Stats.m_RenderMs = m_RenderTimer.Toc();//Record total time from the very beginning to the very end, including all intermediate calls. @@ -839,17 +861,17 @@ eRenderStatus Renderer::GaussianDensityFilter() parallel_for(size_t(0), threads, [&] (size_t threadIndex) { size_t pixelNumber = 0; - int localStartRow = int(min(startRow + (threadIndex * chunkSize), endRow - 1)); - int localEndRow = int(min(localStartRow + chunkSize, endRow)); + int localStartRow = int(std::min(startRow + (threadIndex * chunkSize), endRow - 1)); + int localEndRow = int(std::min(localStartRow + chunkSize, endRow)); size_t pixelsThisThread = size_t(localEndRow - localStartRow) * m_SuperRasW; double lastPercent = 0; - glm::detail::tvec4 logScaleBucket; + tvec4 logScaleBucket; for (intmax_t j = localStartRow; (j < localEndRow) && !m_Abort; j++) { size_t bucketRowStart = j * m_SuperRasW;//Pull out of inner loop for optimization. - const glm::detail::tvec4* bucket; - const glm::detail::tvec4* buckets = m_HistBuckets.data(); + const tvec4* bucket; + const tvec4* buckets = m_HistBuckets.data(); const T* filterCoefs = m_DensityFilter->Coefs(); const T* filterWidths = m_DensityFilter->Widths(); @@ -875,10 +897,10 @@ eRenderStatus Renderer::GaussianDensityFilter() //The original contained a glaring flaw as it would run past the boundaries of the buffers //when calculating the density for a box centered on the last row or column. //Clamp here to not run over the edge. - intmax_t densityBoxLeftX = (i - min(i, ss)); - intmax_t densityBoxRightX = (i + min(ss, intmax_t(m_SuperRasW) - i - 1)); - intmax_t densityBoxTopY = (j - min(j, ss)); - intmax_t densityBoxBottomY = (j + min(ss, intmax_t(m_SuperRasH) - j - 1)); + intmax_t densityBoxLeftX = (i - std::min(i, ss)); + intmax_t densityBoxRightX = (i + std::min(ss, intmax_t(m_SuperRasW) - i - 1)); + intmax_t densityBoxTopY = (j - std::min(j, ss)); + intmax_t densityBoxBottomY = (j + std::min(ss, intmax_t(m_SuperRasH) - j - 1)); //Count density in ssxss area. //Original went one col at a time, which is cache inefficient. Go one row at at time here for a slight speedup. @@ -1048,7 +1070,7 @@ eRenderStatus Renderer::AccumulatorToFinalImage(byte* pixels, size_t Color newBucket; size_t pixelsRowStart = (m_YAxisUp ? ((FinalRasH() - j) - 1) : j) * FinalRowSize();//Pull out of inner loop for optimization. size_t y = m_DensityFilterOffset + (j * Supersample());//Start at the beginning row of each super sample block. - uint16* p16; + glm::uint16* p16; for (size_t i = 0; i < FinalRasW(); i++, pixelsRowStart += PixelSize()) { @@ -1074,13 +1096,20 @@ eRenderStatus Renderer::AccumulatorToFinalImage(byte* pixels, size_t if (BytesPerChannel() == 2) { - p16 = reinterpret_cast(pixels + pixelsRowStart); + p16 = reinterpret_cast(pixels + pixelsRowStart); if (EarlyClip()) { - p16[0] = uint16(Clamp(newBucket.r, 0, 255) * bucketT(256)); - p16[1] = uint16(Clamp(newBucket.g, 0, 255) * bucketT(256)); - p16[2] = uint16(Clamp(newBucket.b, 0, 255) * bucketT(256)); + if (m_CurvesSet) + { + CurveAdjust(newBucket.r, 1); + CurveAdjust(newBucket.g, 2); + CurveAdjust(newBucket.b, 3); + } + + p16[0] = glm::uint16(Clamp(newBucket.r, 0, 255) * bucketT(256)); + p16[1] = glm::uint16(Clamp(newBucket.g, 0, 255) * bucketT(256)); + p16[2] = glm::uint16(Clamp(newBucket.b, 0, 255) * bucketT(256)); if (NumChannels() > 3) { @@ -1092,13 +1121,20 @@ eRenderStatus Renderer::AccumulatorToFinalImage(byte* pixels, size_t } else { - GammaCorrection(*(reinterpret_cast*>(&newBucket)), background, g, linRange, vibrancy, NumChannels() > 3, true, p16); + GammaCorrection(*(reinterpret_cast*>(&newBucket)), background, g, linRange, vibrancy, NumChannels() > 3, true, p16); } } else { if (EarlyClip()) { + if (m_CurvesSet) + { + CurveAdjust(newBucket.r, 1); + CurveAdjust(newBucket.g, 2); + CurveAdjust(newBucket.b, 3); + } + pixels[pixelsRowStart] = byte(Clamp(newBucket.r, 0, 255)); pixels[pixelsRowStart + 1] = byte(Clamp(newBucket.g, 0, 255)); pixels[pixelsRowStart + 2] = byte(Clamp(newBucket.b, 0, 255)); @@ -1113,7 +1149,7 @@ eRenderStatus Renderer::AccumulatorToFinalImage(byte* pixels, size_t } else { - GammaCorrection(*(reinterpret_cast*>(&newBucket)), background, g, linRange, vibrancy, NumChannels() > 3, true, pixels + pixelsRowStart); + GammaCorrection(*(reinterpret_cast*>(&newBucket)), background, g, linRange, vibrancy, NumChannels() > 3, true, pixels + pixelsRowStart); } } } @@ -1183,7 +1219,7 @@ EmberStats Renderer::Iterate(size_t iterCount, size_t temporalSample IterParams params; m_BadVals[threadIndex] = 0; - params.m_Count = min(totalItersPerThread, SubBatchSize()); + params.m_Count = std::min(totalItersPerThread, SubBatchSize()); params.m_Skip = FuseCount(); //params.m_OneColDiv2 = m_CarToRas.OneCol() / 2; //params.m_OneRowDiv2 = m_CarToRas.OneRow() / 2; @@ -1193,7 +1229,7 @@ EmberStats Renderer::Iterate(size_t iterCount, size_t temporalSample { //Must recalculate the number of iters to run on each sub batch because the last batch will most likely have less than SubBatchSize iters. //For example, if 51,000 are requested, and the sbs is 10,000, it should run 5 sub batches of 10,000 iters, and one final sub batch of 1,000 iters. - params.m_Count = min(params.m_Count, totalItersPerThread - m_SubBatch[threadIndex]); + params.m_Count = std::min(params.m_Count, totalItersPerThread - m_SubBatch[threadIndex]); //Use first as random point, the rest are iterated points. //Note that this gets reset with a new random point for each subBatchSize iterations. @@ -1290,16 +1326,16 @@ void Renderer::PixelAspectRatio(T pixelAspectRatio) /// Non-virtual renderer properties, getters only. /// -template T Renderer::Scale() const { return m_Scale; } -template T Renderer::PixelsPerUnitX() const { return m_PixelsPerUnitX; } -template T Renderer::PixelsPerUnitY() const { return m_PixelsPerUnitY; } -template T Renderer::K1() const { return m_K1; } -template T Renderer::K2() const { return m_K2; } -template const CarToRas* Renderer::CoordMap() const { return &m_CarToRas; } -template glm::detail::tvec4* Renderer::HistBuckets() { return m_HistBuckets.data(); } -template glm::detail::tvec4* Renderer::AccumulatorBuckets() { return m_AccumulatorBuckets.data(); } -template SpatialFilter* Renderer::GetSpatialFilter() { return m_SpatialFilter.get(); } -template TemporalFilter* Renderer::GetTemporalFilter() { return m_TemporalFilter.get(); } +template T Renderer::Scale() const { return m_Scale; } +template T Renderer::PixelsPerUnitX() const { return m_PixelsPerUnitX; } +template T Renderer::PixelsPerUnitY() const { return m_PixelsPerUnitY; } +template T Renderer::K1() const { return m_K1; } +template T Renderer::K2() const { return m_K2; } +template const CarToRas* Renderer::CoordMap() const { return &m_CarToRas; } +template tvec4* Renderer::HistBuckets() { return m_HistBuckets.data(); } +template tvec4* Renderer::AccumulatorBuckets() { return m_AccumulatorBuckets.data(); } +template SpatialFilter* Renderer::GetSpatialFilter() { return m_SpatialFilter.get(); } +template TemporalFilter* Renderer::GetTemporalFilter() { return m_TemporalFilter.get(); } /// /// Virtual renderer properties overridden from RendererBase, getters only. @@ -1405,7 +1441,7 @@ void Renderer::Accumulate(QTIsaac& rand, Poin { size_t histIndex, intColorIndex, histSize = m_HistBuckets.size(); bucketT colorIndex, colorIndexFrac; - const glm::detail::tvec4* dmap = &(palette->m_Entries[0]); + const tvec4* dmap = &(palette->m_Entries[0]); //T oneColDiv2 = m_CarToRas.OneCol() / 2; //T oneRowDiv2 = m_CarToRas.OneRow() / 2; @@ -1512,7 +1548,7 @@ void Renderer::Accumulate(QTIsaac& rand, Poin /// The row of the bucket /// The offset to add to the row template -void Renderer::AddToAccum(const glm::detail::tvec4& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj) +void Renderer::AddToAccum(const tvec4& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj) { if (j + jj >= 0 && j + jj < intmax_t(m_SuperRasH) && i + ii >= 0 && i + ii < intmax_t(m_SuperRasW)) m_AccumulatorBuckets[(i + ii) + ((j + jj) * m_SuperRasW)] += bucket; @@ -1536,7 +1572,7 @@ void Renderer::AddToAccum(const glm::detail::tvec4The storage space for the corrected values to be written to template template -void Renderer::GammaCorrection(glm::detail::tvec4& bucket, Color& background, T g, T linRange, T vibrancy, bool doAlpha, bool scale, accumT* correctedChannels) +void Renderer::GammaCorrection(tvec4& bucket, Color& background, T g, T linRange, T vibrancy, bool doAlpha, bool scale, accumT* correctedChannels) { T alpha, ls, a; bucketT newRgb[3];//Would normally use a Color, but don't want to call a needless constructor every time this function is called, which is once per pixel. @@ -1573,9 +1609,16 @@ void Renderer::GammaCorrection(glm::detail::tvec4(a, 0, 255));//Early clip, just assign directly. + } else + { + if (m_CurvesSet) + CurveAdjust(a, rgbi + 1); + correctedChannels[rgbi] = accumT(Clamp(a, 0, 255) * scaleVal);//Final accum, multiply by 1 for 8 bpc, or 256 for 16 bpc. + } } if (doAlpha) @@ -1589,6 +1632,15 @@ void Renderer::GammaCorrection(glm::detail::tvec4 +void Renderer::CurveAdjust(T& a, const glm::length_t& index) +{ + size_t tempIndex = size_t(Clamp(a, 0, COLORMAP_LENGTH_MINUS_1)); + size_t tempIndex2 = size_t(Clamp(m_Csa[tempIndex].x, 0, COLORMAP_LENGTH_MINUS_1)); + + a = std::round(m_Csa[tempIndex2][index]); +} + //This class had to be implemented in a cpp file because the compiler was breaking. //So the explicit instantiation must be declared here rather than in Ember.cpp where //all of the other classes are done. diff --git a/Source/Ember/Renderer.h b/Source/Ember/Renderer.h index 3756715..61c04f0 100644 --- a/Source/Ember/Renderer.h +++ b/Source/Ember/Renderer.h @@ -56,13 +56,14 @@ public: //Virtual processing functions overriden from RendererBase. virtual void ComputeBounds() override; + virtual void ComputeQuality() override; virtual void ComputeCamera() override; virtual void SetEmber(Ember& ember, eProcessAction action = FULL_RENDER) override; virtual void SetEmber(vector>& embers) override; virtual bool CreateDEFilter(bool& newAlloc) override; virtual bool CreateSpatialFilter(bool& newAlloc) override; virtual bool CreateTemporalFilter(bool& newAlloc) override; - virtual size_t HistBucketSize() const override { return sizeof(glm::detail::tvec4); } + virtual size_t HistBucketSize() const override { return sizeof(tvec4); } virtual eRenderStatus Run(vector& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) override; virtual EmberImageComments ImageComments(EmberStats& stats, size_t printEditDepth = 0, bool intPalette = false, bool hexPalette = true) override; @@ -83,16 +84,16 @@ public: void PixelAspectRatio(T pixelAspectRatio); //Non-virtual renderer properties, getters only. - inline T Scale() const; - inline T PixelsPerUnitX() const; - inline T PixelsPerUnitY() const; - inline T K1() const; - inline T K2() const; - inline const CarToRas* CoordMap() const; - inline glm::detail::tvec4* HistBuckets(); - inline glm::detail::tvec4* AccumulatorBuckets(); - inline SpatialFilter* GetSpatialFilter(); - inline TemporalFilter* GetTemporalFilter(); + inline T Scale() const; + inline T PixelsPerUnitX() const; + inline T PixelsPerUnitY() const; + inline T K1() const; + inline T K2() const; + inline const CarToRas* CoordMap() const; + inline tvec4* HistBuckets(); + inline tvec4* AccumulatorBuckets(); + inline SpatialFilter* GetSpatialFilter(); + inline TemporalFilter* GetTemporalFilter(); //Virtual renderer properties overridden from RendererBase, getters only. virtual double ScaledQuality() const override; @@ -150,8 +151,9 @@ protected: private: //Miscellaneous non-virtual functions used only in this class. void Accumulate(QTIsaac& rand, Point* samples, size_t sampleCount, const Palette* palette); - /*inline*/ void AddToAccum(const glm::detail::tvec4& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj); - template void GammaCorrection(glm::detail::tvec4& bucket, Color& background, T g, T linRange, T vibrancy, bool doAlpha, bool scale, accumT* correctedChannels); + /*inline*/ void AddToAccum(const tvec4& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj); + template void GammaCorrection(tvec4& bucket, Color& background, T g, T linRange, T vibrancy, bool doAlpha, bool scale, accumT* correctedChannels); + void CurveAdjust(T& a, const glm::length_t& index); protected: T m_Scale; @@ -177,9 +179,9 @@ protected: Iterator* m_Iterator; unique_ptr> m_StandardIterator; unique_ptr> m_XaosIterator; - Palette m_Dmap; - vector> m_HistBuckets; - vector> m_AccumulatorBuckets; + Palette m_Dmap, m_Csa; + vector> m_HistBuckets; + vector> m_AccumulatorBuckets; unique_ptr> m_SpatialFilter; unique_ptr> m_TemporalFilter; unique_ptr> m_DensityFilter; diff --git a/Source/Ember/RendererBase.cpp b/Source/Ember/RendererBase.cpp index bd2241d..b84ab74 100644 --- a/Source/Ember/RendererBase.cpp +++ b/Source/Ember/RendererBase.cpp @@ -16,6 +16,7 @@ RendererBase::RendererBase() m_YAxisUp = false; m_InsertPalette = false; m_ReclaimOnResize = false; + m_CurvesSet = false; m_NumChannels = 3; m_BytesPerChannel = 1; m_SuperSize = 0; @@ -469,7 +470,7 @@ void RendererBase::ThreadCount(size_t threads, const char* seedString) if (seedString) { memset(seeds, 0, isaacSize * sizeof(ISAAC_INT)); - memcpy(reinterpret_cast(seeds), seedString, min(strlen(seedString), isaacSize * sizeof(ISAAC_INT))); + memcpy(reinterpret_cast(seeds), seedString, std::min(strlen(seedString), isaacSize * sizeof(ISAAC_INT))); } //This is critical for multithreading, otherwise the threads all happen diff --git a/Source/Ember/RendererBase.h b/Source/Ember/RendererBase.h index 66f06b4..b62e555 100644 --- a/Source/Ember/RendererBase.h +++ b/Source/Ember/RendererBase.h @@ -117,6 +117,7 @@ public: virtual bool CreateSpatialFilter(bool& newAlloc) = 0; virtual bool CreateTemporalFilter(bool& newAlloc) = 0; virtual void ComputeBounds() = 0; + virtual void ComputeQuality() = 0; virtual void ComputeCamera() = 0; virtual eRenderStatus Run(vector& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) = 0; virtual EmberImageComments ImageComments(EmberStats& stats, size_t printEditDepth = 0, bool intPalette = false, bool hexPalette = true) = 0; @@ -201,6 +202,7 @@ protected: bool m_InFinalAccum; bool m_InsertPalette; bool m_ReclaimOnResize; + bool m_CurvesSet; volatile bool m_Abort; size_t m_SuperRasW; size_t m_SuperRasH; diff --git a/Source/Ember/SheepTools.h b/Source/Ember/SheepTools.h index 8eb4e43..a9efc9e 100644 --- a/Source/Ember/SheepTools.h +++ b/Source/Ember/SheepTools.h @@ -1288,6 +1288,7 @@ public: m_Renderer->CreateSpatialFilter(newAlloc); m_Renderer->CreateDEFilter(newAlloc); m_Renderer->ComputeBounds(); + m_Renderer->ComputeQuality(); m_Renderer->ComputeCamera(); if (ember.XaosPresent()) diff --git a/Source/Ember/Variation.h b/Source/Ember/Variation.h index ce39fc2..6bba03e 100644 --- a/Source/Ember/Variation.h +++ b/Source/Ember/Variation.h @@ -1506,7 +1506,7 @@ public: { case REAL : { - *m_Param = max(min(val, m_Max), m_Min); + *m_Param = std::max(std::min(val, m_Max), m_Min); break; } @@ -1524,7 +1524,7 @@ public: case REAL_NONZERO : { - T vd = max(min(val, m_Max), m_Min); + T vd = std::max(std::min(val, m_Max), m_Min); if (IsNearZero(vd)) *m_Param = EPS * SignNz(vd); @@ -1536,14 +1536,14 @@ public: case INTEGER : { - *m_Param = T(int(max(min(T(Floor(val + T(0.5))), m_Max), m_Min))); + *m_Param = T(int(std::max(std::min(T(Floor(val + T(0.5))), m_Max), m_Min))); break; } case INTEGER_NONZERO : default: { - int vi = int(max(min(T(Floor(val + T(0.5))), m_Max), m_Min)); + int vi = int(std::max(std::min(T(Floor(val + T(0.5))), m_Max), m_Min)); if (vi == 0) vi = int(SignNz(val)); diff --git a/Source/Ember/Variations01.h b/Source/Ember/Variations01.h index a62baf3..560ab19 100644 --- a/Source/Ember/Variations01.h +++ b/Source/Ember/Variations01.h @@ -3773,8 +3773,8 @@ public: { m_XAmpV = m_Weight * m_XAmp; m_YAmpV = m_Weight * m_YAmp; - m_XLengthV = 1 / max(SQR(m_XLength), T(1e-20)); - m_YLengthV = 1 / max(SQR(m_YLength), T(1e-20)); + m_XLengthV = 1 / std::max(SQR(m_XLength), T(1e-20)); + m_YLengthV = 1 / std::max(SQR(m_YLength), T(1e-20)); } virtual void Random(QTIsaac& rand) override diff --git a/Source/Ember/Variations02.h b/Source/Ember/Variations02.h index 784645e..dffc286 100644 --- a/Source/Ember/Variations02.h +++ b/Source/Ember/Variations02.h @@ -4610,7 +4610,7 @@ public: T y = (helper.In.y * m_S) + m_CenterY; //Calculate distance from center but constrain it to EPS. - T d = max(EPS, sqrt(SQR(x) * SQR(y))); + T d = std::max(EPS, sqrt(SQR(x) * SQR(y))); //Normalize x and y. T nx = x / d; diff --git a/Source/Ember/Variations04.h b/Source/Ember/Variations04.h index 3e1f409..acf4410 100644 --- a/Source/Ember/Variations04.h +++ b/Source/Ember/Variations04.h @@ -4530,7 +4530,7 @@ public: m_Ay = ((fabs(m_AreaY) < 0.1) ? T(0.1) : fabs(m_AreaY)) * agdoa; m_Cx = m_CenterX * agdoc; m_Cy = m_CenterY * agdoc; - m_B = m_Gamma * agdoa / (max(m_Ax, m_Ay)); + m_B = m_Gamma * agdoa / (std::max(m_Ax, m_Ay)); } protected: diff --git a/Source/Ember/Variations05.h b/Source/Ember/Variations05.h index a4b3116..663d8fc 100644 --- a/Source/Ember/Variations05.h +++ b/Source/Ember/Variations05.h @@ -2119,8 +2119,8 @@ public: const T ay = rand.Frand(T(-0.5), T(0.5)); const T az = rand.Frand(T(-0.5), T(0.5)); const T r = sqrt(Sqr(helper.In.x - m_X0) + Sqr(helper.In.y - m_Y0) + Sqr(helper.In.z - m_Z0)); - const T rc = ((m_Invert != 0 ? max(1 - r, 0) : max(r, 0)) - m_MinDist) * m_InternalScatter;//Original called a macro named min, which internally performed max. - const T rs = max(rc, 0); + const T rc = ((m_Invert != 0 ? std::max(1 - r, 0) : std::max(r, 0)) - m_MinDist) * m_InternalScatter;//Original called a macro named min, which internally performed max. + const T rs = std::max(rc, 0); T sigma, phi, rad, sigmas, sigmac, phis, phic; T scale, denom; @@ -2279,8 +2279,8 @@ public: { const v4T random(rand.Frand(T(-0.5), T(0.5)), rand.Frand(T(-0.5), T(0.5)), rand.Frand(T(-0.5), T(0.5)), rand.Frand(T(-0.5), T(0.5))); const T distA = sqrt(Sqr(helper.In.x - m_X0) + Sqr(helper.In.y - m_Y0) + Sqr(helper.In.z - m_Z0)); - const T distB = m_Invert != 0 ? max(1 - distA, 0) : max(distA, 0);//Original called a macro named min, which internally performed max. - const T dist = max((distB - m_MinDist) * m_RMax, 0); + const T distB = m_Invert != 0 ? std::max(1 - distA, 0) : std::max(distA, 0);//Original called a macro named min, which internally performed max. + const T dist = std::max((distB - m_MinDist) * m_RMax, 0); switch (int(m_Type)) { @@ -2484,11 +2484,11 @@ public: break; case 1://Square. default: - radius = max(fabs(helper.In.x - m_CenterX), max(fabs(helper.In.y - m_CenterY), (fabs(helper.In.z - m_CenterZ))));//Original called a macro named min, which internally performed max. + radius = std::max(fabs(helper.In.x - m_CenterX), std::max(fabs(helper.In.y - m_CenterY), (fabs(helper.In.z - m_CenterZ))));//Original called a macro named min, which internally performed max. break; } - const T dist = max(((m_InvertDistance != 0 ? max(1 - radius, 0) : max(radius, 0)) - m_MinDistance) * m_RMax, 0); + const T dist = std::max(((m_InvertDistance != 0 ? std::max(1 - radius, 0) : std::max(radius, 0)) - m_MinDistance) * m_RMax, 0); switch (int(m_BlurType)) { diff --git a/Source/Ember/XmlToEmber.h b/Source/Ember/XmlToEmber.h index 94b479f..88785c8 100644 --- a/Source/Ember/XmlToEmber.h +++ b/Source/Ember/XmlToEmber.h @@ -550,8 +550,8 @@ private: char* attStr; const char* loc = __FUNCTION__; int soloXform = -1; - uint i, count, index = 0; - double vals[10]; + uint i, j, count, index = 0; + double vals[16]; xmlAttrPtr att, curAtt; xmlNodePtr editNode, childNode, motionNode; @@ -704,6 +704,20 @@ private: Atof(attStr, currentEmber.m_Hue); currentEmber.m_Hue = fmod(currentEmber.m_Hue, T(0.5));//Orig did fmod 1, but want it in the range -0.5 - 0.5. } + else if (!Compare(curAtt->name, "curves")) + { + stringstream ss(attStr); + + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + ss >> currentEmber.m_Curves.m_Points[i][j].x; + ss >> currentEmber.m_Curves.m_Points[i][j].y; + ss >> currentEmber.m_Curves.m_Weights[i][j]; + } + } + } xmlFree(attStr); } diff --git a/Source/EmberAnimate/EmberAnimate.rc b/Source/EmberAnimate/EmberAnimate.rc index 12b7e37..4784b2e 100644 --- a/Source/EmberAnimate/EmberAnimate.rc +++ b/Source/EmberAnimate/EmberAnimate.rc @@ -49,8 +49,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,4,1,8 - PRODUCTVERSION 0,4,1,8 + FILEVERSION 0,4,1,9 + PRODUCTVERSION 0,4,1,9 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -67,12 +67,12 @@ BEGIN BEGIN VALUE "CompanyName", "Open Source" VALUE "FileDescription", "Renders fractal flames as animations with motion blur" - VALUE "FileVersion", "0.4.1.8" + VALUE "FileVersion", "0.4.1.9" VALUE "InternalName", "EmberAnimate.rc" VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2013, GPL v3" VALUE "OriginalFilename", "EmberAnimate.rc" VALUE "ProductName", "Ember Animate" - VALUE "ProductVersion", "0.4.1.8" + VALUE "ProductVersion", "0.4.1.9" END END BLOCK "VarFileInfo" diff --git a/Source/EmberCL/EmberCLFunctions.h b/Source/EmberCL/EmberCLFunctions.h index 657f16f..5f0f897 100644 --- a/Source/EmberCL/EmberCLFunctions.h +++ b/Source/EmberCL/EmberCLFunctions.h @@ -140,6 +140,21 @@ static const char* CalcAlphaFunctionString = "}\n" "\n"; + +/// +/// OpenCL equivalent of Renderer::CurveAdjust(). +/// Only use float here instead of real_t because the output will be passed to write_imagef() +/// during final accumulation, which only takes floats. +/// +static const char* CurveAdjustFunctionString = +"static inline void CurveAdjust(__constant real4reals* csa, float* a, uint index)\n" +"{\n" +" uint tempIndex = (uint)Clamp(*a, 0.0, (float)COLORMAP_LENGTH_MINUS_1);\n" +" uint tempIndex2 = (uint)Clamp(csa[tempIndex].m_Real4.x, 0.0, (real_t)COLORMAP_LENGTH_MINUS_1);\n" +"\n" +" *a = (float)round(csa[tempIndex2].m_Reals[index]);\n" +"}\n"; + /// /// Use MWC 64 from David Thomas at the Imperial College of London for /// random numbers in OpenCL, instead of ISAAC which was used diff --git a/Source/EmberCL/FinalAccumOpenCLKernelCreator.cpp b/Source/EmberCL/FinalAccumOpenCLKernelCreator.cpp index a4650fe..19b383f 100644 --- a/Source/EmberCL/FinalAccumOpenCLKernelCreator.cpp +++ b/Source/EmberCL/FinalAccumOpenCLKernelCreator.cpp @@ -197,6 +197,7 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool early RgbToHsvFunctionString << HsvToRgbFunctionString << CalcAlphaFunctionString << + CurveAdjustFunctionString << SpatialFilterCLStructString; if (earlyClip) @@ -231,6 +232,8 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool early " __write_only image2d_t pixels,\n" " __constant SpatialFilterCL* spatialFilter,\n" " __constant real_t* filterCoefs,\n" + " __constant real4reals* csa,\n" + " const uint doCurves,\n" " const real_t alphaBase,\n" " const real_t alphaScale\n" "\t)\n" @@ -245,14 +248,11 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool early " finalCoord.x = GLOBAL_ID_X;\n" " finalCoord.y = (int)((spatialFilter->m_YAxisUp == 1) ? ((spatialFilter->m_FinalRasH - GLOBAL_ID_Y) - 1) : GLOBAL_ID_Y);\n" " float4floats finalColor;\n" - " real_t alpha, ls;\n" " int ii, jj;\n" " uint filterKRowIndex;\n" " const __global real4reals* accumBucket;\n" " real4reals newBucket;\n" " newBucket.m_Real4 = 0;\n" - " real4reals newRgb;\n" - " newRgb.m_Real4 = 0;\n" "\n" " for (jj = 0; jj < spatialFilter->m_FilterWidth; jj++)\n" " {\n" @@ -269,7 +269,7 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool early "\n"; //Not supporting 2 bytes per channel on the GPU. If the user wants it, run on the CPU. - if (earlyClip)//If early clip, simply assign values directly to the temp uint4 since they've been gamma corrected already, then write it straight to the output image below. + if (earlyClip)//If early clip, simply assign values directly to the temp float4 since they've been gamma corrected already, then write it straight to the output image below. { os << " finalColor.m_Float4.x = (float)newBucket.m_Real4.x;\n"//CPU side clamps, skip here because write_imagef() does the clamping for us. @@ -307,6 +307,14 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool early } os << + "\n" + " if (doCurves)\n" + " {\n" + " CurveAdjust(csa, &(finalColor.m_Floats[0]), 1);\n" + " CurveAdjust(csa, &(finalColor.m_Floats[1]), 2);\n" + " CurveAdjust(csa, &(finalColor.m_Floats[2]), 3);\n" + " }\n" + "\n" " finalColor.m_Float4 /= 255.0f;\n" " write_imagef(pixels, finalCoord, finalColor.m_Float4);\n"//Use write_imagef instead of write_imageui because only the former works when sharing with an OpenGL texture. " barrier(CLK_GLOBAL_MEM_FENCE);\n"//Required, or else page tearing will occur during interactive rendering. diff --git a/Source/EmberCL/RendererCL.cpp b/Source/EmberCL/RendererCL.cpp index 4fe07ca..5e8420a 100644 --- a/Source/EmberCL/RendererCL.cpp +++ b/Source/EmberCL/RendererCL.cpp @@ -33,6 +33,7 @@ RendererCL::RendererCL(uint platform, uint device, bool shared, GLuint output m_DEWidthsBufferName = "DEWidths"; m_DECoefIndicesBufferName = "DECoefIndices"; m_SpatialFilterCoefsBufferName = "SpatialFilterCoefs"; + m_CurvesCsaName = "CurvesCsa"; m_HistBufferName = "Hist"; m_AccumBufferName = "Accum"; m_FinalImageName = "Final"; @@ -282,6 +283,21 @@ bool RendererCL::WriteRandomPoints() template string RendererCL::IterKernel() { return m_IterKernel; } + +/// +/// Get the kernel string for the last built density filtering program. +/// +/// The string representation of the kernel for the last built density filtering program. +template +string RendererCL::DEKernel() { return m_DEOpenCLKernelCreator.GaussianDEKernel(Supersample(), m_DensityFilterCL.m_FilterWidth); } + +/// +/// Get the kernel string for the last built final accumulation program. +/// +/// The string representation of the kernel for the last built final accumulation program. +template +string RendererCL::FinalAccumKernel() { return m_FinalAccumOpenCLKernelCreator.FinalAccumKernel(EarlyClip(), Renderer::NumChannels(), Transparency()); } + /// /// Virtual functions overridden from RendererCLBase. /// @@ -551,17 +567,17 @@ bool RendererCL::Alloc() size_t accumLength = SuperSize() * sizeof(v4T); const char* loc = __FUNCTION__; - if (b && !(b = m_Wrapper.AddBuffer(m_EmberBufferName, sizeof(m_EmberCL)))) { m_ErrorReport.push_back(loc); } - if (b && !(b = m_Wrapper.AddBuffer(m_XformsBufferName, SizeOf(m_XformsCL)))) { m_ErrorReport.push_back(loc); } - if (b && !(b = m_Wrapper.AddBuffer(m_ParVarsBufferName, 128 * sizeof(T)))) { m_ErrorReport.push_back(loc); } - if (b && !(b = m_Wrapper.AddBuffer(m_DistBufferName, CHOOSE_XFORM_GRAIN))) { m_ErrorReport.push_back(loc); }//Will be resized for xaos. - if (b && !(b = m_Wrapper.AddBuffer(m_CarToRasBufferName, sizeof(m_CarToRasCL)))) { m_ErrorReport.push_back(loc); } - if (b && !(b = m_Wrapper.AddBuffer(m_DEFilterParamsBufferName, sizeof(m_DensityFilterCL)))) { m_ErrorReport.push_back(loc); } - if (b && !(b = m_Wrapper.AddBuffer(m_SpatialFilterParamsBufferName, sizeof(m_SpatialFilterCL)))) { m_ErrorReport.push_back(loc); } - - if (b && !(b = m_Wrapper.AddBuffer(m_HistBufferName, histLength))) { m_ErrorReport.push_back(loc); }//Histogram. Will memset to zero later. - if (b && !(b = m_Wrapper.AddBuffer(m_AccumBufferName, accumLength))) { m_ErrorReport.push_back(loc); }//Accum buffer. - if (b && !(b = m_Wrapper.AddBuffer(m_PointsBufferName, IterGridKernelCount() * sizeof(PointCL)))) { m_ErrorReport.push_back(loc); }//Points between iter calls. + if (b && !(b = m_Wrapper.AddBuffer(m_EmberBufferName, sizeof(m_EmberCL)))) { m_ErrorReport.push_back(loc); } + if (b && !(b = m_Wrapper.AddBuffer(m_XformsBufferName, SizeOf(m_XformsCL)))) { m_ErrorReport.push_back(loc); } + if (b && !(b = m_Wrapper.AddBuffer(m_ParVarsBufferName, 128 * sizeof(T)))) { m_ErrorReport.push_back(loc); } + if (b && !(b = m_Wrapper.AddBuffer(m_DistBufferName, CHOOSE_XFORM_GRAIN))) { m_ErrorReport.push_back(loc); }//Will be resized for xaos. + if (b && !(b = m_Wrapper.AddBuffer(m_CarToRasBufferName, sizeof(m_CarToRasCL)))) { m_ErrorReport.push_back(loc); } + if (b && !(b = m_Wrapper.AddBuffer(m_DEFilterParamsBufferName, sizeof(m_DensityFilterCL)))) { m_ErrorReport.push_back(loc); } + if (b && !(b = m_Wrapper.AddBuffer(m_SpatialFilterParamsBufferName, sizeof(m_SpatialFilterCL)))) { m_ErrorReport.push_back(loc); } + if (b && !(b = m_Wrapper.AddBuffer(m_CurvesCsaName, SizeOf(m_Csa.m_Entries)))) { m_ErrorReport.push_back(loc); } + if (b && !(b = m_Wrapper.AddBuffer(m_HistBufferName, histLength))) { m_ErrorReport.push_back(loc); }//Histogram. Will memset to zero later. + if (b && !(b = m_Wrapper.AddBuffer(m_AccumBufferName, accumLength))) { m_ErrorReport.push_back(loc); }//Accum buffer. + if (b && !(b = m_Wrapper.AddBuffer(m_PointsBufferName, IterGridKernelCount() * sizeof(PointCL)))) { m_ErrorReport.push_back(loc); }//Points between iter calls. LeaveResize(); @@ -809,8 +825,8 @@ bool RendererCL::RunIter(size_t iterCount, size_t temporalSample, size_t& ite //fuse = ((m_Calls % 4) == 0 ? 100u : 0u); #endif itersRemaining = iterCount - itersRan; - uint gridW = uint(min(ceil(double(itersRemaining) / double(iterCountPerBlock)), double(IterGridBlockWidth()))); - uint gridH = uint(min(ceil(double(itersRemaining) / double(gridW * iterCountPerBlock)), double(IterGridBlockHeight()))); + uint gridW = uint(std::min(ceil(double(itersRemaining) / double(iterCountPerBlock)), double(IterGridBlockWidth()))); + uint gridH = uint(std::min(ceil(double(itersRemaining) / double(gridW * iterCountPerBlock)), double(IterGridBlockHeight()))); uint iterCountThisLaunch = iterCountPerBlock * gridW * gridH; //Similar to what's done in the base class. @@ -1071,6 +1087,7 @@ eRenderStatus RendererCL::RunFinalAccum() uint gridH; uint blockW; uint blockH; + uint curvesSet = m_CurvesSet ? 1 : 0; const char* loc = __FUNCTION__; if (!m_Abort && accumKernelIndex != -1) @@ -1079,6 +1096,7 @@ eRenderStatus RendererCL::RunFinalAccum() m_SpatialFilterCL = ConvertSpatialFilter(); if (b && !(b = m_Wrapper.AddAndWriteBuffer(m_SpatialFilterParamsBufferName, reinterpret_cast(&m_SpatialFilterCL), sizeof(m_SpatialFilterCL)))) { m_ErrorReport.push_back(loc); } + if (b && !(b = m_Wrapper.AddAndWriteBuffer(m_CurvesCsaName, m_Csa.m_Entries.data(), SizeOf(m_Csa.m_Entries)))) { m_ErrorReport.push_back(loc); } //Since early clip requires gamma correcting the entire accumulator first, //it can't be done inside of the normal final accumulation kernel, so @@ -1119,6 +1137,9 @@ eRenderStatus RendererCL::RunFinalAccum() if (b && !(b = m_Wrapper.SetImageArg (accumKernelIndex, argIndex++, m_Wrapper.Shared(), m_FinalImageName))) { m_ErrorReport.push_back(loc); }//Final image. if (b && !(b = m_Wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_SpatialFilterParamsBufferName))) { m_ErrorReport.push_back(loc); }//SpatialFilterCL. if (b && !(b = m_Wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_SpatialFilterCoefsBufferName))) { m_ErrorReport.push_back(loc); }//Filter coefs. + if (b && !(b = m_Wrapper.SetBufferArg(accumKernelIndex, argIndex++, m_CurvesCsaName))) { m_ErrorReport.push_back(loc); }//Curve points. + + if (b && !(b = m_Wrapper.SetArg (accumKernelIndex, argIndex++, curvesSet))) { m_ErrorReport.push_back(loc); }//Do curves. if (b && !(b = m_Wrapper.SetArg (accumKernelIndex, argIndex++, alphaBase))) { m_ErrorReport.push_back(loc); }//Alpha base. if (b && !(b = m_Wrapper.SetArg (accumKernelIndex, argIndex++, alphaScale))) { m_ErrorReport.push_back(loc); }//Alpha scale. diff --git a/Source/EmberCL/RendererCL.h b/Source/EmberCL/RendererCL.h index 1478851..d08c1eb 100644 --- a/Source/EmberCL/RendererCL.h +++ b/Source/EmberCL/RendererCL.h @@ -121,6 +121,8 @@ public: bool WriteRandomPoints(); #endif string IterKernel(); + string DEKernel(); + string FinalAccumKernel(); //Virtual functions overridden from RendererCLBase. virtual bool ReadFinal(byte* pixels); @@ -193,6 +195,7 @@ private: string m_CarToRasBufferName; string m_DEFilterParamsBufferName; string m_SpatialFilterParamsBufferName; + string m_CurvesCsaName; string m_DECoefsBufferName; string m_DEWidthsBufferName; string m_DECoefIndicesBufferName; diff --git a/Source/EmberCommon/JpegUtils.h b/Source/EmberCommon/JpegUtils.h index 68787de..5fcc05c 100644 --- a/Source/EmberCommon/JpegUtils.h +++ b/Source/EmberCommon/JpegUtils.h @@ -146,7 +146,7 @@ static bool WritePng(const char* filename, byte* image, size_t width, size_t hei png_infop info_ptr; png_text text[PNG_COMMENT_MAX]; size_t i; - uint16 testbe = 1; + glm::uint16 testbe = 1; vector rows(height); text[0].compression = PNG_TEXT_COMPRESSION_NONE; diff --git a/Source/EmberGenome/EmberGenome.rc b/Source/EmberGenome/EmberGenome.rc index 1d67f0f..045fdab 100644 --- a/Source/EmberGenome/EmberGenome.rc +++ b/Source/EmberGenome/EmberGenome.rc @@ -49,8 +49,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,4,1,8 - PRODUCTVERSION 0,4,1,8 + FILEVERSION 0,4,1,9 + PRODUCTVERSION 0,4,1,9 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -67,12 +67,12 @@ BEGIN BEGIN VALUE "CompanyName", "Open Source" VALUE "FileDescription", "Manipulates fractal flames parameter files" - VALUE "FileVersion", "0.4.1.8" + VALUE "FileVersion", "0.4.1.9" VALUE "InternalName", "EmberGenome.rc" VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2013, GPL v3" VALUE "OriginalFilename", "EmberGenome.rc" VALUE "ProductName", "Ember Genome" - VALUE "ProductVersion", "0.4.1.8" + VALUE "ProductVersion", "0.4.1.9" END END BLOCK "VarFileInfo" diff --git a/Source/EmberRender/EmberRender.cpp b/Source/EmberRender/EmberRender.cpp index a04340d..8053c26 100644 --- a/Source/EmberRender/EmberRender.cpp +++ b/Source/EmberRender/EmberRender.cpp @@ -158,7 +158,7 @@ bool EmberRender(EmberOptions& opt) { if (opt.Verbose() && embers.size() > 1) cout << "\nFlame = " << i + 1 << "/" << embers.size() << endl; - else + else if (embers.size() > 1) VerbosePrint(endl); if (opt.Supersample() > 0) diff --git a/Source/EmberRender/EmberRender.rc b/Source/EmberRender/EmberRender.rc index d9adf05..96dc832 100644 --- a/Source/EmberRender/EmberRender.rc +++ b/Source/EmberRender/EmberRender.rc @@ -49,8 +49,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,4,1,8 - PRODUCTVERSION 0,4,1,8 + FILEVERSION 0,4,1,9 + PRODUCTVERSION 0,4,1,9 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -67,12 +67,12 @@ BEGIN BEGIN VALUE "CompanyName", "Open Source" VALUE "FileDescription", "Renders fractal flames as single images" - VALUE "FileVersion", "0.4.1.8" + VALUE "FileVersion", "0.4.1.9" VALUE "InternalName", "EmberRender.rc" VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2013, GPL v3" VALUE "OriginalFilename", "EmberRender.rc" VALUE "ProductName", "Ember Render" - VALUE "ProductVersion", "0.4.1.8" + VALUE "ProductVersion", "0.4.1.9" END END BLOCK "VarFileInfo" diff --git a/Source/EmberTester/EmberTester.cpp b/Source/EmberTester/EmberTester.cpp index 91c90ac..47deb50 100644 --- a/Source/EmberTester/EmberTester.cpp +++ b/Source/EmberTester/EmberTester.cpp @@ -1529,6 +1529,7 @@ void TestCpuGpuResults() renderer.CreateSpatialFilter(newAlloc); renderer.CreateDEFilter(newAlloc); renderer.ComputeBounds(); + renderer.ComputeQuality(); renderer.ComputeCamera(); renderer.AssignIterator(); @@ -1614,6 +1615,7 @@ void TestGpuVectorRead() renderer.CreateSpatialFilter(newAlloc); renderer.CreateDEFilter(newAlloc); renderer.ComputeBounds(); + renderer.ComputeQuality(); renderer.ComputeCamera(); renderer.AssignIterator(); @@ -1746,11 +1748,84 @@ double RandD(QTIsaac& rand) return ((((rand.Rand()^(rand.Rand()<<15))&0xfffffff)*3.72529e-09)-0.5); } +#define BEZ_POINT_LENGTH 4 + +void BezierSolve(double t, glm::vec2* src, double* w, glm::vec2& solution) +{ + double s, s2, s3, t2, t3, nom_x, nom_y, denom; + + s = 1 - t; + s2 = s * s; + s3 = s * s * s; + t2 = t * t; + t3 = t * t * t; + + nom_x = w[0] * s3 * src[0].x + w[1] * s2 * 3 * t * src[1].x + w[2] * s * 3 * t2 * src[2].x + w[3] * t3 * src[3].x; + + nom_y = w[0] * s3 * src[0].y + w[1] * s2 * 3 * t * src[1].y + w[2] * s * 3 * t2 * src[2].y + w[3] * t3 * src[3].y; + + denom = w[0] * s3 + w[1] * s2 * 3 * t + w[2] * s * 3 * t2 + w[3] * t3; + + + if (isnan(nom_x) || isnan(nom_y) || isnan(denom) || denom == 0) + return; + + solution.x = nom_x / denom; + solution.y = nom_y / denom; +} + +void BezierSetRect(glm::vec2* points, bool flip, glm::vec4& rect) +{ + double f; + + for (int i = 0; i < BEZ_POINT_LENGTH; i++) + { + if (flip) + f = 1 - points[i].y; + else + f = points[i].y; + + points[i].x = points[i].x * (rect.z - rect.x) + rect.x; + points[i].y = f * (rect.w - rect.y) + rect.y; + } +} + +void BezierUnsetRect(glm::vec2* points, bool flip, glm::vec4& rect) +{ + if ((rect.z - rect.x) == 0 || (rect.w - rect.y) == 0) + return; + + for (int i = 0; i < BEZ_POINT_LENGTH; i++) + { + points[i].x = (points[i].x - rect.x) / (rect.z - rect.x); + points[i].y = (points[i].y - rect.y) / (rect.w - rect.y); + + if (flip) + points[i].y = 1 - points[i].y; + } +} + +struct BezierPoints +{ + glm::vec2 points[4]; +}; + +struct BezierWeights +{ + double points[4]; +}; + int _tmain(int argc, _TCHAR* argv[]) { //int i; Timing t(4); QTIsaac rand; + glm::vec2 solution, src[4]; + double bezT = 1, w[4]; + BezierPoints curvePoints[4]; + BezierWeights curveWeights[4]; + + BezierSolve(bezT, src, w, solution); //cout << pow(-1, 5.1) << endl; @@ -1818,7 +1893,7 @@ int _tmain(int argc, _TCHAR* argv[]) //std::complex cd, cd2; //cd2 = sin(cd); - + /* t.Tic(); VariationList vlf; t.Toc("Creating VariationList"); @@ -1905,7 +1980,7 @@ int _tmain(int argc, _TCHAR* argv[]) t.Tic(); TestOperations(); t.Toc("TestOperations()"); - + */ //t.Tic(); //TestVarsSimilar(); //t.Toc("TestVarsSimilar()"); diff --git a/Source/Fractorium/AboutDialog.ui b/Source/Fractorium/AboutDialog.ui index 1850f40..4a13f20 100644 --- a/Source/Fractorium/AboutDialog.ui +++ b/Source/Fractorium/AboutDialog.ui @@ -58,7 +58,7 @@ - <html><head/><body><p align="center"><br/><span style=" font-size:12pt;">Fractorium 0.4.1.8 Beta</span></p><p align="center"><span style=" font-size:10pt;"><br/>A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.</span></p><p align="center"><span style=" font-size:10pt;">Matt Feemster</span></p></body></html> + <html><head/><body><p align="center"><br/><span style=" font-size:12pt;">Fractorium 0.4.1.9 Beta</span></p><p align="center"><span style=" font-size:10pt;"><br/>A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.</span></p><p align="center"><span style=" font-size:10pt;">Matt Feemster</span></p></body></html> Qt::RichText diff --git a/Source/Fractorium/CurvesGraphicsView.cpp b/Source/Fractorium/CurvesGraphicsView.cpp new file mode 100644 index 0000000..b237aaa --- /dev/null +++ b/Source/Fractorium/CurvesGraphicsView.cpp @@ -0,0 +1,213 @@ +#include "FractoriumPch.h" +#include "CurvesGraphicsView.h" + +/// +/// Construct the scene which will have a fixed rect. +/// Construct all points, pens and axes. +/// +/// Pass to the parent +CurvesGraphicsView::CurvesGraphicsView(QWidget* parent) + : QGraphicsView(parent) +{ + m_Scene.setSceneRect(0, 0, 245, 245); + + m_AllP1 = new EllipseItem(QRectF(-5, -5, 10, 10), 0, 1, this); + m_AllP1->setBrush(QBrush(Qt::GlobalColor::black)); + m_AllP2 = new EllipseItem(QRectF(-5, -5, 10, 10), 0, 2, this); + m_AllP2->setBrush(QBrush(Qt::GlobalColor::black)); + + m_RedP1 = new EllipseItem(QRectF(-5, -5, 10, 10), 1, 1, this); + m_RedP1->setBrush(QBrush(Qt::GlobalColor::red)); + m_RedP2 = new EllipseItem(QRectF(-5, -5, 10, 10), 1, 2, this); + m_RedP2->setBrush(QBrush(Qt::GlobalColor::red)); + + m_GrnP1 = new EllipseItem(QRectF(-5, -5, 10, 10), 2, 1, this); + m_GrnP1->setBrush(QBrush(Qt::GlobalColor::green)); + m_GrnP2 = new EllipseItem(QRectF(-5, -5, 10, 10), 2, 2, this); + m_GrnP2->setBrush(QBrush(Qt::GlobalColor::green)); + + m_BluP1 = new EllipseItem(QRectF(-5, -5, 10, 10), 3, 1, this); + m_BluP1->setBrush(QBrush(Qt::GlobalColor::blue)); + m_BluP2 = new EllipseItem(QRectF(-5, -5, 10, 10), 3, 2, this); + m_BluP2->setBrush(QBrush(Qt::GlobalColor::blue)); + + m_AxisPen = QPen(Qt::GlobalColor::white); + m_XLine = new QGraphicsLineItem(); + m_XLine->setPen(m_AxisPen); + m_XLine->setZValue(0); + m_YLine = new QGraphicsLineItem(); + m_YLine->setPen(m_AxisPen); + m_YLine->setZValue(0); + + m_Scene.addItem(m_AllP1); m_Points[0].first = m_AllP1; m_AllP1->setZValue(2); + m_Scene.addItem(m_AllP2); m_Points[0].second = m_AllP2; m_AllP2->setZValue(2); + m_Scene.addItem(m_RedP1); m_Points[1].first = m_RedP1; + m_Scene.addItem(m_RedP2); m_Points[1].second = m_RedP2; + m_Scene.addItem(m_GrnP1); m_Points[2].first = m_GrnP1; + m_Scene.addItem(m_GrnP2); m_Points[2].second = m_GrnP2; + m_Scene.addItem(m_BluP1); m_Points[3].first = m_BluP1; + m_Scene.addItem(m_BluP2); m_Points[3].second = m_BluP2; + m_Scene.addItem(m_XLine); + m_Scene.addItem(m_YLine); + + m_APen = QPen(Qt::GlobalColor::black); m_Pens[0] = &m_APen; + m_RPen = QPen(Qt::GlobalColor::red); m_Pens[1] = &m_RPen; + m_GPen = QPen(Qt::GlobalColor::green); m_Pens[2] = &m_GPen; + m_BPen = QPen(Qt::GlobalColor::blue); m_Pens[3] = &m_BPen; + + m_APen.setWidth(2); + m_RPen.setWidth(2); + m_GPen.setWidth(2); + m_BPen.setWidth(2); + + setScene(&m_Scene); + SetTop(CurveIndex::ALL); + //qDebug() << "Original scene rect before setting anything is: " << sceneRect(); + m_OriginalRect = sceneRect(); + show(); + //qDebug() << "Original scene rect is: " << m_OriginalRect; +} + +/// +/// Get the position of a given point within a given curve. +/// +/// The curve whose point value will be retrieved, 0-3. +/// The point within the curve value will be retrieved, 1-2. +/// The position of the point. X,Y will each be within 0-1. +void CurvesGraphicsView::PointChanged(int curveIndex, int pointIndex, const QPointF& point) +{ + double x = point.x() / width(); + double y = (height() - point.y()) / height(); + + emit PointChangedSignal(curveIndex, pointIndex, QPointF(x, y)); +} + +/// +/// Get the position of a given point within a given curve. +/// +/// The curve whose point value will be retrieved, 0-3. +/// The point within the curve whose value will be retrieved, 1-2. +/// The position of the point. X,Y will each be within 0-1. +QPointF CurvesGraphicsView::Get(int curveIndex, int pointIndex) +{ + if (curveIndex < 4) + { + EllipseItem* item = (pointIndex == 1) ? m_Points[curveIndex].first : m_Points[curveIndex].second; + + return QPointF(item->pos().x() / width(), (height() - item->pos().y()) / height()); + } + + return QPointF(); +} + +/// +/// Set the position of a given point within a given curve. +/// +/// The curve whose point will be set, 0-3. +/// The point within the curve which will be set, 1-2 +/// The position to set the point to. X,Y will each be within 0-1. +void CurvesGraphicsView::Set(int curveIndex, int pointIndex, const QPointF& point) +{ + if (curveIndex < 4) + { + if (pointIndex == 1) + m_Points[curveIndex].first->setPos(point.x() * width(), (1.0 - point.y()) * height());//Scale to scene dimensions, Y axis is flipped. + else + m_Points[curveIndex].second->setPos(point.x() * width(), (1.0 - point.y()) * height()); + } +} + +/// +/// Set the topmost curve but setting its Z value. +/// All other curves will get a value one less. +/// +/// The curve to set +void CurvesGraphicsView::SetTop(CurveIndex curveIndex) +{ + int index; + + switch (curveIndex) + { + case CurveIndex::ALL: + index = 0; + break; + case CurveIndex::RED: + index = 1; + break; + case CurveIndex::GREEN: + index = 2; + break; + case CurveIndex::BLUE: + default: + index = 3; + } + + for (int i = 0; i < 4; i++) + { + if (i == index) + { + m_Points[i].first->setZValue(2); + m_Points[i].second->setZValue(2); + } + else + { + m_Points[i].first->setZValue(1); + m_Points[i].second->setZValue(1); + } + } +} + +/// +/// Overridden paint even which draws the points, axes and curves. +/// +/// Ignored +void CurvesGraphicsView::paintEvent(QPaintEvent* e) +{ + QGraphicsView::paintEvent(e); + + int i; + QRectF rect = scene()->sceneRect(); + double w2 = width() / 2; + double h2 = height() / 2; + + //Draw axis lines. + m_XLine->setLine(QLineF(0, h2, width(), h2)); + m_YLine->setLine(QLineF(w2, 0, w2, height())); + + //This must be constructed every time and cannot be a member. + QPainter painter(viewport()); + painter.setClipRect(rect); + painter.setRenderHint(QPainter::Antialiasing); + + //Create 4 new paths. These must be constructed every time and cannot be members. + QPainterPath paths[4] = + { + QPainterPath(mapFromScene(rect.bottomLeft())), + QPainterPath(mapFromScene(rect.bottomLeft())), + QPainterPath(mapFromScene(rect.bottomLeft())), + QPainterPath(mapFromScene(rect.bottomLeft())) + }; + + //Draw paths for all but the topmost curve. + for (i = 0; i < 4; i++) + { + paths[i].cubicTo(m_Points[i].first->pos(), m_Points[i].second->pos(), mapFromScene(rect.topRight())); + + if (m_Points[i].first->zValue() == 1) + { + painter.setPen(*m_Pens[i]); + painter.drawPath(paths[i]); + } + } + + //Draw the topmost curve. + for (i = 0; i < 4; i++) + { + if (m_Points[i].first->zValue() == 2) + { + painter.setPen(*m_Pens[i]); + painter.drawPath(paths[i]); + break; + } + } +} \ No newline at end of file diff --git a/Source/Fractorium/CurvesGraphicsView.h b/Source/Fractorium/CurvesGraphicsView.h new file mode 100644 index 0000000..4c430f5 --- /dev/null +++ b/Source/Fractorium/CurvesGraphicsView.h @@ -0,0 +1,150 @@ +#pragma once +#include "FractoriumPch.h" + +/// +/// CurvesGraphicsView and EllipseItem classes. +/// + +class EllipseItem; + +/// +/// Enumeration used for setting values on a specific curve. +/// +enum CurveIndex +{ + ALL, + RED, + GREEN, + BLUE +}; + +/// +/// Derivation to display points on bezier cuves for the user to drag. +/// Selection logic and updating is handled by the base class. +/// Changes here will affect the current ember and vice versa. +/// The points, axis lines and pens are kept as members and the curves +/// themselves are drawn on the fly during each paint event. +/// Pointers to these are kept in arrays to make manipulating them in loops easier. +/// Note that this must work off a fixed rect size, hence why the control is not resizeable. +/// +class CurvesGraphicsView : public QGraphicsView +{ + Q_OBJECT + +public: + CurvesGraphicsView(QWidget* parent = 0); + + void PointChanged(int curveIndex, int pointIndex, const QPointF& point); + QPointF Get(int curveIndex, int pointIndex); + void Set(int curveIndex, int pointIndex, const QPointF& point); + void SetTop(CurveIndex curveIndex); + +Q_SIGNALS: + void PointChangedSignal(int curveIndex, int pointIndex, const QPointF& point); + +protected: + virtual void paintEvent(QPaintEvent* e) override; + + QPen m_APen; + QPen m_RPen; + QPen m_GPen; + QPen m_BPen; + QPen m_AxisPen; + EllipseItem* m_AllP1; + EllipseItem* m_AllP2; + EllipseItem* m_RedP1; + EllipseItem* m_RedP2; + EllipseItem* m_GrnP1; + EllipseItem* m_GrnP2; + EllipseItem* m_BluP1; + EllipseItem* m_BluP2; + QGraphicsLineItem* m_XLine; + QGraphicsLineItem* m_YLine; + QPen* m_Pens[4]; + QGraphicsScene m_Scene; + QRectF m_OriginalRect; + std::pair m_Points[4]; +}; + +/// +/// Derivation for draggable points needed to trigger an event whenever the item is changed. +/// Custom drawing is also done to omit drawing a selection rectangle. +/// +class EllipseItem : public QGraphicsEllipseItem +{ +public: + /// + /// Construct the point and specify the curve index it's part of, as well as the + /// point index within the curve. + /// + /// Pass to the parent + /// The curve's index this point is a part of, 0-3. + /// The point index within the curve + /// The graphics view this point is displayed on + /// The parent widget of this item + EllipseItem(const QRectF &rect, int curveIndex, int pointIndex, CurvesGraphicsView* viewParent, QGraphicsItem *parent = 0) + : QGraphicsEllipseItem(rect, parent) + { + setFlag(QGraphicsItem::ItemSendsScenePositionChanges); + setFlag(QGraphicsItem::ItemIsSelectable); + setFlag(QGraphicsItem::ItemIsMovable); + setPen(Qt::NoPen); + + m_CurveIndex = curveIndex; + m_PointIndex = pointIndex; + m_ViewParent = viewParent; + } + + /// + /// Index properties, getters only. + /// + int CurveIndex() const { return m_CurveIndex; } + int PointIndex() const { return m_PointIndex; } + +protected: + /// + /// Overridden paint event to disable the selection rectangle. + /// + /// Unused and just passed to QGraphicsEllipseItem::paint() + /// Drawing options used which will have the QStyle::State_Selected flag unset + /// Unused and just passed to QGraphicsEllipseItem::paint() + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override + { + QStyleOptionGraphicsItem myOption(*option); + myOption.state &= ~QStyle::State_Selected; + QGraphicsEllipseItem::paint(painter, &myOption, widget); + } + + /// + /// Overridden itemChange event to notify the parent control that it has moved. + /// Movement is also restriced to the scene rect. + /// + /// Action is only taken if this value equals ItemPositionChange + /// The new position. This will be clamped to the scene rect. + /// The new position + virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value) override + { + if (change == ItemPositionChange && scene()) + { + //Value is the new position. + QPointF newPos = value.toPointF(); + QRectF rect = scene()->sceneRect(); + + if (!rect.contains(newPos)) + { + //Keep the item inside the scene rect. + newPos.setX(qMin(rect.right(), qMax(newPos.x(), rect.left()))); + newPos.setY(qMin(rect.bottom(), qMax(newPos.y(), rect.top()))); + } + + m_ViewParent->PointChanged(m_CurveIndex, m_PointIndex, newPos); + return newPos; + } + + return QGraphicsEllipseItem::itemChange(change, value); + } + + int m_CurveIndex; + int m_PointIndex; + CurvesGraphicsView* m_ViewParent; +}; diff --git a/Source/Fractorium/EmberFile.h b/Source/Fractorium/EmberFile.h index 488295c..f923996 100644 --- a/Source/Fractorium/EmberFile.h +++ b/Source/Fractorium/EmberFile.h @@ -84,6 +84,23 @@ public: return m_Embers.size(); } + /// + /// Delete the ember at the given index. + /// Will not delete anything if the size is already 1. + /// + /// The index of the ember to delete + /// True if successfully deleted, else false. + bool Delete(size_t index) + { + if (Size() > 1 && index < Size()) + { + m_Embers.erase(m_Embers.begin() + index); + return true; + } + else + return false; + } + /// /// Ensure all ember names are unique. /// @@ -116,6 +133,7 @@ public: /// /// Ensures a given input filename is unique by appending a count to the end. /// + /// The filename to ensure is unique /// The passed in name if it was unique, else a uniquely made name. static QString UniqueFilename(const QString& filename) { diff --git a/Source/Fractorium/FinalRenderDialog.cpp b/Source/Fractorium/FinalRenderDialog.cpp index bb58405..c526ed1 100644 --- a/Source/Fractorium/FinalRenderDialog.cpp +++ b/Source/Fractorium/FinalRenderDialog.cpp @@ -139,7 +139,7 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(FractoriumSettings* set QSize s = size(); int desktopHeight = qApp->desktop()->availableGeometry().height(); - s.setHeight(min(s.height(), int(double(desktopHeight * 0.90)))); + s.setHeight(std::min(s.height(), int(double(desktopHeight * 0.90)))); setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, qApp->desktop()->availableGeometry())); QWidget* w = SetTabOrder(this, ui.FinalRenderEarlyClipCheckBox, ui.FinalRenderYAxisUpCheckBox); diff --git a/Source/Fractorium/FinalRenderEmberController.cpp b/Source/Fractorium/FinalRenderEmberController.cpp index a28847d..91bdd9f 100644 --- a/Source/Fractorium/FinalRenderEmberController.cpp +++ b/Source/Fractorium/FinalRenderEmberController.cpp @@ -115,8 +115,8 @@ FinalRenderEmberController::FinalRenderEmberController(FractoriumFinalRenderD m_PreviewEmber = *m_Ember; m_PreviewEmber.m_Quality = 100; m_PreviewEmber.m_TemporalSamples = 1; - m_PreviewEmber.m_FinalRasW = max(1, min(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasW)));//Ensure neither is zero. - m_PreviewEmber.m_FinalRasH = max(1, min(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasH))); + m_PreviewEmber.m_FinalRasW = std::max(1, std::min(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasW)));//Ensure neither is zero. + m_PreviewEmber.m_FinalRasH = std::max(1, std::min(maxDim, size_t(scalePercentage * m_Ember->m_FinalRasH))); m_PreviewEmber.m_PixelsPerUnit = scalePercentage * m_Ember->m_PixelsPerUnit; m_FinalPreviewRenderer->EarlyClip(m_FinalRenderDialog->EarlyClip()); @@ -597,6 +597,7 @@ tuple FinalRenderEmberController::SyncAndComputeMemor m_Renderer->CreateTemporalFilter(b); m_Renderer->NumChannels(channels); m_Renderer->ComputeBounds(); + m_Renderer->ComputeQuality(); m_Renderer->ComputeCamera(); CancelPreviewRender(); m_FinalPreviewRenderFunc(); @@ -741,8 +742,8 @@ void FinalRenderEmberController::SyncGuiToEmber(Ember& ember, size_t width h = ember.m_OrigFinalRasH * hScale; } - w = max(w, 10); - h = max(h, 10); + w = std::max(w, 10); + h = std::max(h, 10); ember.SetSizeAndAdjustScale(w, h, false, m_FinalRenderDialog->Scale()); ember.m_Quality = m_FinalRenderDialog->m_QualitySpin->value(); diff --git a/Source/Fractorium/Fractorium.cpp b/Source/Fractorium/Fractorium.cpp index 7aacea1..067941a 100644 --- a/Source/Fractorium/Fractorium.cpp +++ b/Source/Fractorium/Fractorium.cpp @@ -264,8 +264,9 @@ void Fractorium::dockLocationChanged(Qt::DockWidgetArea area) /// /// -/// Event filter for taking special action on dock widget resize events, -/// which in turn trigger GLParentScrollArea events. +/// Event filter for taking special action on: +/// Dock widget resize events, which in turn trigger GLParentScrollArea events. +/// Library tree key events, specifically delete. /// /// The object /// The eevent @@ -278,6 +279,19 @@ bool Fractorium::eventFilter(QObject* o, QEvent* e) m_HeightSpin->DoubleClickNonZero(ui.GLParentScrollArea->height()); //qDebug() << "scroll area resized"; } + else if (o == ui.LibraryTree) + { + if (QKeyEvent* ke = dynamic_cast(e)) + { + if (ke->key() == Qt::Key_Delete && e->type() == QEvent::KeyRelease) + { + auto p = GetCurrentEmberIndex(); + + if (ui.LibraryTree->topLevelItem(0)->childCount() > 1 && p.second) + OnDelete(p); + } + } + } return QMainWindow::eventFilter(o, e); } diff --git a/Source/Fractorium/Fractorium.h b/Source/Fractorium/Fractorium.h index e56b557..77c38ca 100644 --- a/Source/Fractorium/Fractorium.h +++ b/Source/Fractorium/Fractorium.h @@ -10,6 +10,7 @@ #include "FinalRenderDialog.h" #include "OptionsDialog.h" #include "AboutDialog.h" +#include "CurvesGraphicsView.h" /// /// Fractorium class. @@ -140,6 +141,11 @@ public slots: void OnSaveEntireFileAsXmlButtonClicked(bool checked); void OnSaveCurrentToOpenedFileButtonClicked(bool checked); + //Library. + void OnEmberTreeItemChanged(QTreeWidgetItem* item, int col); + void OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col); + void OnDelete(const pair& p); + //Params. void OnBrightnessChanged(double d);//Color. void OnGammaChanged(double d); @@ -222,7 +228,13 @@ public slots: void OnXformDirectColorChanged(double d); void OnSoloXformCheckBoxStateChanged(int state); void OnXformRefPaletteResized(int logicalIndex, int oldSize, int newSize); - + void OnResetCurvesButtonClicked(bool checked); + void OnCurvesPointChanged(int curveIndex, int pointIndex, const QPointF& point); + void OnCurvesAllRadioButtonToggled(bool checked); + void OnCurvesRedRadioButtonToggled(bool checked); + void OnCurvesGreenRadioButtonToggled(bool checked); + void OnCurvesBlueRadioButtonToggled(bool checked); + //Xforms Variations. void OnVariationSpinBoxValueChanged(double d); void OnTreeHeaderSectionClicked(int); @@ -242,10 +254,6 @@ public slots: void OnPaletteRandomSelectButtonClicked(bool checked); void OnPaletteRandomAdjustButtonClicked(bool checked); - //Library. - void OnEmberTreeItemChanged(QTreeWidgetItem* item, int col); - void OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col); - //Rendering/progress. void StartRenderTimer(); void IdleTimer(); @@ -287,6 +295,9 @@ private: //Embers. bool HaveFinal(); + //Library. + pair GetCurrentEmberIndex(); + //Params. //Xforms. @@ -304,8 +315,6 @@ private: //Palette. void ResetPaletteControls(); - //Library. - //Info. void UpdateHistogramBounds(); void ErrorReportToQTextEdit(const vector& errors, QTextEdit* textEdit, bool clear = true); diff --git a/Source/Fractorium/Fractorium.rc b/Source/Fractorium/Fractorium.rc index a55d353207d4cb8c5f783a01ac2d1308edc27c2e..4b10610edc01ea0df930b1a6b829a38f74f7bf78 100644 GIT binary patch delta 46 wcmcbod{23U7YC!|WN!{bM$65$9PG?Mjv|-%WC32W&6~KHm>}HEXLyBJ05HP~%m4rY delta 46 wcmcbod{23U7YC!oWN!{bMvKk09PG?Mjv|-%WC32W&6~KHm>}HEXLyBJ05Dn$$N&HU diff --git a/Source/Fractorium/Fractorium.ui b/Source/Fractorium/Fractorium.ui index e2c7779..d3db396 100644 --- a/Source/Fractorium/Fractorium.ui +++ b/Source/Fractorium/Fractorium.ui @@ -353,7 +353,7 @@ QTabWidget::Triangular - 2 + 0 true @@ -420,7 +420,7 @@ - Qt::StrongFocus + Qt::WheelFocus Qt::CustomContextMenu @@ -504,8 +504,8 @@ 0 0 - 73 - 837 + 259 + 852 @@ -2285,7 +2285,7 @@ SpinBox QTabWidget::Triangular - 1 + 2 @@ -2775,6 +2775,134 @@ SpinBox + + + + + 0 + 0 + + + + + 245 + 245 + + + + + 245 + 245 + + + + true + + + false + + + QFrame::NoFrame + + + QFrame::Plain + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + + + 219 + 219 + 219 + + + + + + 0.000000000000000 + 0.000000000000000 + 245.000000000000000 + 245.000000000000000 + + + + QGraphicsView::NoAnchor + + + QGraphicsView::FullViewportUpdate + + + + + + + Curve + + + + 6 + + + 2 + + + 6 + + + 6 + + + + + All + + + true + + + + + + + Red + + + + + + + Green + + + + + + + Blue + + + + + + + + + + + 0 + 0 + + + + Reset Curves + + + @@ -2825,8 +2953,8 @@ SpinBox 0 0 - 245 - 747 + 118 + 618 @@ -5048,8 +5176,8 @@ SpinBox 0 0 - 73 - 454 + 259 + 853 @@ -5855,6 +5983,11 @@ SpinBox
GLWidget.h
1 + + CurvesGraphicsView + QGraphicsView +
curvesgraphicsview.h
+
DockWidget diff --git a/Source/Fractorium/FractoriumEmberController.cpp b/Source/Fractorium/FractoriumEmberController.cpp index a1d13f7..ff5c89e 100644 --- a/Source/Fractorium/FractoriumEmberController.cpp +++ b/Source/Fractorium/FractoriumEmberController.cpp @@ -95,7 +95,7 @@ FractoriumEmberController::FractoriumEmberController(Fractorium* fractorium) m_PreviewRun = true; m_PreviewRunning = true; - m_PreviewRenderer->ThreadCount(max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe. + m_PreviewRenderer->ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe. QTreeWidget* tree = m_Fractorium->ui.LibraryTree; if (QTreeWidgetItem* top = tree->topLevelItem(0)) @@ -208,7 +208,7 @@ void FractoriumEmberController::SetEmber(size_t index) /// Wrapper to call a function, then optionally add the requested action to the rendering queue. ///
/// The function to call -/// True to update renderer, else false. Default: false. +/// True to update renderer, else false. Default: true. /// The action to add to the rendering queue. Default: FULL_RENDER. template void FractoriumEmberController::Update(std::function func, bool updateRender, eProcessAction action) diff --git a/Source/Fractorium/FractoriumEmberController.h b/Source/Fractorium/FractoriumEmberController.h index 008f374..4969aab 100644 --- a/Source/Fractorium/FractoriumEmberController.h +++ b/Source/Fractorium/FractoriumEmberController.h @@ -55,7 +55,7 @@ public: virtual void CopyTempPalette(Palette& palette) { } #endif virtual void SetEmber(size_t index) { } - virtual void Clear() { } + //virtual void Clear() { } virtual void AddXform() { } virtual void DuplicateXform() { } virtual void ClearCurrentXform() { } @@ -98,6 +98,17 @@ public: //Toolbar. + //Library. + virtual void SyncNames() { } + virtual void SyncPointers() { } + virtual void FillLibraryTree(int selectIndex = -1) { } + virtual void UpdateLibraryTree() { } + virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) { } + virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { } + virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) { } + virtual void StopPreviewRender() { } + virtual void Delete(const pair& p) { } + //Params. virtual void SetCenter(double x, double y) { } virtual void FillParamTablesAndPalette() { } @@ -156,6 +167,8 @@ public: virtual void XformColorSpeedChanged(double d) { } virtual void XformOpacityChanged(double d) { } virtual void XformDirectColorChanged(double d) { } + virtual void ClearColorCurves() { } + virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) { }//need to put this in a different section because it's not xform specific.//TODO void SetPaletteRefTable(QPixmap* pixmap); //Xforms Variations. @@ -177,15 +190,6 @@ public: virtual QRgb GetQRgbFromPaletteIndex(uint i) { return QRgb(); } virtual void PaletteCellClicked(int row, int col) { } - //Library. - virtual void SyncNames() { } - virtual void FillLibraryTree(int selectIndex = -1) { } - virtual void UpdateLibraryTree() { } - virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) { } - virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { } - virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) { } - virtual void StopPreviewRender() { } - //Info. //Rendering/progress. @@ -198,6 +202,7 @@ public: void StartRenderTimer(); void DelayedStartRenderTimer(); void StopRenderTimer(bool wait); + void ClearFinalImages(); void Shutdown(); void UpdateRender(eProcessAction action = FULL_RENDER); void DeleteRenderer(); @@ -271,7 +276,7 @@ public: virtual void CopyTempPalette(Palette& palette) override; #endif virtual void SetEmber(size_t index) override; - virtual void Clear() override { } + //virtual void Clear() override { } virtual void AddXform() override; virtual void DuplicateXform() override; virtual void ClearCurrentXform() override; @@ -317,6 +322,17 @@ public: //Toolbar. + //Library. + virtual void SyncNames() override; + virtual void SyncPointers() override; + virtual void FillLibraryTree(int selectIndex = -1) override; + virtual void UpdateLibraryTree() override; + virtual void Delete(const pair& p) override; + virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) override; + virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) override; + virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) override; + virtual void StopPreviewRender() override; + //Params. virtual void SetCenter(double x, double y) override; virtual void FillParamTablesAndPalette() override; @@ -378,6 +394,8 @@ public: virtual void XformColorSpeedChanged(double d) override; virtual void XformOpacityChanged(double d) override; virtual void XformDirectColorChanged(double d) override; + virtual void ClearColorCurves() override; + virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) override; void FillColorWithXform(Xform* xform); //Xforms Variations. @@ -400,15 +418,6 @@ public: virtual QRgb GetQRgbFromPaletteIndex(uint i) override { return QRgb(); } virtual void PaletteCellClicked(int row, int col) override; - //Library. - virtual void SyncNames() override; - virtual void FillLibraryTree(int selectIndex = -1) override; - virtual void UpdateLibraryTree() override; - virtual void EmberTreeItemChanged(QTreeWidgetItem* item, int col) override; - virtual void EmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) override; - virtual void RenderPreviews(uint start = UINT_MAX, uint end = UINT_MAX) override; - virtual void StopPreviewRender() override; - //Info. //Rendering/progress. @@ -433,6 +442,7 @@ private: //Xforms Color. void SetCurrentXformColorIndex(double d); + void FillCurvesControl(); //Palette. void UpdateAdjustedPaletteGUI(Palette& palette); diff --git a/Source/Fractorium/FractoriumLibrary.cpp b/Source/Fractorium/FractoriumLibrary.cpp index bb76feb..c759630 100644 --- a/Source/Fractorium/FractoriumLibrary.cpp +++ b/Source/Fractorium/FractoriumLibrary.cpp @@ -8,6 +8,35 @@ void Fractorium::InitLibraryUI() { connect(ui.LibraryTree, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemChanged(QTreeWidgetItem*, int)), Qt::QueuedConnection); connect(ui.LibraryTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection); + connect(ui.LibraryTree, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(OnEmberTreeItemDoubleClicked(QTreeWidgetItem*, int)), Qt::QueuedConnection); + ui.LibraryTree->installEventFilter(this);//Needed for keypress events other than enter. +} + + +/// +/// Get the index of the currently selected ember in the library tree. +/// +/// A pair containing the index of the item clicked and a pointer to the item +pair Fractorium::GetCurrentEmberIndex() +{ + size_t index = 0; + QTreeWidgetItem* item = nullptr; + QTreeWidget* tree = ui.LibraryTree; + + if (QTreeWidgetItem* top = tree->topLevelItem(0)) + { + for (int i = 0; i < top->childCount(); i++)//Iterate through all of the children, which will represent the open embers. + { + item = top->child(index); + + if (item && !item->isSelected()) + index++; + else + break; + } + } + + return pair(index, item); } /// @@ -30,11 +59,10 @@ void FractoriumEmberController::SyncNames() { EmberTreeWidgetItem* item; QTreeWidget* tree = m_Fractorium->ui.LibraryTree; - QTreeWidgetItem* top = tree->topLevelItem(0); tree->blockSignals(true); - if (top) + if (QTreeWidgetItem* top = tree->topLevelItem(0)) { for (int i = 0; i < top->childCount(); i++)//Iterate through all of the children, which will represent the open embers. { @@ -46,6 +74,31 @@ void FractoriumEmberController::SyncNames() tree->blockSignals(false); } +/// +/// Set all libary tree entries to point to the underlying ember they represent. +/// +template +void FractoriumEmberController::SyncPointers() +{ + EmberTreeWidgetItem* item; + QTreeWidget* tree = m_Fractorium->ui.LibraryTree; + + tree->blockSignals(true); + + if (QTreeWidgetItem* top = tree->topLevelItem(0)) + { + size_t childCount = top->childCount(); + + for (int i = 0; i < childCount; i++)//Iterate through all of the children, which will represent the open embers. + { + if ((item = dynamic_cast*>(top->child(i))) && i < m_EmberFile.Size())//Cast the child widget to the EmberTreeWidgetItem type. + item->SetEmberPointer(&m_EmberFile.m_Embers[i]); + } + } + + tree->blockSignals(false); +} + /// /// Fill the library tree with the names of the embers in the /// currently opened file. @@ -135,12 +188,7 @@ void FractoriumEmberController::UpdateLibraryTree() //When adding elements to the vector, they may have been reshuffled which will have invalidated //the pointers contained in the EmberTreeWidgetItems. So reassign all pointers here. - for (i = 0; i < m_EmberFile.Size(); i++) - { - if (EmberTreeWidgetItem* emberItem = dynamic_cast*>(top->child(i))) - emberItem->SetEmberPointer(&m_EmberFile.m_Embers[i]); - } - + SyncPointers(); tree->blockSignals(false); RenderPreviews(childCount, m_EmberFile.Size()); } @@ -221,6 +269,47 @@ void FractoriumEmberController::EmberTreeItemDoubleClicked(QTreeWidgetItem* i void Fractorium::OnEmberTreeItemDoubleClicked(QTreeWidgetItem* item, int col) { m_Controller->EmberTreeItemDoubleClicked(item, col); } +/// +/// Delete the currently selected item in the tree. +/// Note this is not necessarilly the current ember, it's just the item +/// in the tree that is selected. +/// +/// A pair containing the index of the item clicked and a pointer to the item +template +void FractoriumEmberController::Delete(const pair& p) +{ + QTreeWidget* tree = m_Fractorium->ui.LibraryTree; + + tree->blockSignals(true); + + if (m_EmberFile.Delete(p.first)) + { + delete p.second; + SyncPointers(); + } + + tree->blockSignals(false); + + //If there is now only one item left and it wasn't selected, select it. + if (QTreeWidgetItem* top = tree->topLevelItem(0)) + { + if (top->childCount() == 1) + if (auto item = dynamic_cast*>(top->child(0))) + if (item->GetEmber()->m_Name != m_Ember.m_Name) + EmberTreeItemDoubleClicked(top->child(0), 0); + } +} + +/// +/// Called when the user presses and releases the delete key while the library tree has the focus, +/// and an item is selected. +/// +/// A pair containing the index of the item clicked and a pointer to the item +void Fractorium::OnDelete(const pair& p) +{ + m_Controller->Delete(p); +} + /// /// Stop the preview renderer if it's already running. /// Clear all of the existing preview images, then start the preview rendering thread. diff --git a/Source/Fractorium/FractoriumMenus.cpp b/Source/Fractorium/FractoriumMenus.cpp index b8dcad4..3a68655 100644 --- a/Source/Fractorium/FractoriumMenus.cpp +++ b/Source/Fractorium/FractoriumMenus.cpp @@ -252,7 +252,11 @@ void FractoriumEmberController::SaveCurrentAsXml() QString filename; FractoriumSettings* s = m_Fractorium->m_Settings; - if (QFile::exists(m_LastSaveCurrent)) + if (s->SaveAutoUnique() && m_LastSaveCurrent != "") + { + filename = EmberFile::UniqueFilename(m_LastSaveCurrent); + } + else if (QFile::exists(m_LastSaveCurrent)) { filename = m_LastSaveCurrent; } @@ -281,7 +285,9 @@ void FractoriumEmberController::SaveCurrentAsXml() if (writer.Save(filename.toStdString().c_str(), ember, 0, true, false, true)) { s->SaveFolder(fileInfo.canonicalPath()); - m_LastSaveCurrent = filename; + + if (!s->SaveAutoUnique() || m_LastSaveCurrent == "")//Only save filename on first time through when doing auto unique names. + m_LastSaveCurrent = filename; } else m_Fractorium->ShowCritical("Save Failed", "Could not save file, try saving to a different folder."); @@ -301,7 +307,9 @@ void FractoriumEmberController::SaveEntireFileAsXml() QString filename; FractoriumSettings* s = m_Fractorium->m_Settings; - if (QFile::exists(m_LastSaveAll)) + if (s->SaveAutoUnique() && m_LastSaveAll != "") + filename = EmberFile::UniqueFilename(m_LastSaveAll); + else if (QFile::exists(m_LastSaveAll)) filename = m_LastSaveAll; else filename = m_Fractorium->SetupSaveXmlDialog(m_EmberFile.m_Filename); @@ -320,7 +328,9 @@ void FractoriumEmberController::SaveEntireFileAsXml() if (writer.Save(filename.toStdString().c_str(), emberFile.m_Embers, 0, true, false, true)) { - m_LastSaveAll = filename; + if (!s->SaveAutoUnique() || m_LastSaveAll == "")//Only save filename on first time through when doing auto unique names. + m_LastSaveAll = filename; + s->SaveFolder(fileInfo.canonicalPath()); } else @@ -398,7 +408,7 @@ void FractoriumEmberController::Undo() int index = m_Ember.GetTotalXformIndex(CurrentXform()); m_LastEditWasUndoRedo = true; - m_UndoIndex = max(0u, m_UndoIndex - 1u); + m_UndoIndex = std::max(0u, m_UndoIndex - 1u); SetEmber(m_UndoList[m_UndoIndex], true); m_EditState = UNDO_REDO; @@ -423,7 +433,7 @@ void FractoriumEmberController::Redo() int index = m_Ember.GetTotalXformIndex(CurrentXform()); m_LastEditWasUndoRedo = true; - m_UndoIndex = min(m_UndoIndex + 1, m_UndoList.size() - 1); + m_UndoIndex = std::min(m_UndoIndex + 1, m_UndoList.size() - 1); SetEmber(m_UndoList[m_UndoIndex], true); m_EditState = UNDO_REDO; diff --git a/Source/Fractorium/FractoriumPch.h b/Source/Fractorium/FractoriumPch.h index 5ca78b3..bfb9f72 100644 --- a/Source/Fractorium/FractoriumPch.h +++ b/Source/Fractorium/FractoriumPch.h @@ -26,9 +26,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include diff --git a/Source/Fractorium/FractoriumRender.cpp b/Source/Fractorium/FractoriumRender.cpp index a0a8e4b..1d428e6 100644 --- a/Source/Fractorium/FractoriumRender.cpp +++ b/Source/Fractorium/FractoriumRender.cpp @@ -67,11 +67,22 @@ void FractoriumEmberControllerBase::StopRenderTimer(bool wait) void FractoriumEmberControllerBase::Shutdown() { StopRenderTimer(true); + ClearFinalImages(); while(m_Fractorium->ui.GLDisplay->Drawing()) QApplication::processEvents(); } +/// +/// Clear the output image buffers. +/// +void FractoriumEmberControllerBase::ClearFinalImages() +{ + Memset(m_FinalImage[0]); + Memset(m_FinalImage[1]); + //Unsure if we should also call RendererCL::ClearFinal() as well. At the moment it seems unnecessary. +} + /// /// Update the state of the renderer. /// Upon changing values, some intelligence is used to avoid blindly restarting the @@ -442,7 +453,11 @@ bool FractoriumEmberController::Render() //Uncomment for debugging kernel build and execution errors. //m_Fractorium->ui.InfoRenderingTextEdit->setText(QString::fromStdString(m_Fractorium->m_Wrapper.DumpInfo())); //if (rendererCL) - // m_Fractorium->ui.InfoRenderingTextEdit->setText(QString::fromStdString(rendererCL->IterKernel())); + //{ + // string s = "OpenCL Kernels: \r\n" + rendererCL->IterKernel() + "\r\n" + rendererCL->DEKernel() + "\r\n" + rendererCL->FinalAccumKernel(); + // + // QMetaObject::invokeMethod(m_Fractorium->ui.InfoRenderingTextEdit, "setText", Qt::QueuedConnection, Q_ARG(const QString&, QString::fromStdString(s))); + //} } } else//Something went very wrong, show error report. @@ -460,12 +475,16 @@ bool FractoriumEmberController::Render() m_Rendering = false; StopRenderTimer(true); m_Fractorium->m_RenderStatusLabel->setText("Rendering failed 3 or more times, stopping all rendering, see info tab. Try changing renderer types."); - Memset(m_FinalImage[m_FinalImageIndex]); - - if (rendererCL) - rendererCL->ClearFinal(); - + ClearFinalImages(); m_GLController->ClearWindow(); + + if (rendererCL) + { + //string s = "OpenCL Kernels: \r\n" + rendererCL->IterKernel() + "\r\n" + rendererCL->DEKernel() + "\r\n" + rendererCL->FinalAccumKernel(); + + rendererCL->ClearFinal(); + //QMetaObject::invokeMethod(m_Fractorium->ui.InfoRenderingTextEdit, "setText", Qt::QueuedConnection, Q_ARG(const QString&, QString::fromStdString(s))); + } } } } diff --git a/Source/Fractorium/FractoriumSettings.cpp b/Source/Fractorium/FractoriumSettings.cpp index 769124f..c752b8e 100644 --- a/Source/Fractorium/FractoriumSettings.cpp +++ b/Source/Fractorium/FractoriumSettings.cpp @@ -39,7 +39,7 @@ void FractoriumSettings::EnsureDefaults() XmlSupersample(2); if (ThreadCount() == 0 || ThreadCount() > Timing::ProcessorCount()) - ThreadCount(max(1u, Timing::ProcessorCount() - 1));//Default to one less to keep the UI responsive for first time users. + ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Default to one less to keep the UI responsive for first time users. if (FinalThreadCount() == 0 || FinalThreadCount() > Timing::ProcessorCount()) FinalThreadCount(Timing::ProcessorCount()); @@ -172,8 +172,8 @@ void FractoriumSettings::FinalKeepAspect(bool b) { setValue(FINALKEEPASPECT, uint FractoriumSettings::FinalScale() { return value(FINALSCALE).toUInt(); } void FractoriumSettings::FinalScale(uint i) { setValue(FINALSCALE, i); } -QString FractoriumSettings::FinalExt() { return value(FINALEXT).toString(); } -void FractoriumSettings::FinalExt(const QString& s) { setValue(FINALEXT, s); } +QString FractoriumSettings::FinalExt() { return value(FINALEXT).toString(); } +void FractoriumSettings::FinalExt(const QString& s) { setValue(FINALEXT, s); } uint FractoriumSettings::FinalPlatformIndex() { return value(FINALPLATFORMINDEX).toUInt(); } void FractoriumSettings::FinalPlatformIndex(uint i) { setValue(FINALPLATFORMINDEX, i); } @@ -239,3 +239,6 @@ void FractoriumSettings::OpenImageExt(const QString& s) { setValue(OPENIMAGEE QString FractoriumSettings::SaveImageExt() { return value(SAVEIMAGEEXT).toString(); } void FractoriumSettings::SaveImageExt(const QString& s) { setValue(SAVEIMAGEEXT, s); } + +bool FractoriumSettings::SaveAutoUnique() { return value(AUTOUNIQUE).toBool(); } +void FractoriumSettings::SaveAutoUnique(bool b) { setValue(AUTOUNIQUE, b); } \ No newline at end of file diff --git a/Source/Fractorium/FractoriumSettings.h b/Source/Fractorium/FractoriumSettings.h index 4d276a8..05f57e9 100644 --- a/Source/Fractorium/FractoriumSettings.h +++ b/Source/Fractorium/FractoriumSettings.h @@ -52,6 +52,7 @@ #define SAVEXMLEXT "file/savexmlext" #define OPENIMAGEEXT "file/openimageext" #define SAVEIMAGEEXT "file/saveimageext" +#define AUTOUNIQUE "file/autounique" #define IDENTITYID "identity/id" #define IDENTITYURL "identity/url" @@ -189,6 +190,9 @@ public: QString SaveImageExt(); void SaveImageExt(const QString& s); + bool SaveAutoUnique(); + void SaveAutoUnique(bool b); + QString Id(); void Id(const QString& s); diff --git a/Source/Fractorium/FractoriumXformsColor.cpp b/Source/Fractorium/FractoriumXformsColor.cpp index fad8939..594d17d 100644 --- a/Source/Fractorium/FractoriumXformsColor.cpp +++ b/Source/Fractorium/FractoriumXformsColor.cpp @@ -25,8 +25,15 @@ void Fractorium::InitXformsColorUI() m_XformColorSpeedSpin->setDecimals(3); m_XformOpacitySpin->setDecimals(3); m_XformDirectColorSpin->setDecimals(3); - connect(ui.XformColorScroll, SIGNAL(valueChanged(int)), this, SLOT(OnXformScrollColorIndexChanged(int)), Qt::QueuedConnection); - connect(ui.SoloXformCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSoloXformCheckBoxStateChanged(int)), Qt::QueuedConnection); + connect(ui.XformColorScroll, SIGNAL(valueChanged(int)), this, SLOT(OnXformScrollColorIndexChanged(int)), Qt::QueuedConnection); + connect(ui.SoloXformCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSoloXformCheckBoxStateChanged(int)), Qt::QueuedConnection); + + connect(ui.ResetCurvesButton, SIGNAL(clicked(bool)), this, SLOT(OnResetCurvesButtonClicked(bool)), Qt::QueuedConnection); + connect(ui.CurvesView, SIGNAL(PointChangedSignal(int, int, const QPointF&)), this, SLOT(OnCurvesPointChanged(int, int, const QPointF&)), Qt::QueuedConnection); + connect(ui.CurvesAllRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesAllRadioButtonToggled(bool)), Qt::QueuedConnection); + connect(ui.CurvesRedRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesRedRadioButtonToggled(bool)), Qt::QueuedConnection); + connect(ui.CurvesGreenRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesGreenRadioButtonToggled(bool)), Qt::QueuedConnection); + connect(ui.CurvesBlueRadio, SIGNAL(toggled(bool)), this, SLOT(OnCurvesBlueRadioButtonToggled(bool)), Qt::QueuedConnection); } /// @@ -144,6 +151,55 @@ void Fractorium::OnXformRefPaletteResized(int logicalIndex, int oldSize, int new m_Controller->SetPaletteRefTable(nullptr); } +/// +/// Reset the color curve values in the current ember to their default state and also update the curves control. +/// Called when ResetCurvesButton is clicked. +/// Resets the rendering process at either ACCUM_ONLY by default, or FILTER_AND_ACCUM when using early clip. +/// +template +void FractoriumEmberController::ClearColorCurves() +{ + Update([&] + { + m_Ember.m_Curves.Init(); + }, true, m_Renderer->EarlyClip() ? FILTER_AND_ACCUM : ACCUM_ONLY); + + FillCurvesControl(); +} + +void Fractorium::OnResetCurvesButtonClicked(bool checked) { m_Controller->ClearColorCurves(); } + +/// +/// Set the coordinate of the curve point. +/// Called when the position of any of the points in the curves editor is is changed. +/// Resets the rendering process at either ACCUM_ONLY by default, or FILTER_AND_ACCUM when using early clip. +/// +/// The curve index, 0-1/ +/// The point index within the selected curve, 1-2. +/// The new coordinate of the point in terms of the curves control rect. +template +void FractoriumEmberController::ColorCurveChanged(int curveIndex, int pointIndex, const QPointF& point) +{ + Update([&] + { + m_Ember.m_Curves.m_Points[curveIndex][pointIndex].x = point.x(); + m_Ember.m_Curves.m_Points[curveIndex][pointIndex].y = point.y(); + }, true, m_Renderer->EarlyClip() ? FILTER_AND_ACCUM : ACCUM_ONLY); +} + +void Fractorium::OnCurvesPointChanged(int curveIndex, int pointIndex, const QPointF& point) { m_Controller->ColorCurveChanged(curveIndex, pointIndex, point); } + +/// +/// Set the top most points in the curves control, which makes it easier to +/// select a point by putting it on top of all the others. +/// Called when the any of the curve color radio buttons are toggled. +/// +/// The curve index, 0-1/ +void Fractorium::OnCurvesAllRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::ALL); } +void Fractorium::OnCurvesRedRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::RED); } +void Fractorium::OnCurvesGreenRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::GREEN); } +void Fractorium::OnCurvesBlueRadioButtonToggled(bool checked) { if (checked) ui.CurvesView->SetTop(CurveIndex::BLUE); } + /// /// Set the current xform color index spinner to the current xform's color index. /// Set the color cell in the palette ref table. @@ -168,6 +224,28 @@ void FractoriumEmberController::SetCurrentXformColorIndex(double d) }, false); } +/// +/// Set the points in the curves control to the values of the curve points in the current ember. +/// +template +void FractoriumEmberController::FillCurvesControl() +{ + m_Fractorium->ui.CurvesView->blockSignals(true); + + for (int i = 0; i < 4; i++) + { + for (int j = 1; j < 3; j++)//Only do middle points. + { + QPointF point(m_Ember.m_Curves.m_Points[i][j].x, m_Ember.m_Curves.m_Points[i][j].y); + + m_Fractorium->ui.CurvesView->Set(i, j, point); + } + } + + m_Fractorium->ui.CurvesView->blockSignals(false); + m_Fractorium->ui.CurvesView->update(); +} + /// /// Set the color index, speed and opacity spinners with the values of the current xform. /// Set the cells of the palette ref table as well. @@ -180,6 +258,7 @@ void FractoriumEmberController::FillColorWithXform(Xform* xform) m_Fractorium->m_XformColorSpeedSpin->SetValueStealth(xform->m_ColorSpeed); m_Fractorium->m_XformOpacitySpin->SetValueStealth(xform->m_Opacity); m_Fractorium->m_XformDirectColorSpin->SetValueStealth(xform->m_DirectColor); + FillCurvesControl(); m_Fractorium->OnXformColorIndexChanged(xform->m_ColorX, false);//Had to call stealth before to avoid doing an update, now manually update related controls, still without doing an update. } diff --git a/Source/Fractorium/GLEmberController.cpp b/Source/Fractorium/GLEmberController.cpp index 6b37b4b..2ddeaed 100644 --- a/Source/Fractorium/GLEmberController.cpp +++ b/Source/Fractorium/GLEmberController.cpp @@ -203,7 +203,7 @@ void GLEmberController::QueryVMP() /// by an m4. /// template <> -void GLEmberController::MultMatrix(glm::detail::tmat4x4& mat) +void GLEmberController::MultMatrix(tmat4x4& mat) { m_GL->glMultMatrixf(glm::value_ptr(mat)); } @@ -214,7 +214,7 @@ void GLEmberController::MultMatrix(glm::detail::tmat4x4. /// template <> -void GLEmberController::MultMatrix(glm::detail::tmat4x4& mat) +void GLEmberController::MultMatrix(tmat4x4& mat) { m_GL->glMultMatrixd(glm::value_ptr(mat)); } diff --git a/Source/Fractorium/GLWidget.cpp b/Source/Fractorium/GLWidget.cpp index a23f992..1e57a26 100644 --- a/Source/Fractorium/GLWidget.cpp +++ b/Source/Fractorium/GLWidget.cpp @@ -855,7 +855,7 @@ void GLWidget::DrawGrid() RendererBase* renderer = m_Fractorium->m_Controller->Renderer(); float unitX = fabs(renderer->UpperRightX(false) - renderer->LowerLeftX(false)) / 2.0f; float unitY = fabs(renderer->UpperRightY(false) - renderer->LowerLeftY(false)) / 2.0f; - float rad = max(unitX, unitY); + float rad = std::max(unitX, unitY); float xLow = floor(-unitX); float xHigh = ceil(unitX); float yLow = floor(-unitY); diff --git a/Source/Fractorium/OptionsDialog.cpp b/Source/Fractorium/OptionsDialog.cpp index 76191a6..c94164b 100644 --- a/Source/Fractorium/OptionsDialog.cpp +++ b/Source/Fractorium/OptionsDialog.cpp @@ -88,6 +88,7 @@ FractoriumOptionsDialog::FractoriumOptionsDialog(FractoriumSettings* settings, Q m_XmlTemporalSamplesSpin->setValue(m_Settings->XmlTemporalSamples()); m_XmlQualitySpin->setValue(m_Settings->XmlQuality()); m_XmlSupersampleSpin->setValue(m_Settings->XmlSupersample()); + ui.AutoUniqueCheckBox->setChecked(m_Settings->SaveAutoUnique()); OnOpenCLCheckBoxStateChanged(ui.OpenCLCheckBox->isChecked()); } @@ -102,6 +103,7 @@ bool FractoriumOptionsDialog::Transparency() { return ui.TransparencyCheckBox->i bool FractoriumOptionsDialog::OpenCL() { return ui.OpenCLCheckBox->isChecked(); } bool FractoriumOptionsDialog::Double() { return ui.DoublePrecisionCheckBox->isChecked(); } bool FractoriumOptionsDialog::ShowAllXforms() { return ui.ShowAllXformsCheckBox->isChecked(); } +bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); } uint FractoriumOptionsDialog::PlatformIndex() { return ui.PlatformCombo->currentIndex(); } uint FractoriumOptionsDialog::DeviceIndex() { return ui.DeviceCombo->currentIndex(); } uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); } @@ -162,6 +164,7 @@ void FractoriumOptionsDialog::accept() m_Settings->XmlTemporalSamples(m_XmlTemporalSamplesSpin->value()); m_Settings->XmlQuality(m_XmlQualitySpin->value()); m_Settings->XmlSupersample(m_XmlSupersampleSpin->value()); + m_Settings->SaveAutoUnique(AutoUnique()); //Identity. m_Settings->Id(m_IdEdit->text()); @@ -196,7 +199,8 @@ void FractoriumOptionsDialog::reject() m_XmlTemporalSamplesSpin->setValue(m_Settings->XmlTemporalSamples()); m_XmlQualitySpin->setValue(m_Settings->XmlQuality()); m_XmlSupersampleSpin->setValue(m_Settings->XmlSupersample()); - + ui.AutoUniqueCheckBox->setChecked(m_Settings->SaveAutoUnique()); + //Identity. m_IdEdit->setText(m_Settings->Id()); m_UrlEdit->setText(m_Settings->Url()); diff --git a/Source/Fractorium/OptionsDialog.h b/Source/Fractorium/OptionsDialog.h index 1badfe8..81d3269 100644 --- a/Source/Fractorium/OptionsDialog.h +++ b/Source/Fractorium/OptionsDialog.h @@ -40,6 +40,7 @@ private: bool OpenCL(); bool Double(); bool ShowAllXforms(); + bool AutoUnique(); uint PlatformIndex(); uint DeviceIndex(); uint ThreadCount(); diff --git a/Source/Fractorium/OptionsDialog.ui b/Source/Fractorium/OptionsDialog.ui index 3f88c84..1a8fdb3 100644 --- a/Source/Fractorium/OptionsDialog.ui +++ b/Source/Fractorium/OptionsDialog.ui @@ -486,6 +486,13 @@ in interactive mode for each mouse movement + + + + Auto Unique Filenames + + + diff --git a/Source/Fractorium/SpinBox.cpp b/Source/Fractorium/SpinBox.cpp index 1931406..923c375 100644 --- a/Source/Fractorium/SpinBox.cpp +++ b/Source/Fractorium/SpinBox.cpp @@ -77,7 +77,7 @@ void SpinBox::DoubleClickNonZero(int val) /// The small step to use for scrolling while the shift key is down void SpinBox::SmallStep(int step) { - m_SmallStep = min(1, step); + m_SmallStep = std::min(1, step); } ///