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 @@
-
+
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 27ea16f..1cbefa4 100644
Binary files a/Builds/MSVC/VS2015/EmberCL.rc and b/Builds/MSVC/VS2015/EmberCL.rc differ
diff --git a/Builds/MSVC/VS2015/EmberGenome.rc b/Builds/MSVC/VS2015/EmberGenome.rc
index f11a85e..8a735b6 100644
--- a/Builds/MSVC/VS2015/EmberGenome.rc
+++ b/Builds/MSVC/VS2015/EmberGenome.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", "Manipulates fractal flames parameter files"
- VALUE "FileVersion", "1.0.0.4"
+ VALUE "FileVersion", "1.0.0.5"
VALUE "InternalName", "EmberGenome.exe"
VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3"
VALUE "OriginalFilename", "EmberGenome.exe"
VALUE "ProductName", "Ember Genome"
- VALUE "ProductVersion", "1.0.0.4"
+ VALUE "ProductVersion", "1.0.0.5"
END
END
BLOCK "VarFileInfo"
diff --git a/Builds/MSVC/VS2015/EmberGenome.vcxproj b/Builds/MSVC/VS2015/EmberGenome.vcxproj
index 2959f7a..5842ca0 100644
--- a/Builds/MSVC/VS2015/EmberGenome.vcxproj
+++ b/Builds/MSVC/VS2015/EmberGenome.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/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 908195e..e4433c8 100644
Binary files a/Builds/MSVC/VS2015/Fractorium.rc and b/Builds/MSVC/VS2015/Fractorium.rc differ
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
-