Features:

--Add support for Exr files which use 32-bit floats for each RGBA channel. Seems to come out too washed out.
--Allow for clearing an individual color curve.
--Allow for saving multiple image types in EmberRender and EmberAnimate. All writes are threaded.
--Remove --bpc command line argument. Add format png16 as a replacement.
--Remove --enable_jpg_comments and --enable_png_comments command line arguments, and replace them with --enable_comments which applies to jpg, png and exr.
--Add menu items to variations and affine spinners which allow for easy entry of specific numeric values like pi.
--Make final render dialog be wider rather than so tall.

Bug fixes:
--Fix some OpenCL compile errors on Mac.
--Remove ability to save bitmap files on all platforms but Windows.

Code changes:
--New dependency on OpenEXR.
--Allow Curves class to interact with objects of a different template type.
--Make m_Curves member of Ember always use float as template type.
--Set the length of the curves array to always be 2^17 which should offer enough precision with new 32-bit float pixel types.
--Set pixel types to always be 32-bit float. This results in a major reduction of code in the final accumulation part of Renderer.h/cpp.
--Remove corresponding code from RendererCL and FinalAccumOpenCLKernelCreator.
--Remove Transparency, NumChannels and BytesPerPixel setters from Renderer.h/cpp.
--Add new global functions to format final image buffers and place all alpha calculation and scaling code in them.
--Blending is no longer needed in OpenGLWidget because of the new pixel type.
--Make new class, AffineDoubleSpinBox.
--Attempt to make file save dialog code work the same on all OSes.
--Remove some unused functions.
This commit is contained in:
Person 2017-07-22 13:43:35 -07:00
parent d5760e451a
commit de613404de
68 changed files with 1755 additions and 1276 deletions

160
.gitignore vendored
View File

@ -89,3 +89,163 @@ Builds/include/GL
/Builds/MSVC/VS2013/flam3-palettes.xml /Builds/MSVC/VS2013/flam3-palettes.xml
/Builds/MSVC/VS2015/flam3-palettes.xml /Builds/MSVC/VS2015/flam3-palettes.xml
*.dmg *.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

View File

@ -6,7 +6,7 @@
<ProductVersion>3.7</ProductVersion> <ProductVersion>3.7</ProductVersion>
<ProjectGuid>{c8096c47-e358-438c-a520-146d46b0637d}</ProjectGuid> <ProjectGuid>{c8096c47-e358-438c-a520-146d46b0637d}</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
<OutputName>Fractorium_1.0.0.4</OutputName> <OutputName>Fractorium_1.0.0.5</OutputName>
<OutputType>Package</OutputType> <OutputType>Package</OutputType>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath> <WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath> <WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"> <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define ProductVersion="1.0.0.4" ?> <?define ProductVersion="1.0.0.5" ?>
<?define ProductName="Fractorium $(var.ProductVersion) ($(var.GpuType))" ?> <?define ProductName="Fractorium $(var.ProductVersion) ($(var.GpuType))" ?>
<?define UpgradeCode="{4714cd15-bfba-44f6-8059-9e1466ebfa6e}"?> <?define UpgradeCode="{4714cd15-bfba-44f6-8059-9e1466ebfa6e}"?>
<?define Manufacturer="Fractorium"?> <?define Manufacturer="Fractorium"?>
@ -13,7 +13,7 @@
<!-- <!--
Change this for every release. Change this for every release.
--> -->
<?define ProductCode="{344D73B1-F519-44CD-8F62-9FE6595816F9}"?> <?define ProductCode="{290BBFDC-BD1C-45F7-80AC-DE59BA4378F0}"?>
<Product Id="$(var.ProductCode)" Name="$(var.ProductName)" Language="1033" Version="$(var.ProductVersion)" Manufacturer="$(var.Manufacturer)" UpgradeCode="$(var.UpgradeCode)"> <Product Id="$(var.ProductCode)" Name="$(var.ProductName)" Language="1033" Version="$(var.ProductVersion)" Manufacturer="$(var.Manufacturer)" UpgradeCode="$(var.UpgradeCode)">
<Package <Package

Binary file not shown.

View File

@ -49,8 +49,8 @@
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1, 0, 0, 4 FILEVERSION 1, 0, 0, 5
PRODUCTVERSION 1, 0, 0, 4 PRODUCTVERSION 1, 0, 0, 5
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -67,12 +67,12 @@
BEGIN BEGIN
VALUE "CompanyName", "Open Source" VALUE "CompanyName", "Open Source"
VALUE "FileDescription", "Renders fractal flames as animations with motion blur" VALUE "FileDescription", "Renders fractal flames as animations with motion blur"
VALUE "FileVersion", "1.0.0.4" VALUE "FileVersion", "1.0.0.5"
VALUE "InternalName", "EmberAnimate.exe" VALUE "InternalName", "EmberAnimate.exe"
VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3" VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3"
VALUE "OriginalFilename", "EmberAnimate.exe" VALUE "OriginalFilename", "EmberAnimate.exe"
VALUE "ProductName", "Ember Animate" VALUE "ProductName", "Ember Animate"
VALUE "ProductVersion", "1.0.0.4" VALUE "ProductVersion", "1.0.0.5"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -58,7 +58,7 @@
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName>
<AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories>
<DisableSpecificWarnings> <DisableSpecificWarnings>
</DisableSpecificWarnings> </DisableSpecificWarnings>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -72,7 +72,7 @@
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
@ -90,7 +90,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName>
<AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories>
<DisableSpecificWarnings> <DisableSpecificWarnings>
</DisableSpecificWarnings> </DisableSpecificWarnings>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -105,7 +105,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>

Binary file not shown.

View File

@ -49,8 +49,8 @@
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1, 0, 0, 4 FILEVERSION 1, 0, 0, 5
PRODUCTVERSION 1, 0, 0, 4 PRODUCTVERSION 1, 0, 0, 5
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -67,12 +67,12 @@
BEGIN BEGIN
VALUE "CompanyName", "Open Source" VALUE "CompanyName", "Open Source"
VALUE "FileDescription", "Manipulates fractal flames parameter files" VALUE "FileDescription", "Manipulates fractal flames parameter files"
VALUE "FileVersion", "1.0.0.4" VALUE "FileVersion", "1.0.0.5"
VALUE "InternalName", "EmberGenome.exe" VALUE "InternalName", "EmberGenome.exe"
VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3" VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3"
VALUE "OriginalFilename", "EmberGenome.exe" VALUE "OriginalFilename", "EmberGenome.exe"
VALUE "ProductName", "Ember Genome" VALUE "ProductName", "Ember Genome"
VALUE "ProductVersion", "1.0.0.4" VALUE "ProductVersion", "1.0.0.5"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -58,7 +58,7 @@
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName>
<AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories>
<DisableSpecificWarnings> <DisableSpecificWarnings>
</DisableSpecificWarnings> </DisableSpecificWarnings>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -72,7 +72,7 @@
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
@ -90,7 +90,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName>
<AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories>
<DisableSpecificWarnings> <DisableSpecificWarnings>
</DisableSpecificWarnings> </DisableSpecificWarnings>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -105,7 +105,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>

View File

@ -49,8 +49,8 @@
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1, 0, 0, 4 FILEVERSION 1, 0, 0, 5
PRODUCTVERSION 1, 0, 0, 4 PRODUCTVERSION 1, 0, 0, 5
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -67,12 +67,12 @@
BEGIN BEGIN
VALUE "CompanyName", "Open Source" VALUE "CompanyName", "Open Source"
VALUE "FileDescription", "Renders fractal flames as single images" 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 "InternalName", "EmberRender.exe"
VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3" VALUE "LegalCopyright", "Copyright (C) Matt Feemster 2017, GPL v3"
VALUE "OriginalFilename", "EmberRender.exe" VALUE "OriginalFilename", "EmberRender.exe"
VALUE "ProductName", "Ember Render" VALUE "ProductName", "Ember Render"
VALUE "ProductVersion", "1.0.0.4" VALUE "ProductVersion", "1.0.0.5"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -58,7 +58,7 @@
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName>
<AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories>
<DisableSpecificWarnings> <DisableSpecificWarnings>
</DisableSpecificWarnings> </DisableSpecificWarnings>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -72,7 +72,7 @@
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
@ -90,7 +90,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName>
<AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories>
<DisableSpecificWarnings> <DisableSpecificWarnings>
</DisableSpecificWarnings> </DisableSpecificWarnings>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -106,7 +106,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;Iex.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>

View File

@ -56,9 +56,9 @@
<PrecompiledHeader>Use</PrecompiledHeader> <PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;OPENEXR_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName>
<AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories>
<DisableSpecificWarnings> <DisableSpecificWarnings>
</DisableSpecificWarnings> </DisableSpecificWarnings>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -72,7 +72,7 @@
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
@ -88,9 +88,9 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<Optimization>MaxSpeed</Optimization> <Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking> <FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions> <IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;OPENEXR_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(TargetDir)$(TargetName).pdb</ProgramDataBaseFileName>
<AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>$(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</AdditionalIncludeDirectories>
<DisableSpecificWarnings> <DisableSpecificWarnings>
</DisableSpecificWarnings> </DisableSpecificWarnings>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile> <PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -105,7 +105,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;opencl.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>libjpeg.lib;libpng.lib;libxml2.lib;tbb.lib;zlib.lib;Half.lib;IlmImf.lib;opencl.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName)</AdditionalLibraryDirectories>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>

Binary file not shown.

View File

@ -57,7 +57,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <ClCompile>
<PreprocessorDefinitions>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)</PreprocessorDefinitions> <PreprocessorDefinitions>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)</PreprocessorDefinitions>
<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;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include;.\GeneratedFiles\$(ConfigurationName);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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)</AdditionalIncludeDirectories>
<Optimization>Disabled</Optimization> <Optimization>Disabled</Optimization>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -81,7 +81,7 @@
<OutputFile>$(OutDir)\$(ProjectName).exe</OutputFile> <OutputFile>$(OutDir)\$(ProjectName).exe</OutputFile>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(QTDIR)\lib;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(QTDIR)\lib;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<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;%(AdditionalDependencies)</AdditionalDependencies> <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)</AdditionalDependencies>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
<Command>xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Deps\*.dll" "$(OutDir)" <Command>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_02_warmer.gradient" "$(OutDir)"
xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\tatasz_pack_03.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\Qt5Cored.dll" "$(OutDir)"
xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Guid.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
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <ClCompile>
<PreprocessorDefinitions>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)</PreprocessorDefinitions> <PreprocessorDefinitions>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)</PreprocessorDefinitions>
<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;$(AMDAPPSDKROOT)\include;$(CUDA_PATH)\include;.\GeneratedFiles\$(ConfigurationName);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> <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)</AdditionalIncludeDirectories>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType> <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
@ -138,7 +138,7 @@ xcopy /F /Y /R /D "$(QTDIR)\plugins\platforms\qwindowsd.dll" "$(OutDir)\platform
<OutputFile>$(OutDir)\$(ProjectName).exe</OutputFile> <OutputFile>$(OutDir)\$(ProjectName).exe</OutputFile>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(QTDIR)\lib;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(QTDIR)\lib;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<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;%(AdditionalDependencies)</AdditionalDependencies> <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)</AdditionalDependencies>
<Version>0.1</Version> <Version>0.1</Version>
</Link> </Link>
<PostBuildEvent> <PostBuildEvent>
@ -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_02_warmer.gradient" "$(OutDir)"
xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\tatasz_pack_03.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\Qt5Core.dll" "$(OutDir)"
xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Gui.dll" "$(OutDir)" xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Gui.dll" "$(OutDir)"

View File

@ -1,6 +1,7 @@
VERSION = 1.0.0.4 VERSION = 1.0.0.5
win32:CONFIG += skip_target_version_ext win32:CONFIG += skip_target_version_ext
CONFIG += c++14 CONFIG += c++14
#message(PWD: $$absolute_path($$PWD)) #message(PWD: $$absolute_path($$PWD))
#1) Declare the root of all files in this project, everything else will be #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) SRC_COMMON_DIR = $$absolute_path($$EMBER_ROOT/Source/EmberCommon)
ASSETS_DIR = $$absolute_path($$EMBER_ROOT/Data) ASSETS_DIR = $$absolute_path($$EMBER_ROOT/Data)
QTCREATOR_DIR = $$absolute_path($$EMBER_ROOT/Builds/QtCreator) 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. #4) Add up all include paths.
INCLUDEPATH += $$LOCAL_INCLUDE_DIR INCLUDEPATH += $$LOCAL_INCLUDE_DIR
@ -41,6 +42,7 @@ win32 {
INCLUDEPATH += $$EXTERNAL_DIR/libxml2/include INCLUDEPATH += $$EXTERNAL_DIR/libxml2/include
INCLUDEPATH += $$EXTERNAL_DIR/tbb/include INCLUDEPATH += $$EXTERNAL_DIR/tbb/include
INCLUDEPATH += $$EXTERNAL_DIR/zlib INCLUDEPATH += $$EXTERNAL_DIR/zlib
INCLUDEPATH += $$absolute_path($$EXTERNAL_LIB)/include/OpenEXR
} }
!win32 { !win32 {
@ -51,6 +53,7 @@ win32 {
INCLUDEPATH += /usr/local/include/GL INCLUDEPATH += /usr/local/include/GL
INCLUDEPATH += /usr/include/glm INCLUDEPATH += /usr/include/glm
INCLUDEPATH += /usr/include/tbb INCLUDEPATH += /usr/include/tbb
INCLUDEPATH += /usr/include/OpenEXR
unix:!macx { unix:!macx {
INCLUDEPATH += /usr/include/libxml2 INCLUDEPATH += /usr/include/libxml2
@ -82,6 +85,9 @@ else {
LIBS += $$absolute_path($$EXTERNAL_LIB)/libxml2.lib LIBS += $$absolute_path($$EXTERNAL_LIB)/libxml2.lib
LIBS += $$absolute_path($$EXTERNAL_LIB)/tbb.lib LIBS += $$absolute_path($$EXTERNAL_LIB)/tbb.lib
LIBS += $$absolute_path($$EXTERNAL_LIB)/zlib.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 { !win32 {
@ -89,6 +95,9 @@ else {
LIBS += -lpng LIBS += -lpng
LIBS += -ltbb LIBS += -ltbb
LIBS += -lpthread LIBS += -lpthread
LIBS += -lHalf
LIBS += -lIex
LIBS += -lIlmImf
unix:!macx { unix:!macx {
LIBS += -lxml2 LIBS += -lxml2
@ -242,6 +251,26 @@ win32 {
tbb.path = $$BIN_INSTALL_DIR tbb.path = $$BIN_INSTALL_DIR
tbb.files = $$absolute_path($$EMBER_ROOT/Deps/tbb.dll) tbb.files = $$absolute_path($$EMBER_ROOT/Deps/tbb.dll)
INSTALLS += tbb 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. #11) Print values of relevant variables for debugging.

View File

@ -85,7 +85,8 @@ public:
/// </summary> /// </summary>
/// <param name="curves">The Curves object to add</param> /// <param name="curves">The Curves object to add</param>
/// <returns>Reference to updated self</returns> /// <returns>Reference to updated self</returns>
Curves<T>& operator += (const Curves<T>& curves) template <typename U>
Curves<T>& operator += (const Curves<U>& curves)
{ {
for (size_t i = 0; i < 4; i++) for (size_t i = 0; i < 4; i++)
{ {
@ -93,7 +94,6 @@ public:
m_Points[i][1] += curves.m_Points[i][1]; m_Points[i][1] += curves.m_Points[i][1];
m_Points[i][2] += curves.m_Points[i][2]; m_Points[i][2] += curves.m_Points[i][2];
m_Points[i][3] += curves.m_Points[i][3]; m_Points[i][3] += curves.m_Points[i][3];
m_Weights[i] += curves.m_Weights[i]; m_Weights[i] += curves.m_Weights[i];
} }
@ -105,7 +105,8 @@ public:
/// </summary> /// </summary>
/// <param name="curves">The Curves object to multiply this one by</param> /// <param name="curves">The Curves object to multiply this one by</param>
/// <returns>Reference to updated self</returns> /// <returns>Reference to updated self</returns>
Curves<T>& operator *= (const Curves<T>& curves) template <typename U>
Curves<T>& operator *= (const Curves<U>& curves)
{ {
for (size_t i = 0; i < 4; i++) for (size_t i = 0; i < 4; i++)
{ {
@ -113,7 +114,6 @@ public:
m_Points[i][1] *= curves.m_Points[i][1]; m_Points[i][1] *= curves.m_Points[i][1];
m_Points[i][2] *= curves.m_Points[i][2]; m_Points[i][2] *= curves.m_Points[i][2];
m_Points[i][3] *= curves.m_Points[i][3]; m_Points[i][3] *= curves.m_Points[i][3];
m_Weights[i] *= curves.m_Weights[i]; m_Weights[i] *= curves.m_Weights[i];
} }
@ -125,16 +125,16 @@ public:
/// </summary> /// </summary>
/// <param name="t">The scalar to multiply this object by</param> /// <param name="t">The scalar to multiply this object by</param>
/// <returns>Reference to updated self</returns> /// <returns>Reference to updated self</returns>
Curves<T>& operator *= (const T& t) template <typename U>
Curves<T>& operator *= (const U& t)
{ {
for (size_t i = 0; i < 4; i++) for (size_t i = 0; i < 4; i++)
{ {
m_Points[i][0] *= t; m_Points[i][0] *= T(t);
m_Points[i][1] *= t; m_Points[i][1] *= T(t);
m_Points[i][2] *= t; m_Points[i][2] *= T(t);
m_Points[i][3] *= t; m_Points[i][3] *= T(t);
m_Weights[i] *= T(t);
m_Weights[i] *= t;
} }
return *this; return *this;
@ -151,7 +151,21 @@ public:
m_Points[i][1] = v2T(0); m_Points[i][1] = v2T(0);
m_Points[i][2] = v2T(1); m_Points[i][2] = v2T(1);
m_Points[i][3] = v2T(1); m_Points[i][3] = v2T(1);
m_Weights[i] = v4T(1);
}
}
/// <summary>
/// Set the a specific curve and its weight value to their default state.
/// </summary>
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); m_Weights[i] = v4T(1);
} }
} }
@ -197,12 +211,10 @@ public:
{ {
v4T result; v4T result;
v2T solution(0, 0); v2T solution(0, 0);
BezierSolve(t, m_Points[0], &m_Weights[0], solution); result.x = solution.y; 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[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[2], &m_Weights[2], solution); result.z = solution.y;
BezierSolve(t, m_Points[3], &m_Weights[3], solution); result.w = solution.y; BezierSolve(t, m_Points[3], &m_Weights[3], solution); result.w = solution.y;
return result; return result;
} }
@ -217,20 +229,14 @@ private:
/// <param name="solution">The vec2 to store the solution in</param> /// <param name="solution">The vec2 to store the solution in</param>
void BezierSolve(const T& t, v2T* src, v4T* w, v2T& solution) void BezierSolve(const T& t, v2T* src, v4T* w, v2T& solution)
{ {
T s, s2, s3, t2, t3, nom_x, nom_y, denom; T s = 1 - t;
T s2 = s * s;
s = 1 - t; T s3 = s * s * s;
s2 = s * s; T t2 = t * t;
s3 = s * s * s; T t3 = t * t * t;
t2 = 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);
t3 = t * t * t; 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);
nom_x = (w->x * s3 * src->x) + (w->y * s2 * 3 * t * src[1].x) + (w->z * s * 3 * t2 * src[2].x) + (w->w * t3 * src[3].x);
nom_y = (w->x * s3 * src->y) + (w->y * s2 * 3 * t * src[1].y) + (w->z * s * 3 * t2 * src[2].y) + (w->w * t3 * src[3].y);
denom = (w->x * s3) + (w->y * s2 * 3 * t) + (w->z * s * 3 * t2) + (w->w * t3);
if (std::isnan(nom_x) || std::isnan(nom_y) || std::isnan(denom) || denom == 0) if (std::isnan(nom_x) || std::isnan(nom_y) || std::isnan(denom) || denom == 0)
return; return;
@ -247,24 +253,24 @@ public:
//Must declare this outside of the class to provide for both orders of parameters. //Must declare this outside of the class to provide for both orders of parameters.
/// <summary> /// <summary>
/// Multiplication operator to multiply a Curves<T> object by a scalar of type T. /// Multiplication operator to multiply a Curves<T> object by a scalar of type U.
/// </summary> /// </summary>
/// <param name="curves">The curves object to multiply</param> /// <param name="curves">The curves object to multiply</param>
/// <param name="t">The scalar to multiply curves by by</param> /// <param name="t">The scalar to multiply curves by by</param>
/// <returns>Copy of new Curves<T></returns> /// <returns>Copy of new Curves<T></returns>
template<typename T> template <typename T, typename U>
Curves<T> operator * (const Curves<T>& curves, const T& t) Curves<T> operator * (const Curves<T>& curves, const U& t)
{ {
T tt = T(t);
Curves<T> c(curves); Curves<T> c(curves);
for (size_t i = 0; i < 4; i++) for (size_t i = 0; i < 4; i++)
{ {
c.m_Points[i][0] *= t; c.m_Points[i][0] *= tt;
c.m_Points[i][1] *= t; c.m_Points[i][1] *= tt;
c.m_Points[i][2] *= t; c.m_Points[i][2] *= tt;
c.m_Points[i][3] *= t; c.m_Points[i][3] *= tt;
c.m_Weights[i] *= tt;
c.m_Weights[i] *= t;
} }
return c; return c;
@ -276,8 +282,8 @@ Curves<T> operator * (const Curves<T>& curves, const T& t)
/// <param name="t">The scalar to multiply curves by by</param> /// <param name="t">The scalar to multiply curves by by</param>
/// <param name="curves">The curves object to multiply</param> /// <param name="curves">The curves object to multiply</param>
/// <returns>Copy of new Curves<T></returns> /// <returns>Copy of new Curves<T></returns>
template<typename T> template <typename T, typename U>
Curves<T> operator * (const T& t, const Curves<T>& curves) Curves<T> operator * (const U& t, const Curves<T>& curves)
{ {
return curves * t; return curves * t;
} }

View File

@ -727,7 +727,7 @@ public:
InterpT<&Ember<T>::m_MinRadDE>(embers, coefs, size); InterpT<&Ember<T>::m_MinRadDE>(embers, coefs, size);
InterpT<&Ember<T>::m_CurveDE>(embers, coefs, size); InterpT<&Ember<T>::m_CurveDE>(embers, coefs, size);
InterpT<&Ember<T>::m_SpatialFilterRadius>(embers, coefs, size); InterpT<&Ember<T>::m_SpatialFilterRadius>(embers, coefs, size);
InterpX<Curves<T>, &Ember<T>::m_Curves>(embers, coefs, size); InterpX<Curves<float>, &Ember<T>::m_Curves>(embers, coefs, size);
//Normally done in assignment, must manually do here. //Normally done in assignment, must manually do here.
SetProjFunc(); SetProjFunc();
//An extra step needed here due to the OOD that was not needed in the original. //An extra step needed here due to the OOD that was not needed in the original.
@ -1653,7 +1653,7 @@ public:
Palette<float> m_Palette;//Final palette that is actually used is a copy of this inside of render, which will be of type bucketT (float). Palette<float> 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 used to adjust the color during final accumulation.
Curves<T> m_Curves; Curves<float> m_Curves;
//Strings. //Strings.

View File

@ -37,7 +37,7 @@ static void sincos(float x, float* s, float* c)
namespace EmberNs namespace EmberNs
{ {
#define EMBER_VERSION "1.0.0.4" #define EMBER_VERSION "1.0.0.5"
#define EPS6 T(1e-6) #define EPS6 T(1e-6)
#define EPS std::numeric_limits<T>::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way. #define EPS std::numeric_limits<T>::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way.
#define ISAAC_SIZE 4 #define ISAAC_SIZE 4
@ -68,6 +68,9 @@ namespace EmberNs
#define TMAX std::numeric_limits<T>::max() #define TMAX std::numeric_limits<T>::max()
#define FLOAT_MAX_TAN 8388607.0f #define FLOAT_MAX_TAN 8388607.0f
#define FLOAT_MIN_TAN -FLOAT_MAX_TAN #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 #define EMPTYFIELD -9999
typedef std::chrono::high_resolution_clock Clock; typedef std::chrono::high_resolution_clock Clock;
typedef std::chrono::duration<double, std::ratio<1, 1000>> DoubleMs; typedef std::chrono::duration<double, std::ratio<1, 1000>> DoubleMs;
@ -110,6 +113,7 @@ typedef std::lock_guard <std::recursive_mutex> rlg;
#define m3T glm::tmat3x3<T, glm::defaultp> #define m3T glm::tmat3x3<T, glm::defaultp>
#define m4T glm::tmat4x4<T, glm::defaultp> #define m4T glm::tmat4x4<T, glm::defaultp>
#define m23T glm::tmat2x3<T, glm::defaultp> #define m23T glm::tmat2x3<T, glm::defaultp>
typedef vector<glm::tvec4<float, glm::defaultp>> vv4F;
#else #else
#define v2T glm::detail::tvec2<T, glm::defaultp> #define v2T glm::detail::tvec2<T, glm::defaultp>
#define v3T glm::detail::tvec3<T, glm::defaultp> #define v3T glm::detail::tvec3<T, glm::defaultp>
@ -121,6 +125,7 @@ typedef std::lock_guard <std::recursive_mutex> rlg;
#define m3T glm::detail::tmat3x3<T, glm::defaultp> #define m3T glm::detail::tmat3x3<T, glm::defaultp>
#define m4T glm::detail::tmat4x4<T, glm::defaultp> #define m4T glm::detail::tmat4x4<T, glm::defaultp>
#define m23T glm::detail::tmat2x3<T, glm::defaultp> #define m23T glm::detail::tmat2x3<T, glm::defaultp>
typedef vector<glm::detail::tvec4<float, glm::defaultp>> vv4F;
#endif #endif
enum class eInterp : et { EMBER_INTERP_LINEAR = 0, EMBER_INTERP_SMOOTH = 1 }; enum class eInterp : et { EMBER_INTERP_LINEAR = 0, EMBER_INTERP_SMOOTH = 1 };

View File

@ -9,7 +9,9 @@ namespace EmberNs
template <typename T, typename bucketT> template <typename T, typename bucketT>
Renderer<T, bucketT>::Renderer() Renderer<T, bucketT>::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));
} }
/// <summary> /// <summary>
@ -344,7 +346,7 @@ bool Renderer<T, bucketT>::CreateTemporalFilter(bool& newAlloc)
/// <param name="finalOffset">Offset in finalImage to store the pixels to. Default: 0.</param> /// <param name="finalOffset">Offset in finalImage to store the pixels to. Default: 0.</param>
/// <returns>True if nothing went wrong, else false.</returns> /// <returns>True if nothing went wrong, else false.</returns>
template <typename T, typename bucketT> template <typename T, typename bucketT>
eRenderStatus Renderer<T, bucketT>::Run(vector<byte>& finalImage, double time, size_t subBatchCountOverride, bool forceOutput, size_t finalOffset) eRenderStatus Renderer<T, bucketT>::Run(vector<v4F>& finalImage, double time, size_t subBatchCountOverride, bool forceOutput, size_t finalOffset)
{ {
m_InRender = true; m_InRender = true;
EnterRender(); EnterRender();
@ -645,7 +647,7 @@ AccumOnly:
CreateSpatialFilter(newFilterAlloc); CreateSpatialFilter(newFilterAlloc);
m_DensityFilterOffset = m_GutterWidth - size_t(Clamp<T>((T(m_SpatialFilter->FinalFilterWidth()) - T(Supersample())) / 2, 0, T(m_GutterWidth))); m_DensityFilterOffset = m_GutterWidth - size_t(Clamp<T>((T(m_SpatialFilter->FinalFilterWidth()) - T(Supersample())) / 2, 0, T(m_GutterWidth)));
m_CurvesSet = m_Ember.m_Curves.CurvesSet(); 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) if (AccumulatorToFinalImage(finalImage, finalOffset) == eRenderStatus::RENDER_OK)
{ {
@ -822,6 +824,7 @@ bool Renderer<T, bucketT>::ResetBuckets(bool resetHist, bool resetAccum)
} }
/// <summary> /// <summary>
/// THIS IS UNUSED.
/// Log scales a single row with a specially structured loop that will be vectorized by the compiler. /// 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 /// 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 /// value because the conditional check for zero would have prevented the loop from
@ -882,7 +885,7 @@ eRenderStatus Renderer<T, bucketT>::LogScaleDensityFilter(bool forceOutput)
bucketT* __restrict hist = glm::value_ptr(m_HistBuckets[i]);//Vectorizer can't tell these point to different locations. 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]); 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; acc[v] = hist[v] * logScale;
} }
} }
@ -1063,7 +1066,7 @@ eRenderStatus Renderer<T, bucketT>::GaussianDensityFilter()
/// <param name="finalOffset">Offset in the buffer to store the pixels to</param> /// <param name="finalOffset">Offset in the buffer to store the pixels to</param>
/// <returns>True if not prematurely aborted, else false.</returns> /// <returns>True if not prematurely aborted, else false.</returns>
template <typename T, typename bucketT> template <typename T, typename bucketT>
eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(vector<byte>& pixels, size_t finalOffset) eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(vector<v4F>& pixels, size_t finalOffset)
{ {
if (PrepFinalAccumVector(pixels)) if (PrepFinalAccumVector(pixels))
return AccumulatorToFinalImage(pixels.data(), finalOffset); return AccumulatorToFinalImage(pixels.data(), finalOffset);
@ -1079,14 +1082,13 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(vector<byte>& pixels
/// <param name="finalOffset">Offset in the buffer to store the pixels to. Default: 0.</param> /// <param name="finalOffset">Offset in the buffer to store the pixels to. Default: 0.</param>
/// <returns>True if not prematurely aborted, else false.</returns> /// <returns>True if not prematurely aborted, else false.</returns>
template <typename T, typename bucketT> template <typename T, typename bucketT>
eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t finalOffset) eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(v4F* pixels, size_t finalOffset)
{ {
if (!pixels) if (!pixels)
return eRenderStatus::RENDER_ERROR; return eRenderStatus::RENDER_ERROR;
EnterFinalAccum(); EnterFinalAccum();
//Timing t(4); //Timing t(4);
bool doAlpha = NumChannels() > 3;
size_t filterWidth = m_SpatialFilter->FinalFilterWidth(); size_t filterWidth = m_SpatialFilter->FinalFilterWidth();
bucketT g, linRange, vibrancy; bucketT g, linRange, vibrancy;
Color<bucketT> background; Color<bucketT> background;
@ -1104,7 +1106,7 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
while (rowStart < rowEnd && !m_Abort)//Use the pointer itself as the offset to save an extra addition per iter. 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++; rowStart++;
} }
}); });
@ -1123,11 +1125,12 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
parallel_for(size_t(0), FinalRasH(), size_t(1), [&](size_t j) parallel_for(size_t(0), FinalRasH(), size_t(1), [&](size_t j)
{ {
Color<bucketT> newBucket; Color<bucketT> 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 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. 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 ii, jj;
size_t x = m_DensityFilterOffset + (i * Supersample());//Start at the beginning column of each super sample block. size_t x = m_DensityFilterOffset + (i * Supersample());//Start at the beginning column of each super sample block.
@ -1149,68 +1152,8 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
} }
} }
if (BytesPerChannel() == 2) auto pf = reinterpret_cast<float*>(pv4T);
{ GammaCorrection(*(reinterpret_cast<tvec4<bucketT, glm::defaultp>*>(&newBucket)), background, g, linRange, vibrancy, true, pf);
auto p16 = reinterpret_cast<glm::uint16*>(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<bucketT>(newBucket.r, 0, 65535));
p16[1] = glm::uint16(Clamp<bucketT>(newBucket.g, 0, 65535));
p16[2] = glm::uint16(Clamp<bucketT>(newBucket.b, 0, 65535));
if (doAlpha)
{
if (Transparency())
p16[3] = byte(Clamp<bucketT>(newBucket.a, 0, 65535));
else
p16[3] = 65535;
}
}
else
{
GammaCorrection(*(reinterpret_cast<tvec4<bucketT, glm::defaultp>*>(&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<bucketT>(newBucket.r, 0, 255));
pixels[pixelsRowStart + 1] = byte(Clamp<bucketT>(newBucket.g, 0, 255));
pixels[pixelsRowStart + 2] = byte(Clamp<bucketT>(newBucket.b, 0, 255));
if (doAlpha)
{
if (Transparency())
pixels[pixelsRowStart + 3] = byte(Clamp<bucketT>(newBucket.a, 0, 255));
else
pixels[pixelsRowStart + 3] = 255;
}
}
else
{
GammaCorrection(*(reinterpret_cast<tvec4<bucketT, glm::defaultp>*>(&newBucket)), background, g, linRange, vibrancy, doAlpha, true, pixels + pixelsRowStart);
}
}
} }
}); });
@ -1222,30 +1165,15 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
if (ph >= FinalRasH()) if (ph >= FinalRasH())
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())); auto p = pixels + (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->r = m_TempEmber.m_Palette[i * 256 / FinalRasW()][0];
p[1] = byte(m_TempEmber.m_Palette[i * 256 / FinalRasW()][1] * WHITE); p->g = m_TempEmber.m_Palette[i * 256 / FinalRasW()][1];
p[2] = byte(m_TempEmber.m_Palette[i * 256 / FinalRasW()][2] * WHITE); p->b = m_TempEmber.m_Palette[i * 256 / FinalRasW()][2];
} p->a = 1;
}
}
else//BPC == 2.
{
for (j = 0; j < ph; j++)
{
for (i = 0; i < FinalRasW(); i++)
{
auto p16 = reinterpret_cast<glm::uint16*>(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));
}
} }
} }
} }
@ -1322,7 +1250,6 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
//t.Tic(); //t.Tic();
//Iterating, loop 3. //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_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(); //iterationTime += t.Toc();
if (m_LockAccum) if (m_LockAccum)
@ -1677,23 +1604,38 @@ void Renderer<T, bucketT>::AddToAccum(const tvec4<bucketT, glm::defaultp>& bucke
/// Because this code is used in both early and late clipping, a few extra arguments are passed /// 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 /// 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. /// 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 /// Template argument accumT is expected to always be float4.
/// images with one byte per channel and unsigned short for images with two bytes per channel.
/// </summary> /// </summary>
/// <param name="bucket">The pixel to correct</param> /// <param name="bucket">The pixel to correct</param>
/// <param name="background">The background color</param> /// <param name="background">The background color</param>
/// <param name="g">The gamma to use</param> /// <param name="g">The gamma to use</param>
/// <param name="linRange">The linear range to use</param> /// <param name="linRange">The linear range to use</param>
/// <param name="vibrancy">The vibrancy to use</param> /// <param name="vibrancy">The vibrancy to use</param>
/// <param name="doAlpha">True if either early clip, or late clip with 4 channel output, else false.</param>
/// <param name="scale">True if late clip, else false.</param> /// <param name="scale">True if late clip, else false.</param>
/// <param name="correctedChannels">The storage space for the corrected values to be written to</param> /// <param name="correctedChannels">The storage space for the corrected values to be written to</param>
template <typename T, typename bucketT> template <typename T, typename bucketT>
template <typename accumT> template <typename accumT>
void Renderer<T, bucketT>::GammaCorrection(tvec4<bucketT, glm::defaultp>& bucket, Color<bucketT>& background, bucketT g, bucketT linRange, bucketT vibrancy, bool doAlpha, bool scale, accumT* correctedChannels) void Renderer<T, bucketT>::GammaCorrection(tvec4<bucketT, glm::defaultp>& bucket, Color<bucketT>& background, bucketT g, bucketT linRange, bucketT vibrancy, bool scale, accumT* correctedChannels)
{ {
auto bt1 = bucketT(1);
if (scale && EarlyClip())
{
if (m_CurvesSet)
{
CurveAdjust(bucket.r, 1);
CurveAdjust(bucket.g, 2);
CurveAdjust(bucket.b, 3);
}
correctedChannels[0] = accumT(Clamp<bucketT>(bucket.r, 0, bt1));
correctedChannels[1] = accumT(Clamp<bucketT>(bucket.g, 0, bt1));
correctedChannels[2] = accumT(Clamp<bucketT>(bucket.b, 0, bt1));
correctedChannels[3] = accumT(Clamp<bucketT>(bucket.a, 0, bt1));
}
else
{
bucketT alpha, ls, a, newRgb[3];//Would normally use a Color<bucketT>, but don't want to call a needless constructor every time this function is called, which is once per pixel. bucketT alpha, ls, a, newRgb[3];//Would normally use a Color<bucketT>, 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<accumT>::max();
if (bucket.a <= 0) if (bucket.a <= 0)
{ {
@ -1712,77 +1654,47 @@ void Renderer<T, bucketT>::GammaCorrection(tvec4<bucketT, glm::defaultp>& bucket
for (glm::length_t rgbi = 0; rgbi < 3; rgbi++) 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 = 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())
{
a += (1 - alpha) * background[rgbi]; a += (1 - alpha) * background[rgbi];
}
else
{
if (alpha > 0)
a /= alpha;
else
a = 0;
}
if (!scale) if (scale && m_CurvesSet)
{
correctedChannels[rgbi] = accumT(Clamp<bucketT>(a, 0, 1.0));//Early clip, just assign directly.
}
else
{
a *= scaleVal;
if (m_CurvesSet)
CurveAdjust(a, rgbi + 1); CurveAdjust(a, rgbi + 1);
correctedChannels[rgbi] = accumT(Clamp<bucketT>(a, 0, scaleVal));//Final accum, multiply by 255 for 8 bpc (0-255), or 65535 for 16 bpc (0-65535). correctedChannels[rgbi] = accumT(Clamp<bucketT>(a, 0, bt1));//Early clip, just assign directly.
}
} }
if (doAlpha) correctedChannels[3] = accumT(alpha);
{
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.
} }
} }
/// <summary> /// <summary>
/// Setup the curve values when they are being used. /// Setup the curve values when they are being used.
/// This will be either 255 values for bpc=8, or 65535 values for bpc=16.
/// </summary> /// </summary>
/// <param name="scale">Whether to scale from 0-1 to 0-255 or 0-65535</param>
template <typename T, typename bucketT> template <typename T, typename bucketT>
void Renderer<T, bucketT>::ComputeCurves(bool scale) void Renderer<T, bucketT>::ComputeCurves()
{ {
if (m_CurvesSet) if (m_CurvesSet)
{ {
m_Csa.resize(size_t(std::pow(size_t(256), BytesPerChannel()))); Timing t;
auto st = m_Csa.size(); auto st = m_Csa.size();
auto stm1 = st - 1;
T tscale = scale ? T(stm1) : T(1);
for (size_t i = 0; i < st; i++) 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");
} }
} }
/// <summary> /// <summary>
/// Apply the curve adjustment to a single channel. /// Apply the curve adjustment to a single channel.
/// </summary> /// </summary>
/// <param name="aScaled">The value of the channel to apply curve adjustment to, scaled to either 255 or 65535, depending on bpc.</param> /// <param name="aScaled">The value of the channel to apply curve adjustment to.</param>
/// <param name="index">The index of the channel to apply curve adjustment to</param> /// <param name="index">The index of the channel to apply curve adjustment to</param>
template <typename T, typename bucketT> template <typename T, typename bucketT>
void Renderer<T, bucketT>::CurveAdjust(bucketT& aScaled, const glm::length_t& index) void Renderer<T, bucketT>::CurveAdjust(bucketT& a, const glm::length_t& index)
{ {
bucketT stm1 = bucketT(m_Csa.size() - 1); size_t tempIndex = size_t(Clamp<bucketT>(a * CURVES_LENGTH_M1, 0, CURVES_LENGTH_M1));
size_t tempIndex = size_t(Clamp<bucketT>(aScaled, 0, stm1)); size_t tempIndex2 = size_t(Clamp<bucketT>(m_Csa[tempIndex].x * CURVES_LENGTH_M1, 0, CURVES_LENGTH_M1));
size_t tempIndex2 = size_t(Clamp<bucketT>(m_Csa[tempIndex].x, 0, stm1)); a = m_Csa[tempIndex2][index];
aScaled = m_Csa[tempIndex2][index];
} }
//This class had to be implemented in a cpp file because the compiler was breaking. //This class had to be implemented in a cpp file because the compiler was breaking.

View File

@ -66,7 +66,7 @@ public:
virtual bool CreateSpatialFilter(bool& newAlloc) override; virtual bool CreateSpatialFilter(bool& newAlloc) override;
virtual bool CreateTemporalFilter(bool& newAlloc) override; virtual bool CreateTemporalFilter(bool& newAlloc) override;
virtual size_t HistBucketSize() const override { return sizeof(tvec4<bucketT, glm::defaultp>); } virtual size_t HistBucketSize() const override { return sizeof(tvec4<bucketT, glm::defaultp>); }
virtual eRenderStatus Run(vector<byte>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) override; virtual eRenderStatus Run(vector<v4F>& 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; virtual EmberImageComments ImageComments(const EmberStats& stats, size_t printEditDepth = 0, bool hexPalette = true) override;
protected: protected:
@ -76,10 +76,10 @@ protected:
virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true); virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true);
virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false); virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false);
virtual eRenderStatus GaussianDensityFilter(); virtual eRenderStatus GaussianDensityFilter();
virtual eRenderStatus AccumulatorToFinalImage(vector<byte>& pixels, size_t finalOffset); virtual eRenderStatus AccumulatorToFinalImage(vector<v4F>& pixels, size_t finalOffset);
virtual eRenderStatus AccumulatorToFinalImage(byte* pixels, size_t finalOffset); virtual eRenderStatus AccumulatorToFinalImage(v4F* pixels, size_t finalOffset);
virtual EmberStats Iterate(size_t iterCount, size_t temporalSample); virtual EmberStats Iterate(size_t iterCount, size_t temporalSample);
virtual void ComputeCurves(bool scale); virtual void ComputeCurves();
public: public:
//Non-virtual render properties, getters and setters. //Non-virtual render properties, getters and setters.
@ -118,9 +118,7 @@ public:
inline T CenterX() const; inline T CenterX() const;
inline T CenterY() const; inline T CenterY() const;
inline T Rotate() const; inline T Rotate() const;
inline T Hue() const;
inline bucketT Brightness() const; inline bucketT Brightness() const;
inline bucketT Contrast() const;
inline bucketT Gamma() const; inline bucketT Gamma() const;
inline bucketT Vibrancy() const; inline bucketT Vibrancy() const;
inline bucketT GammaThresh() const; inline bucketT GammaThresh() const;
@ -154,8 +152,8 @@ protected:
private: private:
//Miscellaneous non-virtual functions used only in this class. //Miscellaneous non-virtual functions used only in this class.
void Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Point<T>* samples, size_t sampleCount, const Palette<bucketT>* palette); void Accumulate(QTIsaac<ISAAC_SIZE, ISAAC_INT>& rand, Point<T>* samples, size_t sampleCount, const Palette<bucketT>* palette);
/*inline*/ void AddToAccum(const tvec4<bucketT, glm::defaultp>& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj); void AddToAccum(const tvec4<bucketT, glm::defaultp>& bucket, intmax_t i, intmax_t ii, intmax_t j, intmax_t jj);
template <typename accumT> void GammaCorrection(tvec4<bucketT, glm::defaultp>& bucket, Color<bucketT>& background, bucketT g, bucketT linRange, bucketT vibrancy, bool doAlpha, bool scale, accumT* correctedChannels); template <typename accumT> void GammaCorrection(tvec4<bucketT, glm::defaultp>& bucket, Color<bucketT>& background, bucketT g, bucketT linRange, bucketT vibrancy, bool scale, accumT* correctedChannels);
void CurveAdjust(bucketT& a, const glm::length_t& index); void CurveAdjust(bucketT& a, const glm::length_t& index);
void VectorizedLogScale(size_t row, size_t rowEnd); void VectorizedLogScale(size_t row, size_t rowEnd);

View File

@ -171,10 +171,10 @@ bool RendererBase::RandVec(vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>>& randVec)
/// </summary> /// </summary>
/// <param name="pixels">The vector to allocate</param> /// <param name="pixels">The vector to allocate</param>
/// <returns>True if the vector contains enough space to hold the output image</returns> /// <returns>True if the vector contains enough space to hold the output image</returns>
bool RendererBase::PrepFinalAccumVector(vector<byte>& pixels) bool RendererBase::PrepFinalAccumVector(vector<v4F>& pixels)
{ {
EnterResize(); EnterResize();
size_t size = FinalBufferSize(); size_t size = FinalDimensions();
if (m_ReclaimOnResize) if (m_ReclaimOnResize)
{ {
@ -374,27 +374,6 @@ void RendererBase::ReclaimOnResize(bool reclaimOnResize)
ChangeVal([&] { m_ReclaimOnResize = reclaimOnResize; }, eProcessAction::FULL_RENDER); ChangeVal([&] { m_ReclaimOnResize = reclaimOnResize; }, eProcessAction::FULL_RENDER);
} }
/// <summary>
/// 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.
/// </summary>
/// <returns>True if using transparency, else false.</returns>
bool RendererBase::Transparency() const { return m_Transparency; }
/// <summary>
/// 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.
/// </summary>
/// <param name="transparency">True if using transparency, else false.</param>
void RendererBase::Transparency(bool transparency)
{
ChangeVal([&] { m_Transparency = transparency; }, eProcessAction::ACCUM_ONLY);
}
/// <summary> /// <summary>
/// Set the callback object. /// Set the callback object.
/// </summary> /// </summary>
@ -474,40 +453,18 @@ void RendererBase::ThreadCount(size_t threads, const char* seedString)
/// <summary> /// <summary>
/// Get the bytes per channel of the output image. /// Get the bytes per channel of the output image.
/// The only acceptable values are 1 and 2, and 2 is only /// This will always be 4 since each channel is a 32-bit float.
/// used when the output is Png.
/// Default: 1.
/// </summary> /// </summary>
/// <returns></returns> /// <returns>The number of bytes per channel</returns>
size_t RendererBase::BytesPerChannel() const { return m_BytesPerChannel; } size_t RendererBase::BytesPerChannel() const { return m_BytesPerChannel; }
/// <summary> /// <summary>
/// Set the bytes per channel of the output image. /// Get the number of channels per pixel in the output image.
/// The only acceptable values are 1 and 2, and 2 is only /// This will always be 4 since each pixel is always RGBA.
/// used when the output is Png.
/// Set the render state to ACCUM_ONLY.
/// </summary>
/// <param name="bytesPerChannel">The bytes per channel.</param>
void RendererBase::BytesPerChannel(size_t bytesPerChannel)
{
ChangeVal([&]
{
if (bytesPerChannel == 0 || bytesPerChannel > 2)
m_BytesPerChannel = 1;
else
m_BytesPerChannel = bytesPerChannel;
}, eProcessAction::ACCUM_ONLY);
}
/// <summary>
/// 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.
/// </summary> /// </summary>
/// <returns>The number of channels per pixel in the output image</returns> /// <returns>The number of channels per pixel in the output image</returns>
size_t RendererBase::NumChannels() const { return m_NumChannels; } size_t RendererBase::NumChannels() const { return m_NumChannels; }
/// <summary> /// <summary>
/// Get/set the priority used for the CPU rendering threads. /// Get/set the priority used for the CPU rendering threads.
/// This does not affect OpenCL rendering. /// This does not affect OpenCL rendering.
@ -543,18 +500,6 @@ void RendererBase::InteractiveFilter(eInteractiveFilter filter)
/// Virtual render properties, getters and setters. /// Virtual render properties, getters and setters.
/// </summary> /// </summary>
/// <summary>
/// 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.
/// </summary>
/// <param name="numChannels">The number of channels per pixel in the output image</param>
void RendererBase::NumChannels(size_t numChannels)
{
ChangeVal([&] { m_NumChannels = numChannels; }, eProcessAction::ACCUM_ONLY);
}
/// <summary> /// <summary>
/// Get the number of threads used when rendering. /// Get the number of threads used when rendering.
/// Default: use all avaliable cores. /// Default: use all avaliable cores.

View File

@ -105,7 +105,7 @@ public:
size_t HistMemoryRequired(size_t strips); size_t HistMemoryRequired(size_t strips);
pair<size_t, size_t> MemoryRequired(size_t strips, bool includeFinal, bool threadedWrite); pair<size_t, size_t> MemoryRequired(size_t strips, bool includeFinal, bool threadedWrite);
vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> RandVec(); vector<QTIsaac<ISAAC_SIZE, ISAAC_INT>> RandVec();
bool PrepFinalAccumVector(vector<byte>& pixels); bool PrepFinalAccumVector(vector<v4F>& pixels);
//Virtual processing functions. //Virtual processing functions.
virtual bool Ok() const; virtual bool Ok() const;
@ -121,7 +121,7 @@ public:
virtual void ComputeBounds() = 0; virtual void ComputeBounds() = 0;
virtual void ComputeQuality() = 0; virtual void ComputeQuality() = 0;
virtual void ComputeCamera() = 0; virtual void ComputeCamera() = 0;
virtual eRenderStatus Run(vector<byte>& finalImage, double time = 0, size_t subBatchCountOverride = 0, bool forceOutput = false, size_t finalOffset = 0) = 0; virtual eRenderStatus Run(vector<v4F>& 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 EmberImageComments ImageComments(const EmberStats& stats, size_t printEditDepth = 0, bool hexPalette = true) = 0;
virtual DensityFilterBase* GetDensityFilter() = 0; virtual DensityFilterBase* GetDensityFilter() = 0;
@ -152,12 +152,9 @@ public:
void InsertPalette(bool insertPalette); void InsertPalette(bool insertPalette);
bool ReclaimOnResize() const; bool ReclaimOnResize() const;
void ReclaimOnResize(bool reclaimOnResize); void ReclaimOnResize(bool reclaimOnResize);
bool Transparency() const;
void Transparency(bool transparency);
void Callback(RenderCallback* callback); void Callback(RenderCallback* callback);
void ThreadCount(size_t threads, const char* seedString = nullptr); void ThreadCount(size_t threads, const char* seedString = nullptr);
size_t BytesPerChannel() const; size_t BytesPerChannel() const;
void BytesPerChannel(size_t bytesPerChannel);
size_t NumChannels() const; size_t NumChannels() const;
eThreadPriority Priority() const; eThreadPriority Priority() const;
void Priority(eThreadPriority priority); void Priority(eThreadPriority priority);
@ -165,7 +162,6 @@ public:
void InteractiveFilter(eInteractiveFilter filter); void InteractiveFilter(eInteractiveFilter filter);
//Virtual render properties, getters and setters. //Virtual render properties, getters and setters.
virtual void NumChannels(size_t numChannels);
virtual size_t ThreadCount() const; virtual size_t ThreadCount() const;
virtual eRendererType RendererType() const; virtual eRendererType RendererType() const;
@ -200,7 +196,6 @@ public:
protected: protected:
bool m_EarlyClip = false; bool m_EarlyClip = false;
bool m_YAxisUp = false; bool m_YAxisUp = false;
bool m_Transparency = false;
bool m_LockAccum = false; bool m_LockAccum = false;
bool m_InRender = false; bool m_InRender = false;
bool m_InFinalAccum = false; bool m_InFinalAccum = false;
@ -213,8 +208,8 @@ protected:
size_t m_SuperSize = 0; size_t m_SuperSize = 0;
size_t m_GutterWidth; size_t m_GutterWidth;
size_t m_DensityFilterOffset; size_t m_DensityFilterOffset;
size_t m_NumChannels = 3; size_t m_NumChannels = 4;
size_t m_BytesPerChannel = 1; size_t m_BytesPerChannel = 4;
size_t m_ThreadsToUse; size_t m_ThreadsToUse;
size_t m_VibGamCount; size_t m_VibGamCount;
size_t m_LastTemporalSample = 0; size_t m_LastTemporalSample = 0;

View File

@ -836,7 +836,6 @@ public:
/// <returns>The percentage possible color values that were present in the final output image</returns> /// <returns>The percentage possible color values that were present in the final output image</returns>
T TryColors(Ember<T>& ember, size_t colorResolution) T TryColors(Ember<T>& ember, size_t colorResolution)
{ {
byte* p;
size_t i, hits = 0, res = colorResolution; size_t i, hits = 0, res = colorResolution;
size_t pixTotal, res3 = res * res * res; size_t pixTotal, res3 = res * res * res;
T scalar; T scalar;
@ -862,14 +861,12 @@ public:
m_Hist.resize(res3); m_Hist.resize(res3);
Memset(m_Hist); Memset(m_Hist);
p = m_FinalImage.data(); auto p = m_FinalImage.data();
for (i = 0; i < m_Renderer->FinalDimensions(); i++) for (i = 0; i < m_Renderer->FinalDimensions(); i++)
{ {
m_Hist[(p[0] * res / 256) + 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[1] * res / 256) * res + p++;
(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.
} }
for (i = 0; i < res3; i++) for (i = 0; i < res3; i++)
@ -1352,7 +1349,7 @@ private:
string m_Comment; string m_Comment;
vector<Point<T>> m_Samples; vector<Point<T>> m_Samples;
vector<byte> m_FinalImage; vector<v4F> m_FinalImage;
vector<uint> m_Hist; vector<uint> m_Hist;
EmberToXml<T> m_EmberToXml; EmberToXml<T> m_EmberToXml;
Iterator<T>* m_Iterator; Iterator<T>* m_Iterator;

View File

@ -30,8 +30,7 @@ bool EmberAnimate(EmberOptions& opt)
//Regular variables. //Regular variables.
Timing t; Timing t;
bool unsorted = false; bool unsorted = false;
uint channels; uint padding;
streamsize padding;
size_t i, firstUnsortedIndex = 0; size_t i, firstUnsortedIndex = 0;
string inputPath = GetPath(opt.Input()); string inputPath = GetPath(opt.Input());
vector<Ember<T>> embers; vector<Ember<T>> embers;
@ -99,12 +98,6 @@ bool EmberAnimate(EmberOptions& opt)
renderers[i]->ThreadCount(opt.ThreadCount(), ns.c_str()); 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 else
{ {
@ -153,24 +146,19 @@ bool EmberAnimate(EmberOptions& opt)
return false; return false;
} }
if (opt.Format() != "jpg" && if (!Find(opt.Format(), "jpg") &&
opt.Format() != "png" && !Find(opt.Format(), "png") &&
opt.Format() != "bmp") #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"; #ifdef _WIN32
} cout << "Format must be bmp, jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n";
#else
channels = opt.Format() == "png" ? 4 : 3; cout << "Format must be jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n";
#endif
if (opt.BitsPerChannel() == 16 && opt.Format() != "png") 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);
} }
if (opt.AspectRatio() < 0) if (opt.AspectRatio() < 0)
@ -262,7 +250,7 @@ bool EmberAnimate(EmberOptions& opt)
} }
//Cast to double in case the value exceeds 2^32. //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(ember.m_FinalRasH) * double(renderers[0]->BytesPerChannel());
double maxMem = pow(2.0, double((sizeof(void*) * 8) - 1)); double maxMem = pow(2.0, double((sizeof(void*) * 8) - 1));
@ -316,46 +304,131 @@ bool EmberAnimate(EmberOptions& opt)
r->YAxisUp(opt.YAxisUp()); r->YAxisUp(opt.YAxisUp());
r->LockAccum(opt.LockAccum()); r->LockAccum(opt.LockAccum());
r->PixelAspectRatio(T(opt.AspectRatio())); r->PixelAspectRatio(T(opt.AspectRatio()));
r->Transparency(opt.Transparency());
r->NumChannels(channels);
r->BytesPerChannel(opt.BitsPerChannel() / 8);
r->Priority(eThreadPriority(Clamp<intmax_t>(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST)))); r->Priority(eThreadPriority(Clamp<intmax_t>(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST))));
} }
std::function<void (vector<byte>&, string, EmberImageComments, size_t, size_t, size_t)> saveFunc = [&](vector<byte>& finalImage, std::function<void (vector<v4F>&, string, EmberImageComments, size_t, size_t, size_t)> saveFunc = [&](vector<v4F>& finalImage,
string filename,//These are copies because this will be launched in a thread. string baseFilename,//These are copies because this will be launched in a thread.
EmberImageComments comments, EmberImageComments comments,
size_t w, size_t w,
size_t h, size_t h,
size_t chan) size_t chan)
{ {
bool writeSuccess = false; auto finalImagep = finalImage.data();
byte* 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<byte> rgb8Image;
vector<std::thread> writeFileThreads;
writeFileThreads.reserve(5);
if ((opt.Format() == "jpg" || opt.Format() == "bmp") && chan == 4) if (doBmp || doJpg)
RgbaToRgb(finalImage, finalImage, w, h); {
rgb8Image.resize(size * 3);
Rgba32ToRgb8(finalImagep, rgb8Image.data(), w, h);
if (opt.Format() == "png") if (doBmp)
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") writeFileThreads.push_back(std::thread([&]()
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") auto fn = baseFilename + ".bmp";
writeSuccess = WriteBmp(filename.c_str(), finalImagep, w, h); VerbosePrint("Writing " + fn);
auto writeSuccess = WriteBmp(fn.c_str(), rgb8Image.data(), w, h);
if (!writeSuccess) if (!writeSuccess)
cout << "Error writing " << filename << "\n"; 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<byte> 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<glm::uint16> 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<Rgba> 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()); atomfTime.store(opt.FirstFrame());
std::function<void(size_t)> iterFunc = [&](size_t index) std::function<void(size_t)> iterFunc = [&](size_t index)
{ {
size_t ftime, finalImageIndex = 0; size_t ftime, finalImageIndex = 0;
string filename, flameName;
RendererBase* renderer = renderers[index].get(); RendererBase* renderer = renderers[index].get();
ostringstream fnstream, os; ostringstream os;
EmberStats stats; EmberStats stats;
EmberImageComments comments; EmberImageComments comments;
Ember<T> centerEmber; Ember<T> centerEmber;
vector<byte> finalImages[2]; vector<v4F> finalImages[2];
std::thread writeThread; std::thread writeThread;
os.imbue(std::locale("")); os.imbue(std::locale(""));
@ -390,13 +463,9 @@ bool EmberAnimate(EmberOptions& opt)
break; 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()) 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()) if (opt.Verbose())
{ {
@ -425,21 +494,21 @@ bool EmberAnimate(EmberOptions& opt)
cout << "Render time: " << t.Format(stats.m_RenderMs) << "\n"; cout << "Render time: " << t.Format(stats.m_RenderMs) << "\n";
cout << "Pure iter time: " << t.Format(stats.m_IterMs) << "\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 << "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 //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. //when running with OpenCL. Call join() to ensure the previous thread call has completed.
Join(writeThread); Join(writeThread);
auto threadVecIndex = finalImageIndex;//Cache before launching thread. 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. finalImageIndex ^= 1;//Toggle the index.
} }
else 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. Join(writeThread);//One final check to make sure all writing is done before exiting this thread.

View File

@ -147,13 +147,14 @@ static const char* CalcAlphaFunctionString =
/// during final accumulation, which only takes floats. /// during final accumulation, which only takes floats.
/// </summary> /// </summary>
static const char* CurveAdjustFunctionString = 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" "{\n"
" uint tempIndex = (uint)clamp(*a * (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((float)csa[tempIndex].m_Real4.x * (float)COLORMAP_LENGTH_MINUS_1, (float)0.0, (float)COLORMAP_LENGTH_MINUS_1);\n" " uint tempIndex2 = (uint)clamp(csa[tempIndex].m_Real4.x * CURVES_LENGTH_M1, 0.0f, CURVES_LENGTH_M1);\n"
"\n" "\n"
" *a = (float)csa[tempIndex2].m_Reals[index];\n" " *a = (float)csa[tempIndex2].m_Reals[index];\n"
"}\n"; "}\n"
"\n";
/// <summary> /// <summary>
/// Use MWC 64 from David Thomas at the Imperial College of London for /// Use MWC 64 from David Thomas at the Imperial College of London for

View File

@ -85,6 +85,8 @@ static string ConstantDefinesString(bool doublePrecision)
"#define SQRT5 2.2360679774997896964091736687313\n" "#define SQRT5 2.2360679774997896964091736687313\n"
"#define M_PHI 1.61803398874989484820458683436563\n" "#define M_PHI 1.61803398874989484820458683436563\n"
"#define DEG_2_RAD (MPI / 180)\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" "\n"
"//Index in each dimension of a thread within a block.\n" "//Index in each dimension of a thread within a block.\n"
"#define THREAD_ID_X (get_local_id(0))\n" "#define THREAD_ID_X (get_local_id(0))\n"
@ -314,10 +316,7 @@ struct ALIGN SpatialFilterCL
uint m_FinalRasH; uint m_FinalRasH;
uint m_Supersample; uint m_Supersample;
uint m_FilterWidth; uint m_FilterWidth;
uint m_NumChannels;
uint m_BytesPerChannel;
uint m_DensityFilterOffset; uint m_DensityFilterOffset;
uint m_Transparency;
uint m_YAxisUp; uint m_YAxisUp;
T m_Vibrancy; T m_Vibrancy;
T m_HighlightPower; T m_HighlightPower;
@ -339,10 +338,7 @@ static const char* SpatialFilterCLStructString =
" uint m_FinalRasH;\n" " uint m_FinalRasH;\n"
" uint m_Supersample;\n" " uint m_Supersample;\n"
" uint m_FilterWidth;\n" " uint m_FilterWidth;\n"
" uint m_NumChannels;\n"
" uint m_BytesPerChannel;\n"
" uint m_DensityFilterOffset;\n" " uint m_DensityFilterOffset;\n"
" uint m_Transparency;\n"
" uint m_YAxisUp;\n" " uint m_YAxisUp;\n"
" real_bucket_t m_Vibrancy;\n" " real_bucket_t m_Vibrancy;\n"
" real_bucket_t m_HighlightPower;\n" " real_bucket_t m_HighlightPower;\n"

View File

@ -10,166 +10,58 @@ namespace EmberCLns
FinalAccumOpenCLKernelCreator::FinalAccumOpenCLKernelCreator(bool doublePrecision) FinalAccumOpenCLKernelCreator::FinalAccumOpenCLKernelCreator(bool doublePrecision)
{ {
m_DoublePrecision = doublePrecision; m_DoublePrecision = doublePrecision;
m_GammaCorrectionWithAlphaCalcKernel = CreateGammaCorrectionKernelString(true); m_GammaCorrectionWithoutAlphaCalcKernel = CreateGammaCorrectionKernelString();
m_GammaCorrectionWithoutAlphaCalcKernel = CreateGammaCorrectionKernelString(false); m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(true);
m_FinalAccumEarlyClipKernel = CreateFinalAccumKernelString(true, false, false); m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(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);
} }
/// <summary> /// <summary>
/// Kernel source and entry point properties, getters only. /// Kernel source and entry point properties, getters only.
/// </summary> /// </summary>
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::FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel() const { return m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel; }
const string& FinalAccumOpenCLKernelCreator::FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const { return m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint; } 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::FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel() const { return m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel; }
const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const { return m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint; } const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const { return m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint; }
/// <summary> const string& FinalAccumOpenCLKernelCreator::GammaCorrectionEntryPoint() const { return m_GammaCorrectionWithoutAlphaCalcEntryPoint; }
/// Get the gamma correction entry point. const string& FinalAccumOpenCLKernelCreator::GammaCorrectionKernel() const { return m_GammaCorrectionWithoutAlphaCalcKernel; }
/// </summary>
/// <param name="channels">The number of channels used, 3 or 4.</param>
/// <param name="transparency">True if channels equals 4 and using transparency, else false.</param>
/// <returns>The name of the gamma correction entry point kernel function</returns>
const string& FinalAccumOpenCLKernelCreator::GammaCorrectionEntryPoint(size_t channels, bool transparency) const
{
bool alphaCalc = ((channels > 3) && transparency);
return alphaCalc ? m_GammaCorrectionWithAlphaCalcEntryPoint : m_GammaCorrectionWithoutAlphaCalcEntryPoint;
}
/// <summary>
/// Get the gamma correction kernel string.
/// </summary>
/// <param name="channels">The number of channels used, 3 or 4.</param>
/// <param name="transparency">True if channels equals 4 and using transparency, else false.</param>
/// <returns>The gamma correction kernel string</returns>
const string& FinalAccumOpenCLKernelCreator::GammaCorrectionKernel(size_t channels, bool transparency) const
{
bool alphaCalc = ((channels > 3) && transparency);
return alphaCalc ? m_GammaCorrectionWithAlphaCalcKernel : m_GammaCorrectionWithoutAlphaCalcKernel;
}
/// <summary> /// <summary>
/// Get the final accumulation entry point. /// Get the final accumulation entry point.
/// </summary> /// </summary>
/// <param name="earlyClip">True if early clip is desired, else false.</param> /// <param name="earlyClip">True if early clip is desired, else false.</param>
/// <param name="channels">The number of channels used, 3 or 4.</param>
/// <param name="transparency">True if channels equals 4 and using transparency, else false.</param>
/// <param name="alphaBase">Storage for the alpha base value used in the kernel. 0 if transparency is true, else 255.</param>
/// <param name="alphaScale">Storage for the alpha scale value used in the kernel. 255 if transparency is true, else 0.</param>
/// <returns>The name of the final accumulation entry point kernel function</returns> /// <returns>The name of the final accumulation entry point kernel function</returns>
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 (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(); return FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint();
else else
return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense.
}
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(); return FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint();
else
return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense.
}
} }
/// <summary> /// <summary>
/// Get the final accumulation kernel string. /// Get the final accumulation kernel string.
/// </summary> /// </summary>
/// <param name="earlyClip">True if early clip is desired, else false.</param> /// <param name="earlyClip">True if early clip is desired, else false.</param>
/// <param name="channels">The number of channels used, 3 or 4.</param>
/// <param name="transparency">True if channels equals 4 and using transparency, else false.</param>
/// <returns>The final accumulation kernel string</returns> /// <returns>The final accumulation kernel string</returns>
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 (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(); return FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel();
else else
return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense.
}
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(); return FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel();
else
return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense.
}
}
/// <summary>
/// Wrapper around CreateFinalAccumKernelString().
/// </summary>
/// <param name="earlyClip">True if early clip is desired, else false.</param>
/// <param name="channels">The number of channels used, 3 or 4.</param>
/// <param name="transparency">True if channels equals 4 and using transparency, else false.</param>
/// <returns>The final accumulation kernel string</returns>
string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyClip, size_t channels, bool transparency)
{
return CreateFinalAccumKernelString(earlyClip, (channels > 3 && transparency), channels > 3);
} }
/// <summary> /// <summary>
/// Create the final accumulation kernel string /// Create the final accumulation kernel string
/// </summary> /// </summary>
/// <param name="earlyClip">True if early clip is desired, else false.</param> /// <param name="earlyClip">True if early clip is desired, else false.</param>
/// <param name="alphaCalc">True if channels equals 4 and transparency is desired, else false.</param>
/// <param name="alphaAccum">True if channels equals 4</param>
/// <returns>The final accumulation kernel string</returns> /// <returns>The final accumulation kernel string</returns>
string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyClip, bool alphaCalc, bool alphaAccum) string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyClip)
{ {
ostringstream os; ostringstream os;
string channels = alphaAccum ? "4" : "3";
os << os <<
ConstantDefinesString(m_DoublePrecision) << ConstantDefinesString(m_DoublePrecision) <<
UnionCLStructString << UnionCLStructString <<
@ -181,29 +73,14 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
if (earlyClip) 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"; os << "__kernel void " << m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint << "(\n";
else
return "";//Cannot have alphaCalc and !alphaAccum, it makes no sense.
} }
else else
{ {
os << os <<
CreateCalcNewRgbFunctionString(false) << CreateCalcNewRgbFunctionString(false) <<
CreateGammaCorrectionFunctionString(false, alphaCalc, alphaAccum, true); CreateGammaCorrectionFunctionString(false, true) <<
"__kernel void " << m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint << "(\n";
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.
} }
os << os <<
@ -211,10 +88,8 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
" __write_only image2d_t pixels,\n" " __write_only image2d_t pixels,\n"
" __constant SpatialFilterCL* spatialFilter,\n" " __constant SpatialFilterCL* spatialFilter,\n"
" __constant real_bucket_t* filterCoefs,\n" " __constant real_bucket_t* filterCoefs,\n"
" __constant real4reals_bucket* csa,\n" " __global real4reals_bucket* csa,\n"
" const uint doCurves,\n" " const uint doCurves\n"
" const real_bucket_t alphaBase,\n"
" const real_bucket_t alphaScale\n"
"\t)\n" "\t)\n"
"{\n" "{\n"
"\n" "\n"
@ -249,21 +124,13 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
" }\n" " }\n"
"\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. 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 << 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.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.y = (float)newBucket.m_Real4.y;\n"
" finalColor.m_Float4.z = (float)newBucket.m_Real4.z;\n"; " finalColor.m_Float4.z = (float)newBucket.m_Real4.z;\n"
" finalColor.m_Float4.w = (float)newBucket.m_Real4.w;\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";
}
} }
else else
{ {
@ -273,7 +140,7 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
os << os <<
" real4reals_bucket realFinal;\n" " real4reals_bucket realFinal;\n"
"\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.x = (float)realFinal.m_Real4.x;\n"
" finalColor.m_Float4.y = (float)realFinal.m_Real4.y;\n" " finalColor.m_Float4.y = (float)realFinal.m_Real4.y;\n"
" finalColor.m_Float4.z = (float)realFinal.m_Real4.z;\n" " finalColor.m_Float4.z = (float)realFinal.m_Real4.z;\n"
@ -283,7 +150,7 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
else else
{ {
os << 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,20 +175,17 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
/// This is not a full kernel, just a function that is used in the kernels. /// This is not a full kernel, just a function that is used in the kernels.
/// </summary> /// </summary>
/// <param name="globalBucket">True if writing to a global buffer (early clip), else false (late clip).</param> /// <param name="globalBucket">True if writing to a global buffer (early clip), else false (late clip).</param>
/// <param name="alphaCalc">True if channels equals 4 and transparency is desired, else false.</param>
/// <param name="alphaAccum">True if channels equals 4</param>
/// <param name="finalOut">True if writing to global buffer (late clip), else false (early clip).</param> /// <param name="finalOut">True if writing to global buffer (late clip), else false (early clip).</param>
/// <returns>The gamma correction function string</returns> /// <returns>The gamma correction function string</returns>
string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionFunctionString(bool globalBucket, bool alphaCalc, bool alphaAccum, bool finalOut) string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionFunctionString(bool globalBucket, bool finalOut)
{ {
ostringstream os; ostringstream os;
string dataType; string dataType;
string unionMember; string unionMember;
dataType = "real_bucket_t"; dataType = "real_bucket_t";
//Use real_t for all cases, early clip and final accum. //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 << "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 os << "{\n"
<< "{\n"
<< " real_bucket_t alpha, ls, tmp, a;\n" << " real_bucket_t alpha, ls, tmp, a;\n"
<< " real4reals_bucket newRgb;\n" << " real4reals_bucket newRgb;\n"
<< "\n" << "\n"
@ -343,46 +207,13 @@ string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionFunctionString(bool g
<< " for (uint rgbi = 0; rgbi < 3; rgbi++)\n" << " for (uint rgbi = 0; rgbi < 3; rgbi++)\n"
<< " {\n" << " {\n"
<< " a = newRgb.m_Reals[rgbi] + ((1.0 - vibrancy) * pow(fabs(bucket->m_Reals[rgbi]), g));\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"; << "\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";
return os.str(); return os.str();
} }
@ -451,9 +282,8 @@ string FinalAccumOpenCLKernelCreator::CreateCalcNewRgbFunctionString(bool global
/// <summary> /// <summary>
/// Create the gamma correction kernel string used for early clipping. /// Create the gamma correction kernel string used for early clipping.
/// </summary> /// </summary>
/// <param name="alphaCalc">True if channels equals 4 and transparency is desired, else false.</param>
/// <returns>The gamma correction kernel string used for early clipping</returns> /// <returns>The gamma correction kernel string used for early clipping</returns>
string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString(bool alphaCalc) string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString()
{ {
ostringstream os; ostringstream os;
string dataType; string dataType;
@ -465,8 +295,8 @@ string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString(bool alp
CalcAlphaFunctionString << CalcAlphaFunctionString <<
CreateCalcNewRgbFunctionString(true) << CreateCalcNewRgbFunctionString(true) <<
SpatialFilterCLStructString << SpatialFilterCLStructString <<
CreateGammaCorrectionFunctionString(true, alphaCalc, true, false);//Will only be used with float in this case, early clip. Will always alpha accum. CreateGammaCorrectionFunctionString(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" << os << "__kernel void " << m_GammaCorrectionWithoutAlphaCalcEntryPoint << "(\n" <<
" __global real4reals_bucket* accumulator,\n" " __global real4reals_bucket* accumulator,\n"
" __constant SpatialFilterCL* spatialFilter\n" " __constant SpatialFilterCL* spatialFilter\n"
")\n" ")\n"
@ -478,8 +308,7 @@ string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString(bool alp
"\n" "\n"
" uint superIndex = (GLOBAL_ID_Y * spatialFilter->m_SuperRasW) + GLOBAL_ID_X;\n" " uint superIndex = (GLOBAL_ID_Y * spatialFilter->m_SuperRasW) + GLOBAL_ID_X;\n"
" __global real4reals_bucket* bucket = accumulator + superIndex;\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, &(bucket->m_Reals[0]));\n"
" 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"
"}\n" "}\n"
; ;
return os.str(); return os.str();

View File

@ -17,64 +17,35 @@ namespace EmberCLns
/// with all conditionals and unnecessary calculations stripped out. /// with all conditionals and unnecessary calculations stripped out.
/// The conditionals are: /// The conditionals are:
/// Early clip/late clip /// Early clip/late clip
/// Alpha channel, no alpha channel
/// Alpha with/without transparency
/// </summary> /// </summary>
class EMBERCL_API FinalAccumOpenCLKernelCreator class EMBERCL_API FinalAccumOpenCLKernelCreator
{ {
public: public:
FinalAccumOpenCLKernelCreator(bool doublePrecision); 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& FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel() const;
const string& FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint() 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& FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel() const;
const string& FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const; const string& FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const;
const string& GammaCorrectionEntryPoint(size_t channels, bool transparency) const; const string& GammaCorrectionEntryPoint() const;
const string& GammaCorrectionKernel(size_t channels, bool transparency) const; const string& GammaCorrectionKernel() const;
const string& FinalAccumEntryPoint(bool earlyClip, size_t channels, bool transparency, double& alphaBase, double& alphaScale) const; const string& FinalAccumEntryPoint(bool earlyClip) const;
const string& FinalAccumKernel(bool earlyClip, size_t channels, bool transparency) const; const string& FinalAccumKernel(bool earlyClip) const;
private: private:
string CreateFinalAccumKernelString(bool earlyClip, size_t channels, bool transparency); string CreateFinalAccumKernelString(bool earlyClip);
string CreateGammaCorrectionKernelString(bool alphaCalc); string CreateGammaCorrectionKernelString();
string CreateFinalAccumKernelString(bool earlyClip, bool alphaCalc, bool alphaAccum); string CreateGammaCorrectionFunctionString(bool globalBucket, bool finalOut);
string CreateGammaCorrectionFunctionString(bool globalBucket, bool alphaCalc, bool alphaAccum, bool finalOut);
string CreateCalcNewRgbFunctionString(bool globalBucket); string CreateCalcNewRgbFunctionString(bool globalBucket);
string m_GammaCorrectionWithAlphaCalcKernel;
string m_GammaCorrectionWithAlphaCalcEntryPoint = "GammaCorrectionWithAlphaCalcKernel";
string m_GammaCorrectionWithoutAlphaCalcKernel; string m_GammaCorrectionWithoutAlphaCalcKernel;
string m_GammaCorrectionWithoutAlphaCalcEntryPoint = "GammaCorrectionWithoutAlphaCalcKernel"; string m_GammaCorrectionWithoutAlphaCalcEntryPoint = "GammaCorrectionWithoutAlphaCalcKernel";
string m_FinalAccumEarlyClipKernel;//False, false. string m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel;
string m_FinalAccumEarlyClipEntryPoint = "FinalAccumEarlyClipKernel";
string m_FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel;//True, true.
string m_FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumEntryPoint = "FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel";
string m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel;//False, true.
string m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint = "FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel"; string m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint = "FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel";
string m_FinalAccumLateClipKernel;//False, false. string m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel;
string m_FinalAccumLateClipEntryPoint = "FinalAccumLateClipKernel";
string m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel;//True, true.
string m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint = "FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel";
string m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel;//False, true.
string m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint = "FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel"; string m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint = "FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel";
string m_Empty; string m_Empty;

View File

@ -40,7 +40,6 @@ void RendererCL<T, bucketT>::Init()
{ {
m_Init = false; m_Init = false;
m_DoublePrecision = typeid(T) == typeid(double); m_DoublePrecision = typeid(T) == typeid(double);
m_NumChannels = 4;
//Buffer names. //Buffer names.
m_EmberBufferName = "Ember"; m_EmberBufferName = "Ember";
m_XformsBufferName = "Xforms"; m_XformsBufferName = "Xforms";
@ -73,7 +72,7 @@ void RendererCL<T, bucketT>::Init()
m_PaletteFormat.image_channel_order = CL_RGBA; m_PaletteFormat.image_channel_order = CL_RGBA;
m_PaletteFormat.image_channel_data_type = CL_FLOAT; m_PaletteFormat.image_channel_data_type = CL_FLOAT;
m_FinalFormat.image_channel_order = CL_RGBA; 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;
} }
/// <summary> /// <summary>
@ -386,7 +385,7 @@ const string& RendererCL<T, bucketT>::DEKernel() const { return m_DEOpenCLKernel
/// </summary> /// </summary>
/// <returns>The string representation of the kernel for the last built final accumulation program.</returns> /// <returns>The string representation of the kernel for the last built final accumulation program.</returns>
template <typename T, typename bucketT> template <typename T, typename bucketT>
const string& RendererCL<T, bucketT>::FinalAccumKernel() const { return m_FinalAccumOpenCLKernelCreator.FinalAccumKernel(EarlyClip(), Renderer<T, bucketT>::NumChannels(), Transparency()); } const string& RendererCL<T, bucketT>::FinalAccumKernel() const { return m_FinalAccumOpenCLKernelCreator.FinalAccumKernel(EarlyClip()); }
/// <summary> /// <summary>
/// Get the a const referece to the devices this renderer will use. /// Get the a const referece to the devices this renderer will use.
@ -407,7 +406,7 @@ const vector<unique_ptr<RendererClDevice>>& RendererCL<T, bucketT>::Devices() co
/// <param name="pixels">The host side buffer to read into</param> /// <param name="pixels">The host side buffer to read into</param>
/// <returns>True if success, else false.</returns> /// <returns>True if success, else false.</returns>
template <typename T, typename bucketT> template <typename T, typename bucketT>
bool RendererCL<T, bucketT>::ReadFinal(byte* pixels) bool RendererCL<T, bucketT>::ReadFinal(v4F* pixels)
{ {
if (pixels && !m_Devices.empty()) if (pixels && !m_Devices.empty())
return m_Devices[0]->m_Wrapper.ReadImage(m_FinalImageName, FinalRasW(), FinalRasH(), 0, m_Devices[0]->m_Wrapper.Shared(), pixels); 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<T, bucketT>::ReadFinal(byte* pixels)
template <typename T, typename bucketT> template <typename T, typename bucketT>
bool RendererCL<T, bucketT>::ClearFinal() bool RendererCL<T, bucketT>::ClearFinal()
{ {
vector<byte> v; vector<v4F> v;
if (!m_Devices.empty()) if (!m_Devices.empty())
{ {
@ -470,17 +469,6 @@ bool RendererCL<T, bucketT>::Ok() const
return !m_Devices.empty() && m_Init; return !m_Devices.empty() && m_Init;
} }
/// <summary>
/// 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.
/// </summary>
/// <param name="numChannels">The number of channels, ignored.</param>
template <typename T, typename bucketT>
void RendererCL<T, bucketT>::NumChannels(size_t numChannels)
{
m_NumChannels = 4;
}
/// <summary> /// <summary>
/// Clear the error report for this class as well as the OpenCLWrapper members of each device. /// Clear the error report for this class as well as the OpenCLWrapper members of each device.
/// </summary> /// </summary>
@ -771,7 +759,7 @@ eRenderStatus RendererCL<T, bucketT>::GaussianDensityFilter()
/// <param name="finalOffset">Offset in the buffer to store the pixels to</param> /// <param name="finalOffset">Offset in the buffer to store the pixels to</param>
/// <returns>True if success and not aborted, else false.</returns> /// <returns>True if success and not aborted, else false.</returns>
template <typename T, typename bucketT> template <typename T, typename bucketT>
eRenderStatus RendererCL<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t finalOffset) eRenderStatus RendererCL<T, bucketT>::AccumulatorToFinalImage(v4F* pixels, size_t finalOffset)
{ {
auto status = RunFinalAccum(); auto status = RunFinalAccum();
@ -877,17 +865,6 @@ EmberStats RendererCL<T, bucketT>::Iterate(size_t iterCount, size_t temporalSamp
return stats; return stats;
} }
/// <summary>
/// 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.
/// </summary>
/// <param name="scale">Ignored</param>
template <typename T, typename bucketT>
void RendererCL<T, bucketT>::ComputeCurves(bool scale)
{
Renderer<T, bucketT>::ComputeCurves(false);
}
/// <summary> /// <summary>
/// Private functions for making and running OpenCL programs. /// Private functions for making and running OpenCL programs.
/// </summary> /// </summary>
@ -1304,9 +1281,7 @@ eRenderStatus RendererCL<T, bucketT>::RunFinalAccum()
{ {
//Timing t(4); //Timing t(4);
bool b = true; bool b = true;
double alphaBase; int accumKernelIndex = MakeAndGetFinalAccumProgram();
double alphaScale;
int accumKernelIndex = MakeAndGetFinalAccumProgram(alphaBase, alphaScale);
cl_uint argIndex; cl_uint argIndex;
size_t gridW; size_t gridW;
size_t gridH; size_t gridH;
@ -1373,10 +1348,6 @@ eRenderStatus RendererCL<T, bucketT>::RunFinalAccum()
if (b && !(b = wrapper.SetArg (accumKernelIndex, argIndex++, curvesSet))) { AddToReport(loc); }//Do curves. 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 && wrapper.Shared())
if (b && !(b = wrapper.EnqueueAcquireGLObjects(m_FinalImageName))) { AddToReport(loc); } if (b && !(b = wrapper.EnqueueAcquireGLObjects(m_FinalImageName))) { AddToReport(loc); }
@ -1534,26 +1505,22 @@ int RendererCL<T, bucketT>::MakeAndGetDensityFilterProgram(size_t ss, uint filte
/// <summary> /// <summary>
/// Make the final accumulation on the primary device program and return its index. /// 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.
/// </summary> /// </summary>
/// <param name="alphaBase">Storage for the alpha base value used in the kernel. 0 if transparency is true, else 255.</param>
/// <param name="alphaScale">Storage for the alpha scale value used in the kernel. 255 if transparency is true, else 0.</param>
/// <returns>The kernel index if successful, else -1.</returns> /// <returns>The kernel index if successful, else -1.</returns>
template <typename T, typename bucketT> template <typename T, typename bucketT>
int RendererCL<T, bucketT>::MakeAndGetFinalAccumProgram(double& alphaBase, double& alphaScale) int RendererCL<T, bucketT>::MakeAndGetFinalAccumProgram()
{ {
int kernelIndex = -1; int kernelIndex = -1;
if (!m_Devices.empty()) if (!m_Devices.empty())
{ {
auto& wrapper = m_Devices[0]->m_Wrapper; auto& wrapper = m_Devices[0]->m_Wrapper;
auto& finalAccumEntryPoint = m_FinalAccumOpenCLKernelCreator.FinalAccumEntryPoint(EarlyClip(), Renderer<T, bucketT>::NumChannels(), Transparency(), alphaBase, alphaScale); auto& finalAccumEntryPoint = m_FinalAccumOpenCLKernelCreator.FinalAccumEntryPoint(EarlyClip());
const char* loc = __FUNCTION__; const char* loc = __FUNCTION__;
if ((kernelIndex = wrapper.FindKernelIndex(finalAccumEntryPoint)) == -1)//Has not been built yet. if ((kernelIndex = wrapper.FindKernelIndex(finalAccumEntryPoint)) == -1)//Has not been built yet.
{ {
auto& kernel = m_FinalAccumOpenCLKernelCreator.FinalAccumKernel(EarlyClip(), Renderer<T, bucketT>::NumChannels(), Transparency()); auto& kernel = m_FinalAccumOpenCLKernelCreator.FinalAccumKernel(EarlyClip());
if (wrapper.AddProgram(finalAccumEntryPoint, kernel, finalAccumEntryPoint, m_DoublePrecision)) if (wrapper.AddProgram(finalAccumEntryPoint, kernel, finalAccumEntryPoint, m_DoublePrecision))
kernelIndex = wrapper.FindKernelIndex(finalAccumEntryPoint);//Try to find it again, it will be present if successfully built. kernelIndex = wrapper.FindKernelIndex(finalAccumEntryPoint);//Try to find it again, it will be present if successfully built.
@ -1575,13 +1542,13 @@ int RendererCL<T, bucketT>::MakeAndGetGammaCorrectionProgram()
if (!m_Devices.empty()) if (!m_Devices.empty())
{ {
auto& wrapper = m_Devices[0]->m_Wrapper; auto& wrapper = m_Devices[0]->m_Wrapper;
auto& gammaEntryPoint = m_FinalAccumOpenCLKernelCreator.GammaCorrectionEntryPoint(Renderer<T, bucketT>::NumChannels(), Transparency()); auto& gammaEntryPoint = m_FinalAccumOpenCLKernelCreator.GammaCorrectionEntryPoint();
int kernelIndex = wrapper.FindKernelIndex(gammaEntryPoint); int kernelIndex = wrapper.FindKernelIndex(gammaEntryPoint);
const char* loc = __FUNCTION__; const char* loc = __FUNCTION__;
if (kernelIndex == -1)//Has not been built yet. if (kernelIndex == -1)//Has not been built yet.
{ {
auto& kernel = m_FinalAccumOpenCLKernelCreator.GammaCorrectionKernel(Renderer<T, bucketT>::NumChannels(), Transparency()); auto& kernel = m_FinalAccumOpenCLKernelCreator.GammaCorrectionKernel();
bool b = wrapper.AddProgram(gammaEntryPoint, kernel, gammaEntryPoint, m_DoublePrecision); bool b = wrapper.AddProgram(gammaEntryPoint, kernel, gammaEntryPoint, m_DoublePrecision);
if (b) if (b)
@ -1735,10 +1702,7 @@ void RendererCL<T, bucketT>::ConvertSpatialFilter()
m_SpatialFilterCL.m_FinalRasH = uint(FinalRasH()); m_SpatialFilterCL.m_FinalRasH = uint(FinalRasH());
m_SpatialFilterCL.m_Supersample = uint(Supersample()); m_SpatialFilterCL.m_Supersample = uint(Supersample());
m_SpatialFilterCL.m_FilterWidth = uint(m_SpatialFilter->FinalFilterWidth()); m_SpatialFilterCL.m_FilterWidth = uint(m_SpatialFilter->FinalFilterWidth());
m_SpatialFilterCL.m_NumChannels = uint(Renderer<T, bucketT>::NumChannels());
m_SpatialFilterCL.m_BytesPerChannel = uint(BytesPerChannel());
m_SpatialFilterCL.m_DensityFilterOffset = uint(DensityFilterOffset()); m_SpatialFilterCL.m_DensityFilterOffset = uint(DensityFilterOffset());
m_SpatialFilterCL.m_Transparency = Transparency();
m_SpatialFilterCL.m_YAxisUp = uint(m_YAxisUp); m_SpatialFilterCL.m_YAxisUp = uint(m_YAxisUp);
m_SpatialFilterCL.m_Vibrancy = vibrancy; m_SpatialFilterCL.m_Vibrancy = vibrancy;
m_SpatialFilterCL.m_HighlightPower = HighlightPower(); m_SpatialFilterCL.m_HighlightPower = HighlightPower();

View File

@ -19,7 +19,7 @@ class EMBERCL_API RendererCLBase
{ {
public: public:
virtual ~RendererCLBase() { } virtual ~RendererCLBase() { }
virtual bool ReadFinal(byte* pixels) = 0; virtual bool ReadFinal(v4F* pixels) = 0;
virtual bool ClearFinal() = 0; virtual bool ClearFinal() = 0;
}; };
@ -43,7 +43,6 @@ class EMBERCL_API RendererCL : public Renderer<T, bucketT>, public RendererCLBas
{ {
using EmberNs::Renderer<T, bucketT>::RendererBase::Abort; using EmberNs::Renderer<T, bucketT>::RendererBase::Abort;
using EmberNs::Renderer<T, bucketT>::RendererBase::EarlyClip; using EmberNs::Renderer<T, bucketT>::RendererBase::EarlyClip;
using EmberNs::Renderer<T, bucketT>::RendererBase::Transparency;
using EmberNs::Renderer<T, bucketT>::RendererBase::EnterResize; using EmberNs::Renderer<T, bucketT>::RendererBase::EnterResize;
using EmberNs::Renderer<T, bucketT>::RendererBase::LeaveResize; using EmberNs::Renderer<T, bucketT>::RendererBase::LeaveResize;
using EmberNs::Renderer<T, bucketT>::RendererBase::FinalRasW; using EmberNs::Renderer<T, bucketT>::RendererBase::FinalRasW;
@ -60,7 +59,6 @@ class EMBERCL_API RendererCL : public Renderer<T, bucketT>, public RendererCLBas
using EmberNs::Renderer<T, bucketT>::RendererBase::m_YAxisUp; using EmberNs::Renderer<T, bucketT>::RendererBase::m_YAxisUp;
using EmberNs::Renderer<T, bucketT>::RendererBase::m_LockAccum; using EmberNs::Renderer<T, bucketT>::RendererBase::m_LockAccum;
using EmberNs::Renderer<T, bucketT>::RendererBase::m_Abort; using EmberNs::Renderer<T, bucketT>::RendererBase::m_Abort;
using EmberNs::Renderer<T, bucketT>::RendererBase::m_NumChannels;
using EmberNs::Renderer<T, bucketT>::RendererBase::m_LastIter; using EmberNs::Renderer<T, bucketT>::RendererBase::m_LastIter;
using EmberNs::Renderer<T, bucketT>::RendererBase::m_LastIterPercent; using EmberNs::Renderer<T, bucketT>::RendererBase::m_LastIterPercent;
using EmberNs::Renderer<T, bucketT>::RendererBase::m_Stats; using EmberNs::Renderer<T, bucketT>::RendererBase::m_Stats;
@ -141,13 +139,12 @@ public:
const vector<unique_ptr<RendererClDevice>>& Devices() const; const vector<unique_ptr<RendererClDevice>>& Devices() const;
//Virtual functions overridden from RendererCLBase. //Virtual functions overridden from RendererCLBase.
virtual bool ReadFinal(byte* pixels); virtual bool ReadFinal(v4F* pixels);
virtual bool ClearFinal(); virtual bool ClearFinal();
//Public virtual functions overridden from Renderer or RendererBase. //Public virtual functions overridden from Renderer or RendererBase.
virtual size_t MemoryAvailable() override; virtual size_t MemoryAvailable() override;
virtual bool Ok() const override; virtual bool Ok() const override;
virtual void NumChannels(size_t numChannels) override;
virtual void ClearErrorReport() override; virtual void ClearErrorReport() override;
virtual size_t SubBatchSize() const override; virtual size_t SubBatchSize() const override;
virtual size_t ThreadCount() const override; virtual size_t ThreadCount() const override;
@ -166,9 +163,8 @@ protected:
virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true) override; virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true) override;
virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false) override; virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false) override;
virtual eRenderStatus GaussianDensityFilter() 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 EmberStats Iterate(size_t iterCount, size_t temporalSample) override;
virtual void ComputeCurves(bool scale) override;
#ifndef TEST_CL #ifndef TEST_CL
private: private:
@ -183,7 +179,7 @@ private:
bool ClearBuffer(size_t device, const string& bufferName, uint width, uint height, uint elementSize); 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); 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 MakeAndGetDensityFilterProgram(size_t ss, uint filterWidth);
int MakeAndGetFinalAccumProgram(double& alphaBase, double& alphaScale); int MakeAndGetFinalAccumProgram();
int MakeAndGetGammaCorrectionProgram(); int MakeAndGetGammaCorrectionProgram();
bool CreateHostBuffer(); bool CreateHostBuffer();
bool SumDeviceHist(); bool SumDeviceHist();

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "EmberCommonPch.h" #include "EmberCommonPch.h"
#include "EmberOptions.h"
/// <summary> /// <summary>
/// Global utility classes and functions that are common to all programs that use /// Global utility classes and functions that are common to all programs that use
@ -168,26 +169,133 @@ void FormatName(Ember<T>& result, ostringstream& os, streamsize padding)
} }
/// <summary> /// <summary>
/// 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 two buffers can point to the same memory location if needed.
/// </summary> /// </summary>
/// <param name="rgba">The RGBA buffer</param> /// <param name="rgba">The RGBA 32-bit float buffer</param>
/// <param name="rgb">The RGB buffer</param> /// <param name="rgb">The RGB 8-bit buffer</param>
/// <param name="width">The width of the image in pixels</param> /// <param name="width">The width of the image in pixels</param>
/// <param name="height">The height of the image in pixels</param> /// <param name="height">The height of the image in pixels</param>
static void RgbaToRgb(vector<byte>& rgba, vector<byte>& 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. for (size_t i = 0, j = 0; i < (width * height); i++)
rgb.resize(width * height * 3);
for (size_t i = 0, j = 0; i < (width * height * 4); i += 4, j += 3)
{ {
rgb[j] = rgba[i]; rgb[j++] = byte(Clamp<float>(rgba[i].r * 255.0f, 0.0f, 255.0f));
rgb[j + 1] = rgba[i + 1]; rgb[j++] = byte(Clamp<float>(rgba[i].g * 255.0f, 0.0f, 255.0f));
rgb[j + 2] = rgba[i + 2]; rgb[j++] = byte(Clamp<float>(rgba[i].b * 255.0f, 0.0f, 255.0f));
} }
} }
/// <summary>
/// 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.
/// </summary>
/// <param name="rgba">The RGBA 32-bit float buffer</param>
/// <param name="rgb">The RGBA 8-bit buffer</param>
/// <param name="width">The width of the image in pixels</param>
/// <param name="height">The height of the image in pixels</param>
/// <param name="doAlpha">True to use alpha transparency, false to assign the max alpha value to make each pixel fully visible</param>
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<float>(rgba[i].r * 255.0f, 0.0f, 255.0f));
rgb[j++] = byte(Clamp<float>(rgba[i].g * 255.0f, 0.0f, 255.0f));
rgb[j++] = byte(Clamp<float>(rgba[i].b * 255.0f, 0.0f, 255.0f));
rgb[j++] = doAlpha ? byte(Clamp<float>(rgba[i].a * 255.0f, 0.0f, 255.0f)) : 255;
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="rgba">The RGBA 32-bit float buffer</param>
/// <param name="rgb">The RGBA 16-bit buffer</param>
/// <param name="width">The width of the image in pixels</param>
/// <param name="height">The height of the image in pixels</param>
/// <param name="doAlpha">True to use alpha transparency, false to assign the max alpha value to make each pixel fully visible</param>
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<float>(rgba[i].r * 65535.0f, 0.0f, 65535.0f));
rgb[j++] = glm::uint16(Clamp<float>(rgba[i].g * 65535.0f, 0.0f, 65535.0f));
rgb[j++] = glm::uint16(Clamp<float>(rgba[i].b * 65535.0f, 0.0f, 65535.0f));
rgb[j++] = doAlpha ? glm::uint16(Clamp<float>(rgba[i].a * 65535.0f, 0.0f, 65535.0f)) : glm::uint16(65535);
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="rgba">The RGBA 32-bit float buffer</param>
/// <param name="rgb">The EXR RGBA 32-bit float buffer</param>
/// <param name="width">The width of the image in pixels</param>
/// <param name="height">The height of the image in pixels</param>
/// <param name="doAlpha">True to use alpha transparency, false to assign the max alpha value to make each pixel fully visible</param>
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<float>(rgba[i].r, 0.0f, 1.0f);
ilmfRgba[i].g = Clamp<float>(rgba[i].g, 0.0f, 1.0f);
ilmfRgba[i].b = Clamp<float>(rgba[i].b, 0.0f, 1.0f);
ilmfRgba[i].a = doAlpha ? Clamp<float>(rgba[i].a * 1.0f, 0.0f, 1.0f) : 1.0f;
}
}
/// <summary>
/// Make a filename for a single render. This is used in EmberRender.
/// </summary>
/// <param name="path">The path portion of where to save the file</param>
/// <param name="out">The full name and path to override everything else</param>
/// <param name="finalName">The name to use when useFinalName is true</param>
/// <param name="prefix">The prefix to prepend to the filename</param>
/// <param name="suffix">True suffix to append to the filename</param>
/// <param name="format">The format extention. This must not contain a period.</param>
/// <param name="padding">The width padding to use, which will be zero filled.</param>
/// <param name="i">The numerical value to use for the filename when useFinalName is false and out is empty</param>
/// <param name="useFinalName">Whether to use the name included in the flame. The i parameter is ignored in this case.</param>
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;
}
/// <summary>
/// Make a filename for a frame of an animation render. This is used in EmberAnimate.
/// </summary>
/// <param name="path">The path portion of where to save the file</param>
/// <param name="prefix">The prefix to prepend to the filename</param>
/// <param name="suffix">True suffix to append to the filename</param>
/// <param name="format">The format extention. This must contain a period.</param>
/// <param name="padding">The width padding to use, which will be zero filled.</param>
/// <param name="ftime">The numerical value to use for the filename</param>
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();
}
/// <summary> /// <summary>
/// Calculate the number of strips required if the needed amount of memory /// 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. /// is greater than the system memory, or greater than what the user wants to allow.
@ -439,7 +547,7 @@ static vector<unique_ptr<Renderer<T, float>>> CreateRenderers(eRendererType rend
/// <param name="allStripsFinished">Function called when all strips successfully finish rendering</param> /// <param name="allStripsFinished">Function called when all strips successfully finish rendering</param>
/// <returns>True if all rendering was successful, else false.</returns> /// <returns>True if all rendering was successful, else false.</returns>
template <typename T> template <typename T>
static bool StripsRender(RendererBase* renderer, Ember<T>& ember, vector<byte>& finalImage, double time, size_t strips, bool yAxisUp, static bool StripsRender(RendererBase* renderer, Ember<T>& ember, vector<v4F>& finalImage, double time, size_t strips, bool yAxisUp,
std::function<void(size_t strip)> perStripStart, std::function<void(size_t strip)> perStripStart,
std::function<void(size_t strip)> perStripFinish, std::function<void(size_t strip)> perStripFinish,
std::function<void(size_t strip)> perStripError, std::function<void(size_t strip)> perStripError,

View File

@ -57,5 +57,17 @@
#include "SimpleGlob.h" #include "SimpleGlob.h"
#include "SimpleOpt.h" #include "SimpleOpt.h"
//Exr
#ifdef _WIN32
#define OPENEXR_DLL 1
#endif
#include <ImfRgbaFile.h>
#include <ImfStringAttribute.h>
#include <half.h>
using namespace Imf;
using namespace Imath;
using namespace EmberNs; using namespace EmberNs;
using namespace EmberCLns; using namespace EmberCLns;

View File

@ -58,8 +58,7 @@ enum class eOptionIDs : et
OPT_INT_PALETTE, OPT_INT_PALETTE,
OPT_HEX_PALETTE, OPT_HEX_PALETTE,
OPT_INSERT_PALETTE, OPT_INSERT_PALETTE,
OPT_JPEG_COMMENTS, OPT_ENABLE_COMMENTS,
OPT_PNG_COMMENTS,
OPT_WRITE_GENOME, OPT_WRITE_GENOME,
OPT_THREADED_WRITE, OPT_THREADED_WRITE,
OPT_ENCLOSED, OPT_ENCLOSED,
@ -75,7 +74,6 @@ enum class eOptionIDs : et
OPT_STRIPS, OPT_STRIPS,
OPT_SUPERSAMPLE, OPT_SUPERSAMPLE,
OPT_TEMPSAMPLES, OPT_TEMPSAMPLES,
OPT_BPC,
OPT_PRINT_EDIT_DEPTH, OPT_PRINT_EDIT_DEPTH,
OPT_JPEG, OPT_JPEG,
OPT_BEGIN, 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(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(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(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(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(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(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(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(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")); 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=<val> The number of fractions to split a single render frame into. Useful for print size renders or low memory systems [default: 1].\n")); INITUINTOPTION(Strips, Eou(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_STRIPS, _T("--nstrips"), 1, SO_REQ_SEP, " --nstrips=<val> 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=<val> The supersample value used to override the one specified in the file [default: 0 (use value from file), Range: 0 - 4].\n")); INITUINTOPTION(Supersample, Eou(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SUPERSAMPLE, _T("--supersample"), 0, SO_REQ_SEP, " --supersample=<val> 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=<val> 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(TemporalSamples, Eou(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_TEMPSAMPLES, _T("--ts"), 0, SO_REQ_SEP, " --ts=<val> 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=<val> 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=<val> Depth to truncate <edit> tag structure when converting a flame to Xml. 0 prints all <edit> tags [default: 0].\n")); INITUINTOPTION(PrintEditDepth, Eou(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_PRINT_EDIT_DEPTH, _T("--print_edit_depth"), 0, SO_REQ_SEP, " --print_edit_depth=<val> Depth to truncate <edit> tag structure when converting a flame to Xml. 0 prints all <edit> tags [default: 0].\n"));
INITUINTOPTION(JpegQuality, Eou(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_JPEG, _T("--jpeg"), 95, SO_REQ_SEP, " --jpeg=<val> Jpeg quality 0-100 for compression [default: 95].\n")); INITUINTOPTION(JpegQuality, Eou(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_JPEG, _T("--jpeg"), 95, SO_REQ_SEP, " --jpeg=<val> 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=<val> Time of first frame to render [default: first time specified in file].\n")); INITUINTOPTION(FirstFrame, Eou(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_BEGIN, _T("--begin"), UINT_MAX, SO_REQ_SEP, " --begin=<val> 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=<val> Name of a single output file. Not recommended when rendering more than one image.\n")); INITSTRINGOPTION(Out, Eos(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_OUT, _T("--out"), "", SO_REQ_SEP, " --out=<val> 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=<val> Prefix to prepend to all output files.\n")); INITSTRINGOPTION(Prefix, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PREFIX, _T("--prefix"), "", SO_REQ_SEP, " --prefix=<val> Prefix to prepend to all output files.\n"));
INITSTRINGOPTION(Suffix, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SUFFIX, _T("--suffix"), "", SO_REQ_SEP, " --suffix=<val> Suffix to append to all output files.\n")); INITSTRINGOPTION(Suffix, Eos(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_SUFFIX, _T("--suffix"), "", SO_REQ_SEP, " --suffix=<val> 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=<val> 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=<val> 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=<val> 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=<val> Path and name of the palette file [default: flam3-palettes.xml].\n")); INITSTRINGOPTION(PalettePath, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_PALETTE_FILE, _T("--flam3_palettes"), "flam3-palettes.xml", SO_REQ_SEP, " --flam3_palettes=<val> 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=<val> ID to use in <edit> tags / image comments.\n")); INITSTRINGOPTION(Id, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_ID, _T("--id"), "", SO_REQ_SEP, " --id=<val> ID to use in <edit> tags / image comments.\n"));
INITSTRINGOPTION(Url, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_URL, _T("--url"), "", SO_REQ_SEP, " --url=<val> URL to use in <edit> tags / image comments.\n")); INITSTRINGOPTION(Url, Eos(eOptionUse::OPT_USE_ALL, eOptionIDs::OPT_URL, _T("--url"), "", SO_REQ_SEP, " --url=<val> URL to use in <edit> tags / image comments.\n"));
@ -519,8 +519,7 @@ public:
PARSEBOOLOPTION(eOptionIDs::OPT_NAME_ENABLE, NameEnable); PARSEBOOLOPTION(eOptionIDs::OPT_NAME_ENABLE, NameEnable);
PARSEBOOLOPTION(eOptionIDs::OPT_HEX_PALETTE, HexPalette); PARSEBOOLOPTION(eOptionIDs::OPT_HEX_PALETTE, HexPalette);
PARSEBOOLOPTION(eOptionIDs::OPT_INSERT_PALETTE, InsertPalette); PARSEBOOLOPTION(eOptionIDs::OPT_INSERT_PALETTE, InsertPalette);
PARSEBOOLOPTION(eOptionIDs::OPT_JPEG_COMMENTS, JpegComments); PARSEBOOLOPTION(eOptionIDs::OPT_ENABLE_COMMENTS, EnableComments);
PARSEBOOLOPTION(eOptionIDs::OPT_PNG_COMMENTS, PngComments);
PARSEBOOLOPTION(eOptionIDs::OPT_WRITE_GENOME, WriteGenome); PARSEBOOLOPTION(eOptionIDs::OPT_WRITE_GENOME, WriteGenome);
PARSEBOOLOPTION(eOptionIDs::OPT_THREADED_WRITE, ThreadedWrite); PARSEBOOLOPTION(eOptionIDs::OPT_THREADED_WRITE, ThreadedWrite);
PARSEBOOLOPTION(eOptionIDs::OPT_ENCLOSED, Enclosed); PARSEBOOLOPTION(eOptionIDs::OPT_ENCLOSED, Enclosed);
@ -538,7 +537,6 @@ public:
PARSEOPTION(eOptionIDs::OPT_STRIPS, Strips); PARSEOPTION(eOptionIDs::OPT_STRIPS, Strips);
PARSEOPTION(eOptionIDs::OPT_SUPERSAMPLE, Supersample); PARSEOPTION(eOptionIDs::OPT_SUPERSAMPLE, Supersample);
PARSEOPTION(eOptionIDs::OPT_TEMPSAMPLES, TemporalSamples); PARSEOPTION(eOptionIDs::OPT_TEMPSAMPLES, TemporalSamples);
PARSEOPTION(eOptionIDs::OPT_BPC, BitsPerChannel);
PARSEOPTION(eOptionIDs::OPT_PRINT_EDIT_DEPTH, PrintEditDepth); PARSEOPTION(eOptionIDs::OPT_PRINT_EDIT_DEPTH, PrintEditDepth);
PARSEOPTION(eOptionIDs::OPT_JPEG, JpegQuality); PARSEOPTION(eOptionIDs::OPT_JPEG, JpegQuality);
PARSEOPTION(eOptionIDs::OPT_BEGIN, FirstFrame); PARSEOPTION(eOptionIDs::OPT_BEGIN, FirstFrame);
@ -723,18 +721,18 @@ public:
{ {
cout << "Usage:\n" cout << "Usage:\n"
#ifdef _WIN32 #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 #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 #endif
} }
else if (optUsage == eOptionUse::OPT_USE_ANIMATE) else if (optUsage == eOptionUse::OPT_USE_ANIMATE)
{ {
cout << "Usage:\n" cout << "Usage:\n"
#ifdef _WIN32 #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 #else
"\temberanimate --in=sequence.flame [--format=png --verbose --progress --opencl]\n\n"; "\temberanimate --in=sequence.flame [--verbose --progress --opencl]\n\n";
#endif #endif
} }
else if (optUsage == eOptionUse::OPT_USE_GENOME) else if (optUsage == eOptionUse::OPT_USE_GENOME)
@ -808,8 +806,7 @@ public:
Eob NameEnable; Eob NameEnable;
Eob HexPalette; Eob HexPalette;
Eob InsertPalette; Eob InsertPalette;
Eob JpegComments; Eob EnableComments;
Eob PngComments;
Eob WriteGenome; Eob WriteGenome;
Eob ThreadedWrite; Eob ThreadedWrite;
Eob Enclosed; Eob Enclosed;
@ -828,7 +825,6 @@ public:
Eou Strips; Eou Strips;
Eou Supersample; Eou Supersample;
Eou TemporalSamples; Eou TemporalSamples;
Eou BitsPerChannel;
Eou Bits; Eou Bits;
Eou PrintEditDepth; Eou PrintEditDepth;
Eou JpegQuality; Eou JpegQuality;

View File

@ -136,28 +136,28 @@ static bool WritePng(const char* filename, byte* image, size_t width, size_t hei
glm::uint16 testbe = 1; glm::uint16 testbe = 1;
vector<byte*> rows(height); vector<byte*> rows(height);
text[0].compression = PNG_TEXT_COMPRESSION_NONE; text[0].compression = PNG_TEXT_COMPRESSION_NONE;
text[0].key = const_cast<png_charp>("flam3_version"); text[0].key = const_cast<png_charp>("ember_version");
text[0].text = const_cast<png_charp>(EmberVersion()); text[0].text = const_cast<png_charp>(EmberVersion());
text[1].compression = PNG_TEXT_COMPRESSION_NONE; text[1].compression = PNG_TEXT_COMPRESSION_NONE;
text[1].key = const_cast<png_charp>("flam3_nickname"); text[1].key = const_cast<png_charp>("ember_nickname");
text[1].text = const_cast<png_charp>(nick.c_str()); text[1].text = const_cast<png_charp>(nick.c_str());
text[2].compression = PNG_TEXT_COMPRESSION_NONE; text[2].compression = PNG_TEXT_COMPRESSION_NONE;
text[2].key = const_cast<png_charp>("flam3_url"); text[2].key = const_cast<png_charp>("ember_url");
text[2].text = const_cast<png_charp>(url.c_str()); text[2].text = const_cast<png_charp>(url.c_str());
text[3].compression = PNG_TEXT_COMPRESSION_NONE; text[3].compression = PNG_TEXT_COMPRESSION_NONE;
text[3].key = const_cast<png_charp>("flam3_id"); text[3].key = const_cast<png_charp>("ember_id");
text[3].text = const_cast<png_charp>(id.c_str()); text[3].text = const_cast<png_charp>(id.c_str());
text[4].compression = PNG_TEXT_COMPRESSION_NONE; text[4].compression = PNG_TEXT_COMPRESSION_NONE;
text[4].key = const_cast<png_charp>("flam3_error_rate"); text[4].key = const_cast<png_charp>("ember_error_rate");
text[4].text = const_cast<png_charp>(comments.m_Badvals.c_str()); text[4].text = const_cast<png_charp>(comments.m_Badvals.c_str());
text[5].compression = PNG_TEXT_COMPRESSION_NONE; text[5].compression = PNG_TEXT_COMPRESSION_NONE;
text[5].key = const_cast<png_charp>("flam3_samples"); text[5].key = const_cast<png_charp>("ember_samples");
text[5].text = const_cast<png_charp>(comments.m_NumIters.c_str()); text[5].text = const_cast<png_charp>(comments.m_NumIters.c_str());
text[6].compression = PNG_TEXT_COMPRESSION_NONE; text[6].compression = PNG_TEXT_COMPRESSION_NONE;
text[6].key = const_cast<png_charp>("flam3_time"); text[6].key = const_cast<png_charp>("ember_time");
text[6].text = const_cast<png_charp>(comments.m_Runtime.c_str()); text[6].text = const_cast<png_charp>(comments.m_Runtime.c_str());
text[7].compression = PNG_TEXT_COMPRESSION_zTXt; text[7].compression = PNG_TEXT_COMPRESSION_zTXt;
text[7].key = const_cast<png_charp>("flam3_genome"); text[7].key = const_cast<png_charp>("ember_genome");
text[7].text = const_cast<png_charp>(comments.m_Genome.c_str()); text[7].text = const_cast<png_charp>(comments.m_Genome.c_str());
for (i = 0; i < height; i++) 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); b = SaveBmp(filename, bgrBuf.data(), width, height, newSize);
return b; return b;
} }
/// <summary>
/// Write an EXR file.
/// This is used for extreme color precision because it uses
/// floats for each color channel.
/// </summary>
/// <param name="filename">The full path and name of the file</param>
/// <param name="image">Pointer to the image data to write</param>
/// <param name="width">Width of the image in pixels</param>
/// <param name="height">Height of the image in pixels</param>
/// <param name="enableComments">True to embed comments, else false</param>
/// <param name="comments">The comment string to embed</param>
/// <param name="id">Id of the author</param>
/// <param name="url">Url of the author</param>
/// <param name="nick">Nickname of the author</param>
/// <returns>True if success, else false</returns>
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<Imf::Header&>(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;
}
}

View File

@ -171,13 +171,15 @@ bool EmberGenome(EmberOptions& opt)
bool exactTimeMatch, randomMode, didColor, seqFlag, random = false; bool exactTimeMatch, randomMode, didColor, seqFlag, random = false;
size_t i, i0, i1, rep, val, frame, frameCount, count = 0; size_t i, i0, i1, rep, val, frame, frameCount, count = 0;
size_t ftime, firstFrame, lastFrame; size_t ftime, firstFrame, lastFrame;
size_t n, tot, totb, totw; double tot;
T avgPix, fractionBlack, fractionWhite, blend, spread, mix0, mix1; size_t n, totb, totw;
double avgPix;
T fractionBlack, fractionWhite, blend, spread, mix0, mix1;
string token, filename; string token, filename;
ostringstream os, os2; ostringstream os, os2;
vector<Ember<T>> embers, embers2, templateEmbers; vector<Ember<T>> embers, embers2, templateEmbers;
vector<eVariationId> vars, noVars; vector<eVariationId> vars, noVars;
vector<byte> finalImage; vector<v4F> finalImage;
eCrossMode crossMeth; eCrossMode crossMeth;
eMutateMode mutMeth; eMutateMode mutMeth;
Ember<T> orig, save, selp0, selp1, parent0, parent1; Ember<T> orig, save, selp0, selp1, parent0, parent1;
@ -598,7 +600,6 @@ bool EmberGenome(EmberOptions& opt)
renderer->YAxisUp(opt.YAxisUp()); renderer->YAxisUp(opt.YAxisUp());
renderer->LockAccum(opt.LockAccum()); renderer->LockAccum(opt.LockAccum());
renderer->PixelAspectRatio(T(opt.AspectRatio())); renderer->PixelAspectRatio(T(opt.AspectRatio()));
renderer->Transparency(opt.Transparency());
if (opt.Repeat() == 0) if (opt.Repeat() == 0)
{ {
@ -786,19 +787,20 @@ bool EmberGenome(EmberOptions& opt)
return false; return false;
} }
tot = totb = totw = 0; tot = 0;
totb = totw = 0;
n = orig.m_FinalRasW * orig.m_FinalRasH; 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); fractionBlack = totb / T(n);
fractionWhite = totw / T(n); fractionWhite = totw / T(n);

View File

@ -29,17 +29,15 @@ bool EmberRender(EmberOptions& opt)
VerbosePrint("Using " << (sizeof(T) == sizeof(float) ? "single" : "double") << " precision."); VerbosePrint("Using " << (sizeof(T) == sizeof(float) ? "single" : "double") << " precision.");
Timing t; Timing t;
bool writeSuccess = false; bool writeSuccess = false;
byte* finalImagep;
uint padding; uint padding;
size_t i, channels; size_t i;
size_t strips; size_t strips;
size_t iterCount; size_t iterCount;
string filename;
string inputPath = GetPath(opt.Input()); string inputPath = GetPath(opt.Input());
ostringstream os; ostringstream os;
pair<size_t, size_t> p; pair<size_t, size_t> p;
vector<Ember<T>> embers; vector<Ember<T>> embers;
vector<byte> finalImage; vector<v4F> finalImage;
EmberStats stats; EmberStats stats;
EmberReport emberReport; EmberReport emberReport;
EmberImageComments comments; EmberImageComments comments;
@ -102,12 +100,6 @@ bool EmberRender(EmberOptions& opt)
opt.ThreadCount(1); opt.ThreadCount(1);
renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr); 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()) if (opt.InsertPalette())
{ {
cout << "Inserting palette not supported with OpenCL, insertion will not take place.\n"; 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" && if (!Find(opt.Format(), "jpg") &&
opt.Format() != "png" && !Find(opt.Format(), "png") &&
opt.Format() != "bmp") #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"; #ifdef _WIN32
} cout << "Format must be bmp, jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n";
#else
channels = opt.Format() == "png" ? 4 : 3; cout << "Format must be jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n";
#endif
if (opt.BitsPerChannel() == 16 && opt.Format() != "png") 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);
} }
if (opt.AspectRatio() < 0) if (opt.AspectRatio() < 0)
@ -156,9 +143,6 @@ bool EmberRender(EmberOptions& opt)
renderer->LockAccum(opt.LockAccum()); renderer->LockAccum(opt.LockAccum());
renderer->InsertPalette(opt.InsertPalette()); renderer->InsertPalette(opt.InsertPalette());
renderer->PixelAspectRatio(T(opt.AspectRatio())); renderer->PixelAspectRatio(T(opt.AspectRatio()));
renderer->Transparency(opt.Transparency());
renderer->NumChannels(channels);
renderer->BytesPerChannel(opt.BitsPerChannel() / 8);
renderer->Priority(eThreadPriority(Clamp<intmax_t>(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST)))); renderer->Priority(eThreadPriority(Clamp<intmax_t>(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST))));
renderer->Callback(opt.DoProgress() ? progress.get() : nullptr); 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. //Only write once all strips for this image are finished.
[&](Ember<T>& finalEmber) [&](Ember<T>& 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, //TotalIterCount() is actually using ScaledQuality() which does not get reset upon ember assignment,
//so it ends up using the correct value for quality * strips. //so it ends up using the correct value for quality * strips.
iterCount = renderer->TotalIterCount(1); 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) << "%)"; os << comments.m_NumIters << " / " << iterCount << " (" << std::fixed << std::setprecision(2) << ((double(stats.m_Iters) / double(iterCount)) * 100) << "%)";
VerbosePrint("\nIters ran/requested: " + os.str()); 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("Render time: " + t.Format(stats.m_RenderMs));
VerbosePrint("Pure iter time: " + t.Format(stats.m_IterMs)); VerbosePrint("Pure iter time: " + t.Format(stats.m_IterMs));
VerbosePrint("Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << "\n"); VerbosePrint("Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << "\n");
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<byte> rgb8Image;
vector<std::thread> writeFileThreads;
writeFileThreads.reserve(5);
if (doBmp || doJpg)
{
rgb8Image.resize(size * 3);
Rgba32ToRgb8(finalImagep, rgb8Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH);
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); VerbosePrint("Writing " + filename);
writeSuccess = WriteBmp(filename.c_str(), rgb8Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH);
if ((opt.Format() == "jpg" || opt.Format() == "bmp") && renderer->NumChannels() == 4)
RgbaToRgb(finalImage, finalImage, renderer->FinalRasW(), renderer->FinalRasH());
finalImagep = finalImage.data();
writeSuccess = false;
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) if (!writeSuccess)
cout << "Error writing " << filename << "\n"; 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<byte> 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<glm::uint16> 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<Rgba> 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()) if (opt.EmberCL() && opt.DumpKernel())

View File

@ -5,6 +5,25 @@
#include <list> #include <list>
#include <deque> #include <deque>
#include <ImfRgbaFile.h>
//#include <ImfStringAttribute.h>
//#include <ImfMatrixAttribute.h>
//#include <ImfArray.h>
//#include <ImfChannelList.h>
//#include "drawImage.h"
//
//#include <iostream>
//#include <algorithm>
//
//
//#include <ImfNamespace.h>
//
//namespace IMF = Imf;
//
using namespace Imf;
using namespace Imath;
/// <summary> /// <summary>
/// EmberTester is a scratch area used for on the fly testing. /// EmberTester is a scratch area used for on the fly testing.
/// It may become a more formalized automated testing system /// It may become a more formalized automated testing system
@ -17,6 +36,43 @@ using namespace EmberCommon;
//#define DO_NVIDIA 1 //#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 <typename T> template <typename T>
void SaveFinalImage(Renderer<T, T>& renderer, vector<byte>& pixels, char* suffix) void SaveFinalImage(Renderer<T, T>& renderer, vector<byte>& pixels, char* suffix)
{ {
@ -1981,6 +2037,20 @@ int _tmain(int argc, _TCHAR* argv[])
vector<Ember<double>> dv; vector<Ember<double>> dv;
list<Ember<float>> fl; list<Ember<float>> fl;
list<Ember<double>> dl; list<Ember<double>> dl;
int w = 1000, h = 1000;
string filename = ".\\testexr.exr";
vector<Rgba> 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<float>::max();
}
writeRgba1(filename.c_str(), pixels.data(), w, h);
/* TestFuncs(); /* TestFuncs();
string line = "title=\"cj_aerie\" smooth=no", delim = " =\""; string line = "title=\"cj_aerie\" smooth=no", delim = " =\"";
auto vec = Split(line, delim, true); auto vec = Split(line, delim, true);
@ -2008,9 +2078,8 @@ int _tmain(int argc, _TCHAR* argv[])
return 1; return 1;
*/ */
//MakeTestAllVarsRegPrePostComboFile("testallvarsout.flame"); //MakeTestAllVarsRegPrePostComboFile("testallvarsout.flame");
/* return 0; return 0;
/*
TestThreadedKernel(); TestThreadedKernel();
auto palf = PaletteList<float>::Instance(); auto palf = PaletteList<float>::Instance();

View File

@ -58,7 +58,7 @@
<enum>QFrame::NoFrame</enum> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="text"> <property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Fractorium 1.0.0.4&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;http://fractorium.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;fractorium.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br/&gt;Lead: Matt Feemster&lt;br/&gt;Contributors: Simon Detheridge, Michel Mastriani&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;Fractorium 1.0.0.5&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;A Qt-based fractal flame editor which uses a C++ re-write of the flam3 algorithm named Ember and a GPU capable version named EmberCL which implements a portion of the cuburn algorithm in OpenCL.&lt;/span&gt;&lt;/p&gt;&lt;p align=&quot;center&quot;&gt;&lt;a href=&quot;http://fractorium.com&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;fractorium.com&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;&lt;br/&gt;Lead: Matt Feemster&lt;br/&gt;Contributors: Simon Detheridge, Michel Mastriani&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="textFormat"> <property name="textFormat">
<enum>Qt::RichText</enum> <enum>Qt::RichText</enum>

View File

@ -169,7 +169,7 @@ void DoubleSpinBox::OnTimeout()
/// <returns>false</returns> /// <returns>false</returns>
bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e) bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e)
{ {
QMouseEvent* me = dynamic_cast<QMouseEvent*>(e); auto me = dynamic_cast<QMouseEvent*>(e);
if (isEnabled() && me) if (isEnabled() && me)
{ {
@ -287,3 +287,124 @@ void DoubleSpinBox::StopTimer()
s_Timer.stop(); s_Timer.stop();
disconnect(&s_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout())); disconnect(&s_Timer, SIGNAL(timeout()), this, SLOT(OnTimeout()));
} }
/// <summary>
/// Constructor that passes agruments to the base and assigns the m_Param and m_Variation members.
/// It also sets up the context menu for special numerical values.
/// </summary>
/// <param name="p">The parent widget</param>
/// <param name="widgetItem">The widget item this spinner is contained in</param>
/// <param name="id">The variation this spinner is for</param>
/// <param name="param">The name of the parameter this is for</param>
/// <param name="h">The height of the spin box. Default: 16.</param>
/// <param name="step">The step used to increment/decrement the spin box when using the mouse wheel. Default: 0.05.</param>
VariationTreeDoubleSpinBox::VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, const string& param, int h, double step)
: DoubleSpinBox(p, h, step)
{
m_WidgetItem = widgetItem;
m_Param = param;
m_Id = id;
setDecimals(3);
//PI
auto piAction = new QAction("PI", this);
connect(piAction, SIGNAL(triggered(bool)), this, SLOT(PiActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(piAction);
//PI * 2
auto twoPiAction = new QAction("2 PI", this);
connect(twoPiAction, SIGNAL(triggered(bool)), this, SLOT(TwoPiActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(twoPiAction);
//PI / 2
auto piOver2Action = new QAction("PI / 2", this);
connect(piOver2Action, SIGNAL(triggered(bool)), this, SLOT(PiOver2ActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(piOver2Action);
//PI / 3
auto piOver3Action = new QAction("PI / 3", this);
connect(piOver3Action, SIGNAL(triggered(bool)), this, SLOT(PiOver3ActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(piOver3Action);
//PI / 4
auto piOver4Action = new QAction("PI / 4", this);
connect(piOver4Action, SIGNAL(triggered(bool)), this, SLOT(PiOver4ActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(piOver4Action);
//PI / 6
auto piOver6Action = new QAction("PI / 6", this);
connect(piOver6Action, SIGNAL(triggered(bool)), this, SLOT(PiOver6ActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(piOver6Action);
//1 / PI
auto oneOverPiAction = new QAction("1 / PI", this);
connect(oneOverPiAction, SIGNAL(triggered(bool)), this, SLOT(OneOverPiActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(oneOverPiAction);
//2 / PI
auto twoOverPiAction = new QAction("2 / PI", this);
connect(twoOverPiAction, SIGNAL(triggered(bool)), this, SLOT(TwoOverPiActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(twoOverPiAction);
//3 / PI
auto threeOverPiAction = new QAction("3 / PI", this);
connect(threeOverPiAction, SIGNAL(triggered(bool)), this, SLOT(ThreeOverPiActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(threeOverPiAction);
//4 / PI
auto fourOverPiAction = new QAction("4 / PI", this);
connect(fourOverPiAction, SIGNAL(triggered(bool)), this, SLOT(FourOverPiActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(fourOverPiAction);
//Sqrt(2)
auto sqrtTwoAction = new QAction("Sqrt(2)", this);
connect(sqrtTwoAction, SIGNAL(triggered(bool)), this, SLOT(SqrtTwoActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(sqrtTwoAction);
//Sqrt(2)
auto sqrtThreeAction = new QAction("Sqrt(3)", this);
connect(sqrtThreeAction, SIGNAL(triggered(bool)), this, SLOT(SqrtThreeActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(sqrtThreeAction);
//Need this for it to show up properly.
this->setContextMenuPolicy(Qt::ActionsContextMenu);
}
void VariationTreeDoubleSpinBox::PiActionTriggered(bool checked) { setValue(M_PI); }
void VariationTreeDoubleSpinBox::TwoPiActionTriggered(bool checked) { setValue(M_PI * 2); }
void VariationTreeDoubleSpinBox::PiOver2ActionTriggered(bool checked) { setValue(M_PI_2); }
void VariationTreeDoubleSpinBox::PiOver3ActionTriggered(bool checked) { setValue(M_PI / 3); }
void VariationTreeDoubleSpinBox::PiOver4ActionTriggered(bool checked) { setValue(M_PI / 4); }
void VariationTreeDoubleSpinBox::PiOver6ActionTriggered(bool checked) { setValue(M_PI / 6); }
void VariationTreeDoubleSpinBox::OneOverPiActionTriggered(bool checked) { setValue(1 / M_PI); }
void VariationTreeDoubleSpinBox::TwoOverPiActionTriggered(bool checked) { setValue(2 / M_PI); }
void VariationTreeDoubleSpinBox::ThreeOverPiActionTriggered(bool checked) { setValue(3 / M_PI); }
void VariationTreeDoubleSpinBox::FourOverPiActionTriggered(bool checked) { setValue(4 / M_PI); }
void VariationTreeDoubleSpinBox::SqrtTwoActionTriggered(bool checked) { setValue(M_SQRT2); }
void VariationTreeDoubleSpinBox::SqrtThreeActionTriggered(bool checked) { setValue(std::sqrt(3.0)); }
/// <summary>
/// Constructor that sets up the context menu for special numerical values specific to affine spinners.
/// </summary>
/// <param name="p">The parent widget</param>
/// <param name="h">The height of the spin box. Default: 20.</param>
/// <param name="step">The step used to increment/decrement the spin box when using the mouse wheel. Default: 0.01.</param>
AffineDoubleSpinBox::AffineDoubleSpinBox(QWidget* p, int h, double step)
: DoubleSpinBox(p, h, step)
{
//-1
auto neg1Action = new QAction("-1", this);
connect(neg1Action, SIGNAL(triggered(bool)), this, SLOT(NegOneActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(neg1Action);
//0
auto zeroAction = new QAction("0", this);
connect(zeroAction, SIGNAL(triggered(bool)), this, SLOT(ZeroActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(zeroAction);
//1
auto oneAction = new QAction("1", this);
connect(oneAction, SIGNAL(triggered(bool)), this, SLOT(OneActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(oneAction);
//45
auto fortyFiveAction = new QAction("45", this);
connect(fortyFiveAction, SIGNAL(triggered(bool)), this, SLOT(FortyFiveActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(fortyFiveAction);
//-45
auto negFortyFiveAction = new QAction("-45", this);
connect(negFortyFiveAction, SIGNAL(triggered(bool)), this, SLOT(NegFortyFiveActionTriggered(bool)), Qt::QueuedConnection);
this->addAction(negFortyFiveAction);
//Need this for it to show up properly.
this->setContextMenuPolicy(Qt::ActionsContextMenu);
}
void AffineDoubleSpinBox::NegOneActionTriggered(bool checked) { setValue(-1); }
void AffineDoubleSpinBox::ZeroActionTriggered(bool checked) { setValue(0); }
void AffineDoubleSpinBox::OneActionTriggered(bool checked) { setValue(1); }
void AffineDoubleSpinBox::FortyFiveActionTriggered(bool checked) { setValue(0.707107); }
void AffineDoubleSpinBox::NegFortyFiveActionTriggered(bool checked) { setValue(-0.707107); }

View File

@ -71,33 +71,52 @@ class VariationTreeWidgetItem;
/// </summary> /// </summary>
class VariationTreeDoubleSpinBox : public DoubleSpinBox class VariationTreeDoubleSpinBox : public DoubleSpinBox
{ {
public: Q_OBJECT
/// <summary>
/// Constructor that passes agruments to the base and assigns the m_Param and m_Variation members.
/// </summary>
/// <param name="p">The parent widget</param>
/// <param name="widgetItem">The widget item this spinner is contained in</param>
/// <param name="id">The variation this spinner is for</param>
/// <param name="param">The name of the parameter this is for</param>
/// <param name="h">The height of the spin box. Default: 16.</param>
/// <param name="step">The step used to increment/decrement the spin box when using the mouse wheel. Default: 0.05.</param>
explicit VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, const string& param, int h = 16, double step = 0.05)
: DoubleSpinBox(p, h, step)
{
m_WidgetItem = widgetItem;
m_Param = param;
m_Id = id;
setDecimals(3);
}
public:
explicit VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, const string& param, int h = 16, double step = 0.05);
virtual ~VariationTreeDoubleSpinBox() { } virtual ~VariationTreeDoubleSpinBox() { }
bool IsParam() { return !m_Param.empty(); } bool IsParam() { return !m_Param.empty(); }
string ParamName() { return m_Param; } string ParamName() { return m_Param; }
eVariationId GetVariationId() { return m_Id; } eVariationId GetVariationId() { return m_Id; }
VariationTreeWidgetItem* WidgetItem() { return m_WidgetItem; } VariationTreeWidgetItem* WidgetItem() { return m_WidgetItem; }
public slots:
void PiActionTriggered(bool checked = false);
void TwoPiActionTriggered(bool checked = false);
void PiOver2ActionTriggered(bool checked = false);
void PiOver3ActionTriggered(bool checked = false);
void PiOver4ActionTriggered(bool checked = false);
void PiOver6ActionTriggered(bool checked = false);
void OneOverPiActionTriggered(bool checked = false);
void TwoOverPiActionTriggered(bool checked = false);
void ThreeOverPiActionTriggered(bool checked = false);
void FourOverPiActionTriggered(bool checked = false);
void SqrtTwoActionTriggered(bool checked = false);
void SqrtThreeActionTriggered(bool checked = false);
private: private:
string m_Param; string m_Param;
eVariationId m_Id; eVariationId m_Id;
VariationTreeWidgetItem* m_WidgetItem; VariationTreeWidgetItem* m_WidgetItem;
}; };
/// <summary>
/// Derivation for the double spin boxes that are in the
/// affine controls.
/// </summary>
class AffineDoubleSpinBox : public DoubleSpinBox
{
Q_OBJECT
public:
explicit AffineDoubleSpinBox(QWidget* p, int h = 20, double step = 0.01);
virtual ~AffineDoubleSpinBox() { }
public slots:
void NegOneActionTriggered(bool checked = false);
void ZeroActionTriggered(bool checked = false);
void OneActionTriggered(bool checked = false);
void FortyFiveActionTriggered(bool checked = false);
void NegFortyFiveActionTriggered(bool checked = false);
};

View File

@ -34,10 +34,10 @@ public:
{ {
} }
//~EmberTreeWidgetItemBase() ~EmberTreeWidgetItemBase()
//{ {
// qDebug() << "~EmberTreeWidgetItemBase()"; //qDebug() << "~EmberTreeWidgetItemBase()";
//} }
/// <summary> /// <summary>
/// Set the preview image for the tree widget item. /// Set the preview image for the tree widget item.

View File

@ -52,8 +52,12 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF
m_ItersCellIndex = row++;//Iters. m_ItersCellIndex = row++;//Iters.
m_PathCellIndex = row; m_PathCellIndex = row;
QStringList comboList; QStringList comboList;
#ifndef _WIN32
comboList.append("bmp");
#endif
comboList.append("jpg"); comboList.append("jpg");
comboList.append("png"); comboList.append("png");
comboList.append("exr");
m_Tbcw = new TwoButtonComboWidget("...", "Open", comboList, 22, 40, 22, table); m_Tbcw = new TwoButtonComboWidget("...", "Open", comboList, 22, 40, 22, table);
table->setCellWidget(row, 1, m_Tbcw); table->setCellWidget(row, 1, m_Tbcw);
table->item(row++, 1)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); table->item(row++, 1)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
@ -116,6 +120,7 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF
ui.FinalRenderSaveXmlCheckBox->setChecked( m_Settings->FinalSaveXml()); ui.FinalRenderSaveXmlCheckBox->setChecked( m_Settings->FinalSaveXml());
ui.FinalRenderDoAllCheckBox->setChecked( m_Settings->FinalDoAll()); ui.FinalRenderDoAllCheckBox->setChecked( m_Settings->FinalDoAll());
ui.FinalRenderDoSequenceCheckBox->setChecked( m_Settings->FinalDoSequence()); ui.FinalRenderDoSequenceCheckBox->setChecked( m_Settings->FinalDoSequence());
ui.FinalRenderPng16BitCheckBox->setChecked( m_Settings->FinalPng16Bit());
ui.FinalRenderKeepAspectCheckBox->setChecked( m_Settings->FinalKeepAspect()); ui.FinalRenderKeepAspectCheckBox->setChecked( m_Settings->FinalKeepAspect());
ui.FinalRenderThreadCountSpin->setValue( m_Settings->FinalThreadCount()); ui.FinalRenderThreadCountSpin->setValue( m_Settings->FinalThreadCount());
#ifdef _WIN32 #ifdef _WIN32
@ -136,11 +141,27 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF
m_SupersampleSpin->setValue(m_Settings->FinalSupersample()); m_SupersampleSpin->setValue(m_Settings->FinalSupersample());
m_StripsSpin->setValue(int(m_Settings->FinalStrips())); m_StripsSpin->setValue(int(m_Settings->FinalStrips()));
Scale(eScaleType(m_Settings->FinalScale())); Scale(eScaleType(m_Settings->FinalScale()));
int index = 0;
#ifdef _WIN32
if (m_Settings->FinalExt() == "jpg") if (m_Settings->FinalExt().endsWith("bmp", Qt::CaseInsensitive))
m_Tbcw->m_Combo->setCurrentIndex(0); m_Tbcw->m_Combo->setCurrentIndex(index);
else
m_Tbcw->m_Combo->setCurrentIndex(1); index++;
#endif
if (m_Settings->FinalExt().endsWith("jpg", Qt::CaseInsensitive))
m_Tbcw->m_Combo->setCurrentIndex(index);
index++;
if (m_Settings->FinalExt().endsWith("png", Qt::CaseInsensitive))
m_Tbcw->m_Combo->setCurrentIndex(index);
index++;
if (m_Settings->FinalExt().endsWith("exr", Qt::CaseInsensitive))
m_Tbcw->m_Combo->setCurrentIndex(index);
//Explicitly call these to enable/disable the appropriate controls. //Explicitly call these to enable/disable the appropriate controls.
OnOpenCLCheckBoxStateChanged(ui.FinalRenderOpenCLCheckBox->isChecked()); OnOpenCLCheckBoxStateChanged(ui.FinalRenderOpenCLCheckBox->isChecked());
@ -150,23 +171,20 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF
int desktopHeight = qApp->desktop()->availableGeometry().height(); int desktopHeight = qApp->desktop()->availableGeometry().height();
s.setHeight(std::min(s.height(), int(double(desktopHeight * 0.90)))); s.setHeight(std::min(s.height(), int(double(desktopHeight * 0.90))));
setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, qApp->desktop()->availableGeometry())); setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, qApp->desktop()->availableGeometry()));
ui.FinalRenderThreadHorizontalLayout->setAlignment(Qt::AlignLeft);
ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadCountSpin, Qt::AlignLeft);
ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadPriorityLabel, Qt::AlignLeft);
ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadPriorityComboBox, Qt::AlignLeft);
QWidget* w = SetTabOrder(this, ui.FinalRenderEarlyClipCheckBox, ui.FinalRenderYAxisUpCheckBox);
//Update these with new controls. //Update these with new controls.
QWidget* w = SetTabOrder(this, ui.FinalRenderEarlyClipCheckBox, ui.FinalRenderYAxisUpCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderTransparencyCheckBox); w = SetTabOrder(this, w, ui.FinalRenderTransparencyCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderOpenCLCheckBox); w = SetTabOrder(this, w, ui.FinalRenderOpenCLCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderPng16BitCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderDoublePrecisionCheckBox); w = SetTabOrder(this, w, ui.FinalRenderDoublePrecisionCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderSaveXmlCheckBox); w = SetTabOrder(this, w, ui.FinalRenderSaveXmlCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderDoAllCheckBox); w = SetTabOrder(this, w, ui.FinalRenderDoAllCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderDoSequenceCheckBox); w = SetTabOrder(this, w, ui.FinalRenderDoSequenceCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderCurrentSpin); w = SetTabOrder(this, w, ui.FinalRenderCurrentSpin);
w = SetTabOrder(this, w, ui.FinalRenderDeviceTable); w = SetTabOrder(this, w, ui.FinalRenderDeviceTable);
w = SetTabOrder(this, w, ui.FinalRenderApplyToAllCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderThreadCountSpin); w = SetTabOrder(this, w, ui.FinalRenderThreadCountSpin);
w = SetTabOrder(this, w, ui.FinalRenderThreadPriorityComboBox); w = SetTabOrder(this, w, ui.FinalRenderThreadPriorityComboBox);
w = SetTabOrder(this, w, ui.FinalRenderApplyToAllCheckBox);
w = SetTabOrder(this, w, m_WidthScaleSpin); w = SetTabOrder(this, w, m_WidthScaleSpin);
w = SetTabOrder(this, w, m_HeightScaleSpin); w = SetTabOrder(this, w, m_HeightScaleSpin);
w = SetTabOrder(this, w, ui.FinalRenderScaleNoneRadioButton); w = SetTabOrder(this, w, ui.FinalRenderScaleNoneRadioButton);
@ -215,9 +233,10 @@ bool FractoriumFinalRenderDialog::Double() { return ui.FinalRenderDoublePrecisio
bool FractoriumFinalRenderDialog::SaveXml() { return ui.FinalRenderSaveXmlCheckBox->isChecked(); } bool FractoriumFinalRenderDialog::SaveXml() { return ui.FinalRenderSaveXmlCheckBox->isChecked(); }
bool FractoriumFinalRenderDialog::DoAll() { return ui.FinalRenderDoAllCheckBox->isChecked(); } bool FractoriumFinalRenderDialog::DoAll() { return ui.FinalRenderDoAllCheckBox->isChecked(); }
bool FractoriumFinalRenderDialog::DoSequence() { return ui.FinalRenderDoSequenceCheckBox->isChecked(); } bool FractoriumFinalRenderDialog::DoSequence() { return ui.FinalRenderDoSequenceCheckBox->isChecked(); }
bool FractoriumFinalRenderDialog::Png16Bit() { return ui.FinalRenderPng16BitCheckBox->isChecked(); }
bool FractoriumFinalRenderDialog::KeepAspect() { return ui.FinalRenderKeepAspectCheckBox->isChecked(); } bool FractoriumFinalRenderDialog::KeepAspect() { return ui.FinalRenderKeepAspectCheckBox->isChecked(); }
bool FractoriumFinalRenderDialog::ApplyToAll() { return ui.FinalRenderApplyToAllCheckBox->isChecked(); } bool FractoriumFinalRenderDialog::ApplyToAll() { return ui.FinalRenderApplyToAllCheckBox->isChecked(); }
QString FractoriumFinalRenderDialog::Ext() { return m_Tbcw->m_Combo->currentIndex() == 0 ? "jpg" : "png"; } QString FractoriumFinalRenderDialog::Ext() { return m_Tbcw->m_Combo->currentText(); }
QString FractoriumFinalRenderDialog::Path() { return ui.FinalRenderParamsTable->item(m_PathCellIndex, 1)->text(); } QString FractoriumFinalRenderDialog::Path() { return ui.FinalRenderParamsTable->item(m_PathCellIndex, 1)->text(); }
void FractoriumFinalRenderDialog::Path(const QString& s) { ui.FinalRenderParamsTable->item(m_PathCellIndex, 1)->setText(s); } void FractoriumFinalRenderDialog::Path(const QString& s) { ui.FinalRenderParamsTable->item(m_PathCellIndex, 1)->setText(s); }
QString FractoriumFinalRenderDialog::Prefix() { return m_PrefixEdit->text(); } QString FractoriumFinalRenderDialog::Prefix() { return m_PrefixEdit->text(); }
@ -261,6 +280,7 @@ FinalRenderGuiState FractoriumFinalRenderDialog::State()
state.m_SaveXml = SaveXml(); state.m_SaveXml = SaveXml();
state.m_DoAll = DoAll(); state.m_DoAll = DoAll();
state.m_DoSequence = DoSequence(); state.m_DoSequence = DoSequence();
state.m_Png16Bit = Png16Bit();
state.m_KeepAspect = KeepAspect(); state.m_KeepAspect = KeepAspect();
state.m_Scale = Scale(); state.m_Scale = Scale();
state.m_Path = Path(); state.m_Path = Path();

View File

@ -60,6 +60,7 @@ public:
bool SaveXml(); bool SaveXml();
bool DoAll(); bool DoAll();
bool DoSequence(); bool DoSequence();
bool Png16Bit();
bool KeepAspect(); bool KeepAspect();
bool ApplyToAll(); bool ApplyToAll();
eScaleType Scale(); eScaleType Scale();

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>519</width> <width>573</width>
<height>941</height> <height>751</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -63,8 +63,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>507</width> <width>561</width>
<height>929</height> <height>739</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
@ -84,6 +84,76 @@
<number>0</number> <number>0</number>
</property> </property>
<item> <item>
<layout class="QGridLayout" name="FinalRenderGridLayout2">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item row="0" column="1">
<widget class="QLabel" name="FinalRenderPreviewLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>1</width>
<height>1</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QSpinBox" name="FinalRenderCurrentSpin">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="prefix">
<string/>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QGridLayout" name="FinalRenderGridLayout" columnstretch="0,0"> <layout class="QGridLayout" name="FinalRenderGridLayout" columnstretch="0,0">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QCheckBox" name="FinalRenderEarlyClipCheckBox"> <widget class="QCheckBox" name="FinalRenderEarlyClipCheckBox">
@ -165,77 +235,17 @@
</property> </property>
</widget> </widget>
</item> </item>
</layout> <item row="4" column="0">
</item> <widget class="QCheckBox" name="FinalRenderPng16BitCheckBox">
<item> <property name="toolTip">
<layout class="QGridLayout" name="FinalRenderGridLayout2"> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Save each RGBA component as 16-bits when saving Png files.&lt;/p&gt;&lt;p&gt;This leads to greater color precision for use in high end rendering and display on HDR monitors, however it makes the file size larger.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="FinalRenderPreviewLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>1</width>
<height>1</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property> </property>
<property name="text"> <property name="text">
<string/> <string>Save 16-bit Png</string>
</property>
<property name="textFormat">
<enum>Qt::PlainText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1"> </layout>
<widget class="QSpinBox" name="FinalRenderCurrentSpin">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="prefix">
<string/>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999999999</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item> </item>
</layout> </layout>
</item> </item>
@ -328,7 +338,10 @@
</widget> </widget>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="FinalRenderThreadHorizontalLayout" stretch="0,0,0"> <layout class="QHBoxLayout" name="FinalRenderThreadHorizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<item> <item>
<widget class="QSpinBox" name="FinalRenderThreadCountSpin"> <widget class="QSpinBox" name="FinalRenderThreadCountSpin">
<property name="sizePolicy"> <property name="sizePolicy">
@ -405,6 +418,19 @@
</item> </item>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@ -929,12 +955,29 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="FinalRenderImageCountLabel"> <layout class="QHBoxLayout" name="horizontalLayout">
<property name="text"> <item>
<string>0 / 0</string> <widget class="QTextEdit" name="FinalRenderTextOutput">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property> </property>
<property name="alignment"> <property name="maximumSize">
<set>Qt::AlignCenter</set> <size>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -1049,28 +1092,15 @@
</item> </item>
</layout> </layout>
</item> </item>
</layout>
</item>
<item> <item>
<widget class="QTextEdit" name="FinalRenderTextOutput"> <widget class="QLabel" name="FinalRenderImageCountLabel">
<property name="sizePolicy"> <property name="text">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding"> <string>0 / 0</string>
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property> </property>
<property name="maximumSize"> <property name="alignment">
<size> <set>Qt::AlignCenter</set>
<width>16777215</width>
<height>16777215</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
</property> </property>
</widget> </widget>
</item> </item>
@ -1160,13 +1190,22 @@
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>FinalRenderEarlyClipCheckBox</tabstop> <tabstop>FinalRenderEarlyClipCheckBox</tabstop>
<tabstop>FinalRenderDoublePrecisionCheckBox</tabstop>
<tabstop>FinalRenderYAxisUpCheckBox</tabstop> <tabstop>FinalRenderYAxisUpCheckBox</tabstop>
<tabstop>FinalRenderSaveXmlCheckBox</tabstop>
<tabstop>FinalRenderTransparencyCheckBox</tabstop> <tabstop>FinalRenderTransparencyCheckBox</tabstop>
<tabstop>FinalRenderDoAllCheckBox</tabstop>
<tabstop>FinalRenderOpenCLCheckBox</tabstop> <tabstop>FinalRenderOpenCLCheckBox</tabstop>
<tabstop>FinalRenderParamsTable</tabstop> <tabstop>FinalRenderDoSequenceCheckBox</tabstop>
<tabstop>FinalRenderTextOutput</tabstop> <tabstop>FinalRenderPng16BitCheckBox</tabstop>
<tabstop>FinalRenderStartButton</tabstop> <tabstop>FinalRenderCurrentSpin</tabstop>
<tabstop>FinalRenderStopButton</tabstop> <tabstop>FinalRenderStopButton</tabstop>
<tabstop>FinalRenderStartButton</tabstop>
<tabstop>FinalRenderScaleWidthRadioButton</tabstop>
<tabstop>FinalRenderScaleHeightRadioButton</tabstop>
<tabstop>FinalRenderScaleNoneRadioButton</tabstop>
<tabstop>FinalRenderKeepAspectCheckBox</tabstop>
<tabstop>FinalRenderTextOutput</tabstop>
<tabstop>FinalRenderCloseButton</tabstop> <tabstop>FinalRenderCloseButton</tabstop>
</tabstops> </tabstops>
<resources/> <resources/>

View File

@ -80,7 +80,7 @@ bool FinalRenderEmberControllerBase::CreateRendererFromGUI()
bool useOpenCL = m_Info->Ok() && m_FinalRenderDialog->OpenCL(); bool useOpenCL = m_Info->Ok() && m_FinalRenderDialog->OpenCL();
auto v = Devices(m_FinalRenderDialog->Devices()); auto v = Devices(m_FinalRenderDialog->Devices());
return CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, return CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER,
v, false); //Not shared. v, false, false); //Not shared.
} }
/// <summary> /// <summary>
@ -178,7 +178,7 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
size_t ftime; size_t ftime;
size_t finalImageIndex = 0; size_t finalImageIndex = 0;
std::thread writeThread; std::thread writeThread;
vector<byte> finalImages[2]; vector<v4F> finalImages[2];
EmberStats stats; EmberStats stats;
EmberImageComments comments; EmberImageComments comments;
Timing renderTimer; Timing renderTimer;
@ -225,8 +225,8 @@ FinalRenderEmberController<T>::FinalRenderEmberController(FractoriumFinalRenderD
finalImages[threadFinalImageIndex], finalImages[threadFinalImageIndex],
renderer->FinalRasW(), renderer->FinalRasW(),
renderer->FinalRasH(), renderer->FinalRasH(),
renderer->NumChannels(), m_FinalRenderDialog->Png16Bit(),
renderer->BytesPerChannel()); m_FinalRenderDialog->Transparency());
}, ftime, finalImageIndex); }, ftime, finalImageIndex);
m_FinishedImageCount.fetch_add(1); m_FinishedImageCount.fetch_add(1);
RenderComplete(*m_EmberFile.Get(ftime), stats, renderTimer); RenderComplete(*m_EmberFile.Get(ftime), stats, renderTimer);
@ -422,10 +422,9 @@ bool FinalRenderEmberController<T>::Render()
/// <param name="shared">True if shared with OpenGL, else false. Always false in this case.</param> /// <param name="shared">True if shared with OpenGL, else false. Always false in this case.</param>
/// <returns>True if nothing went wrong, else false.</returns> /// <returns>True if nothing went wrong, else false.</returns>
template <typename T> template <typename T>
bool FinalRenderEmberController<T>::CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared) bool FinalRenderEmberController<T>::CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool updatePreviews, bool shared)
{ {
bool ok = true; bool ok = true;
//uint channels = m_FinalRenderDialog->Ext().endsWith("png", Qt::CaseInsensitive) ? 4 : 3;
bool renderTypeMismatch = (m_Renderer.get() && (m_Renderer->RendererType() != renderType)) || bool renderTypeMismatch = (m_Renderer.get() && (m_Renderer->RendererType() != renderType)) ||
(!m_Renderers.empty() && (m_Renderers[0]->RendererType() != renderType)); (!m_Renderers.empty() && (m_Renderers[0]->RendererType() != renderType));
CancelRender(); CancelRender();
@ -542,35 +541,24 @@ template <typename T>
bool FinalRenderEmberController<T>::SyncGuiToRenderer() bool FinalRenderEmberController<T>::SyncGuiToRenderer()
{ {
bool ok = true; bool ok = true;
uint channels = m_FinalRenderDialog->Ext().endsWith("png", Qt::CaseInsensitive) ? 4 : 3;
if (m_Renderer.get()) if (m_Renderer.get())
{ {
if (m_Renderer->RendererType() == eRendererType::OPENCL_RENDERER)
channels = 4;//Always using 4 since the GL texture is RGBA.
m_Renderer->Callback(this); m_Renderer->Callback(this);
m_Renderer->NumChannels(channels);
m_Renderer->EarlyClip(m_FinalRenderDialog->EarlyClip()); m_Renderer->EarlyClip(m_FinalRenderDialog->EarlyClip());
m_Renderer->YAxisUp(m_FinalRenderDialog->YAxisUp()); m_Renderer->YAxisUp(m_FinalRenderDialog->YAxisUp());
m_Renderer->ThreadCount(m_FinalRenderDialog->ThreadCount()); m_Renderer->ThreadCount(m_FinalRenderDialog->ThreadCount());
m_Renderer->Priority((eThreadPriority)m_FinalRenderDialog->ThreadPriority()); m_Renderer->Priority((eThreadPriority)m_FinalRenderDialog->ThreadPriority());
m_Renderer->Transparency(m_FinalRenderDialog->Transparency());
} }
else if (!m_Renderers.empty()) else if (!m_Renderers.empty())
{ {
for (size_t i = 0; i < m_Renderers.size(); i++) for (size_t i = 0; i < m_Renderers.size(); i++)
{ {
if (m_Renderers[i]->RendererType() == eRendererType::OPENCL_RENDERER)
channels = 4;//Always using 4 since the GL texture is RGBA.
m_Renderers[i]->Callback(!i ? this : nullptr); m_Renderers[i]->Callback(!i ? this : nullptr);
m_Renderers[i]->NumChannels(channels);
m_Renderers[i]->EarlyClip(m_FinalRenderDialog->EarlyClip()); m_Renderers[i]->EarlyClip(m_FinalRenderDialog->EarlyClip());
m_Renderers[i]->YAxisUp(m_FinalRenderDialog->YAxisUp()); m_Renderers[i]->YAxisUp(m_FinalRenderDialog->YAxisUp());
m_Renderers[i]->ThreadCount(m_FinalRenderDialog->ThreadCount()); m_Renderers[i]->ThreadCount(m_FinalRenderDialog->ThreadCount());
m_Renderers[i]->Priority((eThreadPriority)m_FinalRenderDialog->ThreadPriority()); m_Renderers[i]->Priority((eThreadPriority)m_FinalRenderDialog->ThreadPriority());
m_Renderers[i]->Transparency(m_FinalRenderDialog->Transparency());
} }
} }
else else
@ -627,11 +615,11 @@ void FinalRenderEmberController<T>::ResetProgress(bool total)
/// specified in the widgets and compute the amount of memory required to render. /// specified in the widgets and compute the amount of memory required to render.
/// This includes the memory needed for the final output image. /// This includes the memory needed for the final output image.
/// </summary> /// </summary>
/// <returns>If successful, memory required in bytes, else zero.</returns> /// <returns>If successful, a tuple specifying the memory required in bytes for the histogram int he first element, the total memory in the second, and the iter count in the last, else zero.</returns>
template <typename T> template <typename T>
tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemory() tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemory()
{ {
size_t iterCount; size_t iterCount = 0;
pair<size_t, size_t> p(0, 0); pair<size_t, size_t> p(0, 0);
size_t strips; size_t strips;
uint channels = m_FinalRenderDialog->Ext() == "png" ? 4 : 3;//4 channels for Png, else 3. uint channels = m_FinalRenderDialog->Ext() == "png" ? 4 : 3;//4 channels for Png, else 3.
@ -642,7 +630,6 @@ tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemor
strips = VerifyStrips(m_Ember->m_FinalRasH, m_FinalRenderDialog->Strips(), strips = VerifyStrips(m_Ember->m_FinalRasH, m_FinalRenderDialog->Strips(),
[&](const string & s) {}, [&](const string & s) {}, [&](const string & s) {}); [&](const string & s) {}, [&](const string & s) {}, [&](const string & s) {});
m_Renderer->SetEmber(*m_Ember, eProcessAction::FULL_RENDER, true); m_Renderer->SetEmber(*m_Ember, eProcessAction::FULL_RENDER, true);
m_Renderer->NumChannels(channels);
m_FinalPreviewRenderer->Render(UINT_MAX, UINT_MAX); m_FinalPreviewRenderer->Render(UINT_MAX, UINT_MAX);
p = m_Renderer->MemoryRequired(strips, true, m_FinalRenderDialog->DoSequence()); p = m_Renderer->MemoryRequired(strips, true, m_FinalRenderDialog->DoSequence());
iterCount = m_Renderer->TotalIterCount(strips); iterCount = m_Renderer->TotalIterCount(strips);
@ -652,7 +639,6 @@ tuple<size_t, size_t, size_t> FinalRenderEmberController<T>::SyncAndComputeMemor
for (auto& renderer : m_Renderers) for (auto& renderer : m_Renderers)
{ {
renderer->SetEmber(*m_Ember, eProcessAction::FULL_RENDER, true); renderer->SetEmber(*m_Ember, eProcessAction::FULL_RENDER, true);
renderer->NumChannels(channels);
} }
m_FinalPreviewRenderer->Render(UINT_MAX, UINT_MAX); m_FinalPreviewRenderer->Render(UINT_MAX, UINT_MAX);
@ -711,24 +697,24 @@ template<typename T>
void FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember) void FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember)
{ {
auto comments = m_Renderer->ImageComments(m_Stats, 0, true); auto comments = m_Renderer->ImageComments(m_Stats, 0, true);
SaveCurrentRender(ember, comments, m_FinalImage, m_Renderer->FinalRasW(), m_Renderer->FinalRasH(), m_Renderer->NumChannels(), m_Renderer->BytesPerChannel()); SaveCurrentRender(ember, comments, m_FinalImage, m_Renderer->FinalRasW(), m_Renderer->FinalRasH(), m_FinalRenderDialog->Png16Bit(), m_FinalRenderDialog->Transparency());
} }
/// <summary> /// <summary>
/// Save the output of the render. /// Save the output of the render.
/// </summary> /// </summary>
/// <param name="ember">The ember whose rendered output will be saved</param> /// <param name="ember">The ember whose rendered output will be saved</param>
/// <param name="comments">The comments to save in the png or jpg</param> /// <param name="comments">The comments to save in the png, jpg or exr</param>
/// <param name="pixels">The buffer containing the pixels</param> /// <param name="pixels">The buffer containing the pixels</param>
/// <param name="width">The width in pixels of the image</param> /// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param> /// <param name="height">The height in pixels of the image</param>
/// <param name="channels">The number of channels, 3 or 4.</param> /// <param name="png16Bit">Whether to use 16 bits per channel per pixel when saving as Png.</param>
/// <param name="bpc">The bytes per channel, almost always 1.</param> /// <param name="transparency">Whether to use alpha when saving as Png or Exr.</param>
template<typename T> template<typename T>
void FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc) void FinalRenderEmberController<T>::SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency)
{ {
QString filename = ComposePath(QString::fromStdString(ember.m_Name)); QString filename = ComposePath(QString::fromStdString(ember.m_Name));
FractoriumEmberControllerBase::SaveCurrentRender(filename, comments, pixels, width, height, channels, bpc); FractoriumEmberControllerBase::SaveCurrentRender(filename, comments, pixels, width, height, png16Bit, transparency);
} }
/// <summary> /// <summary>
@ -806,6 +792,7 @@ void FinalRenderEmberController<T>::RenderComplete(Ember<T>& ember, const EmberS
m_Settings->FinalSaveXml(m_GuiState.m_SaveXml); m_Settings->FinalSaveXml(m_GuiState.m_SaveXml);
m_Settings->FinalDoAll(m_GuiState.m_DoAll); m_Settings->FinalDoAll(m_GuiState.m_DoAll);
m_Settings->FinalDoSequence(m_GuiState.m_DoSequence); m_Settings->FinalDoSequence(m_GuiState.m_DoSequence);
m_Settings->FinalPng16Bit(m_GuiState.m_Png16Bit);
m_Settings->FinalKeepAspect(m_GuiState.m_KeepAspect); m_Settings->FinalKeepAspect(m_GuiState.m_KeepAspect);
m_Settings->FinalScale(uint(m_GuiState.m_Scale)); m_Settings->FinalScale(uint(m_GuiState.m_Scale));
m_Settings->FinalExt(m_GuiState.m_Ext); m_Settings->FinalExt(m_GuiState.m_Ext);
@ -960,6 +947,7 @@ void FinalRenderPreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
QLabel* widget = d->ui.FinalRenderPreviewLabel; QLabel* widget = d->ui.FinalRenderPreviewLabel;
//Determine how to scale the scaled ember to fit in the label with a max of 100x100. //Determine how to scale the scaled ember to fit in the label with a max of 100x100.
auto e = m_Controller->m_Ember; auto e = m_Controller->m_Ember;
auto settings = FractoriumSettings::Instance();
if (e->m_FinalRasW >= e->m_FinalRasH) if (e->m_FinalRasW >= e->m_FinalRasH)
scalePercentage = T(maxDim) / e->m_FinalRasW; scalePercentage = T(maxDim) / e->m_FinalRasW;
@ -974,9 +962,7 @@ void FinalRenderPreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
m_PreviewEmber.m_PixelsPerUnit = scalePercentage * e->m_PixelsPerUnit; m_PreviewEmber.m_PixelsPerUnit = scalePercentage * e->m_PixelsPerUnit;
m_PreviewRenderer.EarlyClip(d->EarlyClip()); m_PreviewRenderer.EarlyClip(d->EarlyClip());
m_PreviewRenderer.YAxisUp(d->YAxisUp()); m_PreviewRenderer.YAxisUp(d->YAxisUp());
m_PreviewRenderer.Transparency(d->Transparency());
m_PreviewRenderer.Callback(nullptr); m_PreviewRenderer.Callback(nullptr);
m_PreviewRenderer.NumChannels(4);
m_PreviewRenderer.SetEmber(m_PreviewEmber); m_PreviewRenderer.SetEmber(m_PreviewEmber);
m_PreviewRenderer.PrepFinalAccumVector(m_PreviewFinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run(). m_PreviewRenderer.PrepFinalAccumVector(m_PreviewFinalImage);//Must manually call this first because it could be erroneously made smaller due to strips if called inside Renderer::Run().
auto strips = VerifyStrips(m_PreviewEmber.m_FinalRasH, d->Strips(), auto strips = VerifyStrips(m_PreviewEmber.m_FinalRasH, d->Strips(),
@ -987,8 +973,10 @@ void FinalRenderPreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
[&](size_t strip) {},//Error. [&](size_t strip) {},//Error.
[&](Ember<T>& finalEmber)//Final strip. [&](Ember<T>& finalEmber)//Final strip.
{ {
m_PreviewVec.resize(finalEmber.m_FinalRasW * finalEmber.m_FinalRasH * 4);
Rgba32ToRgba8(m_PreviewFinalImage.data(), m_PreviewVec.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, d->Transparency());
QImage image(int(finalEmber.m_FinalRasW), int(finalEmber.m_FinalRasH), QImage::Format_RGBA8888);//The label wants RGBA. QImage image(int(finalEmber.m_FinalRasW), int(finalEmber.m_FinalRasH), QImage::Format_RGBA8888);//The label wants RGBA.
memcpy(image.scanLine(0), m_PreviewFinalImage.data(), finalEmber.m_FinalRasW * finalEmber.m_FinalRasH * 4);//Memcpy the data in. memcpy(image.scanLine(0), m_PreviewVec.data(), SizeOf(m_PreviewVec));//Memcpy the data in.
QPixmap pixmap(QPixmap::fromImage(image)); QPixmap pixmap(QPixmap::fromImage(image));
QMetaObject::invokeMethod(widget, "setPixmap", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap)); QMetaObject::invokeMethod(widget, "setPixmap", Qt::QueuedConnection, Q_ARG(QPixmap, pixmap));
}); });

View File

@ -28,6 +28,7 @@ struct FinalRenderGuiState
bool m_Double; bool m_Double;
bool m_SaveXml; bool m_SaveXml;
bool m_DoAll; bool m_DoAll;
bool m_Png16Bit;
bool m_DoSequence; bool m_DoSequence;
bool m_KeepAspect; bool m_KeepAspect;
eScaleType m_Scale; eScaleType m_Scale;
@ -111,7 +112,7 @@ public:
#endif #endif
virtual void SetEmber(size_t index, bool verbatim) override; virtual void SetEmber(size_t index, bool verbatim) override;
virtual bool Render() override; virtual bool Render() override;
virtual bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared = true) override; virtual bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool updatePreviews, bool shared = true) override;
virtual int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs) override; virtual int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs) override;
virtual size_t Index() const override { return m_Ember->m_Index; } virtual size_t Index() const override { return m_Ember->m_Index; }
virtual uint SizeOfT() const override { return sizeof(T); } virtual uint SizeOfT() const override { return sizeof(T); }
@ -134,7 +135,7 @@ public:
protected: protected:
void HandleFinishedProgress(); void HandleFinishedProgress();
void SaveCurrentRender(Ember<T>& ember); void SaveCurrentRender(Ember<T>& ember);
void SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc); void SaveCurrentRender(Ember<T>& ember, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency);
void RenderComplete(Ember<T>& ember); void RenderComplete(Ember<T>& ember);
void RenderComplete(Ember<T>& ember, const EmberStats& stats, Timing& renderTimer); void RenderComplete(Ember<T>& ember, const EmberStats& stats, Timing& renderTimer);
void SyncGuiToEmber(Ember<T>& ember, size_t widthOverride = 0, size_t heightOverride = 0); void SyncGuiToEmber(Ember<T>& ember, size_t widthOverride = 0, size_t heightOverride = 0);
@ -158,6 +159,7 @@ class FinalRenderPreviewRenderer : public PreviewRenderer<T>
{ {
public: public:
using PreviewRenderer<T>::m_PreviewRun; using PreviewRenderer<T>::m_PreviewRun;
using PreviewRenderer<T>::m_PreviewVec;
using PreviewRenderer<T>::m_PreviewEmber; using PreviewRenderer<T>::m_PreviewEmber;
using PreviewRenderer<T>::m_PreviewRenderer; using PreviewRenderer<T>::m_PreviewRenderer;
using PreviewRenderer<T>::m_PreviewFinalImage; using PreviewRenderer<T>::m_PreviewFinalImage;

View File

@ -26,6 +26,7 @@ Fractorium::Fractorium(QWidget* p)
qRegisterMetaType<size_t>("size_t"); qRegisterMetaType<size_t>("size_t");
qRegisterMetaType<QVector<int>>("QVector<int>");//For previews. qRegisterMetaType<QVector<int>>("QVector<int>");//For previews.
qRegisterMetaType<vector<byte>>("vector<byte>"); qRegisterMetaType<vector<byte>>("vector<byte>");
qRegisterMetaType<vv4F>("vv4F");
qRegisterMetaType<EmberTreeWidgetItemBase*>("EmberTreeWidgetItemBase*"); qRegisterMetaType<EmberTreeWidgetItemBase*>("EmberTreeWidgetItemBase*");
setDockOptions(DockOption::AllowNestedDocks | DockOption::AllowTabbedDocks); setDockOptions(DockOption::AllowNestedDocks | DockOption::AllowTabbedDocks);
setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition::North); setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::TabPosition::North);
@ -505,7 +506,7 @@ void Fractorium::SetFixedTableHeader(QHeaderView* header, QHeaderView::ResizeMod
/// Setup and show the open XML dialog. /// Setup and show the open XML dialog.
/// This will perform lazy instantiation. /// This will perform lazy instantiation.
/// </summary> /// </summary>
/// <returns>The filename selected</returns> /// <returns>The list of filenames selected</returns>
QStringList Fractorium::SetupOpenXmlDialog() QStringList Fractorium::SetupOpenXmlDialog()
{ {
#ifndef __APPLE__ #ifndef __APPLE__
@ -522,7 +523,7 @@ QStringList Fractorium::SetupOpenXmlDialog()
connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->OpenXmlExt(filter); }); connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->OpenXmlExt(filter); });
m_FileDialog->setFileMode(QFileDialog::ExistingFiles); m_FileDialog->setFileMode(QFileDialog::ExistingFiles);
m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen); m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen);
m_FileDialog->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)"); m_FileDialog->setNameFilter("*.flam3;;*.flame;;*.xml");
m_FileDialog->setWindowTitle("Open Flame"); m_FileDialog->setWindowTitle("Open Flame");
m_FileDialog->setDirectory(m_Settings->OpenFolder()); m_FileDialog->setDirectory(m_Settings->OpenFolder());
m_FileDialog->selectNameFilter(m_Settings->OpenXmlExt()); m_FileDialog->selectNameFilter(m_Settings->OpenXmlExt());
@ -567,30 +568,23 @@ QString Fractorium::SetupSaveXmlDialog(const QString& defaultFilename)
QString filename; QString filename;
m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&))); m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&)));
connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->SaveXmlExt(filter); }); connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter)
{
m_Settings->SaveXmlExt(filter);
m_FileDialog->setDefaultSuffix(filter);
});
//This must come first because it clears various internal states which allow the file text to be properly set. //This must come first because it clears various internal states which allow the file text to be properly set.
//This is most likely a bug in QFileDialog. //This is most likely a bug in QFileDialog.
m_FileDialog->setAcceptMode(QFileDialog::AcceptSave); m_FileDialog->setAcceptMode(QFileDialog::AcceptSave);
m_FileDialog->selectFile(defaultFilename); m_FileDialog->selectFile(defaultFilename);
m_FileDialog->setNameFilter("Flam3 (*.flam3);;Flame (*.flame);;Xml (*.xml)"); m_FileDialog->setNameFilter(".flam3;;.flame;;.xml");
m_FileDialog->setWindowTitle("Save flame as xml"); m_FileDialog->setWindowTitle("Save flame as xml");
m_FileDialog->setDirectory(m_Settings->SaveFolder()); m_FileDialog->setDirectory(m_Settings->SaveFolder());
m_FileDialog->selectNameFilter(m_Settings->SaveXmlExt()); m_FileDialog->selectNameFilter(m_Settings->SaveXmlExt());
m_FileDialog->setDefaultSuffix(m_Settings->SaveXmlExt());
if (m_FileDialog->exec() == QDialog::Accepted) if (m_FileDialog->exec() == QDialog::Accepted)
{
filename = m_FileDialog->selectedFiles().value(0); filename = m_FileDialog->selectedFiles().value(0);
//For some reason, linux doesn't automatically append this, but Windows does. Force it to be safe.
auto filt = m_FileDialog->selectedNameFilter().split('*');//Qt makes it very hard to get the actual extension used.
if (filt.size() > 1)//Should always be true.
{
auto s = filt[1].replace(")", "");
if (!filename.endsWith(s))
filename.append(s);
}
}
#else #else
auto defaultFilter(m_Settings->SaveXmlExt()); auto defaultFilter(m_Settings->SaveXmlExt());
@ -619,7 +613,11 @@ QString Fractorium::SetupSaveImageDialog(const QString& defaultFilename)
QString filename; QString filename;
m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&))); m_FileDialog->disconnect(SIGNAL(filterSelected(const QString&)));
connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter) { m_Settings->SaveImageExt(filter); }); connect(m_FileDialog, &QFileDialog::filterSelected, [&](const QString & filter)
{
m_Settings->SaveImageExt(filter);
m_FileDialog->setDefaultSuffix(filter);
});
//This must come first because it clears various internal states which allow the file text to be properly set. //This must come first because it clears various internal states which allow the file text to be properly set.
//This is most likely a bug in QFileDialog. //This is most likely a bug in QFileDialog.
m_FileDialog->setAcceptMode(QFileDialog::AcceptSave); m_FileDialog->setAcceptMode(QFileDialog::AcceptSave);
@ -627,17 +625,22 @@ QString Fractorium::SetupSaveImageDialog(const QString& defaultFilename)
m_FileDialog->setFileMode(QFileDialog::AnyFile); m_FileDialog->setFileMode(QFileDialog::AnyFile);
m_FileDialog->setOption(QFileDialog::ShowDirsOnly, false); m_FileDialog->setOption(QFileDialog::ShowDirsOnly, false);
m_FileDialog->setOption(QFileDialog::DontUseNativeDialog, false); m_FileDialog->setOption(QFileDialog::DontUseNativeDialog, false);
m_FileDialog->setNameFilter("Jpeg (*.jpg);;Png (*.png);;Bmp (*.bmp)"); #ifdef _WIN32
m_FileDialog->setNameFilter(".bmp;;.jpg;;.png;;.exr");
#else
m_FileDialog->setNameFilter(".jpg;;.png;;.exr");
#endif
m_FileDialog->setWindowTitle("Save image"); m_FileDialog->setWindowTitle("Save image");
m_FileDialog->setDirectory(m_Settings->SaveFolder()); m_FileDialog->setDirectory(m_Settings->SaveFolder());
m_FileDialog->selectNameFilter(m_Settings->SaveImageExt()); m_FileDialog->selectNameFilter(m_Settings->SaveImageExt());
m_FileDialog->setDefaultSuffix(m_Settings->SaveImageExt());
if (m_FileDialog->exec() == QDialog::Accepted) if (m_FileDialog->exec() == QDialog::Accepted)
filename = m_FileDialog->selectedFiles().value(0); filename = m_FileDialog->selectedFiles().value(0);
#else #else
auto defaultFilter(m_Settings->SaveImageExt()); auto defaultFilter(m_Settings->SaveImageExt());
auto filename = QFileDialog::getSaveFileName(this, tr("Save image"), m_Settings->SaveFolder() + "/" + defaultFilename, tr("Jpeg (*.jpg);;Png (*.png);;Bmp (*.bmp)"), &defaultFilter); auto filename = QFileDialog::getSaveFileName(this, tr("Save image"), m_Settings->SaveFolder() + "/" + defaultFilename, tr("Jpg (*.jpg);;Png (*.png);;Exr (*.exr)"), &defaultFilter);
m_Settings->SaveImageExt(defaultFilter); m_Settings->SaveImageExt(defaultFilter);
#endif #endif
return filename; return filename;

View File

@ -339,18 +339,18 @@ public slots:
void OnSummaryTreeHeaderSectionClicked(int logicalIndex); void OnSummaryTreeHeaderSectionClicked(int logicalIndex);
//Rendering/progress. //Rendering/progress.
void StartRenderTimer(); void StartRenderTimer(bool updatePreviews);
void IdleTimer(); void IdleTimer();
bool ControllersOk(); bool ControllersOk();
void ShowCritical(const QString& title, const QString& text, bool invokeRequired = false); void ShowCritical(const QString& title, const QString& text, bool invokeRequired = false);
//Can't have a template function be a slot. //Can't have a template function be a slot.
void SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vector<byte>& v, uint w, uint h); void SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vv4F& v, uint w, uint h);
public: public:
//template<typename spinType, typename valType>//See below. //template<typename spinType, typename valType>//See below.
//static void SetupSpinner(QTableWidget* table, const QObject* receiver, int& row, int col, spinType*& spinBox, int height, valType min, valType max, valType step, const char* signal, const char* slot, bool incRow = true, valType val = 0, valType doubleClickZero = -999, valType doubleClickNonZero = -999); //static void SetupSpinner(QTableWidget* table, const QObject* receiver, int& row, int col, spinType*& spinBox, int height, valType min, valType max, valType step, const char* signal, const char* slot, bool incRow = true, valType val = 0, valType doubleClickZero = -999, valType doubleClickNonZero = -999);
static void SetupAffineSpinner(QTableWidget* table, const QObject* receiver, int row, int col, DoubleSpinBox*& spinBox, int height, double min, double max, double step, double prec, const char* signal, const char* slot); static void SetupAffineSpinner(QTableWidget* table, const QObject* receiver, int row, int col, AffineDoubleSpinBox*& spinBox, int height, double min, double max, double step, double prec, const char* signal, const char* slot);
static void SetupCombo(QTableWidget* table, const QObject* receiver, int& row, int col, StealthComboBox*& comboBox, const vector<string>& vals, const char* signal, const char* slot, Qt::ConnectionType connectionType = Qt::QueuedConnection); static void SetupCombo(QTableWidget* table, const QObject* receiver, int& row, int col, StealthComboBox*& comboBox, const vector<string>& vals, const char* signal, const char* slot, Qt::ConnectionType connectionType = Qt::QueuedConnection);
static void SetFixedTableHeader(QHeaderView* header, QHeaderView::ResizeMode mode = QHeaderView::Fixed); static void SetFixedTableHeader(QHeaderView* header, QHeaderView::ResizeMode mode = QHeaderView::Fixed);
@ -422,8 +422,8 @@ private:
void ErrorReportToQTextEdit(const vector<string>& errors, QTextEdit* textEdit, bool clear = true); void ErrorReportToQTextEdit(const vector<string>& errors, QTextEdit* textEdit, bool clear = true);
//Rendering/progress. //Rendering/progress.
void ShutdownAndRecreateFromOptions(); void ShutdownAndRecreateFromOptions(bool updatePreviews);
bool CreateRendererFromOptions(); bool CreateRendererFromOptions(bool updatePreviews);
bool CreateControllerFromOptions(); bool CreateControllerFromOptions();
void EnableRenderControls(bool enable); void EnableRenderControls(bool enable);
@ -489,22 +489,22 @@ private:
DoubleSpinBox* m_XformDirectColorSpin; DoubleSpinBox* m_XformDirectColorSpin;
//Xforms Affine. //Xforms Affine.
DoubleSpinBox* m_PreX1Spin;//Pre. AffineDoubleSpinBox* m_PreX1Spin;//Pre.
DoubleSpinBox* m_PreX2Spin; AffineDoubleSpinBox* m_PreX2Spin;
DoubleSpinBox* m_PreY1Spin; AffineDoubleSpinBox* m_PreY1Spin;
DoubleSpinBox* m_PreY2Spin; AffineDoubleSpinBox* m_PreY2Spin;
DoubleSpinBox* m_PreO1Spin; AffineDoubleSpinBox* m_PreO1Spin;
DoubleSpinBox* m_PreO2Spin; AffineDoubleSpinBox* m_PreO2Spin;
DoubleSpinBox* m_PostX1Spin;//Post. AffineDoubleSpinBox* m_PostX1Spin;//Post.
DoubleSpinBox* m_PostX2Spin; AffineDoubleSpinBox* m_PostX2Spin;
DoubleSpinBox* m_PostY1Spin; AffineDoubleSpinBox* m_PostY1Spin;
DoubleSpinBox* m_PostY2Spin; AffineDoubleSpinBox* m_PostY2Spin;
DoubleSpinBox* m_PostO1Spin; AffineDoubleSpinBox* m_PostO1Spin;
DoubleSpinBox* m_PostO2Spin; AffineDoubleSpinBox* m_PostO2Spin;
DoubleSpinBox* m_PreSpins[6]; AffineDoubleSpinBox* m_PreSpins[6];
DoubleSpinBox* m_PostSpins[6]; AffineDoubleSpinBox* m_PostSpins[6];
//Xaos. //Xaos.
DoubleSpinBox* m_XaosSpinBox; DoubleSpinBox* m_XaosSpinBox;
@ -555,6 +555,7 @@ private:
int m_VarSortMode; int m_VarSortMode;
int m_PaletteSortMode; int m_PaletteSortMode;
int m_PreviousPaletteRow; int m_PreviousPaletteRow;
vector<byte> m_PreviewVec;
shared_ptr<OpenCLInfo> m_Info; shared_ptr<OpenCLInfo> m_Info;
unique_ptr<FractoriumEmberControllerBase> m_Controller; unique_ptr<FractoriumEmberControllerBase> m_Controller;
Ui::FractoriumClass ui; Ui::FractoriumClass ui;

View File

@ -18,7 +18,7 @@ FractoriumEmberControllerBase::FractoriumEmberControllerBase(Fractorium* fractor
m_RenderTimer->setInterval(0); m_RenderTimer->setInterval(0);
m_Fractorium->connect(m_RenderTimer.get(), SIGNAL(timeout()), SLOT(IdleTimer())); m_Fractorium->connect(m_RenderTimer.get(), SIGNAL(timeout()), SLOT(IdleTimer()));
m_RenderRestartTimer = make_unique<QTimer>(m_Fractorium); m_RenderRestartTimer = make_unique<QTimer>(m_Fractorium);
m_Fractorium->connect(m_RenderRestartTimer.get(), SIGNAL(timeout()), SLOT(StartRenderTimer())); m_Fractorium->connect(m_RenderRestartTimer.get(), &QTimer::timeout, [&]() { m_Fractorium->StartRenderTimer(false); });//It's ok to pass false for the first shot because creating the controller will start the preview renders.
} }
/// <summary> /// <summary>
@ -368,7 +368,6 @@ void TreePreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
auto f = m_Controller->m_Fractorium; auto f = m_Controller->m_Fractorium;
m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip()); m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip());
m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp()); m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp());
m_PreviewRenderer.Transparency(f->m_Settings->Transparency());
m_PreviewRenderer.ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe. m_PreviewRenderer.ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
if (auto top = m_Tree->topLevelItem(0)) if (auto top = m_Tree->topLevelItem(0))
@ -394,7 +393,7 @@ void TreePreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
//until the update is complete. //until the update is complete.
QMetaObject::invokeMethod(f, "SetLibraryTreeItemData", Qt::BlockingQueuedConnection, QMetaObject::invokeMethod(f, "SetLibraryTreeItemData", Qt::BlockingQueuedConnection,
Q_ARG(EmberTreeWidgetItemBase*, treeItem), Q_ARG(EmberTreeWidgetItemBase*, treeItem),
Q_ARG(vector<byte>&, m_PreviewFinalImage), Q_ARG(vv4F&, m_PreviewFinalImage),
Q_ARG(uint, PREVIEW_SIZE), Q_ARG(uint, PREVIEW_SIZE),
Q_ARG(uint, PREVIEW_SIZE)); Q_ARG(uint, PREVIEW_SIZE));
} }

View File

@ -172,7 +172,7 @@ public:
virtual void AffineInterpTypeChanged(int i) { } virtual void AffineInterpTypeChanged(int i) { }
virtual void InterpTypeChanged(int i) { } virtual void InterpTypeChanged(int i) { }
virtual void BackgroundChanged(const QColor& color) { } virtual void BackgroundChanged(const QColor& color) { }
virtual void ClearColorCurves() { } virtual void ClearColorCurves(int i) { }
virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) { } virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) { }
//Xforms. //Xforms.
@ -240,7 +240,7 @@ public:
//Rendering/progress. //Rendering/progress.
virtual bool Render() { return false; } virtual bool Render() { return false; }
virtual bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared = true) { return false; } virtual bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool updatePreviews, bool shared = true) { return false; }
virtual uint SizeOfT() const { return 0; } virtual uint SizeOfT() const { return 0; }
virtual void ClearUndo() { } virtual void ClearUndo() { }
virtual GLEmberControllerBase* GLController() { return nullptr; } virtual GLEmberControllerBase* GLController() { return nullptr; }
@ -252,10 +252,10 @@ public:
void Shutdown(); void Shutdown();
void UpdateRender(eProcessAction action = eProcessAction::FULL_RENDER); void UpdateRender(eProcessAction action = eProcessAction::FULL_RENDER);
void DeleteRenderer(); void DeleteRenderer();
void SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc); void SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency);
RendererBase* Renderer() { return m_Renderer.get(); } RendererBase* Renderer() { return m_Renderer.get(); }
vector<byte>* FinalImage() { return &(m_FinalImage); } vector<v4F>* FinalImage() { return &(m_FinalImage); }
vector<byte>* PreviewFinalImage() { return &m_PreviewFinalImage; } vector<v4F>* PreviewFinalImage() { return &m_PreviewFinalImage; }
EmberStats Stats() { return m_Stats; } EmberStats Stats() { return m_Stats; }
protected: protected:
@ -286,8 +286,8 @@ protected:
string m_CurrentPaletteFilePath; string m_CurrentPaletteFilePath;
std::recursive_mutex m_Cs; std::recursive_mutex m_Cs;
std::thread m_WriteThread; std::thread m_WriteThread;
vector<byte> m_FinalImage; vector<v4F> m_FinalImage;
vector<byte> m_PreviewFinalImage; vector<v4F> m_PreviewFinalImage;
vector<eProcessAction> m_ProcessActions; vector<eProcessAction> m_ProcessActions;
vector<eVariationId> m_FilteredVariations; vector<eVariationId> m_FilteredVariations;
unique_ptr<EmberNs::RendererBase> m_Renderer; unique_ptr<EmberNs::RendererBase> m_Renderer;
@ -436,7 +436,7 @@ public:
virtual void AffineInterpTypeChanged(int index) override; virtual void AffineInterpTypeChanged(int index) override;
virtual void InterpTypeChanged(int index) override; virtual void InterpTypeChanged(int index) override;
virtual void BackgroundChanged(const QColor& col) override; virtual void BackgroundChanged(const QColor& col) override;
virtual void ClearColorCurves() override; virtual void ClearColorCurves(int i) override;
virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) override; virtual void ColorCurveChanged(int curveIndex, int pointInxed, const QPointF& point) override;
//Xforms. //Xforms.
@ -510,7 +510,7 @@ public:
//Rendering/progress. //Rendering/progress.
virtual bool Render() override; virtual bool Render() override;
virtual bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared = true) override; virtual bool CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool updatePreviews, bool shared = true) override;
virtual uint SizeOfT() const override { return sizeof(T); } virtual uint SizeOfT() const override { return sizeof(T); }
virtual int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs) override; virtual int ProgressFunc(Ember<T>& ember, void* foo, double fraction, int stage, double etaMs) override;
virtual void ClearUndo() override; virtual void ClearUndo() override;
@ -607,11 +607,6 @@ public:
return m_PreviewRenderer.YAxisUp(); return m_PreviewRenderer.YAxisUp();
} }
bool Transparency()
{
return m_PreviewRenderer.Transparency();
}
bool Running() bool Running()
{ {
return m_PreviewRun || m_PreviewResult.isRunning(); return m_PreviewRun || m_PreviewResult.isRunning();
@ -622,7 +617,8 @@ public:
protected: protected:
volatile bool m_PreviewRun = false; volatile bool m_PreviewRun = false;
Ember<T> m_PreviewEmber; Ember<T> m_PreviewEmber;
vector<byte> m_PreviewFinalImage; vector<byte> m_PreviewVec;
vv4F m_PreviewFinalImage;
EmberNs::Renderer<T, float> m_PreviewRenderer; EmberNs::Renderer<T, float> m_PreviewRenderer;
private: private:
@ -639,6 +635,7 @@ class TreePreviewRenderer : public PreviewRenderer<T>
public: public:
using PreviewRenderer<T>::m_PreviewRun; using PreviewRenderer<T>::m_PreviewRun;
using PreviewRenderer<T>::m_PreviewEmber; using PreviewRenderer<T>::m_PreviewEmber;
using PreviewRenderer<T>::m_PreviewVec;
using PreviewRenderer<T>::m_PreviewRenderer; using PreviewRenderer<T>::m_PreviewRenderer;
using PreviewRenderer<T>::m_PreviewFinalImage; using PreviewRenderer<T>::m_PreviewFinalImage;
@ -655,10 +652,8 @@ public:
{ {
auto f = m_Controller->m_Fractorium; auto f = m_Controller->m_Fractorium;
m_PreviewRenderer.Callback(nullptr); m_PreviewRenderer.Callback(nullptr);
m_PreviewRenderer.NumChannels(4);
m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip()); m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip());
m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp()); m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp());
m_PreviewRenderer.Transparency(f->m_Settings->Transparency());
} }
virtual void PreviewRenderFunc(uint start, uint end) override; virtual void PreviewRenderFunc(uint start, uint end) override;

View File

@ -96,15 +96,18 @@ vector<pair<size_t, QTreeWidgetItem*>> Fractorium::GetCurrentEmberIndex()
/// <param name="v">The vector holding the RGBA bitmap</param> /// <param name="v">The vector holding the RGBA bitmap</param>
/// <param name="w">The width of the bitmap</param> /// <param name="w">The width of the bitmap</param>
/// <param name="h">The height of the bitmap</param> /// <param name="h">The height of the bitmap</param>
void Fractorium::SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vector<byte>& v, uint w, uint h) void Fractorium::SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vv4F& v, uint w, uint h)
{ {
item->SetImage(v, w, h); m_PreviewVec.resize(w * h * 4);
Rgba32ToRgba8(v.data(), m_PreviewVec.data(), w, h, m_Settings->Transparency());
item->SetImage(m_PreviewVec, w, h);
} }
/// <summary> /// <summary>
/// Set all libary tree entries to the name of the corresponding ember they represent. /// Set all libary tree entries to the name of the corresponding ember they represent.
/// Set all libary tree entries to point to the underlying ember they represent. /// Set all libary tree entries to point to the underlying ember they represent.
/// </summary> /// </summary>
/// <param name="update">A bitfield representing the type of synchronizing to do. Update one or more of index, name or pointer.</param>
template <typename T> template <typename T>
void FractoriumEmberController<T>::SyncLibrary(eLibraryUpdate update) void FractoriumEmberController<T>::SyncLibrary(eLibraryUpdate update)
{ {

View File

@ -349,9 +349,10 @@ void Fractorium::OnActionSaveCurrentScreen(bool checked)
auto filename = SetupSaveImageDialog(m_Controller->Name()); auto filename = SetupSaveImageDialog(m_Controller->Name());
auto renderer = m_Controller->Renderer(); auto renderer = m_Controller->Renderer();
auto& pixels = *m_Controller->FinalImage(); auto& pixels = *m_Controller->FinalImage();
auto rendererCL = dynamic_cast<RendererCLBase*>(m_Controller->Renderer()); auto rendererCL = dynamic_cast<RendererCLBase*>(renderer);
auto stats = m_Controller->Stats(); auto stats = m_Controller->Stats();
auto comments = renderer->ImageComments(stats, 0, true); auto comments = renderer->ImageComments(stats, 0, true);
auto settings = FractoriumSettings::Instance();
if (rendererCL && renderer->PrepFinalAccumVector(pixels)) if (rendererCL && renderer->PrepFinalAccumVector(pixels))
{ {
@ -362,7 +363,7 @@ void Fractorium::OnActionSaveCurrentScreen(bool checked)
} }
} }
m_Controller->SaveCurrentRender(filename, comments, pixels, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels(), renderer->BytesPerChannel()); m_Controller->SaveCurrentRender(filename, comments, pixels, renderer->FinalRasW(), renderer->FinalRasH(), settings->Png16Bit(), settings->Transparency());
} }
/// <summary> /// <summary>
@ -879,7 +880,7 @@ void Fractorium::OnActionFinalRender(bool checked)
void Fractorium::OnFinalRenderClose(int result) void Fractorium::OnFinalRenderClose(int result)
{ {
m_RenderStatusLabel->setText("Renderer starting..."); m_RenderStatusLabel->setText("Renderer starting...");
StartRenderTimer();//Re-create the renderer and start rendering again. StartRenderTimer(false);//Re-create the renderer and start rendering again.
ui.ActionStartStopRenderer->setChecked(false);//Re-enable any controls that might have been disabled. ui.ActionStartStopRenderer->setChecked(false);//Re-enable any controls that might have been disabled.
OnActionStartStopRenderer(false); OnActionStartStopRenderer(false);
} }
@ -892,10 +893,17 @@ void Fractorium::OnFinalRenderClose(int result)
/// <param name="checked">Ignored</param> /// <param name="checked">Ignored</param>
void Fractorium::OnActionOptions(bool checked) void Fractorium::OnActionOptions(bool checked)
{ {
bool ec = m_Settings->EarlyClip();
bool yup = m_Settings->YAxisUp();
bool trans = m_Settings->Transparency();
if (m_OptionsDialog->exec()) if (m_OptionsDialog->exec())
{ {
bool updatePreviews = ec != m_Settings->EarlyClip() ||
yup != m_Settings->YAxisUp() ||
trans != m_Settings->Transparency();
SyncOptionsToToolbar();//This won't trigger a recreate, the call below handles it. SyncOptionsToToolbar();//This won't trigger a recreate, the call below handles it.
ShutdownAndRecreateFromOptions();//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer. ShutdownAndRecreateFromOptions(updatePreviews);//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer.
} }
} }

View File

@ -491,21 +491,38 @@ void Fractorium::SetPaletteFileComboIndex(const string& filename)
} }
/// <summary> /// <summary>
/// Reset the color curve values in the current ember to their default state and also update the curves control. /// Reset the color curve values for the selected curve in the current ember to their default state and also update the curves control.
/// Called when ResetCurvesButton is clicked. /// Called when ResetCurvesButton is clicked.
/// Note if they click Reset Curves when the "All" radio button is selected, then it clears all curves.
/// Resets the rendering process at either ACCUM_ONLY by default, or FILTER_AND_ACCUM when using early clip. /// Resets the rendering process at either ACCUM_ONLY by default, or FILTER_AND_ACCUM when using early clip.
/// </summary> /// </summary>
/// <param name="i">The index of the curve to be cleared, 0 to clear all.</param>
template <typename T> template <typename T>
void FractoriumEmberController<T>::ClearColorCurves() void FractoriumEmberController<T>::ClearColorCurves(int i)
{ {
Update([&] Update([&]
{ {
m_Ember.m_Curves.Init(); if (i)
m_Ember.m_Curves.Init(i);
else
m_Ember.m_Curves.Init(0);
}, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY); }, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY);
FillCurvesControl(); FillCurvesControl();
} }
void Fractorium::OnResetCurvesButtonClicked(bool checked) { m_Controller->ClearColorCurves(); } void Fractorium::OnResetCurvesButtonClicked(bool checked)
{
if (ui.CurvesAllRadio->isChecked())
m_Controller->ClearColorCurves(0);
else if (ui.CurvesRedRadio->isChecked())
m_Controller->ClearColorCurves(1);
else if (ui.CurvesGreenRadio->isChecked())
m_Controller->ClearColorCurves(2);
else if (ui.CurvesBlueRadio->isChecked())
m_Controller->ClearColorCurves(3);
else
m_Controller->ClearColorCurves(0);
}
/// <summary> /// <summary>
/// Set the coordinate of the curve point. /// Set the coordinate of the curve point.

View File

@ -115,16 +115,16 @@
#include <QWidget> #include <QWidget>
#include <QWidgetAction> #include <QWidgetAction>
#define GLM_FORCE_RADIANS 1 //#define GLM_FORCE_RADIANS 1
#define GLM_ENABLE_EXPERIMENTAL 1 //#define GLM_ENABLE_EXPERIMENTAL 1
#ifndef __APPLE__ //#ifndef __APPLE__
#define GLM_FORCE_INLINE 1 // #define GLM_FORCE_INLINE 1
#endif //#endif
//
#include "glm/glm.hpp" //#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp" //#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/type_ptr.hpp" //#include "glm/gtc/type_ptr.hpp"
#ifndef _WIN32 #ifndef _WIN32
#undef Bool #undef Bool

View File

@ -111,49 +111,66 @@ void FractoriumEmberControllerBase::DeleteRenderer()
/// This will embed the id, url and nick fields from the options in the image comments. /// This will embed the id, url and nick fields from the options in the image comments.
/// </summary> /// </summary>
/// <param name="filename">The full path and filename</param> /// <param name="filename">The full path and filename</param>
/// <param name="comments">The comments to save in the png or jpg</param> /// <param name="comments">The comments to save in the png, jpg or exr</param>
/// <param name="pixels">The buffer containing the pixels</param> /// <param name="pixels">The buffer containing the pixels</param>
/// <param name="width">The width in pixels of the image</param> /// <param name="width">The width in pixels of the image</param>
/// <param name="height">The height in pixels of the image</param> /// <param name="height">The height in pixels of the image</param>
/// <param name="channels">The number of channels, 3 or 4.</param> /// <param name="png16Bit">Whether to use 16 bits per channel per pixel when saving as Png.</param>
/// <param name="bpc">The bytes per channel, almost always 1.</param> /// <param name="transparency">Whether to use alpha when saving as Png or Exr.</param>
void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector<byte>& pixels, size_t width, size_t height, size_t channels, size_t bpc) void FractoriumEmberControllerBase::SaveCurrentRender(const QString& filename, const EmberImageComments& comments, vector<v4F>& pixels, size_t width, size_t height, bool png16Bit, bool transparency)
{ {
if (filename != "") if (filename != "")
{ {
bool b = false; bool b = false;
byte* data = nullptr; auto size = width * height;
vector<byte> vecRgb; auto settings = m_Fractorium->m_Settings;
QFileInfo fileInfo(filename); QFileInfo fileInfo(filename);
QString suffix = fileInfo.suffix(); QString suffix = fileInfo.suffix();
auto settings = m_Fractorium->m_Settings;
//Ensure dimensions are valid.
if (pixels.size() < (width * height * channels * bpc))
{
m_Fractorium->ShowCritical("Save Failed", "Dimensions didn't match, not saving.", true);
return;
}
data = pixels.data();//Png and channels == 4.
if ((suffix == "jpg" || suffix == "bmp") && channels)
{
RgbaToRgb(pixels, vecRgb, width, height);
data = vecRgb.data();
}
string s = filename.toStdString(); string s = filename.toStdString();
string id = settings->Id().toStdString(); string id = settings->Id().toStdString();
string url = settings->Url().toStdString(); string url = settings->Url().toStdString();
string nick = settings->Nick().toStdString(); string nick = settings->Nick().toStdString();
if (suffix == "png") //Ensure dimensions are valid.
b = WritePng(s.c_str(), data, width, height, 1, true, comments, id, url, nick); if (pixels.size() < size)
else if (suffix == "jpg") {
b = WriteJpeg(s.c_str(), data, width, height, 100, true, comments, id, url, nick); m_Fractorium->ShowCritical("Save Failed", "Dimensions didn't match, not saving.", true);
else if (suffix == "bmp") return;
b = WriteBmp(s.c_str(), data, width, height); }
auto data = pixels.data();
if (suffix.endsWith("bmp", Qt::CaseInsensitive) || suffix.endsWith("jpg", Qt::CaseInsensitive))
{
vector<byte> rgb8Image(size * 3);
Rgba32ToRgb8(data, rgb8Image.data(), width, height);
if (suffix.endsWith("bmp", Qt::CaseInsensitive))
b = WriteBmp(s.c_str(), rgb8Image.data(), width, height);
else if (suffix.endsWith("jpg", Qt::CaseInsensitive))
b = WriteJpeg(s.c_str(), rgb8Image.data(), width, height, 100, true, comments, id, url, nick);
}
else if (suffix.endsWith("png", Qt::CaseInsensitive))
{
if (!png16Bit)
{
vector<byte> rgba8Image(size * 4);
Rgba32ToRgba8(data, rgba8Image.data(), width, height, transparency);
b = WritePng(s.c_str(), rgba8Image.data(), width, height, 1, true, comments, id, url, nick);//Put an opt here for 1 or 2 bytes.//TODO
}
else
{
vector<glm::uint16> rgba16Image(size * 4);
Rgba32ToRgba16(data, rgba16Image.data(), width, height, transparency);
b = WritePng(s.c_str(), (byte*)rgba16Image.data(), width, height, 2, true, comments, id, url, nick);//Put an opt here for 1 or 2 bytes.//TODO
}
}
else if (suffix.endsWith("exr", Qt::CaseInsensitive))
{
vector<Rgba> rgba32Image(size);
Rgba32ToRgbaExr(data, rgba32Image.data(), width, height, transparency);
b = WriteExr(s.c_str(), rgba32Image.data(), width, height, true, comments, id, url, nick);
}
else else
{ {
m_Fractorium->ShowCritical("Save Failed", "Unrecognized format " + suffix + ", not saving.", true); m_Fractorium->ShowCritical("Save Failed", "Unrecognized format " + suffix + ", not saving.", true);
@ -452,7 +469,7 @@ bool FractoriumEmberController<T>::Render()
//Update it on finish because the rendering process is completely done. //Update it on finish because the rendering process is completely done.
if (update || ProcessState() == eProcessState::ACCUM_DONE) if (update || ProcessState() == eProcessState::ACCUM_DONE)
{ {
if (m_FinalImage.size() == m_Renderer->FinalBufferSize())//Make absolutely sure the correct amount of data is passed. if (m_FinalImage.size() == m_Renderer->FinalDimensions())//Make absolutely sure the correct amount of data is passed.
gl->update(); gl->update();
if (ProcessState() == eProcessState::ACCUM_DONE) if (ProcessState() == eProcessState::ACCUM_DONE)
@ -509,10 +526,11 @@ bool FractoriumEmberController<T>::Render()
/// </summary> /// </summary>
/// <param name="renderType">The type of render to create</param> /// <param name="renderType">The type of render to create</param>
/// <param name="devices">The platform,device index pairs of the devices to use</param> /// <param name="devices">The platform,device index pairs of the devices to use</param>
/// <param name="updatePreviews">True to re-render the library previews, else false.</param>
/// <param name="shared">True if shared with OpenGL, else false. Default: true.</param> /// <param name="shared">True if shared with OpenGL, else false. Default: true.</param>
/// <returns>True if nothing went wrong, else false.</returns> /// <returns>True if nothing went wrong, else false.</returns>
template <typename T> template <typename T>
bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool shared) bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, const vector<pair<size_t, size_t>>& devices, bool updatePreviews, bool shared)
{ {
bool ok = true; bool ok = true;
auto s = m_Fractorium->m_Settings; auto s = m_Fractorium->m_Settings;
@ -565,7 +583,6 @@ bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, cons
} }
m_Renderer->Callback(this); m_Renderer->Callback(this);
m_Renderer->NumChannels(4);//Always using 4 since the GL texture is RGBA.
m_Renderer->ReclaimOnResize(true); m_Renderer->ReclaimOnResize(true);
//Give it an initial ember, will be updated many times later. //Give it an initial ember, will be updated many times later.
//Even though the bounds are computed when starting the next render. The OpenGL draw calls use these values, which might get called before the render starts. //Even though the bounds are computed when starting the next render. The OpenGL draw calls use these values, which might get called before the render starts.
@ -573,17 +590,15 @@ bool FractoriumEmberController<T>::CreateRenderer(eRendererType renderType, cons
m_Renderer->EarlyClip(s->EarlyClip()); m_Renderer->EarlyClip(s->EarlyClip());
m_Renderer->YAxisUp(s->YAxisUp()); m_Renderer->YAxisUp(s->YAxisUp());
m_Renderer->ThreadCount(s->ThreadCount()); m_Renderer->ThreadCount(s->ThreadCount());
m_Renderer->Transparency(s->Transparency());
if (m_Renderer->RendererType() == eRendererType::CPU_RENDERER) if (m_Renderer->RendererType() == eRendererType::CPU_RENDERER)
m_Renderer->InteractiveFilter(s->CpuDEFilter() ? eInteractiveFilter::FILTER_DE : eInteractiveFilter::FILTER_LOG); m_Renderer->InteractiveFilter(s->CpuDEFilter() ? eInteractiveFilter::FILTER_DE : eInteractiveFilter::FILTER_LOG);
else else
m_Renderer->InteractiveFilter(s->OpenCLDEFilter() ? eInteractiveFilter::FILTER_DE : eInteractiveFilter::FILTER_LOG); m_Renderer->InteractiveFilter(s->OpenCLDEFilter() ? eInteractiveFilter::FILTER_DE : eInteractiveFilter::FILTER_LOG);
if ((m_Renderer->EarlyClip() != m_LibraryPreviewRenderer->EarlyClip()) || if (updatePreviews)
(m_Renderer->YAxisUp() != m_LibraryPreviewRenderer->YAxisUp()) ||
(m_Renderer->Transparency() != m_LibraryPreviewRenderer->Transparency()))
{ {
m_LibraryPreviewRenderer = make_unique<TreePreviewRenderer<T>>(this, m_Fractorium->ui.LibraryTree, m_EmberFile);//Will take the same settings as the main renderer.
RenderLibraryPreviews(); RenderLibraryPreviews();
} }
@ -618,26 +633,28 @@ void Fractorium::EnableRenderControls(bool enable)
/// <summary> /// <summary>
/// Wrapper to stop the timer, shutdown the controller and recreate, then restart the controller and renderer from the options. /// Wrapper to stop the timer, shutdown the controller and recreate, then restart the controller and renderer from the options.
/// </summary> /// </summary>
void Fractorium::ShutdownAndRecreateFromOptions() /// <param name="updatePreviews">True to re-render the library previews, else false.</param>
void Fractorium::ShutdownAndRecreateFromOptions(bool updatePreviews)
{ {
//First completely stop what the current rendering process is doing. //First completely stop what the current rendering process is doing.
m_Controller->Shutdown(); m_Controller->Shutdown();
StartRenderTimer();//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer. StartRenderTimer(updatePreviews);//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer.
m_Settings->sync(); m_Settings->sync();
} }
/// <summary> /// <summary>
/// Create a new renderer from the options. /// Create a new renderer from the options.
/// </summary> /// </summary>
/// <param name="updatePreviews">True to re-render the library previews, else false.</param>
/// <returns>True if nothing went wrong, else false.</returns> /// <returns>True if nothing went wrong, else false.</returns>
bool Fractorium::CreateRendererFromOptions() bool Fractorium::CreateRendererFromOptions(bool updatePreviews)
{ {
bool ok = true; bool ok = true;
bool useOpenCL = m_Info->Ok() && m_Settings->OpenCL(); bool useOpenCL = m_Info->Ok() && m_Settings->OpenCL();
auto v = Devices(m_Settings->Devices()); auto v = Devices(m_Settings->Devices());
//The most important option to process is what kind of renderer is desired, so do it first. //The most important option to process is what kind of renderer is desired, so do it first.
if (!m_Controller->CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, v)) if (!m_Controller->CreateRenderer((useOpenCL && !v.empty()) ? eRendererType::OPENCL_RENDERER : eRendererType::CPU_RENDERER, v, updatePreviews))
{ {
//If using OpenCL, will only get here if creating RendererCL failed, but creating a backup CPU Renderer succeeded. //If using OpenCL, will only get here if creating RendererCL failed, but creating a backup CPU Renderer succeeded.
ShowCritical("Renderer Creation Error", "Error creating renderer, most likely a GPU problem. Using CPU instead."); ShowCritical("Renderer Creation Error", "Error creating renderer, most likely a GPU problem. Using CPU instead.");
@ -735,25 +752,30 @@ bool Fractorium::CreateControllerFromOptions()
m_PaletteFrequencySpin->SetValueStealth(freq); m_PaletteFrequencySpin->SetValueStealth(freq);
m_Controller->PaletteAdjust();//Applies the adjustments to temp and saves in m_Ember.m_Palette, then fills in the palette preview widget. m_Controller->PaletteAdjust();//Applies the adjustments to temp and saves in m_Ember.m_Palette, then fills in the palette preview widget.
} }
}
return m_Controller.get(); return m_Controller.get();
}
return false;
} }
/// <summary> /// <summary>
/// Start the render timer. /// Start the render timer.
/// If a renderer has not been created yet, or differs form the options, it will first be created from the options. /// If a renderer has not been created yet, or differs form the options, it will first be created from the options.
/// </summary> /// </summary>
void Fractorium::StartRenderTimer() /// <param name="updatePreviews">True to re-render the library previews, else false.</param>
void Fractorium::StartRenderTimer(bool updatePreviews)
{ {
//Starting the render timer, either for the first time //Starting the render timer, either for the first time
//or from a paused state, such as resizing or applying new options. //or from a paused state, such as resizing or applying new options.
CreateControllerFromOptions(); bool newController = CreateControllerFromOptions();
if (m_Controller.get()) if (m_Controller.get())
{ {
//On program startup, the renderer does not get initialized until now. //On program startup, the renderer does not get initialized until now.
CreateRendererFromOptions(); //If a new controller was created, then previews will have started, so only start the previews if a new controller
//was *not* created and updatePreviews is true.
CreateRendererFromOptions(updatePreviews && !newController);
if (m_Controller->Renderer()) if (m_Controller->Renderer())
m_Controller->StartRenderTimer(); m_Controller->StartRenderTimer();

View File

@ -61,16 +61,16 @@ void FractoriumSettings::EnsureDefaults()
FinalScale(0); FinalScale(0);
if (OpenXmlExt() == "") if (OpenXmlExt() == "")
OpenXmlExt("Flame (*.flame)"); OpenXmlExt("*.flame");
if (SaveXmlExt() == "") if (SaveXmlExt() == "")
SaveXmlExt("Flame (*.flame)"); SaveXmlExt(".flame");
if (OpenImageExt() == "") if (OpenImageExt() == "")
OpenImageExt("Png (*.png)"); OpenImageExt("*.png");
if (SaveImageExt() == "") if (SaveImageExt() == "")
SaveImageExt("Png (*.png)"); SaveImageExt(".png");
if (FinalExt() != "jpg" && FinalExt() != "png") if (FinalExt() != "jpg" && FinalExt() != "png")
FinalExt("png"); FinalExt("png");
@ -121,6 +121,9 @@ void FractoriumSettings::ShowGrid(bool b) { setValue(SHOW
bool FractoriumSettings::ToggleType() { return value(TOGGLETYPE).toBool(); } bool FractoriumSettings::ToggleType() { return value(TOGGLETYPE).toBool(); }
void FractoriumSettings::ToggleType(bool b) { setValue(TOGGLETYPE, b); } void FractoriumSettings::ToggleType(bool b) { setValue(TOGGLETYPE, b); }
bool FractoriumSettings::Png16Bit() { return value(PNG16BIT).toBool(); }
void FractoriumSettings::Png16Bit(bool b) { setValue(PNG16BIT, b); }
bool FractoriumSettings::ContinuousUpdate() { return value(CONTUPDATE).toBool(); } bool FractoriumSettings::ContinuousUpdate() { return value(CONTUPDATE).toBool(); }
void FractoriumSettings::ContinuousUpdate(bool b) { setValue(CONTUPDATE, b); } void FractoriumSettings::ContinuousUpdate(bool b) { setValue(CONTUPDATE, b); }
@ -229,6 +232,9 @@ void FractoriumSettings::FinalDoAll(bool b) { setValue(FINALDOALL, b);
bool FractoriumSettings::FinalDoSequence() { return value(FINALDOSEQUENCE).toBool(); } bool FractoriumSettings::FinalDoSequence() { return value(FINALDOSEQUENCE).toBool(); }
void FractoriumSettings::FinalDoSequence(bool b) { setValue(FINALDOSEQUENCE, b); } void FractoriumSettings::FinalDoSequence(bool b) { setValue(FINALDOSEQUENCE, b); }
bool FractoriumSettings::FinalPng16Bit() { return value(FINALPNG16BIT).toBool(); }
void FractoriumSettings::FinalPng16Bit(bool b) { setValue(FINALPNG16BIT, b); }
bool FractoriumSettings::FinalKeepAspect() { return value(FINALKEEPASPECT).toBool(); } bool FractoriumSettings::FinalKeepAspect() { return value(FINALKEEPASPECT).toBool(); }
void FractoriumSettings::FinalKeepAspect(bool b) { setValue(FINALKEEPASPECT, b); } void FractoriumSettings::FinalKeepAspect(bool b) { setValue(FINALKEEPASPECT, b); }

View File

@ -16,6 +16,7 @@
#define SHOWXFORMS "render/showxforms" #define SHOWXFORMS "render/showxforms"
#define SHOWGRID "render/showgrid" #define SHOWGRID "render/showgrid"
#define TOGGLETYPE "render/toggletype" #define TOGGLETYPE "render/toggletype"
#define PNG16BIT "render/png16bit"
#define DEVICES "render/devices" #define DEVICES "render/devices"
#define THREADCOUNT "render/threadcount" #define THREADCOUNT "render/threadcount"
#define CPUDEFILTER "render/cpudefilter" #define CPUDEFILTER "render/cpudefilter"
@ -50,6 +51,7 @@
#define FINALSAVEXML "finalrender/savexml" #define FINALSAVEXML "finalrender/savexml"
#define FINALDOALL "finalrender/doall" #define FINALDOALL "finalrender/doall"
#define FINALDOSEQUENCE "finalrender/dosequence" #define FINALDOSEQUENCE "finalrender/dosequence"
#define FINALPNG16BIT "finalrender/png16bit"
#define FINALKEEPASPECT "finalrender/keepaspect" #define FINALKEEPASPECT "finalrender/keepaspect"
#define FINALSCALE "finalrender/scale" #define FINALSCALE "finalrender/scale"
#define FINALEXT "finalrender/ext" #define FINALEXT "finalrender/ext"
@ -124,6 +126,9 @@ public:
bool ToggleType(); bool ToggleType();
void ToggleType(bool b); void ToggleType(bool b);
bool Png16Bit();
void Png16Bit(bool b);
bool ContinuousUpdate(); bool ContinuousUpdate();
void ContinuousUpdate(bool b); void ContinuousUpdate(bool b);
@ -220,6 +225,9 @@ public:
bool FinalDoSequence(); bool FinalDoSequence();
void FinalDoSequence(bool b); void FinalDoSequence(bool b);
bool FinalPng16Bit();
void FinalPng16Bit(bool b);
bool FinalKeepAspect(); bool FinalKeepAspect();
void FinalKeepAspect(bool b); void FinalKeepAspect(bool b);

View File

@ -43,7 +43,7 @@ void Fractorium::OnActionCpu(bool checked)
if (checked && m_Settings->OpenCL()) if (checked && m_Settings->OpenCL())
{ {
m_Settings->OpenCL(false); m_Settings->OpenCL(false);
ShutdownAndRecreateFromOptions(); ShutdownAndRecreateFromOptions(false);
} }
} }
@ -56,7 +56,7 @@ void Fractorium::OnActionCL(bool checked)
if (checked && !m_Settings->OpenCL()) if (checked && !m_Settings->OpenCL())
{ {
m_Settings->OpenCL(true); m_Settings->OpenCL(true);
ShutdownAndRecreateFromOptions(); ShutdownAndRecreateFromOptions(false);
} }
} }
@ -69,7 +69,7 @@ void Fractorium::OnActionSP(bool checked)
if (checked && m_Settings->Double()) if (checked && m_Settings->Double())
{ {
m_Settings->Double(false); m_Settings->Double(false);
ShutdownAndRecreateFromOptions(); ShutdownAndRecreateFromOptions(true);//Pass true, but it's not needed because creating a new controller will force a library tree re-render.
} }
} }
@ -82,7 +82,7 @@ void Fractorium::OnActionDP(bool checked)
if (checked && !m_Settings->Double()) if (checked && !m_Settings->Double())
{ {
m_Settings->Double(true); m_Settings->Double(true);
ShutdownAndRecreateFromOptions(); ShutdownAndRecreateFromOptions(true);//Pass true, but it's not needed because creating a new controller will force a library tree re-render.
} }
} }

View File

@ -251,7 +251,7 @@ void FractoriumEmberController<T>::AffineSetHelper(double d, int index, bool pre
UpdateXform([&] (Xform<T>* xform) UpdateXform([&] (Xform<T>* xform)
{ {
auto& affine = pre ? xform->m_Affine : xform->m_Post; auto& affine = pre ? xform->m_Affine : xform->m_Post;
DoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins; AffineDoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins;
if (m_Fractorium->ui.PolarAffineCheckBox->isChecked()) if (m_Fractorium->ui.PolarAffineCheckBox->isChecked())
{ {
@ -618,7 +618,7 @@ void FractoriumEmberController<T>::FillBothAffines()
template <typename T> template <typename T>
void FractoriumEmberController<T>::FillAffineWithXform(Xform<T>* xform, bool pre) void FractoriumEmberController<T>::FillAffineWithXform(Xform<T>* xform, bool pre)
{ {
DoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins; AffineDoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins;
auto& affine = pre ? xform->m_Affine : xform->m_Post; auto& affine = pre ? xform->m_Affine : xform->m_Post;
if (m_Fractorium->ui.PolarAffineCheckBox->isChecked()) if (m_Fractorium->ui.PolarAffineCheckBox->isChecked())
@ -715,9 +715,9 @@ void Fractorium::OnPolarAffineCheckBoxStateChanged(int state)
/// <param name="prec">The precision of the spinner</param> /// <param name="prec">The precision of the spinner</param>
/// <param name="signal">The signal the spinner emits</param> /// <param name="signal">The signal the spinner emits</param>
/// <param name="slot">The slot to receive the signal</param> /// <param name="slot">The slot to receive the signal</param>
void Fractorium::SetupAffineSpinner(QTableWidget* table, const QObject* receiver, int row, int col, DoubleSpinBox*& spinBox, int height, double min, double max, double step, double prec, const char* signal, const char* slot) void Fractorium::SetupAffineSpinner(QTableWidget* table, const QObject* receiver, int row, int col, AffineDoubleSpinBox*& spinBox, int height, double min, double max, double step, double prec, const char* signal, const char* slot)
{ {
spinBox = new DoubleSpinBox(table, height, step); spinBox = new AffineDoubleSpinBox(table, height, step);
spinBox->setRange(min, max); spinBox->setRange(min, max);
spinBox->setDecimals(prec); spinBox->setDecimals(prec);
table->setCellWidget(row, col, spinBox); table->setCellWidget(row, col, spinBox);

View File

@ -375,5 +375,5 @@ void Fractorium::OnVariationsFilterClearButtonClicked(bool checked)
template class FractoriumEmberController<float>; template class FractoriumEmberController<float>;
#ifdef DO_DOUBLE #ifdef DO_DOUBLE
template class FractoriumEmberController<double>; template class FractoriumEmberController<double>;
#endif #endif

View File

@ -54,8 +54,6 @@ void GLWidget::InitGL()
void GLWidget::DrawQuad() void GLWidget::DrawQuad()
{ {
glEnable(GL_TEXTURE_2D); glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
auto renderer = m_Fractorium->m_Controller->Renderer(); auto renderer = m_Fractorium->m_Controller->Renderer();
auto finalImage = m_Fractorium->m_Controller->FinalImage(); auto finalImage = m_Fractorium->m_Controller->FinalImage();
@ -65,7 +63,7 @@ void GLWidget::DrawQuad()
glBindTexture(GL_TEXTURE_2D, m_OutputTexID);//The texture to draw to. glBindTexture(GL_TEXTURE_2D, m_OutputTexID);//The texture to draw to.
//Only draw if the dimensions match exactly. //Only draw if the dimensions match exactly.
if (m_TexWidth == width() && m_TexHeight == height() && ((m_TexWidth * m_TexHeight * 4) == GLint(finalImage->size()))) if (m_TexWidth == width() && m_TexHeight == height() && ((m_TexWidth * m_TexHeight) == GLint(finalImage->size())))
{ {
glMatrixMode(GL_PROJECTION); glMatrixMode(GL_PROJECTION);
glPushMatrix(); glPushMatrix();
@ -77,7 +75,7 @@ void GLWidget::DrawQuad()
//Copy data from CPU to OpenGL if using a CPU renderer. This is not needed when using OpenCL. //Copy data from CPU to OpenGL if using a CPU renderer. This is not needed when using OpenCL.
if (renderer->RendererType() == eRendererType::CPU_RENDERER) if (renderer->RendererType() == eRendererType::CPU_RENDERER)
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_TexWidth, m_TexHeight, GL_RGBA, GL_UNSIGNED_BYTE, finalImage->data()); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_TexWidth, m_TexHeight, GL_RGBA, GL_FLOAT, finalImage->data());
glBegin(GL_QUADS);//This will need to be converted to a shader at some point in the future. glBegin(GL_QUADS);//This will need to be converted to a shader at some point in the future.
glTexCoord2f(0.0, 0.0); glVertex2f(0.0, 0.0); glTexCoord2f(0.0, 0.0); glVertex2f(0.0, 0.0);
@ -94,7 +92,6 @@ void GLWidget::DrawQuad()
glBindTexture(GL_TEXTURE_2D, 0);//Stop using this texture. glBindTexture(GL_TEXTURE_2D, 0);//Stop using this texture.
} }
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D); glDisable(GL_TEXTURE_2D);
} }
@ -267,10 +264,10 @@ void GLEmberController<T>::DrawImage()
if (SizesMatch())//Ensure all sizes are correct. If not, do nothing. if (SizesMatch())//Ensure all sizes are correct. If not, do nothing.
{ {
vector<byte>* finalImage = m_FractoriumEmberController->FinalImage(); auto finalImage = m_FractoriumEmberController->FinalImage();
if ((renderer->RendererType() == eRendererType::OPENCL_RENDERER) || finalImage)//Final image only matters for CPU renderer. if ((renderer->RendererType() == eRendererType::OPENCL_RENDERER) || finalImage)//Final image only matters for CPU renderer.
if ((renderer->RendererType() == eRendererType::OPENCL_RENDERER) || finalImage->size() == renderer->FinalBufferSize()) if ((renderer->RendererType() == eRendererType::OPENCL_RENDERER) || finalImage->size() == renderer->FinalDimensions())
m_GL->DrawQuad();//Output image is drawn here. m_GL->DrawQuad();//Output image is drawn here.
} }
@ -757,7 +754,7 @@ bool GLWidget::Allocate(bool force)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TexWidth, m_TexHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, m_TexWidth, m_TexHeight, 0, GL_RGBA, GL_FLOAT, nullptr);
alloc = true; alloc = true;
} }

View File

@ -67,6 +67,7 @@ bool FractoriumOptionsDialog::OpenCL() { return ui.OpenCLCheckBox->isChecked();
bool FractoriumOptionsDialog::Double() { return ui.DoublePrecisionCheckBox->isChecked(); } bool FractoriumOptionsDialog::Double() { return ui.DoublePrecisionCheckBox->isChecked(); }
bool FractoriumOptionsDialog::ShowAllXforms() { return ui.ShowAllXformsCheckBox->isChecked(); } bool FractoriumOptionsDialog::ShowAllXforms() { return ui.ShowAllXformsCheckBox->isChecked(); }
bool FractoriumOptionsDialog::ToggleType() { return ui.ToggleTypeCheckBox->isChecked(); } bool FractoriumOptionsDialog::ToggleType() { return ui.ToggleTypeCheckBox->isChecked(); }
bool FractoriumOptionsDialog::Png16Bit() { return ui.Png16BitCheckBox->isChecked(); }
bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); } bool FractoriumOptionsDialog::AutoUnique() { return ui.AutoUniqueCheckBox->isChecked(); }
uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); } uint FractoriumOptionsDialog::ThreadCount() { return ui.ThreadCountSpin->value(); }
uint FractoriumOptionsDialog::RandomCount() { return ui.RandomCountSpin->value(); } uint FractoriumOptionsDialog::RandomCount() { return ui.RandomCountSpin->value(); }
@ -174,6 +175,7 @@ void FractoriumOptionsDialog::GuiToData()
m_Settings->Double(Double()); m_Settings->Double(Double());
m_Settings->ShowAllXforms(ShowAllXforms()); m_Settings->ShowAllXforms(ShowAllXforms());
m_Settings->ToggleType(ToggleType()); m_Settings->ToggleType(ToggleType());
m_Settings->Png16Bit(Png16Bit());
m_Settings->ThreadCount(ThreadCount()); m_Settings->ThreadCount(ThreadCount());
m_Settings->RandomCount(RandomCount()); m_Settings->RandomCount(RandomCount());
m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value()); m_Settings->CpuSubBatch(ui.CpuSubBatchSpin->value());
@ -207,6 +209,7 @@ void FractoriumOptionsDialog::DataToGui()
ui.DoublePrecisionCheckBox->setChecked(m_Settings->Double()); ui.DoublePrecisionCheckBox->setChecked(m_Settings->Double());
ui.ShowAllXformsCheckBox->setChecked(m_Settings->ShowAllXforms()); ui.ShowAllXformsCheckBox->setChecked(m_Settings->ShowAllXforms());
ui.ToggleTypeCheckBox->setChecked(m_Settings->ToggleType()); ui.ToggleTypeCheckBox->setChecked(m_Settings->ToggleType());
ui.Png16BitCheckBox->setChecked(m_Settings->Png16Bit());
ui.ThreadCountSpin->setValue(m_Settings->ThreadCount()); ui.ThreadCountSpin->setValue(m_Settings->ThreadCount());
ui.RandomCountSpin->setValue(m_Settings->RandomCount()); ui.RandomCountSpin->setValue(m_Settings->RandomCount());
ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch()); ui.CpuSubBatchSpin->setValue(m_Settings->CpuSubBatch());

View File

@ -39,13 +39,13 @@ protected:
private: private:
bool EarlyClip(); bool EarlyClip();
bool YAxisUp(); bool YAxisUp();
bool AlphaChannel();
bool Transparency(); bool Transparency();
bool ContinuousUpdate(); bool ContinuousUpdate();
bool OpenCL(); bool OpenCL();
bool Double(); bool Double();
bool ShowAllXforms(); bool ShowAllXforms();
bool ToggleType(); bool ToggleType();
bool Png16Bit();
bool AutoUnique(); bool AutoUnique();
uint ThreadCount(); uint ThreadCount();
uint RandomCount(); uint RandomCount();

View File

@ -424,6 +424,16 @@ in interactive mode for each mouse movement</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="0">
<widget class="QCheckBox" name="Png16BitCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Save each RGBA component as 16-bits when saving Png files.&lt;/p&gt;&lt;p&gt;This leads to greater color precision for use in high end rendering and display on HDR monitors, however it makes the file size larger.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Save 16-bit Png</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="OptionsXmlSavingTab"> <widget class="QWidget" name="OptionsXmlSavingTab">
@ -846,6 +856,8 @@ in interactive mode for each mouse movement</string>
<tabstop>TransparencyCheckBox</tabstop> <tabstop>TransparencyCheckBox</tabstop>
<tabstop>ShowAllXformsCheckBox</tabstop> <tabstop>ShowAllXformsCheckBox</tabstop>
<tabstop>ContinuousUpdateCheckBox</tabstop> <tabstop>ContinuousUpdateCheckBox</tabstop>
<tabstop>ToggleTypeCheckBox</tabstop>
<tabstop>Png16BitCheckBox</tabstop>
<tabstop>RandomCountSpin</tabstop> <tabstop>RandomCountSpin</tabstop>
<tabstop>ThreadCountSpin</tabstop> <tabstop>ThreadCountSpin</tabstop>
<tabstop>CpuSubBatchSpin</tabstop> <tabstop>CpuSubBatchSpin</tabstop>

View File

@ -496,7 +496,11 @@ QStringList PaletteEditor::SetupOpenImagesDialog()
m_FileDialog->setViewMode(QFileDialog::List); m_FileDialog->setViewMode(QFileDialog::List);
m_FileDialog->setFileMode(QFileDialog::ExistingFile); m_FileDialog->setFileMode(QFileDialog::ExistingFile);
m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen); m_FileDialog->setAcceptMode(QFileDialog::AcceptOpen);
#ifdef _WIN32
m_FileDialog->setNameFilter("Image Files (*.png *.jpg *.bmp)"); m_FileDialog->setNameFilter("Image Files (*.png *.jpg *.bmp)");
#else
m_FileDialog->setNameFilter("Image Files ( *.jpg *.png)");
#endif
m_FileDialog->setWindowTitle("Open Image"); m_FileDialog->setWindowTitle("Open Image");
m_FileDialog->setDirectory(settings->OpenPaletteImageFolder()); m_FileDialog->setDirectory(settings->OpenPaletteImageFolder());
m_FileDialog->selectNameFilter("*.jpg"); m_FileDialog->selectNameFilter("*.jpg");
@ -515,7 +519,7 @@ QStringList PaletteEditor::SetupOpenImagesDialog()
} }
#else #else
auto filename = QFileDialog::getOpenFileName(this, tr("Open Image"), settings->OpenPaletteImageFolder(), tr("Image Files (*.png *.jpg *.bmp)")); auto filename = QFileDialog::getOpenFileName(this, tr("Open Image"), settings->OpenPaletteImageFolder(), tr("Image Files (*.jpg *.png)"));
if (filename.size() > 0) if (filename.size() > 0)
{ {

View File

@ -146,7 +146,7 @@ void SpinBox::OnTimeout()
/// <returns>false</returns> /// <returns>false</returns>
bool SpinBox::eventFilter(QObject* o, QEvent* e) bool SpinBox::eventFilter(QObject* o, QEvent* e)
{ {
QMouseEvent* me = dynamic_cast<QMouseEvent*>(e); auto me = dynamic_cast<QMouseEvent*>(e);
if (isEnabled() && me) if (isEnabled() && me)
{ {

View File

@ -52,4 +52,5 @@ copy X64\Release\tbb.dll ..\..\..\fractorium\Deps
copy X64\Release\tbb.lib ..\..\..\fractorium\Deps copy X64\Release\tbb.lib ..\..\..\fractorium\Deps
cd ..\..\.. cd ..\..\..
cd fractorium openexr.cmd

46
openexr.cmd Normal file
View File

@ -0,0 +1,46 @@
ECHO building OpenEXR
cd..
if not exist ".\openexr" ^
mkdir openexr
git clone git://github.com/meshula/openexr.git
cd openexr
git pull
SET current=%cd%
if not exist ".\output" mkdir .\output
cd IlmBase
cmake -G "Visual Studio 14 2015 Win64"^
-DCMAKE_PREFIX_PATH="%current%\output"^
-DCMAKE_INSTALL_PREFIX="%current%\output"^
.\
cmake --build . --target install --config Release -- /maxcpucount:8
cd ..\OpenEXR
cmake -G "Visual Studio 14 2015 Win64"^
-DCMAKE_PREFIX_PATH="%current%\output"^
-DCMAKE_INSTALL_PREFIX="%current%\output"^
-DILMBASE_PACKAGE_PREFIX="%current%\output" ^
-DZLIB_ROOT="%current%\..\zlib"^
.\
cmake --build . --target install --config Release
cd %current%
copy %current%\output\lib\Half.lib %current%\..\fractorium\Deps\Half.lib
copy %current%\output\lib\Iex-2_2.lib %current%\..\fractorium\Deps\Iex.lib
copy %current%\output\lib\IexMath-2_2.lib %current%\..\fractorium\Deps\IexMath.lib
copy %current%\output\lib\IlmImf-2_2.lib %current%\..\fractorium\Deps\IlmImf.lib
copy %current%\output\lib\IlmImfUtil-2_2.lib %current%\..\fractorium\Deps\IlmImfUtil.lib
copy %current%\output\lib\IlmThread-2_2.lib %current%\..\fractorium\Deps\IlmThread.lib
copy %current%\output\lib\Imath-2_2.lib %current%\..\fractorium\Deps\Imath.lib
xcopy %current%\output\Include %current%\..\fractorium\Deps\Include\ /S /Y
xcopy %current%\output\lib\*.dll %current%\..\fractorium\Deps\ /Y