From de613404def83d530a6e707459e72e9105204add Mon Sep 17 00:00:00 2001 From: Person Date: Sat, 22 Jul 2017 13:43:35 -0700 Subject: [PATCH] Features: --Add support for Exr files which use 32-bit floats for each RGBA channel. Seems to come out too washed out. --Allow for clearing an individual color curve. --Allow for saving multiple image types in EmberRender and EmberAnimate. All writes are threaded. --Remove --bpc command line argument. Add format png16 as a replacement. --Remove --enable_jpg_comments and --enable_png_comments command line arguments, and replace them with --enable_comments which applies to jpg, png and exr. --Add menu items to variations and affine spinners which allow for easy entry of specific numeric values like pi. --Make final render dialog be wider rather than so tall. Bug fixes: --Fix some OpenCL compile errors on Mac. --Remove ability to save bitmap files on all platforms but Windows. Code changes: --New dependency on OpenEXR. --Allow Curves class to interact with objects of a different template type. --Make m_Curves member of Ember always use float as template type. --Set the length of the curves array to always be 2^17 which should offer enough precision with new 32-bit float pixel types. --Set pixel types to always be 32-bit float. This results in a major reduction of code in the final accumulation part of Renderer.h/cpp. --Remove corresponding code from RendererCL and FinalAccumOpenCLKernelCreator. --Remove Transparency, NumChannels and BytesPerPixel setters from Renderer.h/cpp. --Add new global functions to format final image buffers and place all alpha calculation and scaling code in them. --Blending is no longer needed in OpenGLWidget because of the new pixel type. --Make new class, AffineDoubleSpinBox. --Attempt to make file save dialog code work the same on all OSes. --Remove some unused functions. --- .gitignore | 160 ++++++ .../Installer/FractoriumInstaller.wixproj | 2 +- Builds/MSVC/Installer/Product.wxs | 4 +- Builds/MSVC/VS2015/Ember.rc | Bin 4502 -> 4502 bytes Builds/MSVC/VS2015/EmberAnimate.rc | 8 +- Builds/MSVC/VS2015/EmberAnimate.vcxproj | 8 +- Builds/MSVC/VS2015/EmberCL.rc | Bin 4528 -> 4528 bytes Builds/MSVC/VS2015/EmberGenome.rc | 8 +- Builds/MSVC/VS2015/EmberGenome.vcxproj | 8 +- Builds/MSVC/VS2015/EmberRender.rc | 8 +- Builds/MSVC/VS2015/EmberRender.vcxproj | 8 +- Builds/MSVC/VS2015/EmberTester.vcxproj | 12 +- Builds/MSVC/VS2015/Fractorium.rc | Bin 4470 -> 4470 bytes Builds/MSVC/VS2015/Fractorium.vcxproj | 12 +- Builds/QtCreator/defaults.pri | 33 +- Source/Ember/Curves.h | 92 ++-- Source/Ember/Ember.h | 4 +- Source/Ember/EmberDefines.h | 7 +- Source/Ember/Renderer.cpp | 222 +++----- Source/Ember/Renderer.h | 14 +- Source/Ember/RendererBase.cpp | 67 +-- Source/Ember/RendererBase.h | 13 +- Source/Ember/SheepTools.h | 11 +- Source/EmberAnimate/EmberAnimate.cpp | 179 +++++-- Source/EmberCL/EmberCLFunctions.h | 9 +- Source/EmberCL/EmberCLStructs.h | 8 +- .../EmberCL/FinalAccumOpenCLKernelCreator.cpp | 283 ++-------- .../EmberCL/FinalAccumOpenCLKernelCreator.h | 47 +- Source/EmberCL/RendererCL.cpp | 60 +-- Source/EmberCL/RendererCL.h | 12 +- Source/EmberCommon/EmberCommon.h | 132 ++++- Source/EmberCommon/EmberCommonPch.h | 12 + Source/EmberCommon/EmberOptions.h | 30 +- Source/EmberCommon/JpegUtils.h | 63 ++- Source/EmberGenome/EmberGenome.cpp | 22 +- Source/EmberRender/EmberRender.cpp | 175 +++--- Source/EmberTester/EmberTester.cpp | 97 +++- Source/Fractorium/AboutDialog.ui | 2 +- Source/Fractorium/DoubleSpinBox.cpp | 123 ++++- Source/Fractorium/DoubleSpinBox.h | 55 +- Source/Fractorium/EmberTreeWidgetItem.h | 8 +- Source/Fractorium/FinalRenderDialog.cpp | 42 +- Source/Fractorium/FinalRenderDialog.h | 1 + Source/Fractorium/FinalRenderDialog.ui | 499 ++++++++++-------- .../Fractorium/FinalRenderEmberController.cpp | 48 +- .../Fractorium/FinalRenderEmberController.h | 6 +- Source/Fractorium/Fractorium.cpp | 41 +- Source/Fractorium/Fractorium.h | 39 +- .../Fractorium/FractoriumEmberController.cpp | 5 +- Source/Fractorium/FractoriumEmberController.h | 29 +- Source/Fractorium/FractoriumLibrary.cpp | 7 +- Source/Fractorium/FractoriumMenus.cpp | 16 +- Source/Fractorium/FractoriumPalette.cpp | 25 +- Source/Fractorium/FractoriumPch.h | 18 +- Source/Fractorium/FractoriumRender.cpp | 110 ++-- Source/Fractorium/FractoriumSettings.cpp | 14 +- Source/Fractorium/FractoriumSettings.h | 8 + Source/Fractorium/FractoriumToolbar.cpp | 8 +- Source/Fractorium/FractoriumXformsAffine.cpp | 8 +- .../Fractorium/FractoriumXformsVariations.cpp | 2 +- Source/Fractorium/GLWidget.cpp | 13 +- Source/Fractorium/OptionsDialog.cpp | 3 + Source/Fractorium/OptionsDialog.h | 2 +- Source/Fractorium/OptionsDialog.ui | 12 + .../PaletteEditor/PaletteEditor.cpp | 6 +- Source/Fractorium/SpinBox.cpp | 2 +- makedeps.bat | 3 +- openexr.cmd | 46 ++ 68 files changed, 1755 insertions(+), 1276 deletions(-) create mode 100644 openexr.cmd diff --git a/.gitignore b/.gitignore index ffeaa6f..4f98ea0 100644 --- a/.gitignore +++ b/.gitignore @@ -89,3 +89,163 @@ Builds/include/GL /Builds/MSVC/VS2013/flam3-palettes.xml /Builds/MSVC/VS2015/flam3-palettes.xml *.dmg +/Deps/include/OpenEXR/OpenEXRConfig.h +/Deps/include/OpenEXR/ImfXdr.h +/Deps/include/OpenEXR/ImfWav.h +/Deps/include/OpenEXR/ImfVersion.h +/Deps/include/OpenEXR/ImfVecAttribute.h +/Deps/include/OpenEXR/ImfTimeCodeAttribute.h +/Deps/include/OpenEXR/ImfTimeCode.h +/Deps/include/OpenEXR/ImfTiledRgbaFile.h +/Deps/include/OpenEXR/ImfTiledOutputPart.h +/Deps/include/OpenEXR/ImfTiledOutputFile.h +/Deps/include/OpenEXR/ImfTiledInputPart.h +/Deps/include/OpenEXR/ImfTiledInputFile.h +/Deps/include/OpenEXR/ImfTileDescriptionAttribute.h +/Deps/include/OpenEXR/ImfTileDescription.h +/Deps/include/OpenEXR/ImfThreading.h +/Deps/include/OpenEXR/ImfTestFile.h +/Deps/include/OpenEXR/ImfStringVectorAttribute.h +/Deps/include/OpenEXR/ImfStringAttribute.h +/Deps/include/OpenEXR/ImfStandardAttributes.h +/Deps/include/OpenEXR/ImfSampleCountChannel.h +/Deps/include/OpenEXR/ImfRgbaYca.h +/Deps/include/OpenEXR/ImfRgbaFile.h +/Deps/include/OpenEXR/ImfRgba.h +/Deps/include/OpenEXR/ImfRationalAttribute.h +/Deps/include/OpenEXR/ImfRational.h +/Deps/include/OpenEXR/ImfPreviewImageAttribute.h +/Deps/include/OpenEXR/ImfPreviewImage.h +/Deps/include/OpenEXR/ImfPixelType.h +/Deps/include/OpenEXR/ImfPartType.h +/Deps/include/OpenEXR/ImfPartHelper.h +/Deps/include/OpenEXR/ImfOutputPart.h +/Deps/include/OpenEXR/ImfOutputFile.h +/Deps/include/OpenEXR/ImfOpaqueAttribute.h +/Deps/include/OpenEXR/ImfNamespace.h +/Deps/include/OpenEXR/ImfName.h +/Deps/include/OpenEXR/ImfMultiView.h +/Deps/include/OpenEXR/ImfMultiPartOutputFile.h +/Deps/include/OpenEXR/ImfMultiPartInputFile.h +/Deps/include/OpenEXR/ImfMisc.h +/Deps/include/OpenEXR/ImfMatrixAttribute.h +/Deps/include/OpenEXR/ImfLut.h +/Deps/include/OpenEXR/ImfLineOrderAttribute.h +/Deps/include/OpenEXR/ImfLineOrder.h +/Deps/include/OpenEXR/ImfKeyCodeAttribute.h +/Deps/include/OpenEXR/ImfKeyCode.h +/Deps/include/OpenEXR/ImfIO.h +/Deps/include/OpenEXR/ImfIntAttribute.h +/Deps/include/OpenEXR/ImfInt64.h +/Deps/include/OpenEXR/ImfInputPart.h +/Deps/include/OpenEXR/ImfInputFile.h +/Deps/include/OpenEXR/ImfImageLevel.h +/Deps/include/OpenEXR/ImfImageIO.h +/Deps/include/OpenEXR/ImfImageDataWindow.h +/Deps/include/OpenEXR/ImfImageChannelRenaming.h +/Deps/include/OpenEXR/ImfImageChannel.h +/Deps/include/OpenEXR/ImfImage.h +/Deps/include/OpenEXR/ImfHuf.h +/Deps/include/OpenEXR/ImfHeader.h +/Deps/include/OpenEXR/ImfGenericOutputFile.h +/Deps/include/OpenEXR/ImfGenericInputFile.h +/Deps/include/OpenEXR/ImfFramesPerSecond.h +/Deps/include/OpenEXR/ImfFrameBuffer.h +/Deps/include/OpenEXR/ImfForward.h +/Deps/include/OpenEXR/ImfFloatVectorAttribute.h +/Deps/include/OpenEXR/ImfFloatAttribute.h +/Deps/include/OpenEXR/ImfFlatImageLevel.h +/Deps/include/OpenEXR/ImfFlatImageIO.h +/Deps/include/OpenEXR/ImfFlatImageChannel.h +/Deps/include/OpenEXR/ImfFlatImage.h +/Deps/include/OpenEXR/ImfExport.h +/Deps/include/OpenEXR/ImfEnvmapAttribute.h +/Deps/include/OpenEXR/ImfEnvmap.h +/Deps/include/OpenEXR/ImfDoubleAttribute.h +/Deps/include/OpenEXR/ImfDeepTiledOutputPart.h +/Deps/include/OpenEXR/ImfDeepTiledOutputFile.h +/Deps/include/OpenEXR/ImfDeepTiledInputPart.h +/Deps/include/OpenEXR/ImfDeepTiledInputFile.h +/Deps/include/OpenEXR/ImfDeepScanLineOutputPart.h +/Deps/include/OpenEXR/ImfDeepScanLineOutputFile.h +/Deps/include/OpenEXR/ImfDeepScanLineInputPart.h +/Deps/include/OpenEXR/ImfDeepScanLineInputFile.h +/Deps/include/OpenEXR/ImfDeepImageStateAttribute.h +/Deps/include/OpenEXR/ImfDeepImageState.h +/Deps/include/OpenEXR/ImfDeepImageLevel.h +/Deps/include/OpenEXR/ImfDeepImageIO.h +/Deps/include/OpenEXR/ImfDeepImageChannel.h +/Deps/include/OpenEXR/ImfDeepImage.h +/Deps/include/OpenEXR/ImfDeepFrameBuffer.h +/Deps/include/OpenEXR/ImfDeepCompositing.h +/Deps/include/OpenEXR/ImfCRgbaFile.h +/Deps/include/OpenEXR/ImfConvert.h +/Deps/include/OpenEXR/ImfCompressionAttribute.h +/Deps/include/OpenEXR/ImfCompression.h +/Deps/include/OpenEXR/ImfCompositeDeepScanLine.h +/Deps/include/OpenEXR/ImfChromaticitiesAttribute.h +/Deps/include/OpenEXR/ImfChromaticities.h +/Deps/include/OpenEXR/ImfChannelListAttribute.h +/Deps/include/OpenEXR/ImfChannelList.h +/Deps/include/OpenEXR/ImfBoxAttribute.h +/Deps/include/OpenEXR/ImfB44Compressor.h +/Deps/include/OpenEXR/ImfAttribute.h +/Deps/include/OpenEXR/ImfArray.h +/Deps/include/OpenEXR/ImfAcesFile.h +/Deps/include/OpenEXR/ImathVecAlgo.h +/Deps/include/OpenEXR/ImathVec.h +/Deps/include/OpenEXR/ImathSphere.h +/Deps/include/OpenEXR/ImathShear.h +/Deps/include/OpenEXR/ImathRoots.h +/Deps/include/OpenEXR/ImathRandom.h +/Deps/include/OpenEXR/ImathQuat.h +/Deps/include/OpenEXR/ImathPlatform.h +/Deps/include/OpenEXR/ImathPlane.h +/Deps/include/OpenEXR/ImathNamespace.h +/Deps/include/OpenEXR/ImathMatrixAlgo.h +/Deps/include/OpenEXR/ImathMatrix.h +/Deps/include/OpenEXR/ImathMath.h +/Deps/include/OpenEXR/ImathLineAlgo.h +/Deps/include/OpenEXR/ImathLine.h +/Deps/include/OpenEXR/ImathLimits.h +/Deps/include/OpenEXR/ImathInterval.h +/Deps/include/OpenEXR/ImathInt64.h +/Deps/include/OpenEXR/ImathHalfLimits.h +/Deps/include/OpenEXR/ImathGLU.h +/Deps/include/OpenEXR/ImathGL.h +/Deps/include/OpenEXR/ImathFun.h +/Deps/include/OpenEXR/ImathFrustumTest.h +/Deps/include/OpenEXR/ImathFrustum.h +/Deps/include/OpenEXR/ImathFrame.h +/Deps/include/OpenEXR/ImathForward.h +/Deps/include/OpenEXR/ImathExport.h +/Deps/include/OpenEXR/ImathExc.h +/Deps/include/OpenEXR/ImathEuler.h +/Deps/include/OpenEXR/ImathColorAlgo.h +/Deps/include/OpenEXR/ImathColor.h +/Deps/include/OpenEXR/ImathBoxAlgo.h +/Deps/include/OpenEXR/ImathBox.h +/Deps/include/OpenEXR/IlmThreadSemaphore.h +/Deps/include/OpenEXR/IlmThreadPool.h +/Deps/include/OpenEXR/IlmThreadNamespace.h +/Deps/include/OpenEXR/IlmThreadMutex.h +/Deps/include/OpenEXR/IlmThreadForward.h +/Deps/include/OpenEXR/IlmThreadExport.h +/Deps/include/OpenEXR/IlmThread.h +/Deps/include/OpenEXR/IlmBaseConfig.h +/Deps/include/OpenEXR/IexThrowErrnoExc.h +/Deps/include/OpenEXR/IexNamespace.h +/Deps/include/OpenEXR/IexMathIeeeExc.h +/Deps/include/OpenEXR/IexMathFpu.h +/Deps/include/OpenEXR/IexMathFloatExc.h +/Deps/include/OpenEXR/IexMathExc.h +/Deps/include/OpenEXR/IexMacros.h +/Deps/include/OpenEXR/IexForward.h +/Deps/include/OpenEXR/IexExport.h +/Deps/include/OpenEXR/IexErrnoExc.h +/Deps/include/OpenEXR/IexBaseExc.h +/Deps/include/OpenEXR/Iex.h +/Deps/include/OpenEXR/halfLimits.h +/Deps/include/OpenEXR/halfFunction.h +/Deps/include/OpenEXR/halfExport.h +/Deps/include/OpenEXR/half.h diff --git a/Builds/MSVC/Installer/FractoriumInstaller.wixproj b/Builds/MSVC/Installer/FractoriumInstaller.wixproj index 6d98384..d81de6c 100644 --- a/Builds/MSVC/Installer/FractoriumInstaller.wixproj +++ b/Builds/MSVC/Installer/FractoriumInstaller.wixproj @@ -6,7 +6,7 @@ 3.7 {c8096c47-e358-438c-a520-146d46b0637d} 2.0 - Fractorium_1.0.0.4 + Fractorium_1.0.0.5 Package $(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets diff --git a/Builds/MSVC/Installer/Product.wxs b/Builds/MSVC/Installer/Product.wxs index 862d105..9d95ad9 100644 --- a/Builds/MSVC/Installer/Product.wxs +++ b/Builds/MSVC/Installer/Product.wxs @@ -1,6 +1,6 @@ - + @@ -13,7 +13,7 @@ - + Sb-bc&;MhXp` delta 44 zcmbQHJWYAS7Y;^~$zM6l8BI3pa_(mWa`y9xZ)V|{z{qUEpfuT#Pj>Sb-bc&;MWqdy diff --git a/Builds/MSVC/VS2015/EmberAnimate.rc b/Builds/MSVC/VS2015/EmberAnimate.rc index 84739d2..b741f23 100644 --- a/Builds/MSVC/VS2015/EmberAnimate.rc +++ b/Builds/MSVC/VS2015/EmberAnimate.rc @@ -49,8 +49,8 @@ // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1, 0, 0, 4 - PRODUCTVERSION 1, 0, 0, 4 + FILEVERSION 1, 0, 0, 5 + PRODUCTVERSION 1, 0, 0, 5 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -67,12 +67,12 @@ BEGIN VALUE "CompanyName", "Open Source" VALUE "FileDescription", "Renders fractal flames as animations with motion blur" - VALUE "FileVersion", "1.0.0.4" + VALUE "FileVersion", "1.0.0.5" VALUE "InternalName", "EmberAnimate.exe" VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3" VALUE "OriginalFilename", "EmberAnimate.exe" VALUE "ProductName", "Ember Animate" - VALUE "ProductVersion", "1.0.0.4" + VALUE "ProductVersion", "1.0.0.5" END END BLOCK "VarFileInfo" diff --git a/Builds/MSVC/VS2015/EmberAnimate.vcxproj b/Builds/MSVC/VS2015/EmberAnimate.vcxproj index c7d7152..30ebc7a 100644 --- a/Builds/MSVC/VS2015/EmberAnimate.vcxproj +++ b/Builds/MSVC/VS2015/EmberAnimate.vcxproj @@ -58,7 +58,7 @@ Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(TargetDir)$(TargetName).pdb - $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include + $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\Deps\include\OpenEXR;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include EmberCommonPch.h @@ -72,7 +72,7 @@ Console true - libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) + libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) $(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName) @@ -90,7 +90,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(TargetDir)$(TargetName).pdb - $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include + $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\Deps\include\OpenEXR;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include EmberCommonPch.h @@ -105,7 +105,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" true true true - libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) + libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) $(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName) diff --git a/Builds/MSVC/VS2015/EmberCL.rc b/Builds/MSVC/VS2015/EmberCL.rc index 27ea16ff87eee93bb6dedc14a934ad39d74ac2b5..1cbefa4b022ed455da1f2b0f3bc884c663cfdc25 100644 GIT binary patch delta 46 zcmdm>yg_-xCk{r_$)7pQSxp&u8Mr2Iyg_-xCk{rF$)7pQSxp#t8Mr2IDisabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(TargetDir)$(TargetName).pdb - $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include + $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\Deps\include\OpenEXR;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include EmberCommonPch.h @@ -72,7 +72,7 @@ Console true - libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) + libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) $(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName) @@ -90,7 +90,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(TargetDir)$(TargetName).pdb - $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include + $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\Deps\include\OpenEXR;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include EmberCommonPch.h @@ -105,7 +105,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" true true true - libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) + libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) $(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName) diff --git a/Builds/MSVC/VS2015/EmberRender.rc b/Builds/MSVC/VS2015/EmberRender.rc index 850012d..de57dd8 100644 --- a/Builds/MSVC/VS2015/EmberRender.rc +++ b/Builds/MSVC/VS2015/EmberRender.rc @@ -49,8 +49,8 @@ // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1, 0, 0, 4 - PRODUCTVERSION 1, 0, 0, 4 + FILEVERSION 1, 0, 0, 5 + PRODUCTVERSION 1, 0, 0, 5 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -67,12 +67,12 @@ BEGIN VALUE "CompanyName", "Open Source" VALUE "FileDescription", "Renders fractal flames as single images" - VALUE "FileVersion", "1.0.0.4" + VALUE "FileVersion", "1.0.0.5" VALUE "InternalName", "EmberRender.exe" VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3" VALUE "OriginalFilename", "EmberRender.exe" VALUE "ProductName", "Ember Render" - VALUE "ProductVersion", "1.0.0.4" + VALUE "ProductVersion", "1.0.0.5" END END BLOCK "VarFileInfo" diff --git a/Builds/MSVC/VS2015/EmberRender.vcxproj b/Builds/MSVC/VS2015/EmberRender.vcxproj index ad01733..fa55163 100644 --- a/Builds/MSVC/VS2015/EmberRender.vcxproj +++ b/Builds/MSVC/VS2015/EmberRender.vcxproj @@ -58,7 +58,7 @@ Disabled WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) $(TargetDir)$(TargetName).pdb - $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include + $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\Deps\include\OpenEXR;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include EmberCommonPch.h @@ -72,7 +72,7 @@ Console true - libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) + libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) $(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName) @@ -90,7 +90,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) $(TargetDir)$(TargetName).pdb - $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include + $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\Deps\include\OpenEXR;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include EmberCommonPch.h @@ -106,7 +106,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" true true true - libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) + libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) $(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName) diff --git a/Builds/MSVC/VS2015/EmberTester.vcxproj b/Builds/MSVC/VS2015/EmberTester.vcxproj index 7ec4eb5..048f053 100644 --- a/Builds/MSVC/VS2015/EmberTester.vcxproj +++ b/Builds/MSVC/VS2015/EmberTester.vcxproj @@ -56,9 +56,9 @@ Use Level3 Disabled - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + WIN32;_DEBUG;_CONSOLE;OPENEXR_DLL;%(PreprocessorDefinitions) $(TargetDir)$(TargetName).pdb - $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include + $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\Deps\include\OpenEXR;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include EmberCommonPch.h @@ -72,7 +72,7 @@ Console true - libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) + libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) $(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName) @@ -88,9 +88,9 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" MaxSpeed true true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + WIN32;NDEBUG;_CONSOLE;OPENEXR_DLL;%(PreprocessorDefinitions) $(TargetDir)$(TargetName).pdb - $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include + $(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\Deps\include\OpenEXR;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include EmberCommonPch.h @@ -105,7 +105,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)" true true true - libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) + libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies) $(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName) diff --git a/Builds/MSVC/VS2015/Fractorium.rc b/Builds/MSVC/VS2015/Fractorium.rc index 908195e0cf1e8807a2eb85e003e97da79dadd345..e4433c86747e428b9fd9f94592b6cea27bdf8ef8 100644 GIT binary patch delta 44 zcmeyS^i65Q84gC%$!9st8BI5{a;{_oau|8UHy`6($H;8Tpfp*KPj>SZ-bc&;QE&~e delta 44 zcmeyS^i65Q84gC1$!9st8BI2`a;{_oau|8UHy`6($H;8Lpfp*KPj>SZ-bc&;Q40;K diff --git a/Builds/MSVC/VS2015/Fractorium.vcxproj b/Builds/MSVC/VS2015/Fractorium.vcxproj index 2e638c0..bc3b776 100644 --- a/Builds/MSVC/VS2015/Fractorium.vcxproj +++ b/Builds/MSVC/VS2015/Fractorium.vcxproj @@ -57,7 +57,7 @@ UNICODE;WIN32;QT_DLL;QT_CORE_LIB;QT_GUI_LIB;QT_MULTIMEDIA_LIB;QT_HELP_LIB;QT_OPENGL_LIB;QT_WIDGETS_LIB;QT_XML_LIB;%(PreprocessorDefinitions) - .;.\PaletteEditor;$(QTDIR)\include;$(ProjectDir)..\..\..\Fractorium\GeneratedFiles;$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName;$(QTDIR)\..\qtmultimedia\include\QtMultimedia;$(QTDIR)\..\qtmultimedia\include;$(QTDIR)\..\qttools\include;$(QTDIR)\..\qttools\include\QtHelp;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtOpenGL;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtXml;.\GeneratedFiles;$(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\Fractorium;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include;.\GeneratedFiles\$(ConfigurationName);%(AdditionalIncludeDirectories) + .;.\PaletteEditor;$(QTDIR)\include;$(ProjectDir)..\..\..\Fractorium\GeneratedFiles;$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName;$(QTDIR)\..\qtmultimedia\include\QtMultimedia;$(QTDIR)\..\qtmultimedia\include;$(QTDIR)\..\qttools\include;$(QTDIR)\..\qttools\include\QtHelp;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtOpenGL;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtXml;.\GeneratedFiles;$(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\Fractorium;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\Deps\include\OpenEXR;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include;.\GeneratedFiles\$(ConfigurationName);%(AdditionalIncludeDirectories) Disabled ProgramDatabase MultiThreadedDebugDLL @@ -81,7 +81,7 @@ $(OutDir)\$(ProjectName).exe $(ProjectDir)..\..\..\Deps;$(QTDIR)\lib;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName);%(AdditionalLibraryDirectories) true - qtmaind.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5OpenGLd.lib;opengl32.lib;glu32.lib;opencl.lib;Qt5Widgetsd.lib;Qt5Xmld.lib;Ws2_32.lib;libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;%(AdditionalDependencies) + qtmaind.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5OpenGLd.lib;opengl32.lib;glu32.lib;opencl.lib;Qt5Widgetsd.lib;Qt5Xmld.lib;Ws2_32.lib;libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;%(AdditionalDependencies) xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Deps\*.dll" "$(OutDir)" @@ -102,7 +102,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\tatasz_pack_02_dark.gradient" "$( xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\tatasz_pack_02_warmer.gradient" "$(OutDir)" xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\tatasz_pack_03.gradient" "$(OutDir)" -xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\dark.qss" "$(OutDir)" +xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\dark_windows.qss" "$(OutDir)" xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Cored.dll" "$(OutDir)" xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Guid.dll" "$(OutDir)" @@ -115,7 +115,7 @@ xcopy /F /Y /R /D "$(QTDIR)\plugins\platforms\qwindowsd.dll" "$(OutDir)\platform UNICODE;WIN32;QT_DLL;QT_NO_DEBUG;NDEBUG;QT_CORE_LIB;QT_GUI_LIB;QT_MULTIMEDIA_LIB;QT_HELP_LIB;QT_OPENGL_LIB;QT_WIDGETS_LIB;QT_XML_LIB;%(PreprocessorDefinitions) - .;.\PaletteEditor;$(QTDIR)\include;$(ProjectDir)..\..\..\Fractorium\GeneratedFiles;$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName;$(QTDIR)\..\qtmultimedia\include\QtMultimedia;$(QTDIR)\..\qtmultimedia\include;$(QTDIR)\..\qttools\include;$(QTDIR)\..\qttools\include\QtHelp;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtOpenGL;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtXml;.\GeneratedFiles;$(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\Fractorium;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include;.\GeneratedFiles\$(ConfigurationName);%(AdditionalIncludeDirectories) + .;.\PaletteEditor;$(QTDIR)\include;$(ProjectDir)..\..\..\Fractorium\GeneratedFiles;$(ProjectDir)..\..\..\Fractorium\GeneratedFiles\ConfigurationName;$(QTDIR)\..\qtmultimedia\include\QtMultimedia;$(QTDIR)\..\qtmultimedia\include;$(QTDIR)\..\qttools\include;$(QTDIR)\..\qttools\include\QtHelp;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtOpenGL;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtXml;.\GeneratedFiles;$(ProjectDir)..\..\..\Source\Ember;$(ProjectDir)..\..\..\Source\EmberCL;$(ProjectDir)..\..\..\Source\EmberCommon;$(ProjectDir)..\..\..\Source\Fractorium;$(ProjectDir)..\..\..\..\glm;$(ProjectDir)..\..\..\..\tbb\include;$(ProjectDir)..\..\..\..\libjpeg;$(ProjectDir)..\..\..\..\libpng;$(ProjectDir)..\..\..\..\libxml2\include;$(ProjectDir)..\..\..\Deps\include\OpenEXR;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include;.\GeneratedFiles\$(ConfigurationName);%(AdditionalIncludeDirectories) ProgramDatabase MultiThreadedDLL false @@ -138,7 +138,7 @@ xcopy /F /Y /R /D "$(QTDIR)\plugins\platforms\qwindowsd.dll" "$(OutDir)\platform $(OutDir)\$(ProjectName).exe $(ProjectDir)..\..\..\Deps;$(QTDIR)\lib;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName);%(AdditionalLibraryDirectories) true - qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5OpenGL.lib;opengl32.lib;glu32.lib;opencl.lib;Qt5Widgets.lib;Ws2_32.lib;libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;%(AdditionalDependencies) + qtmain.lib;Qt5Core.lib;Qt5Gui.lib;Qt5OpenGL.lib;opengl32.lib;glu32.lib;opencl.lib;Qt5Widgets.lib;Ws2_32.lib;libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;%(AdditionalDependencies) 0.1 @@ -160,7 +160,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\tatasz_pack_02_dark.gradient" "$( xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\tatasz_pack_02_warmer.gradient" "$(OutDir)" xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\tatasz_pack_03.gradient" "$(OutDir)" -xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\dark.qss" "$(OutDir)" +xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\dark_windows.qss" "$(OutDir)" xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Core.dll" "$(OutDir)" xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Gui.dll" "$(OutDir)" diff --git a/Builds/QtCreator/defaults.pri b/Builds/QtCreator/defaults.pri index 000fe27..be0867c 100644 --- a/Builds/QtCreator/defaults.pri +++ b/Builds/QtCreator/defaults.pri @@ -1,6 +1,7 @@ -VERSION = 1.0.0.4 +VERSION = 1.0.0.5 win32:CONFIG += skip_target_version_ext CONFIG += c++14 + #message(PWD: $$absolute_path($$PWD)) #1) Declare the root of all files in this project, everything else will be @@ -26,7 +27,7 @@ SRC_DIR = $$EMBER_ROOT/Source SRC_COMMON_DIR = $$absolute_path($$EMBER_ROOT/Source/EmberCommon) ASSETS_DIR = $$absolute_path($$EMBER_ROOT/Data) QTCREATOR_DIR = $$absolute_path($$EMBER_ROOT/Builds/QtCreator) -win32:RCPATH=$$absolute_path($$QTCREATOR_DIR/../MSVC/VS2013) +win32:RCPATH=$$absolute_path($$QTCREATOR_DIR/../MSVC/VS2015) #4) Add up all include paths. INCLUDEPATH += $$LOCAL_INCLUDE_DIR @@ -41,6 +42,7 @@ win32 { INCLUDEPATH += $$EXTERNAL_DIR/libxml2/include INCLUDEPATH += $$EXTERNAL_DIR/tbb/include INCLUDEPATH += $$EXTERNAL_DIR/zlib + INCLUDEPATH += $$absolute_path($$EXTERNAL_LIB)/include/OpenEXR } !win32 { @@ -51,6 +53,7 @@ win32 { INCLUDEPATH += /usr/local/include/GL INCLUDEPATH += /usr/include/glm INCLUDEPATH += /usr/include/tbb + INCLUDEPATH += /usr/include/OpenEXR unix:!macx { INCLUDEPATH += /usr/include/libxml2 @@ -82,6 +85,9 @@ else { LIBS += $$absolute_path($$EXTERNAL_LIB)/libxml2.lib LIBS += $$absolute_path($$EXTERNAL_LIB)/tbb.lib LIBS += $$absolute_path($$EXTERNAL_LIB)/zlib.lib + LIBS += $$absolute_path($$EXTERNAL_LIB)/Half.lib + LIBS += $$absolute_path($$EXTERNAL_LIB)/Iex.lib + LIBS += $$absolute_path($$EXTERNAL_LIB)/IlmImf.lib } !win32 { @@ -89,6 +95,9 @@ else { LIBS += -lpng LIBS += -ltbb LIBS += -lpthread + LIBS += -lHalf + LIBS += -lIex + LIBS += -lIlmImf unix:!macx { LIBS += -lxml2 @@ -242,6 +251,26 @@ win32 { tbb.path = $$BIN_INSTALL_DIR tbb.files = $$absolute_path($$EMBER_ROOT/Deps/tbb.dll) INSTALLS += tbb + + half.path = $$BIN_INSTALL_DIR + half.files = $$absolute_path($$EMBER_ROOT/Deps/Half.dll) + INSTALLS += half + + iex.path = $$BIN_INSTALL_DIR + iex.files = $$absolute_path($$EMBER_ROOT/Deps/Iex-2_2.dll) + INSTALLS += iex + + imath.path = $$BIN_INSTALL_DIR + imath.files = $$absolute_path($$EMBER_ROOT/Deps/Imath-2_2.dll) + INSTALLS += imath + + ilmthread.path = $$BIN_INSTALL_DIR + ilmthread.files = $$absolute_path($$EMBER_ROOT/Deps/IlmThread-2_2.dll) + INSTALLS += ilmthread + + ilmimf.path = $$BIN_INSTALL_DIR + ilmimf.files = $$absolute_path($$EMBER_ROOT/Deps/IlmImf-2_2.dll) + INSTALLS += ilmimf } #11) Print values of relevant variables for debugging. diff --git a/Source/Ember/Curves.h b/Source/Ember/Curves.h index 0323dba..32cf561 100644 --- a/Source/Ember/Curves.h +++ b/Source/Ember/Curves.h @@ -85,7 +85,8 @@ public: /// /// The Curves object to add /// Reference to updated self - Curves& operator += (const Curves& curves) + template + Curves& operator += (const Curves& curves) { for (size_t i = 0; i < 4; i++) { @@ -93,8 +94,7 @@ public: 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]; + m_Weights[i] += curves.m_Weights[i]; } return *this; @@ -105,7 +105,8 @@ public: /// /// The Curves object to multiply this one by /// Reference to updated self - Curves& operator *= (const Curves& curves) + template + Curves& operator *= (const Curves& curves) { for (size_t i = 0; i < 4; i++) { @@ -113,8 +114,7 @@ public: 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]; + m_Weights[i] *= curves.m_Weights[i]; } return *this; @@ -125,16 +125,16 @@ public: /// /// The scalar to multiply this object by /// Reference to updated self - Curves& operator *= (const T& t) + template + Curves& operator *= (const U& t) { for (size_t 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; + m_Points[i][0] *= T(t); + m_Points[i][1] *= T(t); + m_Points[i][2] *= T(t); + m_Points[i][3] *= T(t); + m_Weights[i] *= T(t); } return *this; @@ -151,7 +151,21 @@ public: 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 a specific curve and its weight value to their default state. + /// + void Init(size_t i) + { + if (i < 4) + { + 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); } } @@ -176,9 +190,9 @@ public: for (size_t 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))) + (m_Points[i][1] != v2T(0)) || + (m_Points[i][2] != v2T(1)) || + (m_Points[i][3] != v2T(1))) { set = true; break; @@ -197,12 +211,10 @@ public: { 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; } @@ -217,20 +229,14 @@ private: /// 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); - + T s = 1 - t; + T s2 = s * s; + T s3 = s * s * s; + T t2 = t * t; + T t3 = t * 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); + T 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); + T denom = (w->x * s3) + (w->y * s2 * 3 * t) + (w->z * s * 3 * t2) + (w->w * t3); if (std::isnan(nom_x) || std::isnan(nom_y) || std::isnan(denom) || denom == 0) return; @@ -247,24 +253,24 @@ public: //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. +/// Multiplication operator to multiply a Curves object by a scalar of type U. /// /// 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) +template +Curves operator * (const Curves& curves, const U& t) { + T tt = T(t); Curves c(curves); for (size_t 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; + c.m_Points[i][0] *= tt; + c.m_Points[i][1] *= tt; + c.m_Points[i][2] *= tt; + c.m_Points[i][3] *= tt; + c.m_Weights[i] *= tt; } return c; @@ -276,8 +282,8 @@ Curves operator * (const Curves& curves, const T& t) /// 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) +template +Curves operator * (const U& t, const Curves& curves) { return curves * t; } diff --git a/Source/Ember/Ember.h b/Source/Ember/Ember.h index 7398698..86008dc 100644 --- a/Source/Ember/Ember.h +++ b/Source/Ember/Ember.h @@ -727,7 +727,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); + InterpX, &Ember::m_Curves>(embers, coefs, size); //Normally done in assignment, must manually do here. SetProjFunc(); //An extra step needed here due to the OOD that was not needed in the original. @@ -1653,7 +1653,7 @@ public: Palette m_Palette;//Final palette that is actually used is a copy of this inside of render, which will be of type bucketT (float). //Curves used to adjust the color during final accumulation. - Curves m_Curves; + Curves m_Curves; //Strings. diff --git a/Source/Ember/EmberDefines.h b/Source/Ember/EmberDefines.h index bb144c4..5a6f80e 100644 --- a/Source/Ember/EmberDefines.h +++ b/Source/Ember/EmberDefines.h @@ -37,7 +37,7 @@ static void sincos(float x, float* s, float* c) namespace EmberNs { -#define EMBER_VERSION "1.0.0.4" +#define EMBER_VERSION "1.0.0.5" #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 @@ -68,6 +68,9 @@ namespace EmberNs #define TMAX std::numeric_limits::max() #define FLOAT_MAX_TAN 8388607.0f #define FLOAT_MIN_TAN -FLOAT_MAX_TAN +#define CURVES_LENGTH 131072 +#define CURVES_LENGTH_M1 131071.0f +#define ONE_OVER_CURVES_LENGTH_M1 7.62945273935e-6f #define EMPTYFIELD -9999 typedef std::chrono::high_resolution_clock Clock; typedef std::chrono::duration> DoubleMs; @@ -110,6 +113,7 @@ typedef std::lock_guard rlg; #define m3T glm::tmat3x3 #define m4T glm::tmat4x4 #define m23T glm::tmat2x3 + typedef vector> vv4F; #else #define v2T glm::detail::tvec2 #define v3T glm::detail::tvec3 @@ -121,6 +125,7 @@ typedef std::lock_guard rlg; #define m3T glm::detail::tmat3x3 #define m4T glm::detail::tmat4x4 #define m23T glm::detail::tmat2x3 + typedef vector> vv4F; #endif enum class eInterp : et { EMBER_INTERP_LINEAR = 0, EMBER_INTERP_SMOOTH = 1 }; diff --git a/Source/Ember/Renderer.cpp b/Source/Ember/Renderer.cpp index a95a60f..8fdbd5b 100644 --- a/Source/Ember/Renderer.cpp +++ b/Source/Ember/Renderer.cpp @@ -9,7 +9,9 @@ namespace EmberNs template Renderer::Renderer() { - m_Csa.resize(size_t(std::pow(size_t(256), BytesPerChannel())));//Need to at least have something here so the derived RendererCL can do the initial buffer allocation. + //Use a very large number regardless of the size of the output pixels. This should be sufficient granularity, even though + //it's technically less than the number of distinct values representable by a 32-bit float. + m_Csa.resize(size_t(CURVES_LENGTH)); } /// @@ -344,7 +346,7 @@ bool Renderer::CreateTemporalFilter(bool& newAlloc) /// Offset in finalImage to store the pixels to. Default: 0. /// True if nothing went wrong, else false. template -eRenderStatus Renderer::Run(vector& finalImage, double time, size_t subBatchCountOverride, bool forceOutput, size_t finalOffset) +eRenderStatus Renderer::Run(vector& finalImage, double time, size_t subBatchCountOverride, bool forceOutput, size_t finalOffset) { m_InRender = true; EnterRender(); @@ -645,7 +647,7 @@ AccumOnly: CreateSpatialFilter(newFilterAlloc); m_DensityFilterOffset = m_GutterWidth - size_t(Clamp((T(m_SpatialFilter->FinalFilterWidth()) - T(Supersample())) / 2, 0, T(m_GutterWidth))); m_CurvesSet = m_Ember.m_Curves.CurvesSet(); - ComputeCurves(true);//Color curves must be re-calculated as well. + ComputeCurves();//Color curves must be re-calculated as well. if (AccumulatorToFinalImage(finalImage, finalOffset) == eRenderStatus::RENDER_OK) { @@ -822,6 +824,7 @@ bool Renderer::ResetBuckets(bool resetHist, bool resetAccum) } /// +/// THIS IS UNUSED. /// Log scales a single row with a specially structured loop that will be vectorized by the compiler. /// Note this adds an epsilon to the denomiator used to compute the logScale /// value because the conditional check for zero would have prevented the loop from @@ -882,7 +885,7 @@ eRenderStatus Renderer::LogScaleDensityFilter(bool forceOutput) bucketT* __restrict hist = glm::value_ptr(m_HistBuckets[i]);//Vectorizer can't tell these point to different locations. bucketT* __restrict acc = glm::value_ptr(m_AccumulatorBuckets[i]); - for (size_t v = 0; v < 4; v++) + for (size_t v = 0; v < 4; v++)//Vectorized by compiler. acc[v] = hist[v] * logScale; } } @@ -1063,7 +1066,7 @@ eRenderStatus Renderer::GaussianDensityFilter() /// Offset in the buffer to store the pixels to /// True if not prematurely aborted, else false. template -eRenderStatus Renderer::AccumulatorToFinalImage(vector& pixels, size_t finalOffset) +eRenderStatus Renderer::AccumulatorToFinalImage(vector& pixels, size_t finalOffset) { if (PrepFinalAccumVector(pixels)) return AccumulatorToFinalImage(pixels.data(), finalOffset); @@ -1079,14 +1082,13 @@ eRenderStatus Renderer::AccumulatorToFinalImage(vector& pixels /// Offset in the buffer to store the pixels to. Default: 0. /// True if not prematurely aborted, else false. template -eRenderStatus Renderer::AccumulatorToFinalImage(byte* pixels, size_t finalOffset) +eRenderStatus Renderer::AccumulatorToFinalImage(v4F* pixels, size_t finalOffset) { if (!pixels) return eRenderStatus::RENDER_ERROR; EnterFinalAccum(); //Timing t(4); - bool doAlpha = NumChannels() > 3; size_t filterWidth = m_SpatialFilter->FinalFilterWidth(); bucketT g, linRange, vibrancy; Color background; @@ -1104,7 +1106,7 @@ eRenderStatus Renderer::AccumulatorToFinalImage(byte* pixels, size_t while (rowStart < rowEnd && !m_Abort)//Use the pointer itself as the offset to save an extra addition per iter. { - GammaCorrection(*rowStart, background, g, linRange, vibrancy, true, false, glm::value_ptr(*rowStart));//Write back in place. + GammaCorrection(*rowStart, background, g, linRange, vibrancy, false, glm::value_ptr(*rowStart));//Write back in place. rowStart++; } }); @@ -1123,11 +1125,12 @@ eRenderStatus Renderer::AccumulatorToFinalImage(byte* pixels, size_t parallel_for(size_t(0), FinalRasH(), size_t(1), [&](size_t j) { Color newBucket; - size_t pixelsRowStart = (m_YAxisUp ? ((FinalRasH() - j) - 1) : j) * FinalRowSize();//Pull out of inner loop for optimization. + size_t pixelsRowStart = (m_YAxisUp ? ((FinalRasH() - j) - 1) : j) * FinalRasW();//Pull out of inner loop for optimization. size_t y = m_DensityFilterOffset + (j * Supersample());//Start at the beginning row of each super sample block. size_t clampedFilterH = std::min(filterWidth, m_SuperRasH - y);//Make sure the filter doesn't go past the bottom of the gutter. + auto pv4T = pixels + pixelsRowStart; - for (size_t i = 0; i < FinalRasW(); i++, pixelsRowStart += PixelSize()) + for (size_t i = 0; i < FinalRasW(); i++, pv4T++) { size_t ii, jj; size_t x = m_DensityFilterOffset + (i * Supersample());//Start at the beginning column of each super sample block. @@ -1149,68 +1152,8 @@ eRenderStatus Renderer::AccumulatorToFinalImage(byte* pixels, size_t } } - if (BytesPerChannel() == 2) - { - auto p16 = reinterpret_cast(pixels + pixelsRowStart); - - if (EarlyClip()) - { - newBucket *= bucketT(65535); - - if (m_CurvesSet) - { - CurveAdjust(newBucket.r, 1); - CurveAdjust(newBucket.g, 2); - CurveAdjust(newBucket.b, 3); - } - - p16[0] = glm::uint16(Clamp(newBucket.r, 0, 65535)); - p16[1] = glm::uint16(Clamp(newBucket.g, 0, 65535)); - p16[2] = glm::uint16(Clamp(newBucket.b, 0, 65535)); - - if (doAlpha) - { - if (Transparency()) - p16[3] = byte(Clamp(newBucket.a, 0, 65535)); - else - p16[3] = 65535; - } - } - else - { - GammaCorrection(*(reinterpret_cast*>(&newBucket)), background, g, linRange, vibrancy, doAlpha, true, p16); - } - } - else - { - if (EarlyClip()) - { - newBucket *= bucketT(255); - - 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)); - - if (doAlpha) - { - if (Transparency()) - pixels[pixelsRowStart + 3] = byte(Clamp(newBucket.a, 0, 255)); - else - pixels[pixelsRowStart + 3] = 255; - } - } - else - { - GammaCorrection(*(reinterpret_cast*>(&newBucket)), background, g, linRange, vibrancy, doAlpha, true, pixels + pixelsRowStart); - } - } + auto pf = reinterpret_cast(pv4T); + GammaCorrection(*(reinterpret_cast*>(&newBucket)), background, g, linRange, vibrancy, true, pf); } }); @@ -1222,30 +1165,15 @@ eRenderStatus Renderer::AccumulatorToFinalImage(byte* pixels, size_t if (ph >= FinalRasH()) ph = FinalRasH(); - if (BytesPerChannel() == 1) + for (j = 0; j < ph; j++) { - for (j = 0; j < ph; j++) + for (i = 0; i < FinalRasW(); i++) { - for (i = 0; i < FinalRasW(); i++) - { - auto p = pixels + (NumChannels() * (i + j * FinalRasW())); - p[0] = byte(m_TempEmber.m_Palette[i * 256 / FinalRasW()][0] * WHITE);//The palette is [0..1], output image is [0..255]. - p[1] = byte(m_TempEmber.m_Palette[i * 256 / FinalRasW()][1] * WHITE); - p[2] = byte(m_TempEmber.m_Palette[i * 256 / FinalRasW()][2] * WHITE); - } - } - } - else//BPC == 2. - { - for (j = 0; j < ph; j++) - { - for (i = 0; i < FinalRasW(); i++) - { - auto p16 = reinterpret_cast(pixels + (PixelSize() * (i + j * FinalRasW()))); - p16[0] = glm::uint16(m_TempEmber.m_Palette[i * 256 / FinalRasW()][0] * WHITE * bucketT(256)); //The palette is [0..1], output image is [0..65535]. - p16[1] = glm::uint16(m_TempEmber.m_Palette[i * 256 / FinalRasW()][1] * WHITE * bucketT(256)); - p16[2] = glm::uint16(m_TempEmber.m_Palette[i * 256 / FinalRasW()][2] * WHITE * bucketT(256)); - } + auto p = pixels + (i + j * FinalRasW()); + p->r = m_TempEmber.m_Palette[i * 256 / FinalRasW()][0]; + p->g = m_TempEmber.m_Palette[i * 256 / FinalRasW()][1]; + p->b = m_TempEmber.m_Palette[i * 256 / FinalRasW()][2]; + p->a = 1; } } } @@ -1322,7 +1250,6 @@ EmberStats Renderer::Iterate(size_t iterCount, size_t temporalSample //t.Tic(); //Iterating, loop 3. m_BadVals[threadIndex] += m_Iterator->Iterate(m_ThreadEmbers[threadIndex], params, m_Samples[threadIndex].data(), m_Rand[threadIndex]); - //m_BadVals[threadIndex] += m_Iterator->Iterate(m_Ember, params, m_Samples[threadIndex].data(), m_Rand[threadIndex]); //iterationTime += t.Toc(); if (m_LockAccum) @@ -1677,112 +1604,97 @@ void Renderer::AddToAccum(const tvec4& bucke /// Because this code is used in both early and late clipping, a few extra arguments are passed /// to specify what actions to take. Coupled with an additional template argument, this allows /// using one function to perform all color clipping, gamma correction and final accumulation. -/// Template argument accumT is expected to match bucketT for the case of early clipping, byte for late clip for -/// images with one byte per channel and unsigned short for images with two bytes per channel. +/// Template argument accumT is expected to always be float4. /// /// The pixel to correct /// The background color /// The gamma to use /// The linear range to use /// The vibrancy to use -/// True if either early clip, or late clip with 4 channel output, else false. /// True if late clip, else false. /// The storage space for the corrected values to be written to template template -void Renderer::GammaCorrection(tvec4& bucket, Color& background, bucketT g, bucketT linRange, bucketT vibrancy, bool doAlpha, bool scale, accumT* correctedChannels) +void Renderer::GammaCorrection(tvec4& bucket, Color& background, bucketT g, bucketT linRange, bucketT vibrancy, bool scale, accumT* correctedChannels) { - bucketT alpha, ls, a, 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. - static bucketT scaleVal = numeric_limits::max(); + auto bt1 = bucketT(1); - if (bucket.a <= 0) + if (scale && EarlyClip()) { - alpha = 0; - ls = 0; + if (m_CurvesSet) + { + CurveAdjust(bucket.r, 1); + CurveAdjust(bucket.g, 2); + CurveAdjust(bucket.b, 3); + } + + correctedChannels[0] = accumT(Clamp(bucket.r, 0, bt1)); + correctedChannels[1] = accumT(Clamp(bucket.g, 0, bt1)); + correctedChannels[2] = accumT(Clamp(bucket.b, 0, bt1)); + correctedChannels[3] = accumT(Clamp(bucket.a, 0, bt1)); } else { - alpha = Palette::CalcAlpha(bucket.a, g, linRange); - ls = vibrancy * alpha / bucket.a; - ClampRef(alpha, 0, 1); - } + bucketT alpha, ls, a, 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. - Palette::template CalcNewRgb(glm::value_ptr(bucket), ls, HighlightPower(), newRgb); - - for (glm::length_t rgbi = 0; rgbi < 3; rgbi++) - { - a = newRgb[rgbi] + ((1 - vibrancy) * std::pow(std::abs(bucket[rgbi]), g));//Must use abs(), else it it could be a negative value and return NAN. - - if (NumChannels() <= 3 || !Transparency()) + if (bucket.a <= 0) { + alpha = 0; + ls = 0; + } + else + { + alpha = Palette::CalcAlpha(bucket.a, g, linRange); + ls = vibrancy * alpha / bucket.a; + ClampRef(alpha, 0, 1); + } + + Palette::template CalcNewRgb(glm::value_ptr(bucket), ls, HighlightPower(), newRgb); + + for (glm::length_t rgbi = 0; rgbi < 3; rgbi++) + { + a = newRgb[rgbi] + ((1 - vibrancy) * std::pow(std::abs(bucket[rgbi]), g));//Must use abs(), else it it could be a negative value and return NAN. a += (1 - alpha) * background[rgbi]; - } - else - { - if (alpha > 0) - a /= alpha; - else - a = 0; - } - if (!scale) - { - correctedChannels[rgbi] = accumT(Clamp(a, 0, 1.0));//Early clip, just assign directly. - } - else - { - a *= scaleVal; - - if (m_CurvesSet) + if (scale && m_CurvesSet) CurveAdjust(a, rgbi + 1); - correctedChannels[rgbi] = accumT(Clamp(a, 0, scaleVal));//Final accum, multiply by 255 for 8 bpc (0-255), or 65535 for 16 bpc (0-65535). + correctedChannels[rgbi] = accumT(Clamp(a, 0, bt1));//Early clip, just assign directly. } - } - if (doAlpha) - { - if (!scale) - correctedChannels[3] = accumT(alpha);//Early clip, just assign alpha directly. - else if (Transparency()) - correctedChannels[3] = accumT(alpha * scaleVal);//Final accum, 4 channels, using transparency. Scale alpha from 0-1 to 0-255 for 8 bpc or 0-65535 for 16 bpc. - else - correctedChannels[3] = accumT(scaleVal);//Final accum, 4 channels, but not using transparency. 255 for 8 bpc, 65535 for 16 bpc. + correctedChannels[3] = accumT(alpha); } } /// /// Setup the curve values when they are being used. -/// This will be either 255 values for bpc=8, or 65535 values for bpc=16. /// -/// Whether to scale from 0-1 to 0-255 or 0-65535 template -void Renderer::ComputeCurves(bool scale) +void Renderer::ComputeCurves() { if (m_CurvesSet) { - m_Csa.resize(size_t(std::pow(size_t(256), BytesPerChannel()))); + Timing t; auto st = m_Csa.size(); - auto stm1 = st - 1; - T tscale = scale ? T(stm1) : T(1); for (size_t i = 0; i < st; i++) - m_Csa[i] = m_Ember.m_Curves.BezierFunc(i / T(stm1)) * tscale; + m_Csa[i] = m_Ember.m_Curves.BezierFunc(i * ONE_OVER_CURVES_LENGTH_M1); + + t.Toc("ComputeCurves"); } } /// /// Apply the curve adjustment to a single channel. /// -/// The value of the channel to apply curve adjustment to, scaled to either 255 or 65535, depending on bpc. +/// The value of the channel to apply curve adjustment to. /// The index of the channel to apply curve adjustment to template -void Renderer::CurveAdjust(bucketT& aScaled, const glm::length_t& index) +void Renderer::CurveAdjust(bucketT& a, const glm::length_t& index) { - bucketT stm1 = bucketT(m_Csa.size() - 1); - size_t tempIndex = size_t(Clamp(aScaled, 0, stm1)); - size_t tempIndex2 = size_t(Clamp(m_Csa[tempIndex].x, 0, stm1)); - aScaled = m_Csa[tempIndex2][index]; + size_t tempIndex = size_t(Clamp(a * CURVES_LENGTH_M1, 0, CURVES_LENGTH_M1)); + size_t tempIndex2 = size_t(Clamp(m_Csa[tempIndex].x * CURVES_LENGTH_M1, 0, CURVES_LENGTH_M1)); + a = m_Csa[tempIndex2][index]; } //This class had to be implemented in a cpp file because the compiler was breaking. diff --git a/Source/Ember/Renderer.h b/Source/Ember/Renderer.h index d66eb71..bc213f4 100644 --- a/Source/Ember/Renderer.h +++ b/Source/Ember/Renderer.h @@ -66,7 +66,7 @@ public: virtual bool CreateSpatialFilter(bool& newAlloc) override; virtual bool CreateTemporalFilter(bool& newAlloc) override; 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 eRenderStatus Run(vector& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) override; virtual EmberImageComments ImageComments(const EmberStats& stats, size_t printEditDepth = 0, bool hexPalette = true) override; protected: @@ -76,10 +76,10 @@ protected: virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true); virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false); virtual eRenderStatus GaussianDensityFilter(); - virtual eRenderStatus AccumulatorToFinalImage(vector& pixels, size_t finalOffset); - virtual eRenderStatus AccumulatorToFinalImage(byte* pixels, size_t finalOffset); + virtual eRenderStatus AccumulatorToFinalImage(vector& pixels, size_t finalOffset); + virtual eRenderStatus AccumulatorToFinalImage(v4F* pixels, size_t finalOffset); virtual EmberStats Iterate(size_t iterCount, size_t temporalSample); - virtual void ComputeCurves(bool scale); + virtual void ComputeCurves(); public: //Non-virtual render properties, getters and setters. @@ -118,9 +118,7 @@ public: inline T CenterX() const; inline T CenterY() const; inline T Rotate() const; - inline T Hue() const; inline bucketT Brightness() const; - inline bucketT Contrast() const; inline bucketT Gamma() const; inline bucketT Vibrancy() const; inline bucketT GammaThresh() const; @@ -154,8 +152,8 @@ 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 tvec4& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj); - template void GammaCorrection(tvec4& bucket, Color& background, bucketT g, bucketT linRange, bucketT vibrancy, bool doAlpha, bool scale, accumT* correctedChannels); + void AddToAccum(const tvec4& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj); + template void GammaCorrection(tvec4& bucket, Color& background, bucketT g, bucketT linRange, bucketT vibrancy, bool scale, accumT* correctedChannels); void CurveAdjust(bucketT& a, const glm::length_t& index); void VectorizedLogScale(size_t row, size_t rowEnd); diff --git a/Source/Ember/RendererBase.cpp b/Source/Ember/RendererBase.cpp index 02c27e6..a55d476 100644 --- a/Source/Ember/RendererBase.cpp +++ b/Source/Ember/RendererBase.cpp @@ -171,10 +171,10 @@ bool RendererBase::RandVec(vector>& randVec) /// /// The vector to allocate /// True if the vector contains enough space to hold the output image -bool RendererBase::PrepFinalAccumVector(vector& pixels) +bool RendererBase::PrepFinalAccumVector(vector& pixels) { EnterResize(); - size_t size = FinalBufferSize(); + size_t size = FinalDimensions(); if (m_ReclaimOnResize) { @@ -374,27 +374,6 @@ void RendererBase::ReclaimOnResize(bool reclaimOnResize) ChangeVal([&] { m_ReclaimOnResize = reclaimOnResize; }, eProcessAction::FULL_RENDER); } -/// -/// Get whether to use transparency in the alpha channel. -/// This only applies when the number of channels is 4 and the output -/// image is Png. -/// Default: false. -/// -/// True if using transparency, else false. -bool RendererBase::Transparency() const { return m_Transparency; } - -/// -/// Set whether to use transparency in the alpha channel. -/// This only applies when the number of channels is 4 and the output -/// image is Png. -/// Set the render state to ACCUM_ONLY. -/// -/// True if using transparency, else false. -void RendererBase::Transparency(bool transparency) -{ - ChangeVal([&] { m_Transparency = transparency; }, eProcessAction::ACCUM_ONLY); -} - /// /// Set the callback object. /// @@ -474,40 +453,18 @@ void RendererBase::ThreadCount(size_t threads, const char* seedString) /// /// Get the bytes per channel of the output image. -/// The only acceptable values are 1 and 2, and 2 is only -/// used when the output is Png. -/// Default: 1. +/// This will always be 4 since each channel is a 32-bit float. /// -/// +/// The number of bytes per channel size_t RendererBase::BytesPerChannel() const { return m_BytesPerChannel; } /// -/// Set the bytes per channel of the output image. -/// The only acceptable values are 1 and 2, and 2 is only -/// used when the output is Png. -/// Set the render state to ACCUM_ONLY. -/// -/// The bytes per channel. -void RendererBase::BytesPerChannel(size_t bytesPerChannel) -{ - ChangeVal([&] - { - if (bytesPerChannel == 0 || bytesPerChannel > 2) - m_BytesPerChannel = 1; - else - m_BytesPerChannel = bytesPerChannel; - }, eProcessAction::ACCUM_ONLY); -} - -/// -/// Get the number of channels per pixel in the output image. 3 for RGB images -/// like Bitmap and Jpeg, 4 for Png. -/// Default is 3. +/// Get the number of channels per pixel in the output image. +/// This will always be 4 since each pixel is always RGBA. /// /// The number of channels per pixel in the output image size_t RendererBase::NumChannels() const { return m_NumChannels; } - /// /// Get/set the priority used for the CPU rendering threads. /// This does not affect OpenCL rendering. @@ -543,18 +500,6 @@ void RendererBase::InteractiveFilter(eInteractiveFilter filter) /// Virtual render properties, getters and setters. /// -/// -/// Set the number of channels per pixel in the output image. 3 for RGB images -/// like Bitmap and Jpeg, 4 for Png. -/// Default is 3. -/// Set the render state to ACCUM_ONLY. -/// -/// The number of channels per pixel in the output image -void RendererBase::NumChannels(size_t numChannels) -{ - ChangeVal([&] { m_NumChannels = numChannels; }, eProcessAction::ACCUM_ONLY); -} - /// /// Get the number of threads used when rendering. /// Default: use all avaliable cores. diff --git a/Source/Ember/RendererBase.h b/Source/Ember/RendererBase.h index 25580ff..92fe2a8 100644 --- a/Source/Ember/RendererBase.h +++ b/Source/Ember/RendererBase.h @@ -105,7 +105,7 @@ public: size_t HistMemoryRequired(size_t strips); pair MemoryRequired(size_t strips, bool includeFinal, bool threadedWrite); vector> RandVec(); - bool PrepFinalAccumVector(vector& pixels); + bool PrepFinalAccumVector(vector& pixels); //Virtual processing functions. virtual bool Ok() const; @@ -121,7 +121,7 @@ public: 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 eRenderStatus Run(vector& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) = 0; virtual EmberImageComments ImageComments(const EmberStats& stats, size_t printEditDepth = 0, bool hexPalette = true) = 0; virtual DensityFilterBase* GetDensityFilter() = 0; @@ -152,12 +152,9 @@ public: void InsertPalette(bool insertPalette); bool ReclaimOnResize() const; void ReclaimOnResize(bool reclaimOnResize); - bool Transparency() const; - void Transparency(bool transparency); void Callback(RenderCallback* callback); void ThreadCount(size_t threads, const char* seedString = nullptr); size_t BytesPerChannel() const; - void BytesPerChannel(size_t bytesPerChannel); size_t NumChannels() const; eThreadPriority Priority() const; void Priority(eThreadPriority priority); @@ -165,7 +162,6 @@ public: void InteractiveFilter(eInteractiveFilter filter); //Virtual render properties, getters and setters. - virtual void NumChannels(size_t numChannels); virtual size_t ThreadCount() const; virtual eRendererType RendererType() const; @@ -200,7 +196,6 @@ public: protected: bool m_EarlyClip = false; bool m_YAxisUp = false; - bool m_Transparency = false; bool m_LockAccum = false; bool m_InRender = false; bool m_InFinalAccum = false; @@ -213,8 +208,8 @@ protected: size_t m_SuperSize = 0; size_t m_GutterWidth; size_t m_DensityFilterOffset; - size_t m_NumChannels = 3; - size_t m_BytesPerChannel = 1; + size_t m_NumChannels = 4; + size_t m_BytesPerChannel = 4; size_t m_ThreadsToUse; size_t m_VibGamCount; size_t m_LastTemporalSample = 0; diff --git a/Source/Ember/SheepTools.h b/Source/Ember/SheepTools.h index e9cb219..fe15a1c 100644 --- a/Source/Ember/SheepTools.h +++ b/Source/Ember/SheepTools.h @@ -836,7 +836,6 @@ public: /// The percentage possible color values that were present in the final output image T TryColors(Ember& ember, size_t colorResolution) { - byte* p; size_t i, hits = 0, res = colorResolution; size_t pixTotal, res3 = res * res * res; T scalar; @@ -862,14 +861,12 @@ public: m_Hist.resize(res3); Memset(m_Hist); - p = m_FinalImage.data(); + auto p = m_FinalImage.data(); for (i = 0; i < m_Renderer->FinalDimensions(); i++) { - m_Hist[(p[0] * res / 256) + - (p[1] * res / 256) * res + - (p[2] * res / 256) * res * res]++;//A specific histogram index representing the sum of R,G,B values. - p += m_Renderer->PixelSize();//Advance the pointer by 1 pixel. + m_Hist[size_t((p->r * res) + (p->g * res) * res + (p->b * res) * res * res)]++;//A specific histogram index representing the sum of R,G,B values. + p++; } for (i = 0; i < res3; i++) @@ -1352,7 +1349,7 @@ private: string m_Comment; vector> m_Samples; - vector m_FinalImage; + vector m_FinalImage; vector m_Hist; EmberToXml m_EmberToXml; Iterator* m_Iterator; diff --git a/Source/EmberAnimate/EmberAnimate.cpp b/Source/EmberAnimate/EmberAnimate.cpp index 57cbe33..282ad6e 100644 --- a/Source/EmberAnimate/EmberAnimate.cpp +++ b/Source/EmberAnimate/EmberAnimate.cpp @@ -30,8 +30,7 @@ bool EmberAnimate(EmberOptions& opt) //Regular variables. Timing t; bool unsorted = false; - uint channels; - streamsize padding; + uint padding; size_t i, firstUnsortedIndex = 0; string inputPath = GetPath(opt.Input()); vector> embers; @@ -99,12 +98,6 @@ bool EmberAnimate(EmberOptions& opt) renderers[i]->ThreadCount(opt.ThreadCount(), ns.c_str()); } } - - if (opt.BitsPerChannel() != 8) - { - cout << "Bits per channel cannot be anything other than 8 with OpenCL, setting to 8.\n"; - opt.BitsPerChannel(8); - } } else { @@ -153,24 +146,19 @@ bool EmberAnimate(EmberOptions& opt) return false; } - if (opt.Format() != "jpg" && - opt.Format() != "png" && - opt.Format() != "bmp") + if (!Find(opt.Format(), "jpg") && + !Find(opt.Format(), "png") && +#ifdef _WIN32 + !Find(opt.Format(), "bmp") && +#endif + !Find(opt.Format(), "exr")) { - cout << "Format must be jpg, png, or bmp not " << opt.Format() << ". Setting to jpg.\n"; - } - - channels = opt.Format() == "png" ? 4 : 3; - - if (opt.BitsPerChannel() == 16 && opt.Format() != "png") - { - cout << "Support for 16 bits per channel images is only present for the png format. Setting to 8.\n"; - opt.BitsPerChannel(8); - } - else if (opt.BitsPerChannel() != 8 && opt.BitsPerChannel() != 16) - { - cout << "Unexpected bits per channel specified " << opt.BitsPerChannel() << ". Setting to 8.\n"; - opt.BitsPerChannel(8); +#ifdef _WIN32 + cout << "Format must be bmp, jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n"; +#else + cout << "Format must be jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n"; +#endif + opt.Format("png"); } if (opt.AspectRatio() < 0) @@ -262,7 +250,7 @@ bool EmberAnimate(EmberOptions& opt) } //Cast to double in case the value exceeds 2^32. - double imageMem = double(channels) * double(ember.m_FinalRasW) + double imageMem = 4 * double(ember.m_FinalRasW) * double(ember.m_FinalRasH) * double(renderers[0]->BytesPerChannel()); double maxMem = pow(2.0, double((sizeof(void*) * 8) - 1)); @@ -316,46 +304,131 @@ bool EmberAnimate(EmberOptions& opt) r->YAxisUp(opt.YAxisUp()); r->LockAccum(opt.LockAccum()); r->PixelAspectRatio(T(opt.AspectRatio())); - r->Transparency(opt.Transparency()); - r->NumChannels(channels); - r->BytesPerChannel(opt.BitsPerChannel() / 8); r->Priority(eThreadPriority(Clamp(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST)))); } - std::function&, string, EmberImageComments, size_t, size_t, size_t)> saveFunc = [&](vector& finalImage, - string filename,//These are copies because this will be launched in a thread. + std::function&, string, EmberImageComments, size_t, size_t, size_t)> saveFunc = [&](vector& finalImage, + string baseFilename,//These are copies because this will be launched in a thread. EmberImageComments comments, size_t w, size_t h, size_t chan) { - bool writeSuccess = false; - byte* finalImagep = finalImage.data(); + auto finalImagep = finalImage.data(); + auto size = w * h; + bool doBmp = Find(opt.Format(), "bmp"); + bool doJpg = Find(opt.Format(), "jpg"); + bool doExr = Find(opt.Format(), "exr"); + bool doPng8 = Find(opt.Format(), "png"); + bool doPng16 = Find(opt.Format(), "png16"); + bool doOnlyPng8 = doPng8 && !doPng16; + vector rgb8Image; + vector writeFileThreads; + writeFileThreads.reserve(5); - if ((opt.Format() == "jpg" || opt.Format() == "bmp") && chan == 4) - RgbaToRgb(finalImage, finalImage, w, h); + if (doBmp || doJpg) + { + rgb8Image.resize(size * 3); + Rgba32ToRgb8(finalImagep, rgb8Image.data(), w, h); - if (opt.Format() == "png") - writeSuccess = WritePng(filename.c_str(), finalImagep, w, h, opt.BitsPerChannel() / 8, opt.PngComments(), comments, opt.Id(), opt.Url(), opt.Nick()); - else if (opt.Format() == "jpg") - writeSuccess = WriteJpeg(filename.c_str(), finalImagep, w, h, int(opt.JpegQuality()), opt.JpegComments(), comments, opt.Id(), opt.Url(), opt.Nick()); - else if (opt.Format() == "bmp") - writeSuccess = WriteBmp(filename.c_str(), finalImagep, w, h); + if (doBmp) + { + writeFileThreads.push_back(std::thread([&]() + { + auto fn = baseFilename + ".bmp"; + VerbosePrint("Writing " + fn); + auto writeSuccess = WriteBmp(fn.c_str(), rgb8Image.data(), w, h); - if (!writeSuccess) - cout << "Error writing " << filename << "\n"; + if (!writeSuccess) + cout << "Error writing " << fn << "\n"; + })); + } + + if (doJpg) + { + writeFileThreads.push_back(std::thread([&]() + { + auto fn = baseFilename + ".jpg"; + VerbosePrint("Writing " + fn); + auto writeSuccess = WriteJpeg(fn.c_str(), rgb8Image.data(), w, h, int(opt.JpegQuality()), opt.EnableComments(), comments, opt.Id(), opt.Url(), opt.Nick()); + + if (!writeSuccess) + cout << "Error writing " << fn << "\n"; + })); + } + } + + if (doPng8) + { + bool doBothPng = doPng16 && (opt.Format().find("png") != opt.Format().rfind("png")); + + if (doBothPng || doOnlyPng8)//8-bit PNG + { + writeFileThreads.push_back(std::thread([&]() + { + auto fn = baseFilename + ".png"; + VerbosePrint("Writing " + fn); + vector rgba8Image(size * 4); + Rgba32ToRgba8(finalImagep, rgba8Image.data(), w, h, opt.Transparency()); + auto writeSuccess = WritePng(fn.c_str(), rgba8Image.data(), w, h, 1, opt.EnableComments(), comments, opt.Id(), opt.Url(), opt.Nick()); + + if (!writeSuccess) + cout << "Error writing " << fn << "\n"; + })); + } + + if (doPng16) + { + writeFileThreads.push_back(std::thread([&]() + { + auto suffix = opt.Suffix(); + auto fn = baseFilename; + + if (doBothPng)//Add suffix if they specified both PNG. + { + VerbosePrint("Doing both PNG formats, so adding suffix _p16 to avoid overwriting the same file."); + fn += "_p16"; + } + + fn += ".png"; + VerbosePrint("Writing " + fn); + vector rgba16Image(size * 4); + Rgba32ToRgba16(finalImagep, rgba16Image.data(), w, h, opt.Transparency()); + auto writeSuccess = WritePng(fn.c_str(), (byte*)rgba16Image.data(), w, h, 2, opt.EnableComments(), comments, opt.Id(), opt.Url(), opt.Nick()); + + if (!writeSuccess) + cout << "Error writing " << fn << "\n"; + })); + } + } + + if (doExr) + { + writeFileThreads.push_back(std::thread([&]() + { + auto fn = baseFilename + ".exr"; + VerbosePrint("Writing " + fn); + vector rgba32Image(size); + Rgba32ToRgbaExr(finalImagep, rgba32Image.data(), w, h, opt.Transparency()); + auto writeSuccess = WriteExr(fn.c_str(), rgba32Image.data(), w, h, opt.EnableComments(), comments, opt.Id(), opt.Url(), opt.Nick()); + + if (!writeSuccess) + cout << "Error writing " << fn << "\n"; + })); + } + + Join(writeFileThreads); }; atomfTime.store(opt.FirstFrame()); std::function iterFunc = [&](size_t index) { size_t ftime, finalImageIndex = 0; - string filename, flameName; RendererBase* renderer = renderers[index].get(); - ostringstream fnstream, os; + ostringstream os; EmberStats stats; EmberImageComments comments; Ember centerEmber; - vector finalImages[2]; + vector finalImages[2]; std::thread writeThread; os.imbue(std::locale("")); @@ -390,13 +463,9 @@ bool EmberAnimate(EmberOptions& opt) break; } - fnstream << inputPath << opt.Prefix() << setfill('0') << setprecision(0) << fixed << setw(padding) << ftime << opt.Suffix() << "." << opt.Format(); - filename = fnstream.str(); - fnstream.str(""); - if (opt.WriteGenome()) { - flameName = filename.substr(0, filename.find_last_of('.')) + ".flame"; + auto flameName = MakeAnimFilename(inputPath, opt.Prefix(), opt.Suffix(), ".flame", padding, ftime); if (opt.Verbose()) { @@ -425,21 +494,21 @@ bool EmberAnimate(EmberOptions& opt) cout << "Render time: " << t.Format(stats.m_RenderMs) << "\n"; cout << "Pure iter time: " << t.Format(stats.m_IterMs) << "\n"; cout << "Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << "\n"; - cout << "Writing " << filename << "\n\n"; } //Run image writing in a thread. Although doing it this way duplicates the final output memory, it saves a lot of time //when running with OpenCL. Call join() to ensure the previous thread call has completed. Join(writeThread); auto threadVecIndex = finalImageIndex;//Cache before launching thread. + auto baseFilename = MakeAnimFilename(inputPath, opt.Prefix(), opt.Suffix(), "", padding, ftime); - if (opt.ThreadedWrite())//Copies are passed of all but the first parameter to saveFunc(), to avoid conflicting with those values changing when starting the render for the next image. + if (opt.ThreadedWrite())//Copies of all but the first parameter are passed to saveFunc(), to avoid conflicting with those values changing when starting the render for the next image. { - writeThread = std::thread(saveFunc, std::ref(finalImages[threadVecIndex]), filename, comments, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels()); + writeThread = std::thread(saveFunc, std::ref(finalImages[threadVecIndex]), baseFilename, comments, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels()); finalImageIndex ^= 1;//Toggle the index. } else - saveFunc(finalImages[threadVecIndex], filename, comments, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels());//Will always use the first index, thereby not requiring more memory. + saveFunc(finalImages[threadVecIndex], baseFilename, comments, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels());//Will always use the first index, thereby not requiring more memory. } Join(writeThread);//One final check to make sure all writing is done before exiting this thread. diff --git a/Source/EmberCL/EmberCLFunctions.h b/Source/EmberCL/EmberCLFunctions.h index 8b546e2..3c1ba6b 100644 --- a/Source/EmberCL/EmberCLFunctions.h +++ b/Source/EmberCL/EmberCLFunctions.h @@ -147,13 +147,14 @@ static const char* CalcAlphaFunctionString = /// during final accumulation, which only takes floats. /// static const char* CurveAdjustFunctionString = - "static inline void CurveAdjust(__constant real4reals_bucket* csa, float* a, uint index)\n" + "static inline void CurveAdjust(__global real4reals_bucket* csa, float* a, uint index)\n" "{\n" - " uint tempIndex = (uint)clamp(*a * (float)COLORMAP_LENGTH_MINUS_1, (float)0.0, (float)COLORMAP_LENGTH_MINUS_1);\n" - " uint tempIndex2 = (uint)clamp((float)csa[tempIndex].m_Real4.x * (float)COLORMAP_LENGTH_MINUS_1, (float)0.0, (float)COLORMAP_LENGTH_MINUS_1);\n" + " uint tempIndex = (uint)clamp(*a * CURVES_LENGTH_M1, 0.0f, CURVES_LENGTH_M1);\n" + " uint tempIndex2 = (uint)clamp(csa[tempIndex].m_Real4.x * CURVES_LENGTH_M1, 0.0f, CURVES_LENGTH_M1);\n" "\n" " *a = (float)csa[tempIndex2].m_Reals[index];\n" - "}\n"; + "}\n" + "\n"; /// /// Use MWC 64 from David Thomas at the Imperial College of London for diff --git a/Source/EmberCL/EmberCLStructs.h b/Source/EmberCL/EmberCLStructs.h index a2f10e5..e8f1437 100644 --- a/Source/EmberCL/EmberCLStructs.h +++ b/Source/EmberCL/EmberCLStructs.h @@ -85,6 +85,8 @@ static string ConstantDefinesString(bool doublePrecision) "#define SQRT5 2.2360679774997896964091736687313\n" "#define M_PHI 1.61803398874989484820458683436563\n" "#define DEG_2_RAD (MPI / 180)\n" + "#define CURVES_LENGTH_M1 131071.0f\n" + "#define ONE_OVER_CURVES_LENGTH_M1 7.62945273935e-6f\n" "\n" "//Index in each dimension of a thread within a block.\n" "#define THREAD_ID_X (get_local_id(0))\n" @@ -314,10 +316,7 @@ struct ALIGN SpatialFilterCL uint m_FinalRasH; uint m_Supersample; uint m_FilterWidth; - uint m_NumChannels; - uint m_BytesPerChannel; uint m_DensityFilterOffset; - uint m_Transparency; uint m_YAxisUp; T m_Vibrancy; T m_HighlightPower; @@ -339,10 +338,7 @@ static const char* SpatialFilterCLStructString = " uint m_FinalRasH;\n" " uint m_Supersample;\n" " uint m_FilterWidth;\n" - " uint m_NumChannels;\n" - " uint m_BytesPerChannel;\n" " uint m_DensityFilterOffset;\n" - " uint m_Transparency;\n" " uint m_YAxisUp;\n" " real_bucket_t m_Vibrancy;\n" " real_bucket_t m_HighlightPower;\n" diff --git a/Source/EmberCL/FinalAccumOpenCLKernelCreator.cpp b/Source/EmberCL/FinalAccumOpenCLKernelCreator.cpp index 7ee8458..8dc9bb2 100644 --- a/Source/EmberCL/FinalAccumOpenCLKernelCreator.cpp +++ b/Source/EmberCL/FinalAccumOpenCLKernelCreator.cpp @@ -10,166 +10,58 @@ namespace EmberCLns FinalAccumOpenCLKernelCreator::FinalAccumOpenCLKernelCreator(bool doublePrecision) { m_DoublePrecision = doublePrecision; - m_GammaCorrectionWithAlphaCalcKernel = CreateGammaCorrectionKernelString(true); - m_GammaCorrectionWithoutAlphaCalcKernel = CreateGammaCorrectionKernelString(false); - m_FinalAccumEarlyClipKernel = CreateFinalAccumKernelString(true, false, false); - m_FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(true, true, true); - m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(true, false, true); - m_FinalAccumLateClipKernel = CreateFinalAccumKernelString(false, false, false); - m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(false, true, true); - m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(false, false, true); + m_GammaCorrectionWithoutAlphaCalcKernel = CreateGammaCorrectionKernelString(); + m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(true); + m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(false); } /// /// Kernel source and entry point properties, getters only. /// -const string& FinalAccumOpenCLKernelCreator::GammaCorrectionWithAlphaCalcKernel() const { return m_GammaCorrectionWithAlphaCalcKernel; } -const string& FinalAccumOpenCLKernelCreator::GammaCorrectionWithAlphaCalcEntryPoint() const { return m_GammaCorrectionWithAlphaCalcEntryPoint; } -const string& FinalAccumOpenCLKernelCreator::GammaCorrectionWithoutAlphaCalcKernel() const { return m_GammaCorrectionWithoutAlphaCalcKernel; } -const string& FinalAccumOpenCLKernelCreator::GammaCorrectionWithoutAlphaCalcEntryPoint() const { return m_GammaCorrectionWithoutAlphaCalcEntryPoint; } - -const string& FinalAccumOpenCLKernelCreator::FinalAccumEarlyClipKernel() const { return m_FinalAccumEarlyClipKernel; } -const string& FinalAccumOpenCLKernelCreator::FinalAccumEarlyClipEntryPoint() const { return m_FinalAccumEarlyClipEntryPoint; } -const string& FinalAccumOpenCLKernelCreator::FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel() const { return m_FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel; } -const string& FinalAccumOpenCLKernelCreator::FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumEntryPoint() const { return m_FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumEntryPoint; } const string& FinalAccumOpenCLKernelCreator::FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel() const { return m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel; } const string& FinalAccumOpenCLKernelCreator::FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const { return m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint; } -const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipKernel() const { return m_FinalAccumLateClipKernel; } -const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipEntryPoint() const { return m_FinalAccumLateClipEntryPoint; } -const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel() const { return m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel; } -const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint() const { return m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint; } const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel() const { return m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel; } const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const { return m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint; } -/// -/// Get the gamma correction entry point. -/// -/// The number of channels used, 3 or 4. -/// True if channels equals 4 and using transparency, else false. -/// The name of the gamma correction entry point kernel function -const string& FinalAccumOpenCLKernelCreator::GammaCorrectionEntryPoint(size_t channels, bool transparency) const -{ - bool alphaCalc = ((channels > 3) && transparency); - return alphaCalc ? m_GammaCorrectionWithAlphaCalcEntryPoint : m_GammaCorrectionWithoutAlphaCalcEntryPoint; -} - -/// -/// Get the gamma correction kernel string. -/// -/// The number of channels used, 3 or 4. -/// True if channels equals 4 and using transparency, else false. -/// The gamma correction kernel string -const string& FinalAccumOpenCLKernelCreator::GammaCorrectionKernel(size_t channels, bool transparency) const -{ - bool alphaCalc = ((channels > 3) && transparency); - return alphaCalc ? m_GammaCorrectionWithAlphaCalcKernel : m_GammaCorrectionWithoutAlphaCalcKernel; -} +const string& FinalAccumOpenCLKernelCreator::GammaCorrectionEntryPoint() const { return m_GammaCorrectionWithoutAlphaCalcEntryPoint; } +const string& FinalAccumOpenCLKernelCreator::GammaCorrectionKernel() const { return m_GammaCorrectionWithoutAlphaCalcKernel; } /// /// Get the final accumulation entry point. /// /// True if early clip is desired, else false. -/// The number of channels used, 3 or 4. -/// True if channels equals 4 and using transparency, else false. -/// Storage for the alpha base value used in the kernel. 0 if transparency is true, else 255. -/// Storage for the alpha scale value used in the kernel. 255 if transparency is true, else 0. /// The name of the final accumulation entry point kernel function -const string& FinalAccumOpenCLKernelCreator::FinalAccumEntryPoint(bool earlyClip, size_t channels, bool transparency, double& alphaBase, double& alphaScale) const +const string& FinalAccumOpenCLKernelCreator::FinalAccumEntryPoint(bool earlyClip) const { - bool alphaCalc = ((channels > 3) && transparency); - bool alphaAccum = channels > 3; - - if (alphaAccum) - { - alphaBase = transparency ? 0 : 1;//See the table below. - alphaScale = transparency ? 1 : 0; - } - if (earlyClip) - { - if (!alphaCalc && !alphaAccum)//Rgb output, the most common case. - return FinalAccumEarlyClipEntryPoint(); - else if (alphaCalc && alphaAccum)//Rgba output and Transparency. - return FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumEntryPoint(); - else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency. - return FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint(); - else - return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense. - } + return FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint(); else - { - if (!alphaCalc && !alphaAccum)//Rgb output, the most common case. - return FinalAccumLateClipEntryPoint(); - else if (alphaCalc && alphaAccum)//Rgba output and Transparency. - return FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint(); - else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency. - return FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint(); - else - return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense. - } + return FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint(); } /// /// Get the final accumulation kernel string. /// /// True if early clip is desired, else false. -/// The number of channels used, 3 or 4. -/// True if channels equals 4 and using transparency, else false. /// The final accumulation kernel string -const string& FinalAccumOpenCLKernelCreator::FinalAccumKernel(bool earlyClip, size_t channels, bool transparency) const +const string& FinalAccumOpenCLKernelCreator::FinalAccumKernel(bool earlyClip) const { - bool alphaCalc = (channels > 3 && transparency); - bool alphaAccum = channels > 3; - if (earlyClip) - { - if (!alphaCalc && !alphaAccum)//Rgb output, the most common case. - return FinalAccumEarlyClipKernel(); - else if (alphaCalc && alphaAccum)//Rgba output and Transparency. - return FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel(); - else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency. - return FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel(); - else - return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense. - } + return FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel(); else - { - if (!alphaCalc && !alphaAccum)//Rgb output, the most common case. - return FinalAccumLateClipKernel(); - else if (alphaCalc && alphaAccum)//Rgba output and Transparency. - return FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel(); - else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency. - return FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel(); - else - return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense. - } -} - -/// -/// Wrapper around CreateFinalAccumKernelString(). -/// -/// True if early clip is desired, else false. -/// The number of channels used, 3 or 4. -/// True if channels equals 4 and using transparency, else false. -/// The final accumulation kernel string -string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyClip, size_t channels, bool transparency) -{ - return CreateFinalAccumKernelString(earlyClip, (channels > 3 && transparency), channels > 3); + return FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel(); } /// /// Create the final accumulation kernel string /// /// True if early clip is desired, else false. -/// True if channels equals 4 and transparency is desired, else false. -/// True if channels equals 4 /// The final accumulation kernel string -string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyClip, bool alphaCalc, bool alphaAccum) +string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyClip) { ostringstream os; - string channels = alphaAccum ? "4" : "3"; os << ConstantDefinesString(m_DoublePrecision) << UnionCLStructString << @@ -181,29 +73,14 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli if (earlyClip) { - if (!alphaCalc && !alphaAccum)//Rgb output, the most common case. - os << "__kernel void " << m_FinalAccumEarlyClipEntryPoint << "(\n"; - else if (alphaCalc && alphaAccum)//Rgba output and Transparency. - os << "__kernel void " << m_FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumEntryPoint << "(\n"; - else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency. - os << "__kernel void " << m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint << "(\n"; - else - return "";//Cannot have alphaCalc and !alphaAccum, it makes no sense. + os << "__kernel void " << m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint << "(\n"; } else { os << CreateCalcNewRgbFunctionString(false) << - CreateGammaCorrectionFunctionString(false, alphaCalc, alphaAccum, true); - - if (!alphaCalc && !alphaAccum)//Rgb output, the most common case. - os << "__kernel void " << m_FinalAccumLateClipEntryPoint << "(\n"; - else if (alphaCalc && alphaAccum)//Rgba output and Transparency. - os << "__kernel void " << m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint << "(\n"; - else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency. - os << "__kernel void " << m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint << "(\n"; - else - return "";//Cannot have alphaCalc and !alphaAccum, it makes no sense. + CreateGammaCorrectionFunctionString(false, true) << + "__kernel void " << m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint << "(\n"; } os << @@ -211,10 +88,8 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli " __write_only image2d_t pixels,\n" " __constant SpatialFilterCL* spatialFilter,\n" " __constant real_bucket_t* filterCoefs,\n" - " __constant real4reals_bucket* csa,\n" - " const uint doCurves,\n" - " const real_bucket_t alphaBase,\n" - " const real_bucket_t alphaScale\n" + " __global real4reals_bucket* csa,\n" + " const uint doCurves\n" "\t)\n" "{\n" "\n" @@ -249,21 +124,13 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli " }\n" "\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 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. " finalColor.m_Float4.y = (float)newBucket.m_Real4.y;\n" - " finalColor.m_Float4.z = (float)newBucket.m_Real4.z;\n"; - - if (alphaAccum) - { - if (alphaCalc) - os << " finalColor.m_Float4.w = (float)newBucket.m_Real4.w;\n"; - else - os << " finalColor.m_Float4.w = 1.0f;\n"; - } + " finalColor.m_Float4.z = (float)newBucket.m_Real4.z;\n" + " finalColor.m_Float4.w = (float)newBucket.m_Real4.w;\n"; } else { @@ -273,7 +140,7 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli os << " real4reals_bucket realFinal;\n" "\n" - " GammaCorrectionFloats(&newBucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, alphaBase, alphaScale, &(realFinal.m_Reals[0]));\n" + " GammaCorrectionFloats(&newBucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, &(realFinal.m_Reals[0]));\n" " finalColor.m_Float4.x = (float)realFinal.m_Real4.x;\n" " finalColor.m_Float4.y = (float)realFinal.m_Real4.y;\n" " finalColor.m_Float4.z = (float)realFinal.m_Real4.z;\n" @@ -283,7 +150,7 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli else { os << - " GammaCorrectionFloats(&newBucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, alphaBase, alphaScale, &(finalColor.m_Floats[0]));\n"; + " GammaCorrectionFloats(&newBucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, &(finalColor.m_Floats[0]));\n"; } } @@ -308,81 +175,45 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli /// This is not a full kernel, just a function that is used in the kernels. /// /// True if writing to a global buffer (early clip), else false (late clip). -/// True if channels equals 4 and transparency is desired, else false. -/// True if channels equals 4 /// True if writing to global buffer (late clip), else false (early clip). /// The gamma correction function string -string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionFunctionString(bool globalBucket, bool alphaCalc, bool alphaAccum, bool finalOut) +string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionFunctionString(bool globalBucket, bool finalOut) { ostringstream os; string dataType; string unionMember; dataType = "real_bucket_t"; //Use real_t for all cases, early clip and final accum. - os << "void GammaCorrectionFloats(" << (globalBucket ? "__global " : "") << "real4reals_bucket* bucket, __constant real_bucket_t* background, real_bucket_t g, real_bucket_t linRange, real_bucket_t vibrancy, real_bucket_t highlightPower, real_bucket_t alphaBase, real_bucket_t alphaScale, " << (finalOut ? "" : "__global") << " real_bucket_t* correctedChannels)\n"; - os - << "{\n" - << " real_bucket_t alpha, ls, tmp, a;\n" - << " real4reals_bucket newRgb;\n" - << "\n" - << " if (bucket->m_Reals[3] <= 0)\n" - << " {\n" - << " alpha = 0;\n" - << " ls = 0;\n" - << " }\n" - << " else\n" - << " {\n" - << " tmp = bucket->m_Reals[3];\n" - << " alpha = CalcAlpha(tmp, g, linRange);\n" - << " ls = vibrancy * alpha / tmp;\n" - << " alpha = clamp(alpha, (real_bucket_t)0.0, (real_bucket_t)1.0);\n" - << " }\n" - << "\n" - << " CalcNewRgb(bucket, ls, highlightPower, &newRgb);\n" - << "\n" - << " for (uint rgbi = 0; rgbi < 3; rgbi++)\n" - << " {\n" - << " a = newRgb.m_Reals[rgbi] + ((1.0 - vibrancy) * pow(fabs(bucket->m_Reals[rgbi]), g));\n" - << "\n"; - - if (!alphaCalc) - { - os << - " a += ((1.0 - alpha) * background[rgbi]);\n"; - } - else - { - os - << " if (alpha > 0)\n" - << " a /= alpha;\n" - << " else\n" - << " a = 0;\n"; - } - - os << - "\n" - " correctedChannels[rgbi] = (" << dataType << ")clamp(a, (real_bucket_t)0.0, (real_bucket_t)1.0);\n" - " }\n" - "\n"; - - //The CPU code has 3 cases for assigning alpha: - //[3] = alpha.//Early clip. - //[3] = alpha * 255.//Final Rgba with transparency. - //[3] = 255.//Final Rgba without transparency. - //Putting conditionals in GPU code is to be avoided. So do base + alpha * scale which will - //work for all 3 cases without using a conditional, which should be faster on a GPU. This gives: - //Base = 0, scale = 1. [3] = (0 + (alpha * 1)). [3] = alpha. - //Base = 0, scale = 255. [3] = (0 + (alpha * 255)). [3] = alpha * 255. - //Base = 255, scale = 0. [3] = (255 + (alpha * 0)). [3] = 255. - if (alphaAccum) - { - os - << " correctedChannels[3] = (" << dataType << ")(alphaBase + (alpha * alphaScale));\n"; - } - - os << - "}\n" - "\n"; + os << "void GammaCorrectionFloats(" << (globalBucket ? "__global " : "") << "real4reals_bucket* bucket, __constant real_bucket_t* background, real_bucket_t g, real_bucket_t linRange, real_bucket_t vibrancy, real_bucket_t highlightPower, " << (finalOut ? "" : "__global") << " real_bucket_t* correctedChannels)\n"; + os << "{\n" + << " real_bucket_t alpha, ls, tmp, a;\n" + << " real4reals_bucket newRgb;\n" + << "\n" + << " if (bucket->m_Reals[3] <= 0)\n" + << " {\n" + << " alpha = 0;\n" + << " ls = 0;\n" + << " }\n" + << " else\n" + << " {\n" + << " tmp = bucket->m_Reals[3];\n" + << " alpha = CalcAlpha(tmp, g, linRange);\n" + << " ls = vibrancy * alpha / tmp;\n" + << " alpha = clamp(alpha, (real_bucket_t)0.0, (real_bucket_t)1.0);\n" + << " }\n" + << "\n" + << " CalcNewRgb(bucket, ls, highlightPower, &newRgb);\n" + << "\n" + << " for (uint rgbi = 0; rgbi < 3; rgbi++)\n" + << " {\n" + << " a = newRgb.m_Reals[rgbi] + ((1.0 - vibrancy) * pow(fabs(bucket->m_Reals[rgbi]), g));\n" + << " a += ((1.0 - alpha) * background[rgbi]);\n" + << " correctedChannels[rgbi] = (" << dataType << ")clamp(a, (real_bucket_t)0.0, (real_bucket_t)1.0);\n" + << " }\n" + << "\n" + << " correctedChannels[3] = (" << dataType << ")alpha;\n" + << "}\n" + << "\n"; return os.str(); } @@ -451,9 +282,8 @@ string FinalAccumOpenCLKernelCreator::CreateCalcNewRgbFunctionString(bool global /// /// Create the gamma correction kernel string used for early clipping. /// -/// True if channels equals 4 and transparency is desired, else false. /// The gamma correction kernel string used for early clipping -string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString(bool alphaCalc) +string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString() { ostringstream os; string dataType; @@ -465,8 +295,8 @@ string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString(bool alp CalcAlphaFunctionString << CreateCalcNewRgbFunctionString(true) << SpatialFilterCLStructString << - CreateGammaCorrectionFunctionString(true, alphaCalc, true, false);//Will only be used with float in this case, early clip. Will always alpha accum. - os << "__kernel void " << (alphaCalc ? m_GammaCorrectionWithAlphaCalcEntryPoint : m_GammaCorrectionWithoutAlphaCalcEntryPoint) << "(\n" << + CreateGammaCorrectionFunctionString(true, false);//Will only be used with float in this case, early clip. Will always alpha accum. + os << "__kernel void " << m_GammaCorrectionWithoutAlphaCalcEntryPoint << "(\n" << " __global real4reals_bucket* accumulator,\n" " __constant SpatialFilterCL* spatialFilter\n" ")\n" @@ -478,8 +308,7 @@ string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString(bool alp "\n" " uint superIndex = (GLOBAL_ID_Y * spatialFilter->m_SuperRasW) + GLOBAL_ID_X;\n" " __global real4reals_bucket* bucket = accumulator + superIndex;\n" - //Pass in an alphaBase and alphaScale of 0, 1 which means to just directly assign the computed alpha value. - " GammaCorrectionFloats(bucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, 0.0, 1.0, &(bucket->m_Reals[0]));\n" + " GammaCorrectionFloats(bucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, &(bucket->m_Reals[0]));\n" "}\n" ; return os.str(); diff --git a/Source/EmberCL/FinalAccumOpenCLKernelCreator.h b/Source/EmberCL/FinalAccumOpenCLKernelCreator.h index bd66e93..9d9955f 100644 --- a/Source/EmberCL/FinalAccumOpenCLKernelCreator.h +++ b/Source/EmberCL/FinalAccumOpenCLKernelCreator.h @@ -17,64 +17,35 @@ namespace EmberCLns /// with all conditionals and unnecessary calculations stripped out. /// The conditionals are: /// Early clip/late clip -/// Alpha channel, no alpha channel -/// Alpha with/without transparency /// class EMBERCL_API FinalAccumOpenCLKernelCreator { public: FinalAccumOpenCLKernelCreator(bool doublePrecision); - const string& GammaCorrectionWithAlphaCalcKernel() const; - const string& GammaCorrectionWithAlphaCalcEntryPoint() const; - - const string& GammaCorrectionWithoutAlphaCalcKernel() const; - const string& GammaCorrectionWithoutAlphaCalcEntryPoint() const; - - const string& FinalAccumEarlyClipKernel() const; - const string& FinalAccumEarlyClipEntryPoint() const; - const string& FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel() const; - const string& FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumEntryPoint() const; const string& FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel() const; const string& FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const; - - const string& FinalAccumLateClipKernel() const; - const string& FinalAccumLateClipEntryPoint() const; - const string& FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel() const; - const string& FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint() const; const string& FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel() const; const string& FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const; - const string& GammaCorrectionEntryPoint(size_t channels, bool transparency) const; - const string& GammaCorrectionKernel(size_t channels, bool transparency) const; - const string& FinalAccumEntryPoint(bool earlyClip, size_t channels, bool transparency, double& alphaBase, double& alphaScale) const; - const string& FinalAccumKernel(bool earlyClip, size_t channels, bool transparency) const; + const string& GammaCorrectionEntryPoint() const; + const string& GammaCorrectionKernel() const; + const string& FinalAccumEntryPoint(bool earlyClip) const; + const string& FinalAccumKernel(bool earlyClip) const; private: - string CreateFinalAccumKernelString(bool earlyClip, size_t channels, bool transparency); - string CreateGammaCorrectionKernelString(bool alphaCalc); + string CreateFinalAccumKernelString(bool earlyClip); + string CreateGammaCorrectionKernelString(); - string CreateFinalAccumKernelString(bool earlyClip, bool alphaCalc, bool alphaAccum); - string CreateGammaCorrectionFunctionString(bool globalBucket, bool alphaCalc, bool alphaAccum, bool finalOut); + string CreateGammaCorrectionFunctionString(bool globalBucket, bool finalOut); string CreateCalcNewRgbFunctionString(bool globalBucket); - string m_GammaCorrectionWithAlphaCalcKernel; - string m_GammaCorrectionWithAlphaCalcEntryPoint = "GammaCorrectionWithAlphaCalcKernel"; - string m_GammaCorrectionWithoutAlphaCalcKernel; string m_GammaCorrectionWithoutAlphaCalcEntryPoint = "GammaCorrectionWithoutAlphaCalcKernel"; - string m_FinalAccumEarlyClipKernel;//False, false. - string m_FinalAccumEarlyClipEntryPoint = "FinalAccumEarlyClipKernel"; - string m_FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel;//True, true. - string m_FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumEntryPoint = "FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel"; - string m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel;//False, true. + string m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel; string m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint = "FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel"; - string m_FinalAccumLateClipKernel;//False, false. - string m_FinalAccumLateClipEntryPoint = "FinalAccumLateClipKernel"; - string m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel;//True, true. - string m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint = "FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel"; - string m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel;//False, true. + string m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel; string m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint = "FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel"; string m_Empty; diff --git a/Source/EmberCL/RendererCL.cpp b/Source/EmberCL/RendererCL.cpp index 5c00c09..19f942d 100644 --- a/Source/EmberCL/RendererCL.cpp +++ b/Source/EmberCL/RendererCL.cpp @@ -40,7 +40,6 @@ void RendererCL::Init() { m_Init = false; m_DoublePrecision = typeid(T) == typeid(double); - m_NumChannels = 4; //Buffer names. m_EmberBufferName = "Ember"; m_XformsBufferName = "Xforms"; @@ -73,7 +72,7 @@ void RendererCL::Init() m_PaletteFormat.image_channel_order = CL_RGBA; m_PaletteFormat.image_channel_data_type = CL_FLOAT; m_FinalFormat.image_channel_order = CL_RGBA; - m_FinalFormat.image_channel_data_type = CL_UNORM_INT8;//Change if this ever supports 2BPC outputs for PNG. + m_FinalFormat.image_channel_data_type = CL_FLOAT; } /// @@ -386,7 +385,7 @@ const string& RendererCL::DEKernel() const { return m_DEOpenCLKernel /// /// The string representation of the kernel for the last built final accumulation program. template -const string& RendererCL::FinalAccumKernel() const { return m_FinalAccumOpenCLKernelCreator.FinalAccumKernel(EarlyClip(), Renderer::NumChannels(), Transparency()); } +const string& RendererCL::FinalAccumKernel() const { return m_FinalAccumOpenCLKernelCreator.FinalAccumKernel(EarlyClip()); } /// /// Get the a const referece to the devices this renderer will use. @@ -407,7 +406,7 @@ const vector>& RendererCL::Devices() co /// The host side buffer to read into /// True if success, else false. template -bool RendererCL::ReadFinal(byte* pixels) +bool RendererCL::ReadFinal(v4F* pixels) { if (pixels && !m_Devices.empty()) return m_Devices[0]->m_Wrapper.ReadImage(m_FinalImageName, FinalRasW(), FinalRasH(), 0, m_Devices[0]->m_Wrapper.Shared(), pixels); @@ -423,7 +422,7 @@ bool RendererCL::ReadFinal(byte* pixels) template bool RendererCL::ClearFinal() { - vector v; + vector v; if (!m_Devices.empty()) { @@ -470,17 +469,6 @@ bool RendererCL::Ok() const return !m_Devices.empty() && m_Init; } -/// -/// Override to force num channels to be 4 because RGBA is always used for OpenCL -/// since the output is actually an image rather than just a buffer. -/// -/// The number of channels, ignored. -template -void RendererCL::NumChannels(size_t numChannels) -{ - m_NumChannels = 4; -} - /// /// Clear the error report for this class as well as the OpenCLWrapper members of each device. /// @@ -771,7 +759,7 @@ eRenderStatus RendererCL::GaussianDensityFilter() /// Offset in the buffer to store the pixels to /// True if success and not aborted, else false. template -eRenderStatus RendererCL::AccumulatorToFinalImage(byte* pixels, size_t finalOffset) +eRenderStatus RendererCL::AccumulatorToFinalImage(v4F* pixels, size_t finalOffset) { auto status = RunFinalAccum(); @@ -877,17 +865,6 @@ EmberStats RendererCL::Iterate(size_t iterCount, size_t temporalSamp return stats; } -/// -/// Override which just passes false to the base. -/// This is because curves are scaled from 0-1 to 0-255 or 0-65535 on the CPU, but need to be kept as 0-1 for OpenCL because the texture expects normalized values. -/// -/// Ignored -template -void RendererCL::ComputeCurves(bool scale) -{ - Renderer::ComputeCurves(false); -} - /// /// Private functions for making and running OpenCL programs. /// @@ -1304,9 +1281,7 @@ eRenderStatus RendererCL::RunFinalAccum() { //Timing t(4); bool b = true; - double alphaBase; - double alphaScale; - int accumKernelIndex = MakeAndGetFinalAccumProgram(alphaBase, alphaScale); + int accumKernelIndex = MakeAndGetFinalAccumProgram(); cl_uint argIndex; size_t gridW; size_t gridH; @@ -1323,7 +1298,7 @@ eRenderStatus RendererCL::RunFinalAccum() if (b && !(b = wrapper.AddAndWriteBuffer(m_SpatialFilterParamsBufferName, reinterpret_cast(&m_SpatialFilterCL), sizeof(m_SpatialFilterCL)))) { AddToReport(loc); } - if (b && !(b = wrapper.AddAndWriteBuffer(m_CurvesCsaName, m_Csa.data(), SizeOf(m_Csa)))) { AddToReport(loc); } + if (b && !(b = wrapper.AddAndWriteBuffer(m_CurvesCsaName, m_Csa.data(), SizeOf(m_Csa)))) { AddToReport(loc); } //Since early clip requires gamma correcting the entire accumulator first, //it can't be done inside of the normal final accumulation kernel, so @@ -1373,10 +1348,6 @@ eRenderStatus RendererCL::RunFinalAccum() if (b && !(b = wrapper.SetArg (accumKernelIndex, argIndex++, curvesSet))) { AddToReport(loc); }//Do curves. - if (b && !(b = wrapper.SetArg (accumKernelIndex, argIndex++, bucketT(alphaBase)))) { AddToReport(loc); }//Alpha base. - - if (b && !(b = wrapper.SetArg (accumKernelIndex, argIndex++, bucketT(alphaScale)))) { AddToReport(loc); }//Alpha scale. - if (b && wrapper.Shared()) if (b && !(b = wrapper.EnqueueAcquireGLObjects(m_FinalImageName))) { AddToReport(loc); } @@ -1534,26 +1505,22 @@ int RendererCL::MakeAndGetDensityFilterProgram(size_t ss, uint filte /// /// Make the final accumulation on the primary device program and return its index. -/// There are many different kernels for final accum, depending on early clip, alpha channel, and transparency. -/// Loading all of these in the beginning is too much, so only load the one for the current case being worked with. /// -/// Storage for the alpha base value used in the kernel. 0 if transparency is true, else 255. -/// Storage for the alpha scale value used in the kernel. 255 if transparency is true, else 0. /// The kernel index if successful, else -1. template -int RendererCL::MakeAndGetFinalAccumProgram(double& alphaBase, double& alphaScale) +int RendererCL::MakeAndGetFinalAccumProgram() { int kernelIndex = -1; if (!m_Devices.empty()) { auto& wrapper = m_Devices[0]->m_Wrapper; - auto& finalAccumEntryPoint = m_FinalAccumOpenCLKernelCreator.FinalAccumEntryPoint(EarlyClip(), Renderer::NumChannels(), Transparency(), alphaBase, alphaScale); + auto& finalAccumEntryPoint = m_FinalAccumOpenCLKernelCreator.FinalAccumEntryPoint(EarlyClip()); const char* loc = __FUNCTION__; if ((kernelIndex = wrapper.FindKernelIndex(finalAccumEntryPoint)) == -1)//Has not been built yet. { - auto& kernel = m_FinalAccumOpenCLKernelCreator.FinalAccumKernel(EarlyClip(), Renderer::NumChannels(), Transparency()); + auto& kernel = m_FinalAccumOpenCLKernelCreator.FinalAccumKernel(EarlyClip()); if (wrapper.AddProgram(finalAccumEntryPoint, kernel, finalAccumEntryPoint, m_DoublePrecision)) kernelIndex = wrapper.FindKernelIndex(finalAccumEntryPoint);//Try to find it again, it will be present if successfully built. @@ -1575,13 +1542,13 @@ int RendererCL::MakeAndGetGammaCorrectionProgram() if (!m_Devices.empty()) { auto& wrapper = m_Devices[0]->m_Wrapper; - auto& gammaEntryPoint = m_FinalAccumOpenCLKernelCreator.GammaCorrectionEntryPoint(Renderer::NumChannels(), Transparency()); + auto& gammaEntryPoint = m_FinalAccumOpenCLKernelCreator.GammaCorrectionEntryPoint(); int kernelIndex = wrapper.FindKernelIndex(gammaEntryPoint); const char* loc = __FUNCTION__; if (kernelIndex == -1)//Has not been built yet. { - auto& kernel = m_FinalAccumOpenCLKernelCreator.GammaCorrectionKernel(Renderer::NumChannels(), Transparency()); + auto& kernel = m_FinalAccumOpenCLKernelCreator.GammaCorrectionKernel(); bool b = wrapper.AddProgram(gammaEntryPoint, kernel, gammaEntryPoint, m_DoublePrecision); if (b) @@ -1735,10 +1702,7 @@ void RendererCL::ConvertSpatialFilter() m_SpatialFilterCL.m_FinalRasH = uint(FinalRasH()); m_SpatialFilterCL.m_Supersample = uint(Supersample()); m_SpatialFilterCL.m_FilterWidth = uint(m_SpatialFilter->FinalFilterWidth()); - m_SpatialFilterCL.m_NumChannels = uint(Renderer::NumChannels()); - m_SpatialFilterCL.m_BytesPerChannel = uint(BytesPerChannel()); m_SpatialFilterCL.m_DensityFilterOffset = uint(DensityFilterOffset()); - m_SpatialFilterCL.m_Transparency = Transparency(); m_SpatialFilterCL.m_YAxisUp = uint(m_YAxisUp); m_SpatialFilterCL.m_Vibrancy = vibrancy; m_SpatialFilterCL.m_HighlightPower = HighlightPower(); diff --git a/Source/EmberCL/RendererCL.h b/Source/EmberCL/RendererCL.h index 2d8309c..8114c29 100644 --- a/Source/EmberCL/RendererCL.h +++ b/Source/EmberCL/RendererCL.h @@ -19,7 +19,7 @@ class EMBERCL_API RendererCLBase { public: virtual ~RendererCLBase() { } - virtual bool ReadFinal(byte* pixels) = 0; + virtual bool ReadFinal(v4F* pixels) = 0; virtual bool ClearFinal() = 0; }; @@ -43,7 +43,6 @@ class EMBERCL_API RendererCL : public Renderer, public RendererCLBas { using EmberNs::Renderer::RendererBase::Abort; using EmberNs::Renderer::RendererBase::EarlyClip; - using EmberNs::Renderer::RendererBase::Transparency; using EmberNs::Renderer::RendererBase::EnterResize; using EmberNs::Renderer::RendererBase::LeaveResize; using EmberNs::Renderer::RendererBase::FinalRasW; @@ -60,7 +59,6 @@ class EMBERCL_API RendererCL : public Renderer, public RendererCLBas using EmberNs::Renderer::RendererBase::m_YAxisUp; using EmberNs::Renderer::RendererBase::m_LockAccum; using EmberNs::Renderer::RendererBase::m_Abort; - using EmberNs::Renderer::RendererBase::m_NumChannels; using EmberNs::Renderer::RendererBase::m_LastIter; using EmberNs::Renderer::RendererBase::m_LastIterPercent; using EmberNs::Renderer::RendererBase::m_Stats; @@ -141,13 +139,12 @@ public: const vector>& Devices() const; //Virtual functions overridden from RendererCLBase. - virtual bool ReadFinal(byte* pixels); + virtual bool ReadFinal(v4F* pixels); virtual bool ClearFinal(); //Public virtual functions overridden from Renderer or RendererBase. virtual size_t MemoryAvailable() override; virtual bool Ok() const override; - virtual void NumChannels(size_t numChannels) override; virtual void ClearErrorReport() override; virtual size_t SubBatchSize() const override; virtual size_t ThreadCount() const override; @@ -166,9 +163,8 @@ protected: virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true) override; virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false) override; virtual eRenderStatus GaussianDensityFilter() override; - virtual eRenderStatus AccumulatorToFinalImage(byte* pixels, size_t finalOffset) override; + virtual eRenderStatus AccumulatorToFinalImage(v4F* pixels, size_t finalOffset) override; virtual EmberStats Iterate(size_t iterCount, size_t temporalSample) override; - virtual void ComputeCurves(bool scale) override; #ifndef TEST_CL private: @@ -183,7 +179,7 @@ private: bool ClearBuffer(size_t device, const string& bufferName, uint width, uint height, uint elementSize); bool RunDensityFilterPrivate(size_t kernelIndex, size_t gridW, size_t gridH, size_t blockW, size_t blockH, uint chunkSizeW, uint chunkSizeH, uint colChunkPass, uint rowChunkPass); int MakeAndGetDensityFilterProgram(size_t ss, uint filterWidth); - int MakeAndGetFinalAccumProgram(double& alphaBase, double& alphaScale); + int MakeAndGetFinalAccumProgram(); int MakeAndGetGammaCorrectionProgram(); bool CreateHostBuffer(); bool SumDeviceHist(); diff --git a/Source/EmberCommon/EmberCommon.h b/Source/EmberCommon/EmberCommon.h index 9c2bb42..71c56ad 100644 --- a/Source/EmberCommon/EmberCommon.h +++ b/Source/EmberCommon/EmberCommon.h @@ -1,6 +1,7 @@ #pragma once #include "EmberCommonPch.h" +#include "EmberOptions.h" /// /// Global utility classes and functions that are common to all programs that use @@ -168,26 +169,133 @@ void FormatName(Ember& result, ostringstream& os, streamsize padding) } /// -/// Convert an RGBA buffer to an RGB buffer. +/// Convert an RGBA 32-bit float buffer to an RGB 8-bit buffer. /// The two buffers can point to the same memory location if needed. /// -/// The RGBA buffer -/// The RGB buffer +/// The RGBA 32-bit float buffer +/// The RGB 8-bit buffer /// The width of the image in pixels /// The height of the image in pixels -static void RgbaToRgb(vector& rgba, vector& rgb, size_t width, size_t height) +static void Rgba32ToRgb8(v4F* rgba, byte* rgb, size_t width, size_t height) { - if (rgba.data() != rgb.data())//Only resize the destination buffer if they are different. - rgb.resize(width * height * 3); - - for (size_t i = 0, j = 0; i < (width * height * 4); i += 4, j += 3) + for (size_t i = 0, j = 0; i < (width * height); i++) { - rgb[j] = rgba[i]; - rgb[j + 1] = rgba[i + 1]; - rgb[j + 2] = rgba[i + 2]; + rgb[j++] = byte(Clamp(rgba[i].r * 255.0f, 0.0f, 255.0f)); + rgb[j++] = byte(Clamp(rgba[i].g * 255.0f, 0.0f, 255.0f)); + rgb[j++] = byte(Clamp(rgba[i].b * 255.0f, 0.0f, 255.0f)); } } +/// +/// Convert an RGBA 32-bit float buffer to an RGBA 8-bit buffer. +/// The two buffers can point to the same memory location if needed. +/// +/// The RGBA 32-bit float buffer +/// The RGBA 8-bit buffer +/// The width of the image in pixels +/// The height of the image in pixels +/// True to use alpha transparency, false to assign the max alpha value to make each pixel fully visible +static void Rgba32ToRgba8(v4F* rgba, byte* rgb, size_t width, size_t height, bool doAlpha) +{ + for (size_t i = 0, j = 0; i < (width * height); i++) + { + rgb[j++] = byte(Clamp(rgba[i].r * 255.0f, 0.0f, 255.0f)); + rgb[j++] = byte(Clamp(rgba[i].g * 255.0f, 0.0f, 255.0f)); + rgb[j++] = byte(Clamp(rgba[i].b * 255.0f, 0.0f, 255.0f)); + rgb[j++] = doAlpha ? byte(Clamp(rgba[i].a * 255.0f, 0.0f, 255.0f)) : 255; + } +} + +/// +/// Convert an RGBA 32-bit float buffer to an RGBA 16-bit buffer. +/// The two buffers can point to the same memory location if needed. +/// +/// The RGBA 32-bit float buffer +/// The RGBA 16-bit buffer +/// The width of the image in pixels +/// The height of the image in pixels +/// True to use alpha transparency, false to assign the max alpha value to make each pixel fully visible +static void Rgba32ToRgba16(v4F* rgba, glm::uint16* rgb, size_t width, size_t height, bool doAlpha) +{ + for (size_t i = 0, j = 0; i < (width * height); i++) + { + rgb[j++] = glm::uint16(Clamp(rgba[i].r * 65535.0f, 0.0f, 65535.0f)); + rgb[j++] = glm::uint16(Clamp(rgba[i].g * 65535.0f, 0.0f, 65535.0f)); + rgb[j++] = glm::uint16(Clamp(rgba[i].b * 65535.0f, 0.0f, 65535.0f)); + rgb[j++] = doAlpha ? glm::uint16(Clamp(rgba[i].a * 65535.0f, 0.0f, 65535.0f)) : glm::uint16(65535); + } +} + +/// +/// Convert an RGBA 32-bit float buffer to an EXR RGBA 32-bit float buffer. +/// The two buffers can point to the same memory location if needed. +/// +/// The RGBA 32-bit float buffer +/// The EXR RGBA 32-bit float buffer +/// The width of the image in pixels +/// The height of the image in pixels +/// True to use alpha transparency, false to assign the max alpha value to make each pixel fully visible +static void Rgba32ToRgbaExr(v4F* rgba, Rgba* ilmfRgba, size_t width, size_t height, bool doAlpha) +{ + for (size_t i = 0; i < (width * height); i++) + { + ilmfRgba[i].r = Clamp(rgba[i].r, 0.0f, 1.0f); + ilmfRgba[i].g = Clamp(rgba[i].g, 0.0f, 1.0f); + ilmfRgba[i].b = Clamp(rgba[i].b, 0.0f, 1.0f); + ilmfRgba[i].a = doAlpha ? Clamp(rgba[i].a * 1.0f, 0.0f, 1.0f) : 1.0f; + } +} + +/// +/// Make a filename for a single render. This is used in EmberRender. +/// +/// The path portion of where to save the file +/// The full name and path to override everything else +/// The name to use when useFinalName is true +/// The prefix to prepend to the filename +/// True suffix to append to the filename +/// The format extention. This must not contain a period. +/// The width padding to use, which will be zero filled. +/// The numerical value to use for the filename when useFinalName is false and out is empty +/// Whether to use the name included in the flame. The i parameter is ignored in this case. +static string MakeSingleFilename(const string& path, const string& out, const string& finalName, const string& prefix, const string& suffix, const string& format, glm::uint padding, size_t i, bool useFinalName) +{ + string filename; + + if (!out.empty()) + { + filename = out; + } + else if (useFinalName) + { + filename = path + prefix + finalName + suffix + "." + format; + } + else + { + ostringstream fnstream; + fnstream << path << prefix << setfill('0') << setprecision(0) << fixed << setw(padding) << i << suffix << "." << format; + filename = fnstream.str(); + } + + return filename; +} + +/// +/// Make a filename for a frame of an animation render. This is used in EmberAnimate. +/// +/// The path portion of where to save the file +/// The prefix to prepend to the filename +/// True suffix to append to the filename +/// The format extention. This must contain a period. +/// The width padding to use, which will be zero filled. +/// The numerical value to use for the filename +static string MakeAnimFilename(const string& path, const string& prefix, const string& suffix, const string& format, glm::uint padding, size_t ftime) +{ + ostringstream fnstream; + fnstream << path << prefix << setfill('0') << setprecision(0) << fixed << setw(padding) << ftime << suffix << format; + return fnstream.str(); +} + /// /// Calculate the number of strips required if the needed amount of memory /// is greater than the system memory, or greater than what the user wants to allow. @@ -439,7 +547,7 @@ static vector>> CreateRenderers(eRendererType rend /// Function called when all strips successfully finish rendering /// True if all rendering was successful, else false. template -static bool StripsRender(RendererBase* renderer, Ember& ember, vector& finalImage, double time, size_t strips, bool yAxisUp, +static bool StripsRender(RendererBase* renderer, Ember& ember, vector& finalImage, double time, size_t strips, bool yAxisUp, std::function perStripStart, std::function perStripFinish, std::function perStripError, diff --git a/Source/EmberCommon/EmberCommonPch.h b/Source/EmberCommon/EmberCommonPch.h index a635a59..f64f851 100644 --- a/Source/EmberCommon/EmberCommonPch.h +++ b/Source/EmberCommon/EmberCommonPch.h @@ -57,5 +57,17 @@ #include "SimpleGlob.h" #include "SimpleOpt.h" +//Exr +#ifdef _WIN32 +#define OPENEXR_DLL 1 +#endif + +#include +#include +#include + +using namespace Imf; +using namespace Imath; + using namespace EmberNs; using namespace EmberCLns; diff --git a/Source/EmberCommon/EmberOptions.h b/Source/EmberCommon/EmberOptions.h index 4678930..885bb56 100644 --- a/Source/EmberCommon/EmberOptions.h +++ b/Source/EmberCommon/EmberOptions.h @@ -58,8 +58,7 @@ enum class eOptionIDs : et OPT_INT_PALETTE, OPT_HEX_PALETTE, OPT_INSERT_PALETTE, - OPT_JPEG_COMMENTS, - OPT_PNG_COMMENTS, + OPT_ENABLE_COMMENTS, OPT_WRITE_GENOME, OPT_THREADED_WRITE, OPT_ENCLOSED, @@ -75,7 +74,6 @@ enum class eOptionIDs : et OPT_STRIPS, OPT_SUPERSAMPLE, OPT_TEMPSAMPLES, - OPT_BPC, OPT_PRINT_EDIT_DEPTH, OPT_JPEG, OPT_BEGIN, @@ -363,8 +361,7 @@ public: INITBOOLOPTION(NameEnable, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_NAME_ENABLE, _T("--name_enable"), false, SO_NONE, " --name_enable Use the name attribute contained in the Xml as the output filename [default: false].\n")); INITBOOLOPTION(HexPalette, Eob(eOptionUse::OPT_ANIM_GENOME, eOptionIDs::OPT_HEX_PALETTE, _T("--hex_palette"), true, SO_OPT, " --hex_palette Force palette RGB values to be hex when saving to Xml [default: true].\n")); INITBOOLOPTION(InsertPalette, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_INSERT_PALETTE, _T("--insert_palette"), false, SO_NONE, " --insert_palette Insert the palette into the image for debugging purposes. Disabled when running with OpenCL [default: false].\n")); - INITBOOLOPTION(JpegComments, Eob(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_JPEG_COMMENTS, _T("--enable_jpg_comments"), false, SO_NONE, " --enable_jpg_comments Enables embedding the flame parameters and user identifying information in the jpeg header [default: false].\n")); - INITBOOLOPTION(PngComments, Eob(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PNG_COMMENTS, _T("--enable_png_comments"), false, SO_NONE, " --enable_png_comments Enables embedding the flame parameters and user identifying information in the png header [default: false].\n")); + INITBOOLOPTION(EnableComments, Eob(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_ENABLE_COMMENTS, _T("--enable_comments"), false, SO_NONE, " --enable_comments Enables embedding the flame parameters and user identifying information in the header of a jpg, png or exr [default: false].\n")); INITBOOLOPTION(WriteGenome, Eob(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_WRITE_GENOME, _T("--write_genome"), false, SO_NONE, " --write_genome Write out flame associated with center of motion blur window [default: false].\n")); INITBOOLOPTION(ThreadedWrite, Eob(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_THREADED_WRITE, _T("--threaded_write"), true, SO_OPT, " --threaded_write Use a separate thread to write images to disk. This gives better performance, but doubles the memory required for the final output buffer. [default: true].\n")); INITBOOLOPTION(Enclosed, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ENCLOSED, _T("--enclosed"), true, SO_OPT, " --enclosed Use enclosing Xml tags [default: true].\n")); @@ -388,7 +385,6 @@ public: INITUINTOPTION(Strips, Eou(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_STRIPS, _T("--nstrips"), 1, SO_REQ_SEP, " --nstrips= The number of fractions to split a single render frame into. Useful for print size renders or low memory systems [default: 1].\n")); INITUINTOPTION(Supersample, Eou(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SUPERSAMPLE, _T("--supersample"), 0, SO_REQ_SEP, " --supersample= The supersample value used to override the one specified in the file [default: 0 (use value from file), Range: 0 - 4].\n")); INITUINTOPTION(TemporalSamples, Eou(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_TEMPSAMPLES, _T("--ts"), 0, SO_REQ_SEP, " --ts= The temporal samples value used to override all of the temporal sample values specified in the file when animating [default: 0 (use value from file)].\n")); - INITUINTOPTION(BitsPerChannel, Eou(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_BPC, _T("--bpc"), 8, SO_REQ_SEP, " --bpc= Bits per channel. 8 or 16 for PNG, 8 for all others, always 8 with OpenCL [default: 8].\n")); INITUINTOPTION(PrintEditDepth, Eou(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_PRINT_EDIT_DEPTH, _T("--print_edit_depth"), 0, SO_REQ_SEP, " --print_edit_depth= Depth to truncate tag structure when converting a flame to Xml. 0 prints all tags [default: 0].\n")); INITUINTOPTION(JpegQuality, Eou(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_JPEG, _T("--jpeg"), 95, SO_REQ_SEP, " --jpeg= Jpeg quality 0-100 for compression [default: 95].\n")); INITUINTOPTION(FirstFrame, Eou(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_BEGIN, _T("--begin"), UINT_MAX, SO_REQ_SEP, " --begin= Time of first frame to render [default: first time specified in file].\n")); @@ -431,7 +427,11 @@ public: INITSTRINGOPTION(Out, Eos(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_OUT, _T("--out"), "", SO_REQ_SEP, " --out= Name of a single output file. Not recommended when rendering more than one image.\n")); INITSTRINGOPTION(Prefix, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PREFIX, _T("--prefix"), "", SO_REQ_SEP, " --prefix= Prefix to prepend to all output files.\n")); INITSTRINGOPTION(Suffix, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SUFFIX, _T("--suffix"), "", SO_REQ_SEP, " --suffix= Suffix to append to all output files.\n")); - INITSTRINGOPTION(Format, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_FORMAT, _T("--format"), "png", SO_REQ_SEP, " --format= Format of the output file. Valid values are: bmp, jpg, png [default: png].\n")); +#ifdef _WIN32 + INITSTRINGOPTION(Format, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_FORMAT, _T("--format"), "png", SO_REQ_SEP, " --format= Format of the output file. Valid values are: bmp, jpg, png or exr [default: png].\n")); +#else + INITSTRINGOPTION(Format, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_FORMAT, _T("--format"), "png", SO_REQ_SEP, " --format= Format of the output file. Valid values are: jpg, png or exr [default: png].\n")); +#endif INITSTRINGOPTION(PalettePath, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_PALETTE_FILE, _T("--flam3_palettes"), "flam3-palettes.xml", SO_REQ_SEP, " --flam3_palettes= Path and name of the palette file [default: flam3-palettes.xml].\n")); INITSTRINGOPTION(Id, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_ID, _T("--id"), "", SO_REQ_SEP, " --id= ID to use in tags / image comments.\n")); INITSTRINGOPTION(Url, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_URL, _T("--url"), "", SO_REQ_SEP, " --url= URL to use in tags / image comments.\n")); @@ -519,8 +519,7 @@ public: PARSEBOOLOPTION(eOptionIDs::OPT_NAME_ENABLE, NameEnable); PARSEBOOLOPTION(eOptionIDs::OPT_HEX_PALETTE, HexPalette); PARSEBOOLOPTION(eOptionIDs::OPT_INSERT_PALETTE, InsertPalette); - PARSEBOOLOPTION(eOptionIDs::OPT_JPEG_COMMENTS, JpegComments); - PARSEBOOLOPTION(eOptionIDs::OPT_PNG_COMMENTS, PngComments); + PARSEBOOLOPTION(eOptionIDs::OPT_ENABLE_COMMENTS, EnableComments); PARSEBOOLOPTION(eOptionIDs::OPT_WRITE_GENOME, WriteGenome); PARSEBOOLOPTION(eOptionIDs::OPT_THREADED_WRITE, ThreadedWrite); PARSEBOOLOPTION(eOptionIDs::OPT_ENCLOSED, Enclosed); @@ -538,7 +537,6 @@ public: PARSEOPTION(eOptionIDs::OPT_STRIPS, Strips); PARSEOPTION(eOptionIDs::OPT_SUPERSAMPLE, Supersample); PARSEOPTION(eOptionIDs::OPT_TEMPSAMPLES, TemporalSamples); - PARSEOPTION(eOptionIDs::OPT_BPC, BitsPerChannel); PARSEOPTION(eOptionIDs::OPT_PRINT_EDIT_DEPTH, PrintEditDepth); PARSEOPTION(eOptionIDs::OPT_JPEG, JpegQuality); PARSEOPTION(eOptionIDs::OPT_BEGIN, FirstFrame); @@ -723,18 +721,18 @@ public: { cout << "Usage:\n" #ifdef _WIN32 - "\tEmberRender.exe --in=test.flame [--out=outfile --format=png --verbose --progress --opencl]\n\n"; + "\tEmberRender.exe --in=test.flame [--out=outfile --verbose --progress --opencl]\n\n"; #else - "\temberrender --in=test.flame [--out=outfile --format=png --verbose --progress --opencl]\n\n"; + "\temberrender --in=test.flame [--out=outfile --verbose --progress --opencl]\n\n"; #endif } else if (optUsage == eOptionUse::OPT_USE_ANIMATE) { cout << "Usage:\n" #ifdef _WIN32 - "\tEmberAnimate.exe --in=sequence.flame [--format=png --verbose --progress --opencl]\n\n"; + "\tEmberAnimate.exe --in=sequence.flame [--verbose --progress --opencl]\n\n"; #else - "\temberanimate --in=sequence.flame [--format=png --verbose --progress --opencl]\n\n"; + "\temberanimate --in=sequence.flame [--verbose --progress --opencl]\n\n"; #endif } else if (optUsage == eOptionUse::OPT_USE_GENOME) @@ -808,8 +806,7 @@ public: Eob NameEnable; Eob HexPalette; Eob InsertPalette; - Eob JpegComments; - Eob PngComments; + Eob EnableComments; Eob WriteGenome; Eob ThreadedWrite; Eob Enclosed; @@ -828,7 +825,6 @@ public: Eou Strips; Eou Supersample; Eou TemporalSamples; - Eou BitsPerChannel; Eou Bits; Eou PrintEditDepth; Eou JpegQuality; diff --git a/Source/EmberCommon/JpegUtils.h b/Source/EmberCommon/JpegUtils.h index 2a2606b..d8eee29 100644 --- a/Source/EmberCommon/JpegUtils.h +++ b/Source/EmberCommon/JpegUtils.h @@ -136,28 +136,28 @@ static bool WritePng(const char* filename, byte* image, size_t width, size_t hei glm::uint16 testbe = 1; vector rows(height); text[0].compression = PNG_TEXT_COMPRESSION_NONE; - text[0].key = const_cast("flam3_version"); + text[0].key = const_cast("ember_version"); text[0].text = const_cast(EmberVersion()); text[1].compression = PNG_TEXT_COMPRESSION_NONE; - text[1].key = const_cast("flam3_nickname"); + text[1].key = const_cast("ember_nickname"); text[1].text = const_cast(nick.c_str()); text[2].compression = PNG_TEXT_COMPRESSION_NONE; - text[2].key = const_cast("flam3_url"); + text[2].key = const_cast("ember_url"); text[2].text = const_cast(url.c_str()); text[3].compression = PNG_TEXT_COMPRESSION_NONE; - text[3].key = const_cast("flam3_id"); + text[3].key = const_cast("ember_id"); text[3].text = const_cast(id.c_str()); text[4].compression = PNG_TEXT_COMPRESSION_NONE; - text[4].key = const_cast("flam3_error_rate"); + text[4].key = const_cast("ember_error_rate"); text[4].text = const_cast(comments.m_Badvals.c_str()); text[5].compression = PNG_TEXT_COMPRESSION_NONE; - text[5].key = const_cast("flam3_samples"); + text[5].key = const_cast("ember_samples"); text[5].text = const_cast(comments.m_NumIters.c_str()); text[6].compression = PNG_TEXT_COMPRESSION_NONE; - text[6].key = const_cast("flam3_time"); + text[6].key = const_cast("ember_time"); text[6].text = const_cast(comments.m_Runtime.c_str()); text[7].compression = PNG_TEXT_COMPRESSION_zTXt; - text[7].key = const_cast("flam3_genome"); + text[7].key = const_cast("ember_genome"); text[7].text = const_cast(comments.m_Genome.c_str()); for (i = 0; i < height; i++) @@ -326,3 +326,50 @@ static bool WriteBmp(const char* filename, byte* image, size_t width, size_t hei b = SaveBmp(filename, bgrBuf.data(), width, height, newSize); return b; } + +/// +/// Write an EXR file. +/// This is used for extreme color precision because it uses +/// floats for each color channel. +/// +/// The full path and name of the file +/// Pointer to the image data to write +/// Width of the image in pixels +/// Height of the image in pixels +/// True to embed comments, else false +/// The comment string to embed +/// Id of the author +/// Url of the author +/// Nickname of the author +/// True if success, else false +static bool WriteExr(const char* filename, Rgba* image, size_t width, size_t height, bool enableComments, const EmberImageComments& comments, const string& id, const string& url, const string& nick) +{ + try + { + int iw = int(width); + int ih = int(height); + RgbaOutputFile file(filename, iw, ih, WRITE_RGBA); + + if (enableComments) + { + auto& header = const_cast(file.header()); + header.insert("ember_version", StringAttribute(EmberVersion())); + header.insert("ember_nickname", StringAttribute(nick)); + header.insert("ember_url", StringAttribute(url)); + header.insert("ember_id", StringAttribute(id)); + header.insert("ember_error_rate", StringAttribute(comments.m_Badvals)); + header.insert("ember_samples", StringAttribute(comments.m_NumIters)); + header.insert("ember_time", StringAttribute(comments.m_Runtime)); + header.insert("ember_genome", StringAttribute(comments.m_Genome)); + } + + file.setFrameBuffer(image, 1, iw); + file.writePixels(ih); + return true; + } + catch (std::exception e) + { + cout << e.what() << endl; + return false; + } +} diff --git a/Source/EmberGenome/EmberGenome.cpp b/Source/EmberGenome/EmberGenome.cpp index 171ece4..918d6ae 100644 --- a/Source/EmberGenome/EmberGenome.cpp +++ b/Source/EmberGenome/EmberGenome.cpp @@ -171,13 +171,15 @@ bool EmberGenome(EmberOptions& opt) bool exactTimeMatch, randomMode, didColor, seqFlag, random = false; size_t i, i0, i1, rep, val, frame, frameCount, count = 0; size_t ftime, firstFrame, lastFrame; - size_t n, tot, totb, totw; - T avgPix, fractionBlack, fractionWhite, blend, spread, mix0, mix1; + double tot; + size_t n, totb, totw; + double avgPix; + T fractionBlack, fractionWhite, blend, spread, mix0, mix1; string token, filename; ostringstream os, os2; vector> embers, embers2, templateEmbers; vector vars, noVars; - vector finalImage; + vector finalImage; eCrossMode crossMeth; eMutateMode mutMeth; Ember orig, save, selp0, selp1, parent0, parent1; @@ -598,7 +600,6 @@ bool EmberGenome(EmberOptions& opt) renderer->YAxisUp(opt.YAxisUp()); renderer->LockAccum(opt.LockAccum()); renderer->PixelAspectRatio(T(opt.AspectRatio())); - renderer->Transparency(opt.Transparency()); if (opt.Repeat() == 0) { @@ -786,19 +787,20 @@ bool EmberGenome(EmberOptions& opt) return false; } - tot = totb = totw = 0; + tot = 0; + totb = totw = 0; n = orig.m_FinalRasW * orig.m_FinalRasH; - for (i = 0; i < 3 * n; i += 3) + for (i = 0; i < n; i++) { - tot += (finalImage[i] + finalImage[i + 1] + finalImage[i + 2]); + tot += (finalImage[i].r + finalImage[i].g + finalImage[i].b); - if (0 == finalImage[i] && 0 == finalImage[i + 1] && 0 == finalImage[i + 2]) totb++; + if (0 == finalImage[i].r && 0 == finalImage[i].g && 0 == finalImage[i].b) totb++; - if (255 == finalImage[i] && 255 == finalImage[i + 1] && 255 == finalImage[i + 2]) totw++; + if (1 == finalImage[i].r && 1 == finalImage[i].g && 1 == finalImage[i].g) totw++; } - avgPix = (tot / T(3 * n)); + avgPix = (tot / (3 * n)); fractionBlack = totb / T(n); fractionWhite = totw / T(n); diff --git a/Source/EmberRender/EmberRender.cpp b/Source/EmberRender/EmberRender.cpp index 72ca9ef..e72855c 100644 --- a/Source/EmberRender/EmberRender.cpp +++ b/Source/EmberRender/EmberRender.cpp @@ -29,17 +29,15 @@ bool EmberRender(EmberOptions& opt) VerbosePrint("Using " << (sizeof(T) == sizeof(float) ? "single" : "double") << " precision."); Timing t; bool writeSuccess = false; - byte* finalImagep; uint padding; - size_t i, channels; + size_t i; size_t strips; size_t iterCount; - string filename; string inputPath = GetPath(opt.Input()); ostringstream os; pair p; vector> embers; - vector finalImage; + vector finalImage; EmberStats stats; EmberReport emberReport; EmberImageComments comments; @@ -102,12 +100,6 @@ bool EmberRender(EmberOptions& opt) opt.ThreadCount(1); renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr); - if (opt.BitsPerChannel() != 8) - { - cout << "Bits per channel cannot be anything other than 8 with OpenCL, setting to 8.\n"; - opt.BitsPerChannel(8); - } - if (opt.InsertPalette()) { cout << "Inserting palette not supported with OpenCL, insertion will not take place.\n"; @@ -115,24 +107,19 @@ bool EmberRender(EmberOptions& opt) } } - if (opt.Format() != "jpg" && - opt.Format() != "png" && - opt.Format() != "bmp") + if (!Find(opt.Format(), "jpg") && + !Find(opt.Format(), "png") && +#ifdef _WIN32 + !Find(opt.Format(), "bmp") && +#endif + !Find(opt.Format(), "exr")) { - cout << "Format must be jpg, png, or bmp not " << opt.Format() << ". Setting to jpg.\n"; - } - - channels = opt.Format() == "png" ? 4 : 3; - - if (opt.BitsPerChannel() == 16 && opt.Format() != "png") - { - cout << "Support for 16 bits per channel images is only present for the png format. Setting to 8.\n"; - opt.BitsPerChannel(8); - } - else if (opt.BitsPerChannel() != 8 && opt.BitsPerChannel() != 16) - { - cout << "Unexpected bits per channel specified " << opt.BitsPerChannel() << ". Setting to 8.\n"; - opt.BitsPerChannel(8); +#ifdef _WIN32 + cout << "Format must be bmp, jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n"; +#else + cout << "Format must be jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n"; +#endif + opt.Format("png"); } if (opt.AspectRatio() < 0) @@ -156,9 +143,6 @@ bool EmberRender(EmberOptions& opt) renderer->LockAccum(opt.LockAccum()); renderer->InsertPalette(opt.InsertPalette()); renderer->PixelAspectRatio(T(opt.AspectRatio())); - renderer->Transparency(opt.Transparency()); - renderer->NumChannels(channels); - renderer->BytesPerChannel(opt.BitsPerChannel() / 8); renderer->Priority(eThreadPriority(Clamp(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST)))); renderer->Callback(opt.DoProgress() ? progress.get() : nullptr); @@ -282,21 +266,6 @@ bool EmberRender(EmberOptions& opt) //Only write once all strips for this image are finished. [&](Ember& finalEmber) { - if (!opt.Out().empty()) - { - filename = opt.Out(); - } - else if (opt.NameEnable() && !finalEmber.m_Name.empty()) - { - filename = inputPath + opt.Prefix() + finalEmber.m_Name + opt.Suffix() + "." + opt.Format(); - } - else - { - ostringstream fnstream; - fnstream << inputPath << opt.Prefix() << setfill('0') << setprecision(0) << fixed << setw(padding) << i << opt.Suffix() << "." << opt.Format(); - filename = fnstream.str(); - } - //TotalIterCount() is actually using ScaledQuality() which does not get reset upon ember assignment, //so it ends up using the correct value for quality * strips. iterCount = renderer->TotalIterCount(1); @@ -305,28 +274,116 @@ bool EmberRender(EmberOptions& opt) os << comments.m_NumIters << " / " << iterCount << " (" << std::fixed << std::setprecision(2) << ((double(stats.m_Iters) / double(iterCount)) * 100) << "%)"; VerbosePrint("\nIters ran/requested: " + os.str()); - if (!opt.EmberCL()) VerbosePrint("Bad values: " << stats.m_Badvals); + if (!opt.EmberCL()) + VerbosePrint("Bad values: " << stats.m_Badvals); VerbosePrint("Render time: " + t.Format(stats.m_RenderMs)); VerbosePrint("Pure iter time: " + t.Format(stats.m_IterMs)); VerbosePrint("Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << "\n"); - VerbosePrint("Writing " + filename); + bool useName = opt.NameEnable() && !finalEmber.m_Name.empty(); + auto finalImagep = finalImage.data(); + auto size = finalEmber.m_FinalRasW * finalEmber.m_FinalRasH; + bool doBmp = Find(opt.Format(), "bmp"); + bool doJpg = Find(opt.Format(), "jpg"); + bool doExr = Find(opt.Format(), "exr"); + bool doPng8 = Find(opt.Format(), "png"); + bool doPng16 = Find(opt.Format(), "png16"); + bool doOnlyPng8 = doPng8 && !doPng16; + vector rgb8Image; + vector writeFileThreads; + writeFileThreads.reserve(5); - if ((opt.Format() == "jpg" || opt.Format() == "bmp") && renderer->NumChannels() == 4) - RgbaToRgb(finalImage, finalImage, renderer->FinalRasW(), renderer->FinalRasH()); + if (doBmp || doJpg) + { + rgb8Image.resize(size * 3); + Rgba32ToRgb8(finalImagep, rgb8Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH); - finalImagep = finalImage.data(); - writeSuccess = false; + if (doBmp) + { + writeFileThreads.push_back(std::thread([&]() + { + auto filename = MakeSingleFilename(inputPath, opt.Out(), finalEmber.m_Name, opt.Prefix(), opt.Suffix(), "bmp", padding, i, useName); + VerbosePrint("Writing " + filename); + writeSuccess = WriteBmp(filename.c_str(), rgb8Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH); - if (opt.Format() == "png") - writeSuccess = WritePng(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.BitsPerChannel() / 8, opt.PngComments(), comments, opt.Id(), opt.Url(), opt.Nick()); - else if (opt.Format() == "jpg") - writeSuccess = WriteJpeg(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, int(opt.JpegQuality()), opt.JpegComments(), comments, opt.Id(), opt.Url(), opt.Nick()); - else if (opt.Format() == "bmp") - writeSuccess = WriteBmp(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH); + if (!writeSuccess) + cout << "Error writing " << filename << "\n"; + })); + } - if (!writeSuccess) - cout << "Error writing " << filename << "\n"; + if (doJpg) + { + writeFileThreads.push_back(std::thread([&]() + { + auto filename = MakeSingleFilename(inputPath, opt.Out(), finalEmber.m_Name, opt.Prefix(), opt.Suffix(), "jpg", padding, i, useName); + VerbosePrint("Writing " + filename); + writeSuccess = WriteJpeg(filename.c_str(), rgb8Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, int(opt.JpegQuality()), opt.EnableComments(), comments, opt.Id(), opt.Url(), opt.Nick()); + + if (!writeSuccess) + cout << "Error writing " << filename << "\n"; + })); + } + } + + if (doPng8) + { + bool doBothPng = doPng16 && (opt.Format().find("png") != opt.Format().rfind("png")); + + if (doBothPng || doOnlyPng8)//8-bit PNG + { + writeFileThreads.push_back(std::thread([&]() + { + auto filename = MakeSingleFilename(inputPath, opt.Out(), finalEmber.m_Name, opt.Prefix(), opt.Suffix(), "png", padding, i, useName); + VerbosePrint("Writing " + filename); + vector rgba8Image(size * 4); + Rgba32ToRgba8(finalImagep, rgba8Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.Transparency()); + writeSuccess = WritePng(filename.c_str(), rgba8Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, 1, opt.EnableComments(), comments, opt.Id(), opt.Url(), opt.Nick()); + + if (!writeSuccess) + cout << "Error writing " << filename << "\n"; + })); + } + + if (doPng16) + { + writeFileThreads.push_back(std::thread([&]() + { + auto suffix = opt.Suffix(); + + if (doBothPng)//Add suffix if they specified both PNG. + { + VerbosePrint("Doing both PNG formats, so adding suffix _p16 to avoid overwriting the same file."); + suffix += "_p16"; + } + + auto filename = MakeSingleFilename(inputPath, opt.Out(), finalEmber.m_Name, opt.Prefix(), suffix, "png", padding, i, useName); + VerbosePrint("Writing " + filename); + vector rgba16Image(size * 4); + Rgba32ToRgba16(finalImagep, rgba16Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.Transparency()); + writeSuccess = WritePng(filename.c_str(), (byte*)rgba16Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, 2, opt.EnableComments(), comments, opt.Id(), opt.Url(), opt.Nick()); + + if (!writeSuccess) + cout << "Error writing " << filename << "\n"; + })); + } + } + + if (doExr) + { + writeFileThreads.push_back(std::thread([&]() + { + auto filename = MakeSingleFilename(inputPath, opt.Out(), finalEmber.m_Name, opt.Prefix(), opt.Suffix(), "exr", padding, i, useName); + VerbosePrint("Writing " + filename); + vector rgba32Image(size); + Rgba32ToRgbaExr(finalImagep, rgba32Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.Transparency()); + writeSuccess = WriteExr(filename.c_str(), rgba32Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.EnableComments(), comments, opt.Id(), opt.Url(), opt.Nick()); + + if (!writeSuccess) + cout << "Error writing " << filename << "\n"; + })); + } + + Join(writeFileThreads); }); if (opt.EmberCL() && opt.DumpKernel()) diff --git a/Source/EmberTester/EmberTester.cpp b/Source/EmberTester/EmberTester.cpp index 44853bb..87389cc 100644 --- a/Source/EmberTester/EmberTester.cpp +++ b/Source/EmberTester/EmberTester.cpp @@ -5,6 +5,25 @@ #include #include +#include +//#include +//#include +//#include +//#include + +//#include "drawImage.h" +// +//#include +//#include +// +// +//#include +// +//namespace IMF = Imf; +// +using namespace Imf; +using namespace Imath; + /// /// EmberTester is a scratch area used for on the fly testing. /// It may become a more formalized automated testing system @@ -17,6 +36,43 @@ using namespace EmberCommon; //#define DO_NVIDIA 1 +void writeRgba1(const char filename[], + const Rgba* pixels, + int width, + int height) +{ + // + // Write an RGBA image using class RgbaOutputFile. + // + // - open the file + // - describe the memory layout of the pixels + // - store the pixels in the file + // + //auto& chl = file.header().channels(); + //chl.findChannel("R")->type = PixelType::FLOAT; + //Header header(width, height); + //header.channels().insert("R", Channel(IMF::FLOAT)); + //header.channels().insert("G", Channel(IMF::FLOAT)); + //header.channels().insert("B", Channel(IMF::FLOAT)); + ////header.channels().insert("A", Channel(IMF::FLOAT)); + //FrameBuffer frameBuffer; + //frameBuffer.insert("Z", // name + // Slice(IMF::FLOAT, // type + // (char *)zPixels, // base + // sizeof(*zPixels) * 1, // xStride + // sizeof(*zPixels) * width)); // yStride + try + { + RgbaOutputFile file(filename, width, height, WRITE_RGBA); + file.setFrameBuffer(pixels, 1, width); + file.writePixels(height); + } + catch (std::exception e) + { + cout << e.what() << endl; + } +} + template void SaveFinalImage(Renderer& renderer, vector& pixels, char* suffix) { @@ -1981,6 +2037,20 @@ int _tmain(int argc, _TCHAR* argv[]) vector> dv; list> fl; list> dl; + int w = 1000, h = 1000; + string filename = ".\\testexr.exr"; + vector pixels; + pixels.resize(w * h); + + for (auto& pix : pixels) + { + pix.r = 1.0; + pix.b = 0.0; + pix.a = 1.0; + //pix.r = std::numeric_limits::max(); + } + + writeRgba1(filename.c_str(), pixels.data(), w, h); /* TestFuncs(); string line = "title=\"cj_aerie\" smooth=no", delim = " =\""; auto vec = Split(line, delim, true); @@ -2008,25 +2078,24 @@ int _tmain(int argc, _TCHAR* argv[]) return 1; */ //MakeTestAllVarsRegPrePostComboFile("testallvarsout.flame"); - /* return 0; + return 0; + /* + TestThreadedKernel(); + auto palf = PaletteList::Instance(); + Palette* pal = palf->GetRandomPalette(); - TestThreadedKernel(); + cout << pal->Size() << endl; - auto palf = PaletteList::Instance(); - Palette* pal = palf->GetRandomPalette(); + double d = 1; - cout << pal->Size() << endl; + for (int i = 0; i < 10; i++) + { + cout << "log10(" << d << ") = " << std::max(1u, uint(std::log10(d)) + 1u) << endl; + d *= 10; + } - double d = 1; - - for (int i = 0; i < 10; i++) - { - cout << "log10(" << d << ") = " << std::max(1u, uint(std::log10(d)) + 1u) << endl; - d *= 10; - } - - return 0;*/ + return 0;*/ /* uint i, iters = (uint)10e7; size_t total = 0; diff --git a/Source/Fractorium/AboutDialog.ui b/Source/Fractorium/AboutDialog.ui index f5face7..85fed3f 100644 --- a/Source/Fractorium/AboutDialog.ui +++ b/Source/Fractorium/AboutDialog.ui @@ -58,7 +58,7 @@ QFrame::NoFrame - <html><head/><body><p align="center">Fractorium 1.0.0.4</p><p align="center"><span style=" font-size:10pt;">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"><a href="http://fractorium.com"><span style=" text-decoration: underline; color:#0000ff;">fractorium.com</span></a><span style=" font-size:10pt;"><br/>Lead: Matt Feemster<br/>Contributors: Simon Detheridge, Michel Mastriani</span></p></body></html> + <html><head/><body><p align="center">Fractorium 1.0.0.5</p><p align="center"><span style=" font-size:10pt;">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"><a href="http://fractorium.com"><span style=" text-decoration: underline; color:#0000ff;">fractorium.com</span></a><span style=" font-size:10pt;"><br/>Lead: Matt Feemster<br/>Contributors: Simon Detheridge, Michel Mastriani</span></p></body></html> Qt::RichText diff --git a/Source/Fractorium/DoubleSpinBox.cpp b/Source/Fractorium/DoubleSpinBox.cpp index 257f0e6..425fb2b 100644 --- a/Source/Fractorium/DoubleSpinBox.cpp +++ b/Source/Fractorium/DoubleSpinBox.cpp @@ -169,7 +169,7 @@ void DoubleSpinBox::OnTimeout() /// false bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e) { - QMouseEvent* me = dynamic_cast(e); + auto me = dynamic_cast(e); if (isEnabled() && me) { @@ -287,3 +287,124 @@ void DoubleSpinBox::StopTimer() s_Timer.stop(); disconnect(&s_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout())); } + +/// +/// Constructor that passes agruments to the base and assigns the m_Param and m_Variation members. +/// It also sets up the context menu for special numerical values. +/// +/// The parent widget +/// The widget item this spinner is contained in +/// The variation this spinner is for +/// The name of the parameter this is for +/// The height of the spin box. Default: 16. +/// The step used to increment/decrement the spin box when using the mouse wheel. Default: 0.05. +VariationTreeDoubleSpinBox::VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, const string& param, int h, double step) + : DoubleSpinBox(p, h, step) +{ + m_WidgetItem = widgetItem; + m_Param = param; + m_Id = id; + setDecimals(3); + //PI + auto piAction = new QAction("PI", this); + connect(piAction, SIGNAL(triggered(bool)), this, SLOT(PiActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(piAction); + //PI * 2 + auto twoPiAction = new QAction("2 PI", this); + connect(twoPiAction, SIGNAL(triggered(bool)), this, SLOT(TwoPiActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(twoPiAction); + //PI / 2 + auto piOver2Action = new QAction("PI / 2", this); + connect(piOver2Action, SIGNAL(triggered(bool)), this, SLOT(PiOver2ActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(piOver2Action); + //PI / 3 + auto piOver3Action = new QAction("PI / 3", this); + connect(piOver3Action, SIGNAL(triggered(bool)), this, SLOT(PiOver3ActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(piOver3Action); + //PI / 4 + auto piOver4Action = new QAction("PI / 4", this); + connect(piOver4Action, SIGNAL(triggered(bool)), this, SLOT(PiOver4ActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(piOver4Action); + //PI / 6 + auto piOver6Action = new QAction("PI / 6", this); + connect(piOver6Action, SIGNAL(triggered(bool)), this, SLOT(PiOver6ActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(piOver6Action); + //1 / PI + auto oneOverPiAction = new QAction("1 / PI", this); + connect(oneOverPiAction, SIGNAL(triggered(bool)), this, SLOT(OneOverPiActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(oneOverPiAction); + //2 / PI + auto twoOverPiAction = new QAction("2 / PI", this); + connect(twoOverPiAction, SIGNAL(triggered(bool)), this, SLOT(TwoOverPiActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(twoOverPiAction); + //3 / PI + auto threeOverPiAction = new QAction("3 / PI", this); + connect(threeOverPiAction, SIGNAL(triggered(bool)), this, SLOT(ThreeOverPiActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(threeOverPiAction); + //4 / PI + auto fourOverPiAction = new QAction("4 / PI", this); + connect(fourOverPiAction, SIGNAL(triggered(bool)), this, SLOT(FourOverPiActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(fourOverPiAction); + //Sqrt(2) + auto sqrtTwoAction = new QAction("Sqrt(2)", this); + connect(sqrtTwoAction, SIGNAL(triggered(bool)), this, SLOT(SqrtTwoActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(sqrtTwoAction); + //Sqrt(2) + auto sqrtThreeAction = new QAction("Sqrt(3)", this); + connect(sqrtThreeAction, SIGNAL(triggered(bool)), this, SLOT(SqrtThreeActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(sqrtThreeAction); + //Need this for it to show up properly. + this->setContextMenuPolicy(Qt::ActionsContextMenu); +} + +void VariationTreeDoubleSpinBox::PiActionTriggered(bool checked) { setValue(M_PI); } +void VariationTreeDoubleSpinBox::TwoPiActionTriggered(bool checked) { setValue(M_PI * 2); } +void VariationTreeDoubleSpinBox::PiOver2ActionTriggered(bool checked) { setValue(M_PI_2); } +void VariationTreeDoubleSpinBox::PiOver3ActionTriggered(bool checked) { setValue(M_PI / 3); } +void VariationTreeDoubleSpinBox::PiOver4ActionTriggered(bool checked) { setValue(M_PI / 4); } +void VariationTreeDoubleSpinBox::PiOver6ActionTriggered(bool checked) { setValue(M_PI / 6); } +void VariationTreeDoubleSpinBox::OneOverPiActionTriggered(bool checked) { setValue(1 / M_PI); } +void VariationTreeDoubleSpinBox::TwoOverPiActionTriggered(bool checked) { setValue(2 / M_PI); } +void VariationTreeDoubleSpinBox::ThreeOverPiActionTriggered(bool checked) { setValue(3 / M_PI); } +void VariationTreeDoubleSpinBox::FourOverPiActionTriggered(bool checked) { setValue(4 / M_PI); } +void VariationTreeDoubleSpinBox::SqrtTwoActionTriggered(bool checked) { setValue(M_SQRT2); } +void VariationTreeDoubleSpinBox::SqrtThreeActionTriggered(bool checked) { setValue(std::sqrt(3.0)); } + +/// +/// Constructor that sets up the context menu for special numerical values specific to affine spinners. +/// +/// The parent widget +/// The height of the spin box. Default: 20. +/// The step used to increment/decrement the spin box when using the mouse wheel. Default: 0.01. +AffineDoubleSpinBox::AffineDoubleSpinBox(QWidget* p, int h, double step) + : DoubleSpinBox(p, h, step) +{ + //-1 + auto neg1Action = new QAction("-1", this); + connect(neg1Action, SIGNAL(triggered(bool)), this, SLOT(NegOneActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(neg1Action); + //0 + auto zeroAction = new QAction("0", this); + connect(zeroAction, SIGNAL(triggered(bool)), this, SLOT(ZeroActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(zeroAction); + //1 + auto oneAction = new QAction("1", this); + connect(oneAction, SIGNAL(triggered(bool)), this, SLOT(OneActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(oneAction); + //45 + auto fortyFiveAction = new QAction("45", this); + connect(fortyFiveAction, SIGNAL(triggered(bool)), this, SLOT(FortyFiveActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(fortyFiveAction); + //-45 + auto negFortyFiveAction = new QAction("-45", this); + connect(negFortyFiveAction, SIGNAL(triggered(bool)), this, SLOT(NegFortyFiveActionTriggered(bool)), Qt::QueuedConnection); + this->addAction(negFortyFiveAction); + //Need this for it to show up properly. + this->setContextMenuPolicy(Qt::ActionsContextMenu); +} + +void AffineDoubleSpinBox::NegOneActionTriggered(bool checked) { setValue(-1); } +void AffineDoubleSpinBox::ZeroActionTriggered(bool checked) { setValue(0); } +void AffineDoubleSpinBox::OneActionTriggered(bool checked) { setValue(1); } +void AffineDoubleSpinBox::FortyFiveActionTriggered(bool checked) { setValue(0.707107); } +void AffineDoubleSpinBox::NegFortyFiveActionTriggered(bool checked) { setValue(-0.707107); } diff --git a/Source/Fractorium/DoubleSpinBox.h b/Source/Fractorium/DoubleSpinBox.h index 4df4b6e..da59818 100644 --- a/Source/Fractorium/DoubleSpinBox.h +++ b/Source/Fractorium/DoubleSpinBox.h @@ -71,33 +71,52 @@ class VariationTreeWidgetItem; /// class VariationTreeDoubleSpinBox : public DoubleSpinBox { -public: - /// - /// Constructor that passes agruments to the base and assigns the m_Param and m_Variation members. - /// - /// The parent widget - /// The widget item this spinner is contained in - /// The variation this spinner is for - /// The name of the parameter this is for - /// The height of the spin box. Default: 16. - /// The step used to increment/decrement the spin box when using the mouse wheel. Default: 0.05. - explicit VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, const string& param, int h = 16, double step = 0.05) - : DoubleSpinBox(p, h, step) - { - m_WidgetItem = widgetItem; - m_Param = param; - m_Id = id; - setDecimals(3); - } + Q_OBJECT +public: + explicit VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, const string& param, int h = 16, double step = 0.05); virtual ~VariationTreeDoubleSpinBox() { } bool IsParam() { return !m_Param.empty(); } string ParamName() { return m_Param; } eVariationId GetVariationId() { return m_Id; } VariationTreeWidgetItem* WidgetItem() { return m_WidgetItem; } +public slots: + void PiActionTriggered(bool checked = false); + void TwoPiActionTriggered(bool checked = false); + void PiOver2ActionTriggered(bool checked = false); + void PiOver3ActionTriggered(bool checked = false); + void PiOver4ActionTriggered(bool checked = false); + void PiOver6ActionTriggered(bool checked = false); + void OneOverPiActionTriggered(bool checked = false); + void TwoOverPiActionTriggered(bool checked = false); + void ThreeOverPiActionTriggered(bool checked = false); + void FourOverPiActionTriggered(bool checked = false); + void SqrtTwoActionTriggered(bool checked = false); + void SqrtThreeActionTriggered(bool checked = false); + private: string m_Param; eVariationId m_Id; VariationTreeWidgetItem* m_WidgetItem; }; + +/// +/// Derivation for the double spin boxes that are in the +/// affine controls. +/// +class AffineDoubleSpinBox : public DoubleSpinBox +{ + Q_OBJECT + +public: + explicit AffineDoubleSpinBox(QWidget* p, int h = 20, double step = 0.01); + virtual ~AffineDoubleSpinBox() { } + +public slots: + void NegOneActionTriggered(bool checked = false); + void ZeroActionTriggered(bool checked = false); + void OneActionTriggered(bool checked = false); + void FortyFiveActionTriggered(bool checked = false); + void NegFortyFiveActionTriggered(bool checked = false); +}; diff --git a/Source/Fractorium/EmberTreeWidgetItem.h b/Source/Fractorium/EmberTreeWidgetItem.h index 2076418..daab373 100644 --- a/Source/Fractorium/EmberTreeWidgetItem.h +++ b/Source/Fractorium/EmberTreeWidgetItem.h @@ -34,10 +34,10 @@ public: { } - //~EmberTreeWidgetItemBase() - //{ - // qDebug() << "~EmberTreeWidgetItemBase()"; - //} + ~EmberTreeWidgetItemBase() + { + //qDebug() << "~EmberTreeWidgetItemBase()"; + } /// /// Set the preview image for the tree widget item. diff --git a/Source/Fractorium/FinalRenderDialog.cpp b/Source/Fractorium/FinalRenderDialog.cpp index 560bd97..0884551 100644 --- a/Source/Fractorium/FinalRenderDialog.cpp +++ b/Source/Fractorium/FinalRenderDialog.cpp @@ -52,8 +52,12 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF m_ItersCellIndex = row++;//Iters. m_PathCellIndex = row; QStringList comboList; +#ifndef _WIN32 + comboList.append("bmp"); +#endif comboList.append("jpg"); comboList.append("png"); + comboList.append("exr"); m_Tbcw = new TwoButtonComboWidget("...", "Open", comboList, 22, 40, 22, table); table->setCellWidget(row, 1, m_Tbcw); table->item(row++, 1)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); @@ -116,6 +120,7 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF ui.FinalRenderSaveXmlCheckBox->setChecked( m_Settings->FinalSaveXml()); ui.FinalRenderDoAllCheckBox->setChecked( m_Settings->FinalDoAll()); ui.FinalRenderDoSequenceCheckBox->setChecked( m_Settings->FinalDoSequence()); + ui.FinalRenderPng16BitCheckBox->setChecked( m_Settings->FinalPng16Bit()); ui.FinalRenderKeepAspectCheckBox->setChecked( m_Settings->FinalKeepAspect()); ui.FinalRenderThreadCountSpin->setValue( m_Settings->FinalThreadCount()); #ifdef _WIN32 @@ -136,11 +141,27 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF m_SupersampleSpin->setValue(m_Settings->FinalSupersample()); m_StripsSpin->setValue(int(m_Settings->FinalStrips())); Scale(eScaleType(m_Settings->FinalScale())); + int index = 0; +#ifdef _WIN32 - if (m_Settings->FinalExt() == "jpg") - m_Tbcw->m_Combo->setCurrentIndex(0); - else - m_Tbcw->m_Combo->setCurrentIndex(1); + if (m_Settings->FinalExt().endsWith("bmp", Qt::CaseInsensitive)) + m_Tbcw->m_Combo->setCurrentIndex(index); + + index++; +#endif + + if (m_Settings->FinalExt().endsWith("jpg", Qt::CaseInsensitive)) + m_Tbcw->m_Combo->setCurrentIndex(index); + + index++; + + if (m_Settings->FinalExt().endsWith("png", Qt::CaseInsensitive)) + m_Tbcw->m_Combo->setCurrentIndex(index); + + index++; + + if (m_Settings->FinalExt().endsWith("exr", Qt::CaseInsensitive)) + m_Tbcw->m_Combo->setCurrentIndex(index); //Explicitly call these to enable/disable the appropriate controls. OnOpenCLCheckBoxStateChanged(ui.FinalRenderOpenCLCheckBox->isChecked()); @@ -150,23 +171,20 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF int desktopHeight = qApp->desktop()->availableGeometry().height(); s.setHeight(std::min(s.height(), int(double(desktopHeight * 0.90)))); setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, qApp->desktop()->availableGeometry())); - ui.FinalRenderThreadHorizontalLayout->setAlignment(Qt::AlignLeft); - ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadCountSpin, Qt::AlignLeft); - ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadPriorityLabel, Qt::AlignLeft); - ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadPriorityComboBox, Qt::AlignLeft); - QWidget* w = SetTabOrder(this, ui.FinalRenderEarlyClipCheckBox, ui.FinalRenderYAxisUpCheckBox); //Update these with new controls. + QWidget* w = SetTabOrder(this, ui.FinalRenderEarlyClipCheckBox, ui.FinalRenderYAxisUpCheckBox); w = SetTabOrder(this, w, ui.FinalRenderTransparencyCheckBox); w = SetTabOrder(this, w, ui.FinalRenderOpenCLCheckBox); + w = SetTabOrder(this, w, ui.FinalRenderPng16BitCheckBox); w = SetTabOrder(this, w, ui.FinalRenderDoublePrecisionCheckBox); w = SetTabOrder(this, w, ui.FinalRenderSaveXmlCheckBox); w = SetTabOrder(this, w, ui.FinalRenderDoAllCheckBox); w = SetTabOrder(this, w, ui.FinalRenderDoSequenceCheckBox); w = SetTabOrder(this, w, ui.FinalRenderCurrentSpin); w = SetTabOrder(this, w, ui.FinalRenderDeviceTable); + w = SetTabOrder(this, w, ui.FinalRenderApplyToAllCheckBox); w = SetTabOrder(this, w, ui.FinalRenderThreadCountSpin); w = SetTabOrder(this, w, ui.FinalRenderThreadPriorityComboBox); - w = SetTabOrder(this, w, ui.FinalRenderApplyToAllCheckBox); w = SetTabOrder(this, w, m_WidthScaleSpin); w = SetTabOrder(this, w, m_HeightScaleSpin); w = SetTabOrder(this, w, ui.FinalRenderScaleNoneRadioButton); @@ -215,9 +233,10 @@ bool FractoriumFinalRenderDialog::Double() { return ui.FinalRenderDoublePrecisio bool FractoriumFinalRenderDialog::SaveXml() { return ui.FinalRenderSaveXmlCheckBox->isChecked(); } bool FractoriumFinalRenderDialog::DoAll() { return ui.FinalRenderDoAllCheckBox->isChecked(); } bool FractoriumFinalRenderDialog::DoSequence() { return ui.FinalRenderDoSequenceCheckBox->isChecked(); } +bool FractoriumFinalRenderDialog::Png16Bit() { return ui.FinalRenderPng16BitCheckBox->isChecked(); } bool FractoriumFinalRenderDialog::KeepAspect() { return ui.FinalRenderKeepAspectCheckBox->isChecked(); } bool FractoriumFinalRenderDialog::ApplyToAll() { return ui.FinalRenderApplyToAllCheckBox->isChecked(); } -QString FractoriumFinalRenderDialog::Ext() { return m_Tbcw->m_Combo->currentIndex() == 0 ? "jpg" : "png"; } +QString FractoriumFinalRenderDialog::Ext() { return m_Tbcw->m_Combo->currentText(); } QString FractoriumFinalRenderDialog::Path() { return ui.FinalRenderParamsTable->item(m_PathCellIndex, 1)->text(); } void FractoriumFinalRenderDialog::Path(const QString& s) { ui.FinalRenderParamsTable->item(m_PathCellIndex, 1)->setText(s); } QString FractoriumFinalRenderDialog::Prefix() { return m_PrefixEdit->text(); } @@ -261,6 +280,7 @@ FinalRenderGuiState FractoriumFinalRenderDialog::State() state.m_SaveXml = SaveXml(); state.m_DoAll = DoAll(); state.m_DoSequence = DoSequence(); + state.m_Png16Bit = Png16Bit(); state.m_KeepAspect = KeepAspect(); state.m_Scale = Scale(); state.m_Path = Path(); diff --git a/Source/Fractorium/FinalRenderDialog.h b/Source/Fractorium/FinalRenderDialog.h index a553767..e0a6677 100644 --- a/Source/Fractorium/FinalRenderDialog.h +++ b/Source/Fractorium/FinalRenderDialog.h @@ -60,6 +60,7 @@ public: bool SaveXml(); bool DoAll(); bool DoSequence(); + bool Png16Bit(); bool KeepAspect(); bool ApplyToAll(); eScaleType Scale(); diff --git a/Source/Fractorium/FinalRenderDialog.ui b/Source/Fractorium/FinalRenderDialog.ui index 79e463b..eac8e98 100644 --- a/Source/Fractorium/FinalRenderDialog.ui +++ b/Source/Fractorium/FinalRenderDialog.ui @@ -6,8 +6,8 @@ 0 0 - 519 - 941 + 573 + 751 @@ -63,8 +63,8 @@ 0 0 - 507 - 929 + 561 + 739 @@ -83,96 +83,12 @@ 0 - - - - - - <html><head/><body><p>Checked: clip colors and gamma correct after density filtering.</p><p>Unchecked: do it after spatial filtering.</p></body></html> - - - Early Clip - - - - - - - <html><head/><body><p>Checked: Positive Y direction is up.</p><p>Unchecked: Positive Y direction is down.</p></body></html> - - - Positive Y Up - - - - - - - <html><head/><body><p>Use OpenCL to render if your video card supports it.</p><p>This is highly recommended as it will dramatically speed up render time.</p></body></html> - - - Use OpenCL - - - - - - - <html><head/><body><p>Use transparency in the final image.</p><p>Only supported for Png format.</p></body></html> - - - Transparency - - - - - - - <html><head/><body><p>Checked: use 64-bit double precision numbers (slower, but better image quality).</p><p>Unchecked: use 32-bit single precision numbers (faster, but worse image quality).</p></body></html> - - - Use Double Precision - - - - - - - Save an Xml parameter file for each flame rendered - - - Save Xml - - - - - - - Render all open flames instead of just the current one - - - Render All - - - - - - - Use temporal samples value to achieve motion blur effect between flames - - - Render as Animation Sequence - - - - - QLayout::SetNoConstraint - + @@ -215,7 +131,7 @@ - + @@ -237,6 +153,100 @@ + + + + + + <html><head/><body><p>Checked: clip colors and gamma correct after density filtering.</p><p>Unchecked: do it after spatial filtering.</p></body></html> + + + Early Clip + + + + + + + <html><head/><body><p>Checked: Positive Y direction is up.</p><p>Unchecked: Positive Y direction is down.</p></body></html> + + + Positive Y Up + + + + + + + <html><head/><body><p>Use OpenCL to render if your video card supports it.</p><p>This is highly recommended as it will dramatically speed up render time.</p></body></html> + + + Use OpenCL + + + + + + + <html><head/><body><p>Use transparency in the final image.</p><p>Only supported for Png format.</p></body></html> + + + Transparency + + + + + + + <html><head/><body><p>Checked: use 64-bit double precision numbers (slower, but better image quality).</p><p>Unchecked: use 32-bit single precision numbers (faster, but worse image quality).</p></body></html> + + + Use Double Precision + + + + + + + Save an Xml parameter file for each flame rendered + + + Save Xml + + + + + + + Render all open flames instead of just the current one + + + Render All + + + + + + + Use temporal samples value to achieve motion blur effect between flames + + + Render as Animation Sequence + + + + + + + <html><head/><body><p>Save each RGBA component as 16-bits when saving Png files.</p><p>This leads to greater color precision for use in high end rendering and display on HDR monitors, however it makes the file size larger.</p></body></html> + + + Save 16-bit Png + + + + + @@ -328,7 +338,10 @@ - + + + 6 + @@ -405,6 +418,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -928,6 +954,146 @@ + + + + + + + 0 + 1 + + + + + 16777215 + 16777215 + + + + Qt::StrongFocus + + + true + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + Iteration: + + + + + + + + 0 + 0 + + + + Final Accumulation: + + + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + Density Filtering: + + + + + + + + 0 + 0 + + + + Total Progress: + + + + + + + + 0 + 0 + + + + 0 + + + + + + + @@ -938,142 +1104,6 @@ - - - - QLayout::SetDefaultConstraint - - - - - - 0 - 0 - - - - 0 - - - - - - - - 0 - 0 - - - - Iteration: - - - - - - - - 0 - 0 - - - - Final Accumulation: - - - - - - - - 0 - 0 - - - - 0 - - - - - - - - 0 - 0 - - - - 0 - - - - - - - - 0 - 0 - - - - Density Filtering: - - - - - - - - 0 - 0 - - - - Total Progress: - - - - - - - - 0 - 0 - - - - 0 - - - - - - - - - - 0 - 1 - - - - - 16777215 - 16777215 - - - - Qt::StrongFocus - - - true - - - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - @@ -1160,13 +1190,22 @@ FinalRenderEarlyClipCheckBox + FinalRenderDoublePrecisionCheckBox FinalRenderYAxisUpCheckBox + FinalRenderSaveXmlCheckBox FinalRenderTransparencyCheckBox + FinalRenderDoAllCheckBox FinalRenderOpenCLCheckBox - FinalRenderParamsTable - FinalRenderTextOutput - FinalRenderStartButton + FinalRenderDoSequenceCheckBox + FinalRenderPng16BitCheckBox + FinalRenderCurrentSpin FinalRenderStopButton + FinalRenderStartButton + FinalRenderScaleWidthRadioButton + FinalRenderScaleHeightRadioButton + FinalRenderScaleNoneRadioButton + FinalRenderKeepAspectCheckBox + FinalRenderTextOutput FinalRenderCloseButton diff --git a/Source/Fractorium/FinalRenderEmberController.cpp b/Source/Fractorium/FinalRenderEmberController.cpp index 61bf4ae..d406713 100644 --- a/Source/Fractorium/FinalRenderEmberController.cpp +++ b/Source/Fractorium/FinalRenderEmberController.cpp @@ -80,7 +80,7 @@ bool FinalRenderEmberControllerBase::CreateRendererFromGUI() bool useOpenCL = m_Info->Ok() && m_FinalRenderDialog->OpenCL(); auto v = Devices(m_FinalRenderDialog->Devices()); return CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, - v, false); //Not shared. + v, false, false); //Not shared. } /// @@ -178,7 +178,7 @@ FinalRenderEmberController::FinalRenderEmberController(FractoriumFinalRenderD size_t ftime; size_t finalImageIndex = 0; std::thread writeThread; - vector finalImages[2]; + vector finalImages[2]; EmberStats stats; EmberImageComments comments; Timing renderTimer; @@ -225,8 +225,8 @@ FinalRenderEmberController::FinalRenderEmberController(FractoriumFinalRenderD finalImages[threadFinalImageIndex], renderer->FinalRasW(), renderer->FinalRasH(), - renderer->NumChannels(), - renderer->BytesPerChannel()); + m_FinalRenderDialog->Png16Bit(), + m_FinalRenderDialog->Transparency()); }, ftime, finalImageIndex); m_FinishedImageCount.fetch_add(1); RenderComplete(*m_EmberFile.Get(ftime), stats, renderTimer); @@ -422,10 +422,9 @@ bool FinalRenderEmberController::Render() /// True if shared with OpenGL, else false. Always false in this case. /// True if nothing went wrong, else false. template -bool FinalRenderEmberController::CreateRenderer(eRendererType renderType, const vector>& devices, bool shared) +bool FinalRenderEmberController::CreateRenderer(eRendererType renderType, const vector>& devices, bool updatePreviews, bool shared) { bool ok = true; - //uint channels = m_FinalRenderDialog->Ext().endsWith("png", Qt::CaseInsensitive) ? 4 : 3; bool renderTypeMismatch = (m_Renderer.get() && (m_Renderer->RendererType() != renderType)) || (!m_Renderers.empty() && (m_Renderers[0]->RendererType() != renderType)); CancelRender(); @@ -542,35 +541,24 @@ template bool FinalRenderEmberController::SyncGuiToRenderer() { bool ok = true; - uint channels = m_FinalRenderDialog->Ext().endsWith("png", Qt::CaseInsensitive) ? 4 : 3; if (m_Renderer.get()) { - if (m_Renderer->RendererType() == eRendererType::OPENCL_RENDERER) - channels = 4;//Always using 4 since the GL texture is RGBA. - m_Renderer->Callback(this); - m_Renderer->NumChannels(channels); m_Renderer->EarlyClip(m_FinalRenderDialog->EarlyClip()); m_Renderer->YAxisUp(m_FinalRenderDialog->YAxisUp()); m_Renderer->ThreadCount(m_FinalRenderDialog->ThreadCount()); m_Renderer->Priority((eThreadPriority)m_FinalRenderDialog->ThreadPriority()); - m_Renderer->Transparency(m_FinalRenderDialog->Transparency()); } else if (!m_Renderers.empty()) { for (size_t i = 0; i < m_Renderers.size(); i++) { - if (m_Renderers[i]->RendererType() == eRendererType::OPENCL_RENDERER) - channels = 4;//Always using 4 since the GL texture is RGBA. - m_Renderers[i]->Callback(!i ? this : nullptr); - m_Renderers[i]->NumChannels(channels); m_Renderers[i]->EarlyClip(m_FinalRenderDialog->EarlyClip()); m_Renderers[i]->YAxisUp(m_FinalRenderDialog->YAxisUp()); m_Renderers[i]->ThreadCount(m_FinalRenderDialog->ThreadCount()); m_Renderers[i]->Priority((eThreadPriority)m_FinalRenderDialog->ThreadPriority()); - m_Renderers[i]->Transparency(m_FinalRenderDialog->Transparency()); } } else @@ -627,11 +615,11 @@ void FinalRenderEmberController::ResetProgress(bool total) /// specified in the widgets and compute the amount of memory required to render. /// This includes the memory needed for the final output image. /// -/// If successful, memory required in bytes, else zero. +/// If successful, a tuple specifying the memory required in bytes for the histogram int he first element, the total memory in the second, and the iter count in the last, else zero. template tuple FinalRenderEmberController::SyncAndComputeMemory() { - size_t iterCount; + size_t iterCount = 0; pair p(0, 0); size_t strips; uint channels = m_FinalRenderDialog->Ext() == "png" ? 4 : 3;//4 channels for Png, else 3. @@ -642,7 +630,6 @@ tuple FinalRenderEmberController::SyncAndComputeMemor strips = VerifyStrips(m_Ember->m_FinalRasH, m_FinalRenderDialog->Strips(), [&](const string & s) {}, [&](const string & s) {}, [&](const string & s) {}); m_Renderer->SetEmber(*m_Ember, eProcessAction::FULL_RENDER, true); - m_Renderer->NumChannels(channels); m_FinalPreviewRenderer->Render(UINT_MAX, UINT_MAX); p = m_Renderer->MemoryRequired(strips, true, m_FinalRenderDialog->DoSequence()); iterCount = m_Renderer->TotalIterCount(strips); @@ -652,7 +639,6 @@ tuple FinalRenderEmberController::SyncAndComputeMemor for (auto& renderer : m_Renderers) { renderer->SetEmber(*m_Ember, eProcessAction::FULL_RENDER, true); - renderer->NumChannels(channels); } m_FinalPreviewRenderer->Render(UINT_MAX, UINT_MAX); @@ -711,24 +697,24 @@ template void FinalRenderEmberController::SaveCurrentRender(Ember& ember) { auto comments = m_Renderer->ImageComments(m_Stats, 0, true); - SaveCurrentRender(ember, comments, m_FinalImage, m_Renderer->FinalRasW(), m_Renderer->FinalRasH(), m_Renderer->NumChannels(), m_Renderer->BytesPerChannel()); + SaveCurrentRender(ember, comments, m_FinalImage, m_Renderer->FinalRasW(), m_Renderer->FinalRasH(), m_FinalRenderDialog->Png16Bit(), m_FinalRenderDialog->Transparency()); } /// /// Save the output of the render. /// /// The ember whose rendered output will be saved -/// The comments to save in the png or jpg +/// The comments to save in the png, jpg or exr /// The buffer containing the pixels /// The width in pixels of the image /// The height in pixels of the image -/// The number of channels, 3 or 4. -/// The bytes per channel, almost always 1. +/// Whether to use 16 bits per channel per pixel when saving as Png. +/// Whether to use alpha when saving as Png or Exr. template -void FinalRenderEmberController::SaveCurrentRender(Ember& ember, const EmberImageComments& comments, vector& pixels, size_t width, size_t height, size_t channels, size_t bpc) +void FinalRenderEmberController::SaveCurrentRender(Ember& ember, const EmberImageComments& comments, vector& pixels, size_t width, size_t height, bool png16Bit, bool transparency) { QString filename = ComposePath(QString::fromStdString(ember.m_Name)); - FractoriumEmberControllerBase::SaveCurrentRender(filename, comments, pixels, width, height, channels, bpc); + FractoriumEmberControllerBase::SaveCurrentRender(filename, comments, pixels, width, height, png16Bit, transparency); } /// @@ -806,6 +792,7 @@ void FinalRenderEmberController::RenderComplete(Ember& ember, const EmberS m_Settings->FinalSaveXml(m_GuiState.m_SaveXml); m_Settings->FinalDoAll(m_GuiState.m_DoAll); m_Settings->FinalDoSequence(m_GuiState.m_DoSequence); + m_Settings->FinalPng16Bit(m_GuiState.m_Png16Bit); m_Settings->FinalKeepAspect(m_GuiState.m_KeepAspect); m_Settings->FinalScale(uint(m_GuiState.m_Scale)); m_Settings->FinalExt(m_GuiState.m_Ext); @@ -960,6 +947,7 @@ void FinalRenderPreviewRenderer::PreviewRenderFunc(uint start, uint end) QLabel* widget = d->ui.FinalRenderPreviewLabel; //Determine how to scale the scaled ember to fit in the label with a max of 100x100. auto e = m_Controller->m_Ember; + auto settings = FractoriumSettings::Instance(); if (e->m_FinalRasW >= e->m_FinalRasH) scalePercentage = T(maxDim) / e->m_FinalRasW; @@ -974,9 +962,7 @@ void FinalRenderPreviewRenderer::PreviewRenderFunc(uint start, uint end) m_PreviewEmber.m_PixelsPerUnit = scalePercentage * e->m_PixelsPerUnit; m_PreviewRenderer.EarlyClip(d->EarlyClip()); m_PreviewRenderer.YAxisUp(d->YAxisUp()); - m_PreviewRenderer.Transparency(d->Transparency()); m_PreviewRenderer.Callback(nullptr); - m_PreviewRenderer.NumChannels(4); m_PreviewRenderer.SetEmber(m_PreviewEmber); m_PreviewRenderer.PrepFinalAccumVector(m_PreviewFinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run(). auto strips = VerifyStrips(m_PreviewEmber.m_FinalRasH, d->Strips(), @@ -987,8 +973,10 @@ void FinalRenderPreviewRenderer::PreviewRenderFunc(uint start, uint end) [&](size_t strip) {},//Error. [&](Ember& finalEmber)//Final strip. { + m_PreviewVec.resize(finalEmber.m_FinalRasW * finalEmber.m_FinalRasH * 4); + Rgba32ToRgba8(m_PreviewFinalImage.data(), m_PreviewVec.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, d->Transparency()); QImage image(int(finalEmber.m_FinalRasW), int(finalEmber.m_FinalRasH), QImage::Format_RGBA8888);//The label wants RGBA. - memcpy(image.scanLine(0), m_PreviewFinalImage.data(), finalEmber.m_FinalRasW * finalEmber.m_FinalRasH * 4);//Memcpy the data in. + memcpy(image.scanLine(0), m_PreviewVec.data(), SizeOf(m_PreviewVec));//Memcpy the data in. QPixmap pixmap(QPixmap::fromImage(image)); QMetaObject::invokeMethod(widget, "setPixmap", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap)); }); diff --git a/Source/Fractorium/FinalRenderEmberController.h b/Source/Fractorium/FinalRenderEmberController.h index 69ee122..c333151 100644 --- a/Source/Fractorium/FinalRenderEmberController.h +++ b/Source/Fractorium/FinalRenderEmberController.h @@ -28,6 +28,7 @@ struct FinalRenderGuiState bool m_Double; bool m_SaveXml; bool m_DoAll; + bool m_Png16Bit; bool m_DoSequence; bool m_KeepAspect; eScaleType m_Scale; @@ -111,7 +112,7 @@ public: #endif virtual void SetEmber(size_t index, bool verbatim) override; virtual bool Render() override; - virtual bool CreateRenderer(eRendererType renderType, const vector>& devices, bool shared = true) override; + virtual bool CreateRenderer(eRendererType renderType, const vector>& devices, bool updatePreviews, bool shared = true) override; virtual int ProgressFunc(Ember& ember, void* foo, double fraction, int stage, double etaMs) override; virtual size_t Index() const override { return m_Ember->m_Index; } virtual uint SizeOfT() const override { return sizeof(T); } @@ -134,7 +135,7 @@ public: protected: void HandleFinishedProgress(); void SaveCurrentRender(Ember& ember); - void SaveCurrentRender(Ember& ember, const EmberImageComments& comments, vector& pixels, size_t width, size_t height, size_t channels, size_t bpc); + void SaveCurrentRender(Ember& ember, const EmberImageComments& comments, vector& pixels, size_t width, size_t height, bool png16Bit, bool transparency); void RenderComplete(Ember& ember); void RenderComplete(Ember& ember, const EmberStats& stats, Timing& renderTimer); void SyncGuiToEmber(Ember& ember, size_t widthOverride = 0, size_t heightOverride = 0); @@ -158,6 +159,7 @@ class FinalRenderPreviewRenderer : public PreviewRenderer { public: using PreviewRenderer::m_PreviewRun; + using PreviewRenderer::m_PreviewVec; using PreviewRenderer::m_PreviewEmber; using PreviewRenderer::m_PreviewRenderer; using PreviewRenderer::m_PreviewFinalImage; diff --git a/Source/Fractorium/Fractorium.cpp b/Source/Fractorium/Fractorium.cpp index de1e550..5ff533f 100644 --- a/Source/Fractorium/Fractorium.cpp +++ b/Source/Fractorium/Fractorium.cpp @@ -26,6 +26,7 @@ Fractorium::Fractorium(QWidget* p) qRegisterMetaType("size_t"); qRegisterMetaType>("QVector");//For previews. qRegisterMetaType>("vector"); + qRegisterMetaType("vv4F"); qRegisterMetaType("EmberTreeWidgetItemBase*"); setDockOptions(DockOption::AllowNestedDocks | DockOption::AllowTabbedDocks); setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition::North); @@ -505,7 +506,7 @@ void Fractorium::SetFixedTableHeader(QHeaderView* header, QHeaderView::ResizeMod /// Setup and show the open XML dialog. /// This will perform lazy instantiation. /// -/// The filename selected +/// The list of filenames selected QStringList Fractorium::SetupOpenXmlDialog() { #ifndef __APPLE__ @@ -522,7 +523,7 @@ QStringList Fractorium::SetupOpenXmlDialog() connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->OpenXmlExt(filter); }); m_FileDialog->setFileMode(QFileDialog::ExistingFiles); m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen); - m_FileDialog->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)"); + m_FileDialog->setNameFilter("*.flam3;;*.flame;;*.xml"); m_FileDialog->setWindowTitle("Open Flame"); m_FileDialog->setDirectory(m_Settings->OpenFolder()); m_FileDialog->selectNameFilter(m_Settings->OpenXmlExt()); @@ -567,30 +568,23 @@ QString Fractorium::SetupSaveXmlDialog(const QString& defaultFilename) QString filename; m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&))); - connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->SaveXmlExt(filter); }); + connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) + { + m_Settings->SaveXmlExt(filter); + m_FileDialog->setDefaultSuffix(filter); + }); //This must come first because it clears various internal states which allow the file text to be properly set. //This is most likely a bug in QFileDialog. m_FileDialog->setAcceptMode(QFileDialog::AcceptSave); m_FileDialog->selectFile(defaultFilename); - m_FileDialog->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)"); + m_FileDialog->setNameFilter(".flam3;;.flame;;.xml"); m_FileDialog->setWindowTitle("Save flame as xml"); m_FileDialog->setDirectory(m_Settings->SaveFolder()); m_FileDialog->selectNameFilter(m_Settings->SaveXmlExt()); + m_FileDialog->setDefaultSuffix(m_Settings->SaveXmlExt()); if (m_FileDialog->exec() == QDialog::Accepted) - { filename = m_FileDialog->selectedFiles().value(0); - //For some reason, linux doesn't automatically append this, but Windows does. Force it to be safe. - auto filt = m_FileDialog->selectedNameFilter().split('*');//Qt makes it very hard to get the actual extension used. - - if (filt.size() > 1)//Should always be true. - { - auto s = filt[1].replace(")", ""); - - if (!filename.endsWith(s)) - filename.append(s); - } - } #else auto defaultFilter(m_Settings->SaveXmlExt()); @@ -619,7 +613,11 @@ QString Fractorium::SetupSaveImageDialog(const QString& defaultFilename) QString filename; m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&))); - connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->SaveImageExt(filter); }); + connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) + { + m_Settings->SaveImageExt(filter); + m_FileDialog->setDefaultSuffix(filter); + }); //This must come first because it clears various internal states which allow the file text to be properly set. //This is most likely a bug in QFileDialog. m_FileDialog->setAcceptMode(QFileDialog::AcceptSave); @@ -627,17 +625,22 @@ QString Fractorium::SetupSaveImageDialog(const QString& defaultFilename) m_FileDialog->setFileMode(QFileDialog::AnyFile); m_FileDialog->setOption(QFileDialog::ShowDirsOnly, false); m_FileDialog->setOption(QFileDialog::DontUseNativeDialog, false); - m_FileDialog->setNameFilter("Jpeg (*.jpg);;Png (*.png);;Bmp (*.bmp)"); +#ifdef _WIN32 + m_FileDialog->setNameFilter(".bmp;;.jpg;;.png;;.exr"); +#else + m_FileDialog->setNameFilter(".jpg;;.png;;.exr"); +#endif m_FileDialog->setWindowTitle("Save image"); m_FileDialog->setDirectory(m_Settings->SaveFolder()); m_FileDialog->selectNameFilter(m_Settings->SaveImageExt()); + m_FileDialog->setDefaultSuffix(m_Settings->SaveImageExt()); if (m_FileDialog->exec() == QDialog::Accepted) filename = m_FileDialog->selectedFiles().value(0); #else auto defaultFilter(m_Settings->SaveImageExt()); - auto filename = QFileDialog::getSaveFileName(this, tr("Save image"), m_Settings->SaveFolder() + "/" + defaultFilename, tr("Jpeg (*.jpg);;Png (*.png);;Bmp (*.bmp)"), &defaultFilter); + auto filename = QFileDialog::getSaveFileName(this, tr("Save image"), m_Settings->SaveFolder() + "/" + defaultFilename, tr("Jpg (*.jpg);;Png (*.png);;Exr (*.exr)"), &defaultFilter); m_Settings->SaveImageExt(defaultFilter); #endif return filename; diff --git a/Source/Fractorium/Fractorium.h b/Source/Fractorium/Fractorium.h index e57155b..fe55221 100644 --- a/Source/Fractorium/Fractorium.h +++ b/Source/Fractorium/Fractorium.h @@ -339,18 +339,18 @@ public slots: void OnSummaryTreeHeaderSectionClicked(int logicalIndex); //Rendering/progress. - void StartRenderTimer(); + void StartRenderTimer(bool updatePreviews); void IdleTimer(); bool ControllersOk(); void ShowCritical(const QString& title, const QString& text, bool invokeRequired = false); //Can't have a template function be a slot. - void SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vector& v, uint w, uint h); + void SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vv4F& v, uint w, uint h); public: //template//See below. //static void SetupSpinner(QTableWidget* table, const QObject* receiver, int& row, int col, spinType*& spinBox, int height, valType min, valType max, valType step, const char* signal, const char* slot, bool incRow = true, valType val = 0, valType doubleClickZero = -999, valType doubleClickNonZero = -999); - static void SetupAffineSpinner(QTableWidget* table, const QObject* receiver, int row, int col, DoubleSpinBox*& spinBox, int height, double min, double max, double step, double prec, const char* signal, const char* slot); + static void SetupAffineSpinner(QTableWidget* table, const QObject* receiver, int row, int col, AffineDoubleSpinBox*& spinBox, int height, double min, double max, double step, double prec, const char* signal, const char* slot); static void SetupCombo(QTableWidget* table, const QObject* receiver, int& row, int col, StealthComboBox*& comboBox, const vector& vals, const char* signal, const char* slot, Qt::ConnectionType connectionType = Qt::QueuedConnection); static void SetFixedTableHeader(QHeaderView* header, QHeaderView::ResizeMode mode = QHeaderView::Fixed); @@ -422,8 +422,8 @@ private: void ErrorReportToQTextEdit(const vector& errors, QTextEdit* textEdit, bool clear = true); //Rendering/progress. - void ShutdownAndRecreateFromOptions(); - bool CreateRendererFromOptions(); + void ShutdownAndRecreateFromOptions(bool updatePreviews); + bool CreateRendererFromOptions(bool updatePreviews); bool CreateControllerFromOptions(); void EnableRenderControls(bool enable); @@ -489,22 +489,22 @@ private: DoubleSpinBox* m_XformDirectColorSpin; //Xforms Affine. - DoubleSpinBox* m_PreX1Spin;//Pre. - DoubleSpinBox* m_PreX2Spin; - DoubleSpinBox* m_PreY1Spin; - DoubleSpinBox* m_PreY2Spin; - DoubleSpinBox* m_PreO1Spin; - DoubleSpinBox* m_PreO2Spin; + AffineDoubleSpinBox* m_PreX1Spin;//Pre. + AffineDoubleSpinBox* m_PreX2Spin; + AffineDoubleSpinBox* m_PreY1Spin; + AffineDoubleSpinBox* m_PreY2Spin; + AffineDoubleSpinBox* m_PreO1Spin; + AffineDoubleSpinBox* m_PreO2Spin; - DoubleSpinBox* m_PostX1Spin;//Post. - DoubleSpinBox* m_PostX2Spin; - DoubleSpinBox* m_PostY1Spin; - DoubleSpinBox* m_PostY2Spin; - DoubleSpinBox* m_PostO1Spin; - DoubleSpinBox* m_PostO2Spin; + AffineDoubleSpinBox* m_PostX1Spin;//Post. + AffineDoubleSpinBox* m_PostX2Spin; + AffineDoubleSpinBox* m_PostY1Spin; + AffineDoubleSpinBox* m_PostY2Spin; + AffineDoubleSpinBox* m_PostO1Spin; + AffineDoubleSpinBox* m_PostO2Spin; - DoubleSpinBox* m_PreSpins[6]; - DoubleSpinBox* m_PostSpins[6]; + AffineDoubleSpinBox* m_PreSpins[6]; + AffineDoubleSpinBox* m_PostSpins[6]; //Xaos. DoubleSpinBox* m_XaosSpinBox; @@ -555,6 +555,7 @@ private: int m_VarSortMode; int m_PaletteSortMode; int m_PreviousPaletteRow; + vector m_PreviewVec; shared_ptr m_Info; unique_ptr m_Controller; Ui::FractoriumClass ui; diff --git a/Source/Fractorium/FractoriumEmberController.cpp b/Source/Fractorium/FractoriumEmberController.cpp index 38cc2b9..0b01540 100644 --- a/Source/Fractorium/FractoriumEmberController.cpp +++ b/Source/Fractorium/FractoriumEmberController.cpp @@ -18,7 +18,7 @@ FractoriumEmberControllerBase::FractoriumEmberControllerBase(Fractorium* fractor m_RenderTimer->setInterval(0); m_Fractorium->connect(m_RenderTimer.get(), SIGNAL(timeout()), SLOT(IdleTimer())); m_RenderRestartTimer = make_unique(m_Fractorium); - m_Fractorium->connect(m_RenderRestartTimer.get(), SIGNAL(timeout()), SLOT(StartRenderTimer())); + m_Fractorium->connect(m_RenderRestartTimer.get(), &QTimer::timeout, [&]() { m_Fractorium->StartRenderTimer(false); });//It's ok to pass false for the first shot because creating the controller will start the preview renders. } /// @@ -368,7 +368,6 @@ void TreePreviewRenderer::PreviewRenderFunc(uint start, uint end) auto f = m_Controller->m_Fractorium; m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip()); m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp()); - m_PreviewRenderer.Transparency(f->m_Settings->Transparency()); m_PreviewRenderer.ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe. if (auto top = m_Tree->topLevelItem(0)) @@ -394,7 +393,7 @@ void TreePreviewRenderer::PreviewRenderFunc(uint start, uint end) //until the update is complete. QMetaObject::invokeMethod(f, "SetLibraryTreeItemData", Qt::BlockingQueuedConnection, Q_ARG(EmberTreeWidgetItemBase*, treeItem), - Q_ARG(vector&, m_PreviewFinalImage), + Q_ARG(vv4F&, m_PreviewFinalImage), Q_ARG(uint, PREVIEW_SIZE), Q_ARG(uint, PREVIEW_SIZE)); } diff --git a/Source/Fractorium/FractoriumEmberController.h b/Source/Fractorium/FractoriumEmberController.h index 483cfe4..6fc7349 100644 --- a/Source/Fractorium/FractoriumEmberController.h +++ b/Source/Fractorium/FractoriumEmberController.h @@ -172,7 +172,7 @@ public: virtual void AffineInterpTypeChanged(int i) { } virtual void InterpTypeChanged(int i) { } virtual void BackgroundChanged(const QColor& color) { } - virtual void ClearColorCurves() { } + virtual void ClearColorCurves(int i) { } virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) { } //Xforms. @@ -240,7 +240,7 @@ public: //Rendering/progress. virtual bool Render() { return false; } - virtual bool CreateRenderer(eRendererType renderType, const vector>& devices, bool shared = true) { return false; } + virtual bool CreateRenderer(eRendererType renderType, const vector>& devices, bool updatePreviews, bool shared = true) { return false; } virtual uint SizeOfT() const { return 0; } virtual void ClearUndo() { } virtual GLEmberControllerBase* GLController() { return nullptr; } @@ -252,10 +252,10 @@ public: void Shutdown(); void UpdateRender(eProcessAction action = eProcessAction::FULL_RENDER); void DeleteRenderer(); - void SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector& pixels, size_t width, size_t height, size_t channels, size_t bpc); + void SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector& pixels, size_t width, size_t height, bool png16Bit, bool transparency); RendererBase* Renderer() { return m_Renderer.get(); } - vector* FinalImage() { return &(m_FinalImage); } - vector* PreviewFinalImage() { return &m_PreviewFinalImage; } + vector* FinalImage() { return &(m_FinalImage); } + vector* PreviewFinalImage() { return &m_PreviewFinalImage; } EmberStats Stats() { return m_Stats; } protected: @@ -286,8 +286,8 @@ protected: string m_CurrentPaletteFilePath; std::recursive_mutex m_Cs; std::thread m_WriteThread; - vector m_FinalImage; - vector m_PreviewFinalImage; + vector m_FinalImage; + vector m_PreviewFinalImage; vector m_ProcessActions; vector m_FilteredVariations; unique_ptr m_Renderer; @@ -436,7 +436,7 @@ public: virtual void AffineInterpTypeChanged(int index) override; virtual void InterpTypeChanged(int index) override; virtual void BackgroundChanged(const QColor& col) override; - virtual void ClearColorCurves() override; + virtual void ClearColorCurves(int i) override; virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) override; //Xforms. @@ -510,7 +510,7 @@ public: //Rendering/progress. virtual bool Render() override; - virtual bool CreateRenderer(eRendererType renderType, const vector>& devices, bool shared = true) override; + virtual bool CreateRenderer(eRendererType renderType, const vector>& devices, bool updatePreviews, bool shared = true) override; virtual uint SizeOfT() const override { return sizeof(T); } virtual int ProgressFunc(Ember& ember, void* foo, double fraction, int stage, double etaMs) override; virtual void ClearUndo() override; @@ -607,11 +607,6 @@ public: return m_PreviewRenderer.YAxisUp(); } - bool Transparency() - { - return m_PreviewRenderer.Transparency(); - } - bool Running() { return m_PreviewRun || m_PreviewResult.isRunning(); @@ -622,7 +617,8 @@ public: protected: volatile bool m_PreviewRun = false; Ember m_PreviewEmber; - vector m_PreviewFinalImage; + vector m_PreviewVec; + vv4F m_PreviewFinalImage; EmberNs::Renderer m_PreviewRenderer; private: @@ -639,6 +635,7 @@ class TreePreviewRenderer : public PreviewRenderer public: using PreviewRenderer::m_PreviewRun; using PreviewRenderer::m_PreviewEmber; + using PreviewRenderer::m_PreviewVec; using PreviewRenderer::m_PreviewRenderer; using PreviewRenderer::m_PreviewFinalImage; @@ -655,10 +652,8 @@ public: { auto f = m_Controller->m_Fractorium; m_PreviewRenderer.Callback(nullptr); - m_PreviewRenderer.NumChannels(4); m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip()); m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp()); - m_PreviewRenderer.Transparency(f->m_Settings->Transparency()); } virtual void PreviewRenderFunc(uint start, uint end) override; diff --git a/Source/Fractorium/FractoriumLibrary.cpp b/Source/Fractorium/FractoriumLibrary.cpp index 1c7cd6f..41ac650 100644 --- a/Source/Fractorium/FractoriumLibrary.cpp +++ b/Source/Fractorium/FractoriumLibrary.cpp @@ -96,15 +96,18 @@ vector> Fractorium::GetCurrentEmberIndex() /// The vector holding the RGBA bitmap /// The width of the bitmap /// The height of the bitmap -void Fractorium::SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vector& v, uint w, uint h) +void Fractorium::SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vv4F& v, uint w, uint h) { - item->SetImage(v, w, h); + m_PreviewVec.resize(w * h * 4); + Rgba32ToRgba8(v.data(), m_PreviewVec.data(), w, h, m_Settings->Transparency()); + item->SetImage(m_PreviewVec, w, h); } /// /// Set all libary tree entries to the name of the corresponding ember they represent. /// Set all libary tree entries to point to the underlying ember they represent. /// +/// A bitfield representing the type of synchronizing to do. Update one or more of index, name or pointer. template void FractoriumEmberController::SyncLibrary(eLibraryUpdate update) { diff --git a/Source/Fractorium/FractoriumMenus.cpp b/Source/Fractorium/FractoriumMenus.cpp index 1504a4e..fe2f3c4 100644 --- a/Source/Fractorium/FractoriumMenus.cpp +++ b/Source/Fractorium/FractoriumMenus.cpp @@ -349,9 +349,10 @@ void Fractorium::OnActionSaveCurrentScreen(bool checked) auto filename = SetupSaveImageDialog(m_Controller->Name()); auto renderer = m_Controller->Renderer(); auto& pixels = *m_Controller->FinalImage(); - auto rendererCL = dynamic_cast(m_Controller->Renderer()); + auto rendererCL = dynamic_cast(renderer); auto stats = m_Controller->Stats(); auto comments = renderer->ImageComments(stats, 0, true); + auto settings = FractoriumSettings::Instance(); if (rendererCL && renderer->PrepFinalAccumVector(pixels)) { @@ -362,7 +363,7 @@ void Fractorium::OnActionSaveCurrentScreen(bool checked) } } - m_Controller->SaveCurrentRender(filename, comments, pixels, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels(), renderer->BytesPerChannel()); + m_Controller->SaveCurrentRender(filename, comments, pixels, renderer->FinalRasW(), renderer->FinalRasH(), settings->Png16Bit(), settings->Transparency()); } /// @@ -879,7 +880,7 @@ void Fractorium::OnActionFinalRender(bool checked) void Fractorium::OnFinalRenderClose(int result) { m_RenderStatusLabel->setText("Renderer starting..."); - StartRenderTimer();//Re-create the renderer and start rendering again. + StartRenderTimer(false);//Re-create the renderer and start rendering again. ui.ActionStartStopRenderer->setChecked(false);//Re-enable any controls that might have been disabled. OnActionStartStopRenderer(false); } @@ -892,10 +893,17 @@ void Fractorium::OnFinalRenderClose(int result) /// Ignored void Fractorium::OnActionOptions(bool checked) { + bool ec = m_Settings->EarlyClip(); + bool yup = m_Settings->YAxisUp(); + bool trans = m_Settings->Transparency(); + if (m_OptionsDialog->exec()) { + bool updatePreviews = ec != m_Settings->EarlyClip() || + yup != m_Settings->YAxisUp() || + trans != m_Settings->Transparency(); SyncOptionsToToolbar();//This won't trigger a recreate, the call below handles it. - ShutdownAndRecreateFromOptions();//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer. + ShutdownAndRecreateFromOptions(updatePreviews);//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer. } } diff --git a/Source/Fractorium/FractoriumPalette.cpp b/Source/Fractorium/FractoriumPalette.cpp index 495d6e2..2278b82 100644 --- a/Source/Fractorium/FractoriumPalette.cpp +++ b/Source/Fractorium/FractoriumPalette.cpp @@ -491,21 +491,38 @@ void Fractorium::SetPaletteFileComboIndex(const string& filename) } /// -/// Reset the color curve values in the current ember to their default state and also update the curves control. +/// Reset the color curve values for the selected curve in the current ember to their default state and also update the curves control. /// Called when ResetCurvesButton is clicked. +/// Note if they click Reset Curves when the "All" radio button is selected, then it clears all curves. /// Resets the rendering process at either ACCUM_ONLY by default, or FILTER_AND_ACCUM when using early clip. /// +/// The index of the curve to be cleared, 0 to clear all. template -void FractoriumEmberController::ClearColorCurves() +void FractoriumEmberController::ClearColorCurves(int i) { Update([&] { - m_Ember.m_Curves.Init(); + if (i) + m_Ember.m_Curves.Init(i); + else + m_Ember.m_Curves.Init(0); }, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY); FillCurvesControl(); } -void Fractorium::OnResetCurvesButtonClicked(bool checked) { m_Controller->ClearColorCurves(); } +void Fractorium::OnResetCurvesButtonClicked(bool checked) +{ + if (ui.CurvesAllRadio->isChecked()) + m_Controller->ClearColorCurves(0); + else if (ui.CurvesRedRadio->isChecked()) + m_Controller->ClearColorCurves(1); + else if (ui.CurvesGreenRadio->isChecked()) + m_Controller->ClearColorCurves(2); + else if (ui.CurvesBlueRadio->isChecked()) + m_Controller->ClearColorCurves(3); + else + m_Controller->ClearColorCurves(0); +} /// /// Set the coordinate of the curve point. diff --git a/Source/Fractorium/FractoriumPch.h b/Source/Fractorium/FractoriumPch.h index 66a55fd..d979ca2 100644 --- a/Source/Fractorium/FractoriumPch.h +++ b/Source/Fractorium/FractoriumPch.h @@ -115,16 +115,16 @@ #include #include -#define GLM_FORCE_RADIANS 1 -#define GLM_ENABLE_EXPERIMENTAL 1 +//#define GLM_FORCE_RADIANS 1 +//#define GLM_ENABLE_EXPERIMENTAL 1 -#ifndef __APPLE__ - #define GLM_FORCE_INLINE 1 -#endif - -#include "glm/glm.hpp" -#include "glm/gtc/matrix_transform.hpp" -#include "glm/gtc/type_ptr.hpp" +//#ifndef __APPLE__ +// #define GLM_FORCE_INLINE 1 +//#endif +// +//#include "glm/glm.hpp" +//#include "glm/gtc/matrix_transform.hpp" +//#include "glm/gtc/type_ptr.hpp" #ifndef _WIN32 #undef Bool diff --git a/Source/Fractorium/FractoriumRender.cpp b/Source/Fractorium/FractoriumRender.cpp index 57b1782..0c18cc9 100644 --- a/Source/Fractorium/FractoriumRender.cpp +++ b/Source/Fractorium/FractoriumRender.cpp @@ -111,49 +111,66 @@ void FractoriumEmberControllerBase::DeleteRenderer() /// This will embed the id, url and nick fields from the options in the image comments. /// /// The full path and filename -/// The comments to save in the png or jpg +/// The comments to save in the png, jpg or exr /// The buffer containing the pixels /// The width in pixels of the image /// The height in pixels of the image -/// The number of channels, 3 or 4. -/// The bytes per channel, almost always 1. -void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector& pixels, size_t width, size_t height, size_t channels, size_t bpc) +/// Whether to use 16 bits per channel per pixel when saving as Png. +/// Whether to use alpha when saving as Png or Exr. +void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector& pixels, size_t width, size_t height, bool png16Bit, bool transparency) { if (filename != "") { bool b = false; - byte* data = nullptr; - vector vecRgb; + auto size = width * height; + auto settings = m_Fractorium->m_Settings; QFileInfo fileInfo(filename); QString suffix = fileInfo.suffix(); - auto settings = m_Fractorium->m_Settings; - - //Ensure dimensions are valid. - if (pixels.size() < (width * height * channels * bpc)) - { - m_Fractorium->ShowCritical("Save Failed", "Dimensions didn't match, not saving.", true); - return; - } - - data = pixels.data();//Png and channels == 4. - - if ((suffix == "jpg" || suffix == "bmp") && channels) - { - RgbaToRgb(pixels, vecRgb, width, height); - data = vecRgb.data(); - } - string s = filename.toStdString(); string id = settings->Id().toStdString(); string url = settings->Url().toStdString(); string nick = settings->Nick().toStdString(); - if (suffix == "png") - b = WritePng(s.c_str(), data, width, height, 1, true, comments, id, url, nick); - else if (suffix == "jpg") - b = WriteJpeg(s.c_str(), data, width, height, 100, true, comments, id, url, nick); - else if (suffix == "bmp") - b = WriteBmp(s.c_str(), data, width, height); + //Ensure dimensions are valid. + if (pixels.size() < size) + { + m_Fractorium->ShowCritical("Save Failed", "Dimensions didn't match, not saving.", true); + return; + } + + auto data = pixels.data(); + + if (suffix.endsWith("bmp", Qt::CaseInsensitive) || suffix.endsWith("jpg", Qt::CaseInsensitive)) + { + vector rgb8Image(size * 3); + Rgba32ToRgb8(data, rgb8Image.data(), width, height); + + if (suffix.endsWith("bmp", Qt::CaseInsensitive)) + b = WriteBmp(s.c_str(), rgb8Image.data(), width, height); + else if (suffix.endsWith("jpg", Qt::CaseInsensitive)) + b = WriteJpeg(s.c_str(), rgb8Image.data(), width, height, 100, true, comments, id, url, nick); + } + else if (suffix.endsWith("png", Qt::CaseInsensitive)) + { + if (!png16Bit) + { + vector rgba8Image(size * 4); + Rgba32ToRgba8(data, rgba8Image.data(), width, height, transparency); + b = WritePng(s.c_str(), rgba8Image.data(), width, height, 1, true, comments, id, url, nick);//Put an opt here for 1 or 2 bytes.//TODO + } + else + { + vector rgba16Image(size * 4); + Rgba32ToRgba16(data, rgba16Image.data(), width, height, transparency); + b = WritePng(s.c_str(), (byte*)rgba16Image.data(), width, height, 2, true, comments, id, url, nick);//Put an opt here for 1 or 2 bytes.//TODO + } + } + else if (suffix.endsWith("exr", Qt::CaseInsensitive)) + { + vector rgba32Image(size); + Rgba32ToRgbaExr(data, rgba32Image.data(), width, height, transparency); + b = WriteExr(s.c_str(), rgba32Image.data(), width, height, true, comments, id, url, nick); + } else { m_Fractorium->ShowCritical("Save Failed", "Unrecognized format " + suffix + ", not saving.", true); @@ -452,7 +469,7 @@ bool FractoriumEmberController::Render() //Update it on finish because the rendering process is completely done. if (update || ProcessState() == eProcessState::ACCUM_DONE) { - if (m_FinalImage.size() == m_Renderer->FinalBufferSize())//Make absolutely sure the correct amount of data is passed. + if (m_FinalImage.size() == m_Renderer->FinalDimensions())//Make absolutely sure the correct amount of data is passed. gl->update(); if (ProcessState() == eProcessState::ACCUM_DONE) @@ -509,10 +526,11 @@ bool FractoriumEmberController::Render() /// /// The type of render to create /// The platform,device index pairs of the devices to use +/// True to re-render the library previews, else false. /// True if shared with OpenGL, else false. Default: true. /// True if nothing went wrong, else false. template -bool FractoriumEmberController::CreateRenderer(eRendererType renderType, const vector>& devices, bool shared) +bool FractoriumEmberController::CreateRenderer(eRendererType renderType, const vector>& devices, bool updatePreviews, bool shared) { bool ok = true; auto s = m_Fractorium->m_Settings; @@ -565,7 +583,6 @@ bool FractoriumEmberController::CreateRenderer(eRendererType renderType, cons } m_Renderer->Callback(this); - m_Renderer->NumChannels(4);//Always using 4 since the GL texture is RGBA. m_Renderer->ReclaimOnResize(true); //Give it an initial ember, will be updated many times later. //Even though the bounds are computed when starting the next render. The OpenGL draw calls use these values, which might get called before the render starts. @@ -573,17 +590,15 @@ bool FractoriumEmberController::CreateRenderer(eRendererType renderType, cons m_Renderer->EarlyClip(s->EarlyClip()); m_Renderer->YAxisUp(s->YAxisUp()); m_Renderer->ThreadCount(s->ThreadCount()); - m_Renderer->Transparency(s->Transparency()); if (m_Renderer->RendererType() == eRendererType::CPU_RENDERER) m_Renderer->InteractiveFilter(s->CpuDEFilter() ? eInteractiveFilter::FILTER_DE : eInteractiveFilter::FILTER_LOG); else m_Renderer->InteractiveFilter(s->OpenCLDEFilter() ? eInteractiveFilter::FILTER_DE : eInteractiveFilter::FILTER_LOG); - if ((m_Renderer->EarlyClip() != m_LibraryPreviewRenderer->EarlyClip()) || - (m_Renderer->YAxisUp() != m_LibraryPreviewRenderer->YAxisUp()) || - (m_Renderer->Transparency() != m_LibraryPreviewRenderer->Transparency())) + if (updatePreviews) { + m_LibraryPreviewRenderer = make_unique>(this, m_Fractorium->ui.LibraryTree, m_EmberFile);//Will take the same settings as the main renderer. RenderLibraryPreviews(); } @@ -618,26 +633,28 @@ void Fractorium::EnableRenderControls(bool enable) /// /// Wrapper to stop the timer, shutdown the controller and recreate, then restart the controller and renderer from the options. /// -void Fractorium::ShutdownAndRecreateFromOptions() +/// True to re-render the library previews, else false. +void Fractorium::ShutdownAndRecreateFromOptions(bool updatePreviews) { //First completely stop what the current rendering process is doing. m_Controller->Shutdown(); - StartRenderTimer();//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer. + StartRenderTimer(updatePreviews);//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer. m_Settings->sync(); } /// /// Create a new renderer from the options. /// +/// True to re-render the library previews, else false. /// True if nothing went wrong, else false. -bool Fractorium::CreateRendererFromOptions() +bool Fractorium::CreateRendererFromOptions(bool updatePreviews) { bool ok = true; bool useOpenCL = m_Info->Ok() && m_Settings->OpenCL(); auto v = Devices(m_Settings->Devices()); //The most important option to process is what kind of renderer is desired, so do it first. - if (!m_Controller->CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, v)) + if (!m_Controller->CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, v, updatePreviews)) { //If using OpenCL, will only get here if creating RendererCL failed, but creating a backup CPU Renderer succeeded. ShowCritical("Renderer Creation Error", "Error creating renderer, most likely a GPU problem. Using CPU instead."); @@ -735,25 +752,30 @@ bool Fractorium::CreateControllerFromOptions() m_PaletteFrequencySpin->SetValueStealth(freq); m_Controller->PaletteAdjust();//Applies the adjustments to temp and saves in m_Ember.m_Palette, then fills in the palette preview widget. } + + return m_Controller.get(); } - return m_Controller.get(); + return false; } /// /// Start the render timer. /// If a renderer has not been created yet, or differs form the options, it will first be created from the options. /// -void Fractorium::StartRenderTimer() +/// True to re-render the library previews, else false. +void Fractorium::StartRenderTimer(bool updatePreviews) { //Starting the render timer, either for the first time //or from a paused state, such as resizing or applying new options. - CreateControllerFromOptions(); + bool newController = CreateControllerFromOptions(); if (m_Controller.get()) { //On program startup, the renderer does not get initialized until now. - CreateRendererFromOptions(); + //If a new controller was created, then previews will have started, so only start the previews if a new controller + //was *not* created and updatePreviews is true. + CreateRendererFromOptions(updatePreviews && !newController); if (m_Controller->Renderer()) m_Controller->StartRenderTimer(); diff --git a/Source/Fractorium/FractoriumSettings.cpp b/Source/Fractorium/FractoriumSettings.cpp index c2782f8..481cede 100644 --- a/Source/Fractorium/FractoriumSettings.cpp +++ b/Source/Fractorium/FractoriumSettings.cpp @@ -61,16 +61,16 @@ void FractoriumSettings::EnsureDefaults() FinalScale(0); if (OpenXmlExt() == "") - OpenXmlExt("Flame (*.flame)"); + OpenXmlExt("*.flame"); if (SaveXmlExt() == "") - SaveXmlExt("Flame (*.flame)"); + SaveXmlExt(".flame"); if (OpenImageExt() == "") - OpenImageExt("Png (*.png)"); + OpenImageExt("*.png"); if (SaveImageExt() == "") - SaveImageExt("Png (*.png)"); + SaveImageExt(".png"); if (FinalExt() != "jpg" && FinalExt() != "png") FinalExt("png"); @@ -121,6 +121,9 @@ void FractoriumSettings::ShowGrid(bool b) { setValue(SHOW bool FractoriumSettings::ToggleType() { return value(TOGGLETYPE).toBool(); } void FractoriumSettings::ToggleType(bool b) { setValue(TOGGLETYPE, b); } +bool FractoriumSettings::Png16Bit() { return value(PNG16BIT).toBool(); } +void FractoriumSettings::Png16Bit(bool b) { setValue(PNG16BIT, b); } + bool FractoriumSettings::ContinuousUpdate() { return value(CONTUPDATE).toBool(); } void FractoriumSettings::ContinuousUpdate(bool b) { setValue(CONTUPDATE, b); } @@ -229,6 +232,9 @@ void FractoriumSettings::FinalDoAll(bool b) { setValue(FINALDOALL, b); bool FractoriumSettings::FinalDoSequence() { return value(FINALDOSEQUENCE).toBool(); } void FractoriumSettings::FinalDoSequence(bool b) { setValue(FINALDOSEQUENCE, b); } +bool FractoriumSettings::FinalPng16Bit() { return value(FINALPNG16BIT).toBool(); } +void FractoriumSettings::FinalPng16Bit(bool b) { setValue(FINALPNG16BIT, b); } + bool FractoriumSettings::FinalKeepAspect() { return value(FINALKEEPASPECT).toBool(); } void FractoriumSettings::FinalKeepAspect(bool b) { setValue(FINALKEEPASPECT, b); } diff --git a/Source/Fractorium/FractoriumSettings.h b/Source/Fractorium/FractoriumSettings.h index 4025f2a..7950451 100644 --- a/Source/Fractorium/FractoriumSettings.h +++ b/Source/Fractorium/FractoriumSettings.h @@ -16,6 +16,7 @@ #define SHOWXFORMS "render/showxforms" #define SHOWGRID "render/showgrid" #define TOGGLETYPE "render/toggletype" +#define PNG16BIT "render/png16bit" #define DEVICES "render/devices" #define THREADCOUNT "render/threadcount" #define CPUDEFILTER "render/cpudefilter" @@ -50,6 +51,7 @@ #define FINALSAVEXML "finalrender/savexml" #define FINALDOALL "finalrender/doall" #define FINALDOSEQUENCE "finalrender/dosequence" +#define FINALPNG16BIT "finalrender/png16bit" #define FINALKEEPASPECT "finalrender/keepaspect" #define FINALSCALE "finalrender/scale" #define FINALEXT "finalrender/ext" @@ -124,6 +126,9 @@ public: bool ToggleType(); void ToggleType(bool b); + bool Png16Bit(); + void Png16Bit(bool b); + bool ContinuousUpdate(); void ContinuousUpdate(bool b); @@ -220,6 +225,9 @@ public: bool FinalDoSequence(); void FinalDoSequence(bool b); + bool FinalPng16Bit(); + void FinalPng16Bit(bool b); + bool FinalKeepAspect(); void FinalKeepAspect(bool b); diff --git a/Source/Fractorium/FractoriumToolbar.cpp b/Source/Fractorium/FractoriumToolbar.cpp index 59c4f8d..ebad1ad 100644 --- a/Source/Fractorium/FractoriumToolbar.cpp +++ b/Source/Fractorium/FractoriumToolbar.cpp @@ -43,7 +43,7 @@ void Fractorium::OnActionCpu(bool checked) if (checked && m_Settings->OpenCL()) { m_Settings->OpenCL(false); - ShutdownAndRecreateFromOptions(); + ShutdownAndRecreateFromOptions(false); } } @@ -56,7 +56,7 @@ void Fractorium::OnActionCL(bool checked) if (checked && !m_Settings->OpenCL()) { m_Settings->OpenCL(true); - ShutdownAndRecreateFromOptions(); + ShutdownAndRecreateFromOptions(false); } } @@ -69,7 +69,7 @@ void Fractorium::OnActionSP(bool checked) if (checked && m_Settings->Double()) { m_Settings->Double(false); - ShutdownAndRecreateFromOptions(); + ShutdownAndRecreateFromOptions(true);//Pass true, but it's not needed because creating a new controller will force a library tree re-render. } } @@ -82,7 +82,7 @@ void Fractorium::OnActionDP(bool checked) if (checked && !m_Settings->Double()) { m_Settings->Double(true); - ShutdownAndRecreateFromOptions(); + ShutdownAndRecreateFromOptions(true);//Pass true, but it's not needed because creating a new controller will force a library tree re-render. } } diff --git a/Source/Fractorium/FractoriumXformsAffine.cpp b/Source/Fractorium/FractoriumXformsAffine.cpp index 4e69b42..f49d7e0 100644 --- a/Source/Fractorium/FractoriumXformsAffine.cpp +++ b/Source/Fractorium/FractoriumXformsAffine.cpp @@ -251,7 +251,7 @@ void FractoriumEmberController::AffineSetHelper(double d, int index, bool pre UpdateXform([&] (Xform* xform) { auto& affine = pre ? xform->m_Affine : xform->m_Post; - DoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins; + AffineDoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins; if (m_Fractorium->ui.PolarAffineCheckBox->isChecked()) { @@ -618,7 +618,7 @@ void FractoriumEmberController::FillBothAffines() template void FractoriumEmberController::FillAffineWithXform(Xform* xform, bool pre) { - DoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins; + AffineDoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins; auto& affine = pre ? xform->m_Affine : xform->m_Post; if (m_Fractorium->ui.PolarAffineCheckBox->isChecked()) @@ -715,9 +715,9 @@ void Fractorium::OnPolarAffineCheckBoxStateChanged(int state) /// The precision of the spinner /// The signal the spinner emits /// The slot to receive the signal -void Fractorium::SetupAffineSpinner(QTableWidget* table, const QObject* receiver, int row, int col, DoubleSpinBox*& spinBox, int height, double min, double max, double step, double prec, const char* signal, const char* slot) +void Fractorium::SetupAffineSpinner(QTableWidget* table, const QObject* receiver, int row, int col, AffineDoubleSpinBox*& spinBox, int height, double min, double max, double step, double prec, const char* signal, const char* slot) { - spinBox = new DoubleSpinBox(table, height, step); + spinBox = new AffineDoubleSpinBox(table, height, step); spinBox->setRange(min, max); spinBox->setDecimals(prec); table->setCellWidget(row, col, spinBox); diff --git a/Source/Fractorium/FractoriumXformsVariations.cpp b/Source/Fractorium/FractoriumXformsVariations.cpp index 206559a..5923f45 100644 --- a/Source/Fractorium/FractoriumXformsVariations.cpp +++ b/Source/Fractorium/FractoriumXformsVariations.cpp @@ -375,5 +375,5 @@ void Fractorium::OnVariationsFilterClearButtonClicked(bool checked) template class FractoriumEmberController; #ifdef DO_DOUBLE -template class FractoriumEmberController; + template class FractoriumEmberController; #endif diff --git a/Source/Fractorium/GLWidget.cpp b/Source/Fractorium/GLWidget.cpp index 1e070a4..377a571 100644 --- a/Source/Fractorium/GLWidget.cpp +++ b/Source/Fractorium/GLWidget.cpp @@ -54,8 +54,6 @@ void GLWidget::InitGL() void GLWidget::DrawQuad() { glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); auto renderer = m_Fractorium->m_Controller->Renderer(); auto finalImage = m_Fractorium->m_Controller->FinalImage(); @@ -65,7 +63,7 @@ void GLWidget::DrawQuad() glBindTexture(GL_TEXTURE_2D, m_OutputTexID);//The texture to draw to. //Only draw if the dimensions match exactly. - if (m_TexWidth == width() && m_TexHeight == height() && ((m_TexWidth * m_TexHeight * 4) == GLint(finalImage->size()))) + if (m_TexWidth == width() && m_TexHeight == height() && ((m_TexWidth * m_TexHeight) == GLint(finalImage->size()))) { glMatrixMode(GL_PROJECTION); glPushMatrix(); @@ -77,7 +75,7 @@ void GLWidget::DrawQuad() //Copy data from CPU to OpenGL if using a CPU renderer. This is not needed when using OpenCL. if (renderer->RendererType() == eRendererType::CPU_RENDERER) - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_TexWidth, m_TexHeight, GL_RGBA, GL_UNSIGNED_BYTE, finalImage->data()); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_TexWidth, m_TexHeight, GL_RGBA, GL_FLOAT, finalImage->data()); glBegin(GL_QUADS);//This will need to be converted to a shader at some point in the future. glTexCoord2f(0.0, 0.0); glVertex2f(0.0, 0.0); @@ -94,7 +92,6 @@ void GLWidget::DrawQuad() glBindTexture(GL_TEXTURE_2D, 0);//Stop using this texture. } - glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); } @@ -267,10 +264,10 @@ void GLEmberController::DrawImage() if (SizesMatch())//Ensure all sizes are correct. If not, do nothing. { - vector* finalImage = m_FractoriumEmberController->FinalImage(); + auto finalImage = m_FractoriumEmberController->FinalImage(); if ((renderer->RendererType() == eRendererType::OPENCL_RENDERER) || finalImage)//Final image only matters for CPU renderer. - if ((renderer->RendererType() == eRendererType::OPENCL_RENDERER) || finalImage->size() == renderer->FinalBufferSize()) + if ((renderer->RendererType() == eRendererType::OPENCL_RENDERER) || finalImage->size() == renderer->FinalDimensions()) m_GL->DrawQuad();//Output image is drawn here. } @@ -757,7 +754,7 @@ bool GLWidget::Allocate(bool force) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TexWidth, m_TexHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_TexWidth, m_TexHeight, 0, GL_RGBA, GL_FLOAT, nullptr); alloc = true; } diff --git a/Source/Fractorium/OptionsDialog.cpp b/Source/Fractorium/OptionsDialog.cpp index ab4234a..2c9e26a 100644 --- a/Source/Fractorium/OptionsDialog.cpp +++ b/Source/Fractorium/OptionsDialog.cpp @@ -67,6 +67,7 @@ bool FractoriumOptionsDialog::OpenCL() { return ui.OpenCLCheckBox->isChecked(); bool FractoriumOptionsDialog::Double() { return ui.DoublePrecisionCheckBox->isChecked(); } bool FractoriumOptionsDialog::ShowAllXforms() { return ui.ShowAllXformsCheckBox->isChecked(); } bool FractoriumOptionsDialog::ToggleType() { return ui.ToggleTypeCheckBox->isChecked(); } +bool FractoriumOptionsDialog::Png16Bit() { return ui.Png16BitCheckBox->isChecked(); } bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); } uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); } uint FractoriumOptionsDialog::RandomCount() { return ui.RandomCountSpin->value(); } @@ -174,6 +175,7 @@ void FractoriumOptionsDialog::GuiToData() m_Settings->Double(Double()); m_Settings->ShowAllXforms(ShowAllXforms()); m_Settings->ToggleType(ToggleType()); + m_Settings->Png16Bit(Png16Bit()); m_Settings->ThreadCount(ThreadCount()); m_Settings->RandomCount(RandomCount()); m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value()); @@ -207,6 +209,7 @@ void FractoriumOptionsDialog::DataToGui() ui.DoublePrecisionCheckBox->setChecked(m_Settings->Double()); ui.ShowAllXformsCheckBox->setChecked(m_Settings->ShowAllXforms()); ui.ToggleTypeCheckBox->setChecked(m_Settings->ToggleType()); + ui.Png16BitCheckBox->setChecked(m_Settings->Png16Bit()); ui.ThreadCountSpin->setValue(m_Settings->ThreadCount()); ui.RandomCountSpin->setValue(m_Settings->RandomCount()); ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch()); diff --git a/Source/Fractorium/OptionsDialog.h b/Source/Fractorium/OptionsDialog.h index 4b83acd..de40dff 100644 --- a/Source/Fractorium/OptionsDialog.h +++ b/Source/Fractorium/OptionsDialog.h @@ -39,13 +39,13 @@ protected: private: bool EarlyClip(); bool YAxisUp(); - bool AlphaChannel(); bool Transparency(); bool ContinuousUpdate(); bool OpenCL(); bool Double(); bool ShowAllXforms(); bool ToggleType(); + bool Png16Bit(); bool AutoUnique(); uint ThreadCount(); uint RandomCount(); diff --git a/Source/Fractorium/OptionsDialog.ui b/Source/Fractorium/OptionsDialog.ui index e1617d5..a70479f 100644 --- a/Source/Fractorium/OptionsDialog.ui +++ b/Source/Fractorium/OptionsDialog.ui @@ -424,6 +424,16 @@ in interactive mode for each mouse movement + + + + <html><head/><body><p>Save each RGBA component as 16-bits when saving Png files.</p><p>This leads to greater color precision for use in high end rendering and display on HDR monitors, however it makes the file size larger.</p></body></html> + + + Save 16-bit Png + + + @@ -846,6 +856,8 @@ in interactive mode for each mouse movement TransparencyCheckBox ShowAllXformsCheckBox ContinuousUpdateCheckBox + ToggleTypeCheckBox + Png16BitCheckBox RandomCountSpin ThreadCountSpin CpuSubBatchSpin diff --git a/Source/Fractorium/PaletteEditor/PaletteEditor.cpp b/Source/Fractorium/PaletteEditor/PaletteEditor.cpp index 91f705a..673d8ed 100644 --- a/Source/Fractorium/PaletteEditor/PaletteEditor.cpp +++ b/Source/Fractorium/PaletteEditor/PaletteEditor.cpp @@ -496,7 +496,11 @@ QStringList PaletteEditor::SetupOpenImagesDialog() m_FileDialog->setViewMode(QFileDialog::List); m_FileDialog->setFileMode(QFileDialog::ExistingFile); m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen); +#ifdef _WIN32 m_FileDialog->setNameFilter("Image Files (*.png *.jpg *.bmp)"); +#else + m_FileDialog->setNameFilter("Image Files ( *.jpg *.png)"); +#endif m_FileDialog->setWindowTitle("Open Image"); m_FileDialog->setDirectory(settings->OpenPaletteImageFolder()); m_FileDialog->selectNameFilter("*.jpg"); @@ -515,7 +519,7 @@ QStringList PaletteEditor::SetupOpenImagesDialog() } #else - auto filename = QFileDialog::getOpenFileName(this, tr("Open Image"), settings->OpenPaletteImageFolder(), tr("Image Files (*.png *.jpg *.bmp)")); + auto filename = QFileDialog::getOpenFileName(this, tr("Open Image"), settings->OpenPaletteImageFolder(), tr("Image Files (*.jpg *.png)")); if (filename.size() > 0) { diff --git a/Source/Fractorium/SpinBox.cpp b/Source/Fractorium/SpinBox.cpp index eaa7a45..f42e85a 100644 --- a/Source/Fractorium/SpinBox.cpp +++ b/Source/Fractorium/SpinBox.cpp @@ -146,7 +146,7 @@ void SpinBox::OnTimeout() /// false bool SpinBox::eventFilter(QObject* o, QEvent* e) { - QMouseEvent* me = dynamic_cast(e); + auto me = dynamic_cast(e); if (isEnabled() && me) { diff --git a/makedeps.bat b/makedeps.bat index d57cd57..4dc8a71 100644 --- a/makedeps.bat +++ b/makedeps.bat @@ -52,4 +52,5 @@ copy X64\Release\tbb.dll ..\..\..\fractorium\Deps copy X64\Release\tbb.lib ..\..\..\fractorium\Deps cd ..\..\.. -cd fractorium +openexr.cmd + diff --git a/openexr.cmd b/openexr.cmd new file mode 100644 index 0000000..86abea7 --- /dev/null +++ b/openexr.cmd @@ -0,0 +1,46 @@ +ECHO building OpenEXR + +cd.. +if not exist ".\openexr" ^ + mkdir openexr + +git clone git://github.com/meshula/openexr.git + +cd openexr +git pull + +SET current=%cd% + +if not exist ".\output" mkdir .\output + +cd IlmBase + +cmake -G "Visual Studio 14 2015 Win64"^ + -DCMAKE_PREFIX_PATH="%current%\output"^ + -DCMAKE_INSTALL_PREFIX="%current%\output"^ + .\ + +cmake --build . --target install --config Release -- /maxcpucount:8 + +cd ..\OpenEXR + +cmake -G "Visual Studio 14 2015 Win64"^ + -DCMAKE_PREFIX_PATH="%current%\output"^ + -DCMAKE_INSTALL_PREFIX="%current%\output"^ + -DILMBASE_PACKAGE_PREFIX="%current%\output" ^ + -DZLIB_ROOT="%current%\..\zlib"^ + .\ + +cmake --build . --target install --config Release + +cd %current% + +copy %current%\output\lib\Half.lib %current%\..\fractorium\Deps\Half.lib +copy %current%\output\lib\Iex-2_2.lib %current%\..\fractorium\Deps\Iex.lib +copy %current%\output\lib\IexMath-2_2.lib %current%\..\fractorium\Deps\IexMath.lib +copy %current%\output\lib\IlmImf-2_2.lib %current%\..\fractorium\Deps\IlmImf.lib +copy %current%\output\lib\IlmImfUtil-2_2.lib %current%\..\fractorium\Deps\IlmImfUtil.lib +copy %current%\output\lib\IlmThread-2_2.lib %current%\..\fractorium\Deps\IlmThread.lib +copy %current%\output\lib\Imath-2_2.lib %current%\..\fractorium\Deps\Imath.lib +xcopy %current%\output\Include %current%\..\fractorium\Deps\Include\ /S /Y +xcopy %current%\output\lib\*.dll %current%\..\fractorium\Deps\ /Y