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/VS2015/flam3-palettes.xml
*.dmg
/Deps/include/OpenEXR/OpenEXRConfig.h
/Deps/include/OpenEXR/ImfXdr.h
/Deps/include/OpenEXR/ImfWav.h
/Deps/include/OpenEXR/ImfVersion.h
/Deps/include/OpenEXR/ImfVecAttribute.h
/Deps/include/OpenEXR/ImfTimeCodeAttribute.h
/Deps/include/OpenEXR/ImfTimeCode.h
/Deps/include/OpenEXR/ImfTiledRgbaFile.h
/Deps/include/OpenEXR/ImfTiledOutputPart.h
/Deps/include/OpenEXR/ImfTiledOutputFile.h
/Deps/include/OpenEXR/ImfTiledInputPart.h
/Deps/include/OpenEXR/ImfTiledInputFile.h
/Deps/include/OpenEXR/ImfTileDescriptionAttribute.h
/Deps/include/OpenEXR/ImfTileDescription.h
/Deps/include/OpenEXR/ImfThreading.h
/Deps/include/OpenEXR/ImfTestFile.h
/Deps/include/OpenEXR/ImfStringVectorAttribute.h
/Deps/include/OpenEXR/ImfStringAttribute.h
/Deps/include/OpenEXR/ImfStandardAttributes.h
/Deps/include/OpenEXR/ImfSampleCountChannel.h
/Deps/include/OpenEXR/ImfRgbaYca.h
/Deps/include/OpenEXR/ImfRgbaFile.h
/Deps/include/OpenEXR/ImfRgba.h
/Deps/include/OpenEXR/ImfRationalAttribute.h
/Deps/include/OpenEXR/ImfRational.h
/Deps/include/OpenEXR/ImfPreviewImageAttribute.h
/Deps/include/OpenEXR/ImfPreviewImage.h
/Deps/include/OpenEXR/ImfPixelType.h
/Deps/include/OpenEXR/ImfPartType.h
/Deps/include/OpenEXR/ImfPartHelper.h
/Deps/include/OpenEXR/ImfOutputPart.h
/Deps/include/OpenEXR/ImfOutputFile.h
/Deps/include/OpenEXR/ImfOpaqueAttribute.h
/Deps/include/OpenEXR/ImfNamespace.h
/Deps/include/OpenEXR/ImfName.h
/Deps/include/OpenEXR/ImfMultiView.h
/Deps/include/OpenEXR/ImfMultiPartOutputFile.h
/Deps/include/OpenEXR/ImfMultiPartInputFile.h
/Deps/include/OpenEXR/ImfMisc.h
/Deps/include/OpenEXR/ImfMatrixAttribute.h
/Deps/include/OpenEXR/ImfLut.h
/Deps/include/OpenEXR/ImfLineOrderAttribute.h
/Deps/include/OpenEXR/ImfLineOrder.h
/Deps/include/OpenEXR/ImfKeyCodeAttribute.h
/Deps/include/OpenEXR/ImfKeyCode.h
/Deps/include/OpenEXR/ImfIO.h
/Deps/include/OpenEXR/ImfIntAttribute.h
/Deps/include/OpenEXR/ImfInt64.h
/Deps/include/OpenEXR/ImfInputPart.h
/Deps/include/OpenEXR/ImfInputFile.h
/Deps/include/OpenEXR/ImfImageLevel.h
/Deps/include/OpenEXR/ImfImageIO.h
/Deps/include/OpenEXR/ImfImageDataWindow.h
/Deps/include/OpenEXR/ImfImageChannelRenaming.h
/Deps/include/OpenEXR/ImfImageChannel.h
/Deps/include/OpenEXR/ImfImage.h
/Deps/include/OpenEXR/ImfHuf.h
/Deps/include/OpenEXR/ImfHeader.h
/Deps/include/OpenEXR/ImfGenericOutputFile.h
/Deps/include/OpenEXR/ImfGenericInputFile.h
/Deps/include/OpenEXR/ImfFramesPerSecond.h
/Deps/include/OpenEXR/ImfFrameBuffer.h
/Deps/include/OpenEXR/ImfForward.h
/Deps/include/OpenEXR/ImfFloatVectorAttribute.h
/Deps/include/OpenEXR/ImfFloatAttribute.h
/Deps/include/OpenEXR/ImfFlatImageLevel.h
/Deps/include/OpenEXR/ImfFlatImageIO.h
/Deps/include/OpenEXR/ImfFlatImageChannel.h
/Deps/include/OpenEXR/ImfFlatImage.h
/Deps/include/OpenEXR/ImfExport.h
/Deps/include/OpenEXR/ImfEnvmapAttribute.h
/Deps/include/OpenEXR/ImfEnvmap.h
/Deps/include/OpenEXR/ImfDoubleAttribute.h
/Deps/include/OpenEXR/ImfDeepTiledOutputPart.h
/Deps/include/OpenEXR/ImfDeepTiledOutputFile.h
/Deps/include/OpenEXR/ImfDeepTiledInputPart.h
/Deps/include/OpenEXR/ImfDeepTiledInputFile.h
/Deps/include/OpenEXR/ImfDeepScanLineOutputPart.h
/Deps/include/OpenEXR/ImfDeepScanLineOutputFile.h
/Deps/include/OpenEXR/ImfDeepScanLineInputPart.h
/Deps/include/OpenEXR/ImfDeepScanLineInputFile.h
/Deps/include/OpenEXR/ImfDeepImageStateAttribute.h
/Deps/include/OpenEXR/ImfDeepImageState.h
/Deps/include/OpenEXR/ImfDeepImageLevel.h
/Deps/include/OpenEXR/ImfDeepImageIO.h
/Deps/include/OpenEXR/ImfDeepImageChannel.h
/Deps/include/OpenEXR/ImfDeepImage.h
/Deps/include/OpenEXR/ImfDeepFrameBuffer.h
/Deps/include/OpenEXR/ImfDeepCompositing.h
/Deps/include/OpenEXR/ImfCRgbaFile.h
/Deps/include/OpenEXR/ImfConvert.h
/Deps/include/OpenEXR/ImfCompressionAttribute.h
/Deps/include/OpenEXR/ImfCompression.h
/Deps/include/OpenEXR/ImfCompositeDeepScanLine.h
/Deps/include/OpenEXR/ImfChromaticitiesAttribute.h
/Deps/include/OpenEXR/ImfChromaticities.h
/Deps/include/OpenEXR/ImfChannelListAttribute.h
/Deps/include/OpenEXR/ImfChannelList.h
/Deps/include/OpenEXR/ImfBoxAttribute.h
/Deps/include/OpenEXR/ImfB44Compressor.h
/Deps/include/OpenEXR/ImfAttribute.h
/Deps/include/OpenEXR/ImfArray.h
/Deps/include/OpenEXR/ImfAcesFile.h
/Deps/include/OpenEXR/ImathVecAlgo.h
/Deps/include/OpenEXR/ImathVec.h
/Deps/include/OpenEXR/ImathSphere.h
/Deps/include/OpenEXR/ImathShear.h
/Deps/include/OpenEXR/ImathRoots.h
/Deps/include/OpenEXR/ImathRandom.h
/Deps/include/OpenEXR/ImathQuat.h
/Deps/include/OpenEXR/ImathPlatform.h
/Deps/include/OpenEXR/ImathPlane.h
/Deps/include/OpenEXR/ImathNamespace.h
/Deps/include/OpenEXR/ImathMatrixAlgo.h
/Deps/include/OpenEXR/ImathMatrix.h
/Deps/include/OpenEXR/ImathMath.h
/Deps/include/OpenEXR/ImathLineAlgo.h
/Deps/include/OpenEXR/ImathLine.h
/Deps/include/OpenEXR/ImathLimits.h
/Deps/include/OpenEXR/ImathInterval.h
/Deps/include/OpenEXR/ImathInt64.h
/Deps/include/OpenEXR/ImathHalfLimits.h
/Deps/include/OpenEXR/ImathGLU.h
/Deps/include/OpenEXR/ImathGL.h
/Deps/include/OpenEXR/ImathFun.h
/Deps/include/OpenEXR/ImathFrustumTest.h
/Deps/include/OpenEXR/ImathFrustum.h
/Deps/include/OpenEXR/ImathFrame.h
/Deps/include/OpenEXR/ImathForward.h
/Deps/include/OpenEXR/ImathExport.h
/Deps/include/OpenEXR/ImathExc.h
/Deps/include/OpenEXR/ImathEuler.h
/Deps/include/OpenEXR/ImathColorAlgo.h
/Deps/include/OpenEXR/ImathColor.h
/Deps/include/OpenEXR/ImathBoxAlgo.h
/Deps/include/OpenEXR/ImathBox.h
/Deps/include/OpenEXR/IlmThreadSemaphore.h
/Deps/include/OpenEXR/IlmThreadPool.h
/Deps/include/OpenEXR/IlmThreadNamespace.h
/Deps/include/OpenEXR/IlmThreadMutex.h
/Deps/include/OpenEXR/IlmThreadForward.h
/Deps/include/OpenEXR/IlmThreadExport.h
/Deps/include/OpenEXR/IlmThread.h
/Deps/include/OpenEXR/IlmBaseConfig.h
/Deps/include/OpenEXR/IexThrowErrnoExc.h
/Deps/include/OpenEXR/IexNamespace.h
/Deps/include/OpenEXR/IexMathIeeeExc.h
/Deps/include/OpenEXR/IexMathFpu.h
/Deps/include/OpenEXR/IexMathFloatExc.h
/Deps/include/OpenEXR/IexMathExc.h
/Deps/include/OpenEXR/IexMacros.h
/Deps/include/OpenEXR/IexForward.h
/Deps/include/OpenEXR/IexExport.h
/Deps/include/OpenEXR/IexErrnoExc.h
/Deps/include/OpenEXR/IexBaseExc.h
/Deps/include/OpenEXR/Iex.h
/Deps/include/OpenEXR/halfLimits.h
/Deps/include/OpenEXR/halfFunction.h
/Deps/include/OpenEXR/halfExport.h
/Deps/include/OpenEXR/half.h

View File

@ -6,7 +6,7 @@
<ProductVersion>3.7</ProductVersion>
<ProjectGuid>{c8096c47-e358-438c-a520-146d46b0637d}</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>Fractorium_1.0.0.4</OutputName>
<OutputName>Fractorium_1.0.0.5</OutputName>
<OutputType>Package</OutputType>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)\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"?>
<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 UpgradeCode="{4714cd15-bfba-44f6-8059-9e1466ebfa6e}"?>
<?define Manufacturer="Fractorium"?>
@ -13,7 +13,7 @@
<!--
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)">
<Package

Binary file not shown.

View File

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

View File

@ -58,7 +58,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<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>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -72,7 +72,7 @@
<Link>
<SubSystem>Console</SubSystem>
<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>
</Link>
<PostBuildEvent>
@ -90,7 +90,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<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>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -105,7 +105,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<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>
</Link>
<PostBuildEvent>

Binary file not shown.

View File

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

View File

@ -58,7 +58,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<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>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -72,7 +72,7 @@
<Link>
<SubSystem>Console</SubSystem>
<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>
</Link>
<PostBuildEvent>
@ -90,7 +90,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<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>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -105,7 +105,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<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>
</Link>
<PostBuildEvent>

View File

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

View File

@ -58,7 +58,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<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>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -72,7 +72,7 @@
<Link>
<SubSystem>Console</SubSystem>
<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>
</Link>
<PostBuildEvent>
@ -90,7 +90,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<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>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -106,7 +106,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<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>
</Link>
<PostBuildEvent>

View File

@ -56,9 +56,9 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;OPENEXR_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<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>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -72,7 +72,7 @@
<Link>
<SubSystem>Console</SubSystem>
<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>
</Link>
<PostBuildEvent>
@ -88,9 +88,9 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;OPENEXR_DLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<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>
<PrecompiledHeaderFile>EmberCommonPch.h</PrecompiledHeaderFile>
@ -105,7 +105,7 @@ xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\flam3-palettes.xml" "$(OutDir)"
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<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>
</Link>
<PostBuildEvent>

Binary file not shown.

View File

@ -57,7 +57,7 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<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>
<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>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
@ -81,7 +81,7 @@
<OutputFile>$(OutDir)\$(ProjectName).exe</OutputFile>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(QTDIR)\lib;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<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>
<PostBuildEvent>
<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_03.gradient" "$(OutDir)"
xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\dark.qss" "$(OutDir)"
xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\dark_windows.qss" "$(OutDir)"
xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Cored.dll" "$(OutDir)"
xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Guid.dll" "$(OutDir)"
@ -115,7 +115,7 @@ xcopy /F /Y /R /D "$(QTDIR)\plugins\platforms\qwindowsd.dll" "$(OutDir)\platform
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<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>
<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>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<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>
<AdditionalLibraryDirectories>$(ProjectDir)..\..\..\Deps;$(QTDIR)\lib;$(AMDAPPSDKROOT)\lib\x86_64;$(CUDA_PATH)\lib\$(PlatformName);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<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>
</Link>
<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_03.gradient" "$(OutDir)"
xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\dark.qss" "$(OutDir)"
xcopy /F /Y /R /D "$(SolutionDir)..\..\..\Data\dark_windows.qss" "$(OutDir)"
xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Core.dll" "$(OutDir)"
xcopy /F /Y /R /D "$(QTDIR)\bin\Qt5Gui.dll" "$(OutDir)"

View File

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

View File

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

View File

@ -727,7 +727,7 @@ public:
InterpT<&Ember<T>::m_MinRadDE>(embers, coefs, size);
InterpT<&Ember<T>::m_CurveDE>(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.
SetProjFunc();
//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).
//Curves used to adjust the color during final accumulation.
Curves<T> m_Curves;
Curves<float> m_Curves;
//Strings.

View File

@ -37,7 +37,7 @@ static void sincos(float x, float* s, float* c)
namespace EmberNs
{
#define EMBER_VERSION "1.0.0.4"
#define EMBER_VERSION "1.0.0.5"
#define EPS6 T(1e-6)
#define EPS std::numeric_limits<T>::epsilon()//Apoplugin.h uses -20, but it's more mathematically correct to do it this way.
#define ISAAC_SIZE 4
@ -68,6 +68,9 @@ namespace EmberNs
#define TMAX std::numeric_limits<T>::max()
#define FLOAT_MAX_TAN 8388607.0f
#define FLOAT_MIN_TAN -FLOAT_MAX_TAN
#define CURVES_LENGTH 131072
#define CURVES_LENGTH_M1 131071.0f
#define ONE_OVER_CURVES_LENGTH_M1 7.62945273935e-6f
#define EMPTYFIELD -9999
typedef std::chrono::high_resolution_clock Clock;
typedef std::chrono::duration<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 m4T glm::tmat4x4<T, glm::defaultp>
#define m23T glm::tmat2x3<T, glm::defaultp>
typedef vector<glm::tvec4<float, glm::defaultp>> vv4F;
#else
#define v2T glm::detail::tvec2<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 m4T glm::detail::tmat4x4<T, glm::defaultp>
#define m23T glm::detail::tmat2x3<T, glm::defaultp>
typedef vector<glm::detail::tvec4<float, glm::defaultp>> vv4F;
#endif
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>
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>
@ -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>
/// <returns>True if nothing went wrong, else false.</returns>
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;
EnterRender();
@ -645,7 +647,7 @@ AccumOnly:
CreateSpatialFilter(newFilterAlloc);
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();
ComputeCurves(true);//Color curves must be re-calculated as well.
ComputeCurves();//Color curves must be re-calculated as well.
if (AccumulatorToFinalImage(finalImage, finalOffset) == eRenderStatus::RENDER_OK)
{
@ -822,6 +824,7 @@ bool Renderer<T, bucketT>::ResetBuckets(bool resetHist, bool resetAccum)
}
/// <summary>
/// THIS IS UNUSED.
/// Log scales a single row with a specially structured loop that will be vectorized by the compiler.
/// Note this adds an epsilon to the denomiator used to compute the logScale
/// value because the conditional check for zero would have prevented the loop from
@ -882,7 +885,7 @@ eRenderStatus Renderer<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 acc = glm::value_ptr(m_AccumulatorBuckets[i]);
for (size_t v = 0; v < 4; v++)
for (size_t v = 0; v < 4; v++)//Vectorized by compiler.
acc[v] = hist[v] * logScale;
}
}
@ -1063,7 +1066,7 @@ eRenderStatus Renderer<T, bucketT>::GaussianDensityFilter()
/// <param name="finalOffset">Offset in the buffer to store the pixels to</param>
/// <returns>True if not prematurely aborted, else false.</returns>
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))
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>
/// <returns>True if not prematurely aborted, else false.</returns>
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)
return eRenderStatus::RENDER_ERROR;
EnterFinalAccum();
//Timing t(4);
bool doAlpha = NumChannels() > 3;
size_t filterWidth = m_SpatialFilter->FinalFilterWidth();
bucketT g, linRange, vibrancy;
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.
{
GammaCorrection(*rowStart, background, g, linRange, vibrancy, true, false, glm::value_ptr(*rowStart));//Write back in place.
GammaCorrection(*rowStart, background, g, linRange, vibrancy, false, glm::value_ptr(*rowStart));//Write back in place.
rowStart++;
}
});
@ -1123,11 +1125,12 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
parallel_for(size_t(0), FinalRasH(), size_t(1), [&](size_t j)
{
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 clampedFilterH = std::min(filterWidth, m_SuperRasH - y);//Make sure the filter doesn't go past the bottom of the gutter.
auto pv4T = pixels + pixelsRowStart;
for (size_t i = 0; i < FinalRasW(); i++, pixelsRowStart += PixelSize())
for (size_t i = 0; i < FinalRasW(); i++, pv4T++)
{
size_t ii, jj;
size_t x = m_DensityFilterOffset + (i * Supersample());//Start at the beginning column of each super sample block.
@ -1149,68 +1152,8 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
}
}
if (BytesPerChannel() == 2)
{
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);
}
}
auto pf = reinterpret_cast<float*>(pv4T);
GammaCorrection(*(reinterpret_cast<tvec4<bucketT, glm::defaultp>*>(&newBucket)), background, g, linRange, vibrancy, true, pf);
}
});
@ -1222,30 +1165,15 @@ eRenderStatus Renderer<T, bucketT>::AccumulatorToFinalImage(byte* pixels, size_t
if (ph >= FinalRasH())
ph = FinalRasH();
if (BytesPerChannel() == 1)
for (j = 0; j < ph; j++)
{
for (j = 0; j < ph; j++)
for (i = 0; i < FinalRasW(); i++)
{
for (i = 0; i < FinalRasW(); i++)
{
auto p = pixels + (NumChannels() * (i + j * FinalRasW()));
p[0] = byte(m_TempEmber.m_Palette[i * 256 / FinalRasW()][0] * WHITE);//The palette is [0..1], output image is [0..255].
p[1] = byte(m_TempEmber.m_Palette[i * 256 / FinalRasW()][1] * WHITE);
p[2] = byte(m_TempEmber.m_Palette[i * 256 / FinalRasW()][2] * WHITE);
}
}
}
else//BPC == 2.
{
for (j = 0; j < ph; j++)
{
for (i = 0; i < FinalRasW(); i++)
{
auto p16 = reinterpret_cast<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));
}
auto p = pixels + (i + j * FinalRasW());
p->r = m_TempEmber.m_Palette[i * 256 / FinalRasW()][0];
p->g = m_TempEmber.m_Palette[i * 256 / FinalRasW()][1];
p->b = m_TempEmber.m_Palette[i * 256 / FinalRasW()][2];
p->a = 1;
}
}
}
@ -1322,7 +1250,6 @@ EmberStats Renderer<T, bucketT>::Iterate(size_t iterCount, size_t temporalSample
//t.Tic();
//Iterating, loop 3.
m_BadVals[threadIndex] += m_Iterator->Iterate(m_ThreadEmbers[threadIndex], params, m_Samples[threadIndex].data(), m_Rand[threadIndex]);
//m_BadVals[threadIndex] += m_Iterator->Iterate(m_Ember, params, m_Samples[threadIndex].data(), m_Rand[threadIndex]);
//iterationTime += t.Toc();
if (m_LockAccum)
@ -1677,112 +1604,97 @@ void Renderer<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
/// to specify what actions to take. Coupled with an additional template argument, this allows
/// using one function to perform all color clipping, gamma correction and final accumulation.
/// Template argument accumT is expected to match bucketT for the case of early clipping, byte for late clip for
/// images with one byte per channel and unsigned short for images with two bytes per channel.
/// Template argument accumT is expected to always be float4.
/// </summary>
/// <param name="bucket">The pixel to correct</param>
/// <param name="background">The background color</param>
/// <param name="g">The gamma to use</param>
/// <param name="linRange">The linear range 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="correctedChannels">The storage space for the corrected values to be written to</param>
template <typename T, typename bucketT>
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)
{
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();
auto bt1 = bucketT(1);
if (bucket.a <= 0)
if (scale && EarlyClip())
{
alpha = 0;
ls = 0;
if (m_CurvesSet)
{
CurveAdjust(bucket.r, 1);
CurveAdjust(bucket.g, 2);
CurveAdjust(bucket.b, 3);
}
correctedChannels[0] = accumT(Clamp<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
{
alpha = Palette<bucketT>::CalcAlpha(bucket.a, g, linRange);
ls = vibrancy * alpha / bucket.a;
ClampRef<bucketT>(alpha, 0, 1);
}
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.
Palette<bucketT>::template CalcNewRgb<bucketT>(glm::value_ptr(bucket), ls, HighlightPower(), newRgb);
for (glm::length_t rgbi = 0; rgbi < 3; rgbi++)
{
a = newRgb[rgbi] + ((1 - vibrancy) * std::pow(std::abs(bucket[rgbi]), g));//Must use abs(), else it it could be a negative value and return NAN.
if (NumChannels() <= 3 || !Transparency())
if (bucket.a <= 0)
{
alpha = 0;
ls = 0;
}
else
{
alpha = Palette<bucketT>::CalcAlpha(bucket.a, g, linRange);
ls = vibrancy * alpha / bucket.a;
ClampRef<bucketT>(alpha, 0, 1);
}
Palette<bucketT>::template CalcNewRgb<bucketT>(glm::value_ptr(bucket), ls, HighlightPower(), newRgb);
for (glm::length_t rgbi = 0; rgbi < 3; rgbi++)
{
a = newRgb[rgbi] + ((1 - vibrancy) * std::pow(std::abs(bucket[rgbi]), g));//Must use abs(), else it it could be a negative value and return NAN.
a += (1 - alpha) * background[rgbi];
}
else
{
if (alpha > 0)
a /= alpha;
else
a = 0;
}
if (!scale)
{
correctedChannels[rgbi] = accumT(Clamp<bucketT>(a, 0, 1.0));//Early clip, just assign directly.
}
else
{
a *= scaleVal;
if (m_CurvesSet)
if (scale && m_CurvesSet)
CurveAdjust(a, rgbi + 1);
correctedChannels[rgbi] = accumT(Clamp<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)
{
if (!scale)
correctedChannels[3] = accumT(alpha);//Early clip, just assign alpha directly.
else if (Transparency())
correctedChannels[3] = accumT(alpha * scaleVal);//Final accum, 4 channels, using transparency. Scale alpha from 0-1 to 0-255 for 8 bpc or 0-65535 for 16 bpc.
else
correctedChannels[3] = accumT(scaleVal);//Final accum, 4 channels, but not using transparency. 255 for 8 bpc, 65535 for 16 bpc.
correctedChannels[3] = accumT(alpha);
}
}
/// <summary>
/// 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>
/// <param name="scale">Whether to scale from 0-1 to 0-255 or 0-65535</param>
template <typename T, typename bucketT>
void Renderer<T, bucketT>::ComputeCurves(bool scale)
void Renderer<T, bucketT>::ComputeCurves()
{
if (m_CurvesSet)
{
m_Csa.resize(size_t(std::pow(size_t(256), BytesPerChannel())));
Timing t;
auto st = m_Csa.size();
auto stm1 = st - 1;
T tscale = scale ? T(stm1) : T(1);
for (size_t i = 0; i < st; i++)
m_Csa[i] = m_Ember.m_Curves.BezierFunc(i / T(stm1)) * tscale;
m_Csa[i] = m_Ember.m_Curves.BezierFunc(i * ONE_OVER_CURVES_LENGTH_M1);
t.Toc("ComputeCurves");
}
}
/// <summary>
/// Apply the curve adjustment to a single channel.
/// </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>
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>(aScaled, 0, stm1));
size_t tempIndex2 = size_t(Clamp<bucketT>(m_Csa[tempIndex].x, 0, stm1));
aScaled = m_Csa[tempIndex2][index];
size_t tempIndex = size_t(Clamp<bucketT>(a * CURVES_LENGTH_M1, 0, CURVES_LENGTH_M1));
size_t tempIndex2 = size_t(Clamp<bucketT>(m_Csa[tempIndex].x * CURVES_LENGTH_M1, 0, CURVES_LENGTH_M1));
a = m_Csa[tempIndex2][index];
}
//This class had to be implemented in a cpp file because the compiler was breaking.

View File

@ -66,7 +66,7 @@ public:
virtual bool CreateSpatialFilter(bool& newAlloc) override;
virtual bool CreateTemporalFilter(bool& newAlloc) override;
virtual size_t HistBucketSize() const override { return sizeof(tvec4<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;
protected:
@ -76,10 +76,10 @@ protected:
virtual bool ResetBuckets(bool resetHist = true, bool resetAccum = true);
virtual eRenderStatus LogScaleDensityFilter(bool forceOutput = false);
virtual eRenderStatus GaussianDensityFilter();
virtual eRenderStatus AccumulatorToFinalImage(vector<byte>& pixels, size_t finalOffset);
virtual eRenderStatus AccumulatorToFinalImage(byte* pixels, size_t finalOffset);
virtual eRenderStatus AccumulatorToFinalImage(vector<v4F>& pixels, size_t finalOffset);
virtual eRenderStatus AccumulatorToFinalImage(v4F* pixels, size_t finalOffset);
virtual EmberStats Iterate(size_t iterCount, size_t temporalSample);
virtual void ComputeCurves(bool scale);
virtual void ComputeCurves();
public:
//Non-virtual render properties, getters and setters.
@ -118,9 +118,7 @@ public:
inline T CenterX() const;
inline T CenterY() const;
inline T Rotate() const;
inline T Hue() const;
inline bucketT Brightness() const;
inline bucketT Contrast() const;
inline bucketT Gamma() const;
inline bucketT Vibrancy() const;
inline bucketT GammaThresh() const;
@ -154,8 +152,8 @@ protected:
private:
//Miscellaneous non-virtual functions used only in this class.
void Accumulate(QTIsaac<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);
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);
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 scale, accumT* correctedChannels);
void CurveAdjust(bucketT& a, const glm::length_t& index);
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>
/// <param name="pixels">The vector to allocate</param>
/// <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();
size_t size = FinalBufferSize();
size_t size = FinalDimensions();
if (m_ReclaimOnResize)
{
@ -374,27 +374,6 @@ void RendererBase::ReclaimOnResize(bool reclaimOnResize)
ChangeVal([&] { m_ReclaimOnResize = reclaimOnResize; }, eProcessAction::FULL_RENDER);
}
/// <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>
/// Set the callback object.
/// </summary>
@ -474,40 +453,18 @@ void RendererBase::ThreadCount(size_t threads, const char* seedString)
/// <summary>
/// Get the bytes per channel of the output image.
/// The only acceptable values are 1 and 2, and 2 is only
/// used when the output is Png.
/// Default: 1.
/// This will always be 4 since each channel is a 32-bit float.
/// </summary>
/// <returns></returns>
/// <returns>The number of bytes per channel</returns>
size_t RendererBase::BytesPerChannel() const { return m_BytesPerChannel; }
/// <summary>
/// Set the bytes per channel of the output image.
/// The only acceptable values are 1 and 2, and 2 is only
/// used when the output is Png.
/// Set the render state to ACCUM_ONLY.
/// </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.
/// Get the number of channels per pixel in the output image.
/// This will always be 4 since each pixel is always RGBA.
/// </summary>
/// <returns>The number of channels per pixel in the output image</returns>
size_t RendererBase::NumChannels() const { return m_NumChannels; }
/// <summary>
/// Get/set the priority used for the CPU rendering threads.
/// This does not affect OpenCL rendering.
@ -543,18 +500,6 @@ void RendererBase::InteractiveFilter(eInteractiveFilter filter)
/// Virtual render properties, getters and setters.
/// </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>
/// Get the number of threads used when rendering.
/// Default: use all avaliable cores.

View File

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

View File

@ -836,7 +836,6 @@ public:
/// <returns>The percentage possible color values that were present in the final output image</returns>
T TryColors(Ember<T>& ember, size_t colorResolution)
{
byte* p;
size_t i, hits = 0, res = colorResolution;
size_t pixTotal, res3 = res * res * res;
T scalar;
@ -862,14 +861,12 @@ public:
m_Hist.resize(res3);
Memset(m_Hist);
p = m_FinalImage.data();
auto p = m_FinalImage.data();
for (i = 0; i < m_Renderer->FinalDimensions(); i++)
{
m_Hist[(p[0] * res / 256) +
(p[1] * res / 256) * res +
(p[2] * res / 256) * res * res]++;//A specific histogram index representing the sum of R,G,B values.
p += m_Renderer->PixelSize();//Advance the pointer by 1 pixel.
m_Hist[size_t((p->r * res) + (p->g * res) * res + (p->b * res) * res * res)]++;//A specific histogram index representing the sum of R,G,B values.
p++;
}
for (i = 0; i < res3; i++)
@ -1352,7 +1349,7 @@ private:
string m_Comment;
vector<Point<T>> m_Samples;
vector<byte> m_FinalImage;
vector<v4F> m_FinalImage;
vector<uint> m_Hist;
EmberToXml<T> m_EmberToXml;
Iterator<T>* m_Iterator;

View File

@ -30,8 +30,7 @@ bool EmberAnimate(EmberOptions& opt)
//Regular variables.
Timing t;
bool unsorted = false;
uint channels;
streamsize padding;
uint padding;
size_t i, firstUnsortedIndex = 0;
string inputPath = GetPath(opt.Input());
vector<Ember<T>> embers;
@ -99,12 +98,6 @@ bool EmberAnimate(EmberOptions& opt)
renderers[i]->ThreadCount(opt.ThreadCount(), ns.c_str());
}
}
if (opt.BitsPerChannel() != 8)
{
cout << "Bits per channel cannot be anything other than 8 with OpenCL, setting to 8.\n";
opt.BitsPerChannel(8);
}
}
else
{
@ -153,24 +146,19 @@ bool EmberAnimate(EmberOptions& opt)
return false;
}
if (opt.Format() != "jpg" &&
opt.Format() != "png" &&
opt.Format() != "bmp")
if (!Find(opt.Format(), "jpg") &&
!Find(opt.Format(), "png") &&
#ifdef _WIN32
!Find(opt.Format(), "bmp") &&
#endif
!Find(opt.Format(), "exr"))
{
cout << "Format must be jpg, png, or bmp not " << opt.Format() << ". Setting to jpg.\n";
}
channels = opt.Format() == "png" ? 4 : 3;
if (opt.BitsPerChannel() == 16 && opt.Format() != "png")
{
cout << "Support for 16 bits per channel images is only present for the png format. Setting to 8.\n";
opt.BitsPerChannel(8);
}
else if (opt.BitsPerChannel() != 8 && opt.BitsPerChannel() != 16)
{
cout << "Unexpected bits per channel specified " << opt.BitsPerChannel() << ". Setting to 8.\n";
opt.BitsPerChannel(8);
#ifdef _WIN32
cout << "Format must be bmp, jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n";
#else
cout << "Format must be jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n";
#endif
opt.Format("png");
}
if (opt.AspectRatio() < 0)
@ -262,7 +250,7 @@ bool EmberAnimate(EmberOptions& opt)
}
//Cast to double in case the value exceeds 2^32.
double imageMem = double(channels) * double(ember.m_FinalRasW)
double imageMem = 4 * double(ember.m_FinalRasW)
* double(ember.m_FinalRasH) * double(renderers[0]->BytesPerChannel());
double maxMem = pow(2.0, double((sizeof(void*) * 8) - 1));
@ -316,46 +304,131 @@ bool EmberAnimate(EmberOptions& opt)
r->YAxisUp(opt.YAxisUp());
r->LockAccum(opt.LockAccum());
r->PixelAspectRatio(T(opt.AspectRatio()));
r->Transparency(opt.Transparency());
r->NumChannels(channels);
r->BytesPerChannel(opt.BitsPerChannel() / 8);
r->Priority(eThreadPriority(Clamp<intmax_t>(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,
string filename,//These are copies because this will be launched in a thread.
std::function<void (vector<v4F>&, string, EmberImageComments, size_t, size_t, size_t)> saveFunc = [&](vector<v4F>& finalImage,
string baseFilename,//These are copies because this will be launched in a thread.
EmberImageComments comments,
size_t w,
size_t h,
size_t chan)
{
bool writeSuccess = false;
byte* finalImagep = finalImage.data();
auto finalImagep = finalImage.data();
auto size = w * h;
bool doBmp = Find(opt.Format(), "bmp");
bool doJpg = Find(opt.Format(), "jpg");
bool doExr = Find(opt.Format(), "exr");
bool doPng8 = Find(opt.Format(), "png");
bool doPng16 = Find(opt.Format(), "png16");
bool doOnlyPng8 = doPng8 && !doPng16;
vector<byte> rgb8Image;
vector<std::thread> writeFileThreads;
writeFileThreads.reserve(5);
if ((opt.Format() == "jpg" || opt.Format() == "bmp") && chan == 4)
RgbaToRgb(finalImage, finalImage, w, h);
if (doBmp || doJpg)
{
rgb8Image.resize(size * 3);
Rgba32ToRgb8(finalImagep, rgb8Image.data(), w, h);
if (opt.Format() == "png")
writeSuccess = WritePng(filename.c_str(), finalImagep, w, h, opt.BitsPerChannel() / 8, opt.PngComments(), comments, opt.Id(), opt.Url(), opt.Nick());
else if (opt.Format() == "jpg")
writeSuccess = WriteJpeg(filename.c_str(), finalImagep, w, h, int(opt.JpegQuality()), opt.JpegComments(), comments, opt.Id(), opt.Url(), opt.Nick());
else if (opt.Format() == "bmp")
writeSuccess = WriteBmp(filename.c_str(), finalImagep, w, h);
if (doBmp)
{
writeFileThreads.push_back(std::thread([&]()
{
auto fn = baseFilename + ".bmp";
VerbosePrint("Writing " + fn);
auto writeSuccess = WriteBmp(fn.c_str(), rgb8Image.data(), w, h);
if (!writeSuccess)
cout << "Error writing " << filename << "\n";
if (!writeSuccess)
cout << "Error writing " << fn << "\n";
}));
}
if (doJpg)
{
writeFileThreads.push_back(std::thread([&]()
{
auto fn = baseFilename + ".jpg";
VerbosePrint("Writing " + fn);
auto writeSuccess = WriteJpeg(fn.c_str(), rgb8Image.data(), w, h, int(opt.JpegQuality()), opt.EnableComments(), comments, opt.Id(), opt.Url(), opt.Nick());
if (!writeSuccess)
cout << "Error writing " << fn << "\n";
}));
}
}
if (doPng8)
{
bool doBothPng = doPng16 && (opt.Format().find("png") != opt.Format().rfind("png"));
if (doBothPng || doOnlyPng8)//8-bit PNG
{
writeFileThreads.push_back(std::thread([&]()
{
auto fn = baseFilename + ".png";
VerbosePrint("Writing " + fn);
vector<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());
std::function<void(size_t)> iterFunc = [&](size_t index)
{
size_t ftime, finalImageIndex = 0;
string filename, flameName;
RendererBase* renderer = renderers[index].get();
ostringstream fnstream, os;
ostringstream os;
EmberStats stats;
EmberImageComments comments;
Ember<T> centerEmber;
vector<byte> finalImages[2];
vector<v4F> finalImages[2];
std::thread writeThread;
os.imbue(std::locale(""));
@ -390,13 +463,9 @@ bool EmberAnimate(EmberOptions& opt)
break;
}
fnstream << inputPath << opt.Prefix() << setfill('0') << setprecision(0) << fixed << setw(padding) << ftime << opt.Suffix() << "." << opt.Format();
filename = fnstream.str();
fnstream.str("");
if (opt.WriteGenome())
{
flameName = filename.substr(0, filename.find_last_of('.')) + ".flame";
auto flameName = MakeAnimFilename(inputPath, opt.Prefix(), opt.Suffix(), ".flame", padding, ftime);
if (opt.Verbose())
{
@ -425,21 +494,21 @@ bool EmberAnimate(EmberOptions& opt)
cout << "Render time: " << t.Format(stats.m_RenderMs) << "\n";
cout << "Pure iter time: " << t.Format(stats.m_IterMs) << "\n";
cout << "Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << "\n";
cout << "Writing " << filename << "\n\n";
}
//Run image writing in a thread. Although doing it this way duplicates the final output memory, it saves a lot of time
//when running with OpenCL. Call join() to ensure the previous thread call has completed.
Join(writeThread);
auto threadVecIndex = finalImageIndex;//Cache before launching thread.
auto baseFilename = MakeAnimFilename(inputPath, opt.Prefix(), opt.Suffix(), "", padding, ftime);
if (opt.ThreadedWrite())//Copies are passed of all but the first parameter to saveFunc(), to avoid conflicting with those values changing when starting the render for the next image.
if (opt.ThreadedWrite())//Copies of all but the first parameter are passed to saveFunc(), to avoid conflicting with those values changing when starting the render for the next image.
{
writeThread = std::thread(saveFunc, std::ref(finalImages[threadVecIndex]), filename, comments, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels());
writeThread = std::thread(saveFunc, std::ref(finalImages[threadVecIndex]), baseFilename, comments, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels());
finalImageIndex ^= 1;//Toggle the index.
}
else
saveFunc(finalImages[threadVecIndex], filename, comments, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels());//Will always use the first index, thereby not requiring more memory.
saveFunc(finalImages[threadVecIndex], baseFilename, comments, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels());//Will always use the first index, thereby not requiring more memory.
}
Join(writeThread);//One final check to make sure all writing is done before exiting this thread.

View File

@ -147,13 +147,14 @@ static const char* CalcAlphaFunctionString =
/// during final accumulation, which only takes floats.
/// </summary>
static const char* CurveAdjustFunctionString =
"static inline void CurveAdjust(__constant real4reals_bucket* csa, float* a, uint index)\n"
"static inline void CurveAdjust(__global real4reals_bucket* csa, float* a, uint index)\n"
"{\n"
" uint tempIndex = (uint)clamp(*a * (float)COLORMAP_LENGTH_MINUS_1, (float)0.0, (float)COLORMAP_LENGTH_MINUS_1);\n"
" uint tempIndex2 = (uint)clamp((float)csa[tempIndex].m_Real4.x * (float)COLORMAP_LENGTH_MINUS_1, (float)0.0, (float)COLORMAP_LENGTH_MINUS_1);\n"
" uint tempIndex = (uint)clamp(*a * CURVES_LENGTH_M1, 0.0f, CURVES_LENGTH_M1);\n"
" uint tempIndex2 = (uint)clamp(csa[tempIndex].m_Real4.x * CURVES_LENGTH_M1, 0.0f, CURVES_LENGTH_M1);\n"
"\n"
" *a = (float)csa[tempIndex2].m_Reals[index];\n"
"}\n";
"}\n"
"\n";
/// <summary>
/// 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 M_PHI 1.61803398874989484820458683436563\n"
"#define DEG_2_RAD (MPI / 180)\n"
"#define CURVES_LENGTH_M1 131071.0f\n"
"#define ONE_OVER_CURVES_LENGTH_M1 7.62945273935e-6f\n"
"\n"
"//Index in each dimension of a thread within a block.\n"
"#define THREAD_ID_X (get_local_id(0))\n"
@ -314,10 +316,7 @@ struct ALIGN SpatialFilterCL
uint m_FinalRasH;
uint m_Supersample;
uint m_FilterWidth;
uint m_NumChannels;
uint m_BytesPerChannel;
uint m_DensityFilterOffset;
uint m_Transparency;
uint m_YAxisUp;
T m_Vibrancy;
T m_HighlightPower;
@ -339,10 +338,7 @@ static const char* SpatialFilterCLStructString =
" uint m_FinalRasH;\n"
" uint m_Supersample;\n"
" uint m_FilterWidth;\n"
" uint m_NumChannels;\n"
" uint m_BytesPerChannel;\n"
" uint m_DensityFilterOffset;\n"
" uint m_Transparency;\n"
" uint m_YAxisUp;\n"
" real_bucket_t m_Vibrancy;\n"
" real_bucket_t m_HighlightPower;\n"

View File

@ -10,166 +10,58 @@ namespace EmberCLns
FinalAccumOpenCLKernelCreator::FinalAccumOpenCLKernelCreator(bool doublePrecision)
{
m_DoublePrecision = doublePrecision;
m_GammaCorrectionWithAlphaCalcKernel = CreateGammaCorrectionKernelString(true);
m_GammaCorrectionWithoutAlphaCalcKernel = CreateGammaCorrectionKernelString(false);
m_FinalAccumEarlyClipKernel = CreateFinalAccumKernelString(true, false, false);
m_FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(true, true, true);
m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(true, false, true);
m_FinalAccumLateClipKernel = CreateFinalAccumKernelString(false, false, false);
m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(false, true, true);
m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(false, false, true);
m_GammaCorrectionWithoutAlphaCalcKernel = CreateGammaCorrectionKernelString();
m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(true);
m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel = CreateFinalAccumKernelString(false);
}
/// <summary>
/// Kernel source and entry point properties, getters only.
/// </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::FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const { return m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint; }
const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipKernel() const { return m_FinalAccumLateClipKernel; }
const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipEntryPoint() const { return m_FinalAccumLateClipEntryPoint; }
const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel() const { return m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel; }
const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint() const { return m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint; }
const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel() const { return m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel; }
const string& FinalAccumOpenCLKernelCreator::FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint() const { return m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint; }
/// <summary>
/// Get the gamma correction entry point.
/// </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;
}
const string& FinalAccumOpenCLKernelCreator::GammaCorrectionEntryPoint() const { return m_GammaCorrectionWithoutAlphaCalcEntryPoint; }
const string& FinalAccumOpenCLKernelCreator::GammaCorrectionKernel() const { return m_GammaCorrectionWithoutAlphaCalcKernel; }
/// <summary>
/// Get the final accumulation entry point.
/// </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>
/// <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>
const string& FinalAccumOpenCLKernelCreator::FinalAccumEntryPoint(bool earlyClip, size_t channels, bool transparency, double& alphaBase, double& alphaScale) const
const string& FinalAccumOpenCLKernelCreator::FinalAccumEntryPoint(bool earlyClip) const
{
bool alphaCalc = ((channels > 3) && transparency);
bool alphaAccum = channels > 3;
if (alphaAccum)
{
alphaBase = transparency ? 0 : 1;//See the table below.
alphaScale = transparency ? 1 : 0;
}
if (earlyClip)
{
if (!alphaCalc && !alphaAccum)//Rgb output, the most common case.
return FinalAccumEarlyClipEntryPoint();
else if (alphaCalc && alphaAccum)//Rgba output and Transparency.
return FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumEntryPoint();
else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency.
return FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint();
else
return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense.
}
return FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint();
else
{
if (!alphaCalc && !alphaAccum)//Rgb output, the most common case.
return FinalAccumLateClipEntryPoint();
else if (alphaCalc && alphaAccum)//Rgba output and Transparency.
return FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint();
else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency.
return FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint();
else
return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense.
}
return FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint();
}
/// <summary>
/// Get the final accumulation kernel string.
/// </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>
const string& FinalAccumOpenCLKernelCreator::FinalAccumKernel(bool earlyClip, size_t channels, bool transparency) const
const string& FinalAccumOpenCLKernelCreator::FinalAccumKernel(bool earlyClip) const
{
bool alphaCalc = (channels > 3 && transparency);
bool alphaAccum = channels > 3;
if (earlyClip)
{
if (!alphaCalc && !alphaAccum)//Rgb output, the most common case.
return FinalAccumEarlyClipKernel();
else if (alphaCalc && alphaAccum)//Rgba output and Transparency.
return FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumKernel();
else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency.
return FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel();
else
return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense.
}
return FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumKernel();
else
{
if (!alphaCalc && !alphaAccum)//Rgb output, the most common case.
return FinalAccumLateClipKernel();
else if (alphaCalc && alphaAccum)//Rgba output and Transparency.
return FinalAccumLateClipWithAlphaCalcWithAlphaAccumKernel();
else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency.
return FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel();
else
return m_Empty;//Cannot have alphaCalc and !alphaAccum, it makes no sense.
}
}
/// <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);
return FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumKernel();
}
/// <summary>
/// Create the final accumulation kernel string
/// </summary>
/// <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>
string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyClip, bool alphaCalc, bool alphaAccum)
string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyClip)
{
ostringstream os;
string channels = alphaAccum ? "4" : "3";
os <<
ConstantDefinesString(m_DoublePrecision) <<
UnionCLStructString <<
@ -181,29 +73,14 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
if (earlyClip)
{
if (!alphaCalc && !alphaAccum)//Rgb output, the most common case.
os << "__kernel void " << m_FinalAccumEarlyClipEntryPoint << "(\n";
else if (alphaCalc && alphaAccum)//Rgba output and Transparency.
os << "__kernel void " << m_FinalAccumEarlyClipWithAlphaCalcWithAlphaAccumEntryPoint << "(\n";
else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency.
os << "__kernel void " << m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint << "(\n";
else
return "";//Cannot have alphaCalc and !alphaAccum, it makes no sense.
os << "__kernel void " << m_FinalAccumEarlyClipWithoutAlphaCalcWithAlphaAccumEntryPoint << "(\n";
}
else
{
os <<
CreateCalcNewRgbFunctionString(false) <<
CreateGammaCorrectionFunctionString(false, alphaCalc, alphaAccum, true);
if (!alphaCalc && !alphaAccum)//Rgb output, the most common case.
os << "__kernel void " << m_FinalAccumLateClipEntryPoint << "(\n";
else if (alphaCalc && alphaAccum)//Rgba output and Transparency.
os << "__kernel void " << m_FinalAccumLateClipWithAlphaCalcWithAlphaAccumEntryPoint << "(\n";
else if (!alphaCalc && alphaAccum)//Rgba output and !Transparency.
os << "__kernel void " << m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint << "(\n";
else
return "";//Cannot have alphaCalc and !alphaAccum, it makes no sense.
CreateGammaCorrectionFunctionString(false, true) <<
"__kernel void " << m_FinalAccumLateClipWithoutAlphaCalcWithAlphaAccumEntryPoint << "(\n";
}
os <<
@ -211,10 +88,8 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
" __write_only image2d_t pixels,\n"
" __constant SpatialFilterCL* spatialFilter,\n"
" __constant real_bucket_t* filterCoefs,\n"
" __constant real4reals_bucket* csa,\n"
" const uint doCurves,\n"
" const real_bucket_t alphaBase,\n"
" const real_bucket_t alphaScale\n"
" __global real4reals_bucket* csa,\n"
" const uint doCurves\n"
"\t)\n"
"{\n"
"\n"
@ -249,21 +124,13 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
" }\n"
"\n";
//Not supporting 2 bytes per channel on the GPU. If the user wants it, run on the CPU.
if (earlyClip)//If early clip, simply assign values directly to the temp float4 since they've been gamma corrected already, then write it straight to the output image below.
{
os <<
" finalColor.m_Float4.x = (float)newBucket.m_Real4.x;\n"//CPU side clamps, skip here because write_imagef() does the clamping for us.
" finalColor.m_Float4.y = (float)newBucket.m_Real4.y;\n"
" finalColor.m_Float4.z = (float)newBucket.m_Real4.z;\n";
if (alphaAccum)
{
if (alphaCalc)
os << " finalColor.m_Float4.w = (float)newBucket.m_Real4.w;\n";
else
os << " finalColor.m_Float4.w = 1.0f;\n";
}
" finalColor.m_Float4.z = (float)newBucket.m_Real4.z;\n"
" finalColor.m_Float4.w = (float)newBucket.m_Real4.w;\n";
}
else
{
@ -273,7 +140,7 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
os <<
" real4reals_bucket realFinal;\n"
"\n"
" GammaCorrectionFloats(&newBucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, alphaBase, alphaScale, &(realFinal.m_Reals[0]));\n"
" GammaCorrectionFloats(&newBucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, &(realFinal.m_Reals[0]));\n"
" finalColor.m_Float4.x = (float)realFinal.m_Real4.x;\n"
" finalColor.m_Float4.y = (float)realFinal.m_Real4.y;\n"
" finalColor.m_Float4.z = (float)realFinal.m_Real4.z;\n"
@ -283,7 +150,7 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
else
{
os <<
" GammaCorrectionFloats(&newBucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, alphaBase, alphaScale, &(finalColor.m_Floats[0]));\n";
" GammaCorrectionFloats(&newBucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, &(finalColor.m_Floats[0]));\n";
}
}
@ -308,81 +175,45 @@ string FinalAccumOpenCLKernelCreator::CreateFinalAccumKernelString(bool earlyCli
/// This is not a full kernel, just a function that is used in the kernels.
/// </summary>
/// <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>
/// <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;
string dataType;
string unionMember;
dataType = "real_bucket_t";
//Use real_t for all cases, early clip and final accum.
os << "void GammaCorrectionFloats(" << (globalBucket ? "__global " : "") << "real4reals_bucket* bucket, __constant real_bucket_t* background, real_bucket_t g, real_bucket_t linRange, real_bucket_t vibrancy, real_bucket_t highlightPower, real_bucket_t alphaBase, real_bucket_t alphaScale, " << (finalOut ? "" : "__global") << " real_bucket_t* correctedChannels)\n";
os
<< "{\n"
<< " real_bucket_t alpha, ls, tmp, a;\n"
<< " real4reals_bucket newRgb;\n"
<< "\n"
<< " if (bucket->m_Reals[3] <= 0)\n"
<< " {\n"
<< " alpha = 0;\n"
<< " ls = 0;\n"
<< " }\n"
<< " else\n"
<< " {\n"
<< " tmp = bucket->m_Reals[3];\n"
<< " alpha = CalcAlpha(tmp, g, linRange);\n"
<< " ls = vibrancy * alpha / tmp;\n"
<< " alpha = clamp(alpha, (real_bucket_t)0.0, (real_bucket_t)1.0);\n"
<< " }\n"
<< "\n"
<< " CalcNewRgb(bucket, ls, highlightPower, &newRgb);\n"
<< "\n"
<< " for (uint rgbi = 0; rgbi < 3; rgbi++)\n"
<< " {\n"
<< " a = newRgb.m_Reals[rgbi] + ((1.0 - vibrancy) * pow(fabs(bucket->m_Reals[rgbi]), g));\n"
<< "\n";
if (!alphaCalc)
{
os <<
" a += ((1.0 - alpha) * background[rgbi]);\n";
}
else
{
os
<< " if (alpha > 0)\n"
<< " a /= alpha;\n"
<< " else\n"
<< " a = 0;\n";
}
os <<
"\n"
" correctedChannels[rgbi] = (" << dataType << ")clamp(a, (real_bucket_t)0.0, (real_bucket_t)1.0);\n"
" }\n"
"\n";
//The CPU code has 3 cases for assigning alpha:
//[3] = alpha.//Early clip.
//[3] = alpha * 255.//Final Rgba with transparency.
//[3] = 255.//Final Rgba without transparency.
//Putting conditionals in GPU code is to be avoided. So do base + alpha * scale which will
//work for all 3 cases without using a conditional, which should be faster on a GPU. This gives:
//Base = 0, scale = 1. [3] = (0 + (alpha * 1)). [3] = alpha.
//Base = 0, scale = 255. [3] = (0 + (alpha * 255)). [3] = alpha * 255.
//Base = 255, scale = 0. [3] = (255 + (alpha * 0)). [3] = 255.
if (alphaAccum)
{
os
<< " correctedChannels[3] = (" << dataType << ")(alphaBase + (alpha * alphaScale));\n";
}
os <<
"}\n"
"\n";
os << "void GammaCorrectionFloats(" << (globalBucket ? "__global " : "") << "real4reals_bucket* bucket, __constant real_bucket_t* background, real_bucket_t g, real_bucket_t linRange, real_bucket_t vibrancy, real_bucket_t highlightPower, " << (finalOut ? "" : "__global") << " real_bucket_t* correctedChannels)\n";
os << "{\n"
<< " real_bucket_t alpha, ls, tmp, a;\n"
<< " real4reals_bucket newRgb;\n"
<< "\n"
<< " if (bucket->m_Reals[3] <= 0)\n"
<< " {\n"
<< " alpha = 0;\n"
<< " ls = 0;\n"
<< " }\n"
<< " else\n"
<< " {\n"
<< " tmp = bucket->m_Reals[3];\n"
<< " alpha = CalcAlpha(tmp, g, linRange);\n"
<< " ls = vibrancy * alpha / tmp;\n"
<< " alpha = clamp(alpha, (real_bucket_t)0.0, (real_bucket_t)1.0);\n"
<< " }\n"
<< "\n"
<< " CalcNewRgb(bucket, ls, highlightPower, &newRgb);\n"
<< "\n"
<< " for (uint rgbi = 0; rgbi < 3; rgbi++)\n"
<< " {\n"
<< " a = newRgb.m_Reals[rgbi] + ((1.0 - vibrancy) * pow(fabs(bucket->m_Reals[rgbi]), g));\n"
<< " a += ((1.0 - alpha) * background[rgbi]);\n"
<< " correctedChannels[rgbi] = (" << dataType << ")clamp(a, (real_bucket_t)0.0, (real_bucket_t)1.0);\n"
<< " }\n"
<< "\n"
<< " correctedChannels[3] = (" << dataType << ")alpha;\n"
<< "}\n"
<< "\n";
return os.str();
}
@ -451,9 +282,8 @@ string FinalAccumOpenCLKernelCreator::CreateCalcNewRgbFunctionString(bool global
/// <summary>
/// Create the gamma correction kernel string used for early clipping.
/// </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>
string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString(bool alphaCalc)
string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString()
{
ostringstream os;
string dataType;
@ -465,8 +295,8 @@ string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString(bool alp
CalcAlphaFunctionString <<
CreateCalcNewRgbFunctionString(true) <<
SpatialFilterCLStructString <<
CreateGammaCorrectionFunctionString(true, alphaCalc, true, false);//Will only be used with float in this case, early clip. Will always alpha accum.
os << "__kernel void " << (alphaCalc ? m_GammaCorrectionWithAlphaCalcEntryPoint : m_GammaCorrectionWithoutAlphaCalcEntryPoint) << "(\n" <<
CreateGammaCorrectionFunctionString(true, false);//Will only be used with float in this case, early clip. Will always alpha accum.
os << "__kernel void " << m_GammaCorrectionWithoutAlphaCalcEntryPoint << "(\n" <<
" __global real4reals_bucket* accumulator,\n"
" __constant SpatialFilterCL* spatialFilter\n"
")\n"
@ -478,8 +308,7 @@ string FinalAccumOpenCLKernelCreator::CreateGammaCorrectionKernelString(bool alp
"\n"
" uint superIndex = (GLOBAL_ID_Y * spatialFilter->m_SuperRasW) + GLOBAL_ID_X;\n"
" __global real4reals_bucket* bucket = accumulator + superIndex;\n"
//Pass in an alphaBase and alphaScale of 0, 1 which means to just directly assign the computed alpha value.
" GammaCorrectionFloats(bucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, 0.0, 1.0, &(bucket->m_Reals[0]));\n"
" GammaCorrectionFloats(bucket, &(spatialFilter->m_Background[0]), spatialFilter->m_Gamma, spatialFilter->m_LinRange, spatialFilter->m_Vibrancy, spatialFilter->m_HighlightPower, &(bucket->m_Reals[0]));\n"
"}\n"
;
return os.str();

View File

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

View File

@ -40,7 +40,6 @@ void RendererCL<T, bucketT>::Init()
{
m_Init = false;
m_DoublePrecision = typeid(T) == typeid(double);
m_NumChannels = 4;
//Buffer names.
m_EmberBufferName = "Ember";
m_XformsBufferName = "Xforms";
@ -73,7 +72,7 @@ void RendererCL<T, bucketT>::Init()
m_PaletteFormat.image_channel_order = CL_RGBA;
m_PaletteFormat.image_channel_data_type = CL_FLOAT;
m_FinalFormat.image_channel_order = CL_RGBA;
m_FinalFormat.image_channel_data_type = CL_UNORM_INT8;//Change if this ever supports 2BPC outputs for PNG.
m_FinalFormat.image_channel_data_type = CL_FLOAT;
}
/// <summary>
@ -386,7 +385,7 @@ const string& RendererCL<T, bucketT>::DEKernel() const { return m_DEOpenCLKernel
/// </summary>
/// <returns>The string representation of the kernel for the last built final accumulation program.</returns>
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>
/// 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>
/// <returns>True if success, else false.</returns>
template <typename T, typename bucketT>
bool RendererCL<T, bucketT>::ReadFinal(byte* pixels)
bool RendererCL<T, bucketT>::ReadFinal(v4F* pixels)
{
if (pixels && !m_Devices.empty())
return m_Devices[0]->m_Wrapper.ReadImage(m_FinalImageName, FinalRasW(), FinalRasH(), 0, m_Devices[0]->m_Wrapper.Shared(), pixels);
@ -423,7 +422,7 @@ bool RendererCL<T, bucketT>::ReadFinal(byte* pixels)
template <typename T, typename bucketT>
bool RendererCL<T, bucketT>::ClearFinal()
{
vector<byte> v;
vector<v4F> v;
if (!m_Devices.empty())
{
@ -470,17 +469,6 @@ bool RendererCL<T, bucketT>::Ok() const
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>
/// Clear the error report for this class as well as the OpenCLWrapper members of each device.
/// </summary>
@ -771,7 +759,7 @@ eRenderStatus RendererCL<T, bucketT>::GaussianDensityFilter()
/// <param name="finalOffset">Offset in the buffer to store the pixels to</param>
/// <returns>True if success and not aborted, else false.</returns>
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();
@ -877,17 +865,6 @@ EmberStats RendererCL<T, bucketT>::Iterate(size_t iterCount, size_t temporalSamp
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>
/// Private functions for making and running OpenCL programs.
/// </summary>
@ -1304,9 +1281,7 @@ eRenderStatus RendererCL<T, bucketT>::RunFinalAccum()
{
//Timing t(4);
bool b = true;
double alphaBase;
double alphaScale;
int accumKernelIndex = MakeAndGetFinalAccumProgram(alphaBase, alphaScale);
int accumKernelIndex = MakeAndGetFinalAccumProgram();
cl_uint argIndex;
size_t gridW;
size_t gridH;
@ -1323,7 +1298,7 @@ eRenderStatus RendererCL<T, bucketT>::RunFinalAccum()
if (b && !(b = wrapper.AddAndWriteBuffer(m_SpatialFilterParamsBufferName, reinterpret_cast<void*>(&m_SpatialFilterCL), sizeof(m_SpatialFilterCL)))) { AddToReport(loc); }
if (b && !(b = wrapper.AddAndWriteBuffer(m_CurvesCsaName, m_Csa.data(), SizeOf(m_Csa)))) { AddToReport(loc); }
if (b && !(b = wrapper.AddAndWriteBuffer(m_CurvesCsaName, m_Csa.data(), SizeOf(m_Csa)))) { AddToReport(loc); }
//Since early clip requires gamma correcting the entire accumulator first,
//it can't be done inside of the normal final accumulation kernel, so
@ -1373,10 +1348,6 @@ eRenderStatus RendererCL<T, bucketT>::RunFinalAccum()
if (b && !(b = wrapper.SetArg (accumKernelIndex, argIndex++, curvesSet))) { AddToReport(loc); }//Do curves.
if (b && !(b = wrapper.SetArg (accumKernelIndex, argIndex++, bucketT(alphaBase)))) { AddToReport(loc); }//Alpha base.
if (b && !(b = wrapper.SetArg (accumKernelIndex, argIndex++, bucketT(alphaScale)))) { AddToReport(loc); }//Alpha scale.
if (b && wrapper.Shared())
if (b && !(b = wrapper.EnqueueAcquireGLObjects(m_FinalImageName))) { AddToReport(loc); }
@ -1534,26 +1505,22 @@ int RendererCL<T, bucketT>::MakeAndGetDensityFilterProgram(size_t ss, uint filte
/// <summary>
/// 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>
/// <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>
template <typename T, typename bucketT>
int RendererCL<T, bucketT>::MakeAndGetFinalAccumProgram(double& alphaBase, double& alphaScale)
int RendererCL<T, bucketT>::MakeAndGetFinalAccumProgram()
{
int kernelIndex = -1;
if (!m_Devices.empty())
{
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__;
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))
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())
{
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);
const char* loc = __FUNCTION__;
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);
if (b)
@ -1735,10 +1702,7 @@ void RendererCL<T, bucketT>::ConvertSpatialFilter()
m_SpatialFilterCL.m_FinalRasH = uint(FinalRasH());
m_SpatialFilterCL.m_Supersample = uint(Supersample());
m_SpatialFilterCL.m_FilterWidth = uint(m_SpatialFilter->FinalFilterWidth());
m_SpatialFilterCL.m_NumChannels = uint(Renderer<T, bucketT>::NumChannels());
m_SpatialFilterCL.m_BytesPerChannel = uint(BytesPerChannel());
m_SpatialFilterCL.m_DensityFilterOffset = uint(DensityFilterOffset());
m_SpatialFilterCL.m_Transparency = Transparency();
m_SpatialFilterCL.m_YAxisUp = uint(m_YAxisUp);
m_SpatialFilterCL.m_Vibrancy = vibrancy;
m_SpatialFilterCL.m_HighlightPower = HighlightPower();

View File

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

View File

@ -1,6 +1,7 @@
#pragma once
#include "EmberCommonPch.h"
#include "EmberOptions.h"
/// <summary>
/// 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>
/// 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.
/// </summary>
/// <param name="rgba">The RGBA buffer</param>
/// <param name="rgb">The RGB buffer</param>
/// <param name="rgba">The RGBA 32-bit float buffer</param>
/// <param name="rgb">The RGB 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>
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.
rgb.resize(width * height * 3);
for (size_t i = 0, j = 0; i < (width * height * 4); i += 4, j += 3)
for (size_t i = 0, j = 0; i < (width * height); i++)
{
rgb[j] = rgba[i];
rgb[j + 1] = rgba[i + 1];
rgb[j + 2] = rgba[i + 2];
rgb[j++] = byte(Clamp<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));
}
}
/// <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>
/// Calculate the number of strips required if the needed amount of memory
/// is greater than the system memory, or greater than what the user wants to allow.
@ -439,7 +547,7 @@ static vector<unique_ptr<Renderer<T, float>>> CreateRenderers(eRendererType rend
/// <param name="allStripsFinished">Function called when all strips successfully finish rendering</param>
/// <returns>True if all rendering was successful, else false.</returns>
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)> perStripFinish,
std::function<void(size_t strip)> perStripError,

View File

@ -57,5 +57,17 @@
#include "SimpleGlob.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 EmberCLns;

View File

@ -58,8 +58,7 @@ enum class eOptionIDs : et
OPT_INT_PALETTE,
OPT_HEX_PALETTE,
OPT_INSERT_PALETTE,
OPT_JPEG_COMMENTS,
OPT_PNG_COMMENTS,
OPT_ENABLE_COMMENTS,
OPT_WRITE_GENOME,
OPT_THREADED_WRITE,
OPT_ENCLOSED,
@ -75,7 +74,6 @@ enum class eOptionIDs : et
OPT_STRIPS,
OPT_SUPERSAMPLE,
OPT_TEMPSAMPLES,
OPT_BPC,
OPT_PRINT_EDIT_DEPTH,
OPT_JPEG,
OPT_BEGIN,
@ -363,8 +361,7 @@ public:
INITBOOLOPTION(NameEnable, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_NAME_ENABLE, _T("--name_enable"), false, SO_NONE, " --name_enable Use the name attribute contained in the Xml as the output filename [default: false].\n"));
INITBOOLOPTION(HexPalette, Eob(eOptionUse::OPT_ANIM_GENOME, eOptionIDs::OPT_HEX_PALETTE, _T("--hex_palette"), true, SO_OPT, " --hex_palette Force palette RGB values to be hex when saving to Xml [default: true].\n"));
INITBOOLOPTION(InsertPalette, Eob(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_INSERT_PALETTE, _T("--insert_palette"), false, SO_NONE, " --insert_palette Insert the palette into the image for debugging purposes. Disabled when running with OpenCL [default: false].\n"));
INITBOOLOPTION(JpegComments, Eob(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_JPEG_COMMENTS, _T("--enable_jpg_comments"), false, SO_NONE, " --enable_jpg_comments Enables embedding the flame parameters and user identifying information in the jpeg header [default: false].\n"));
INITBOOLOPTION(PngComments, Eob(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_PNG_COMMENTS, _T("--enable_png_comments"), false, SO_NONE, " --enable_png_comments Enables embedding the flame parameters and user identifying information in the png header [default: false].\n"));
INITBOOLOPTION(EnableComments, Eob(eOptionUse::OPT_RENDER_ANIM, eOptionIDs::OPT_ENABLE_COMMENTS, _T("--enable_comments"), false, SO_NONE, " --enable_comments Enables embedding the flame parameters and user identifying information in the header of a jpg, png or exr [default: false].\n"));
INITBOOLOPTION(WriteGenome, Eob(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_WRITE_GENOME, _T("--write_genome"), false, SO_NONE, " --write_genome Write out flame associated with center of motion blur window [default: false].\n"));
INITBOOLOPTION(ThreadedWrite, Eob(eOptionUse::OPT_USE_ANIMATE, eOptionIDs::OPT_THREADED_WRITE, _T("--threaded_write"), true, SO_OPT, " --threaded_write Use a separate thread to write images to disk. This gives better performance, but doubles the memory required for the final output buffer. [default: true].\n"));
INITBOOLOPTION(Enclosed, Eob(eOptionUse::OPT_USE_GENOME, eOptionIDs::OPT_ENCLOSED, _T("--enclosed"), true, SO_OPT, " --enclosed Use enclosing Xml tags [default: true].\n"));
@ -388,7 +385,6 @@ public:
INITUINTOPTION(Strips, Eou(eOptionUse::OPT_USE_RENDER, eOptionIDs::OPT_STRIPS, _T("--nstrips"), 1, SO_REQ_SEP, " --nstrips=<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(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(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"));
@ -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(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(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(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"));
@ -519,8 +519,7 @@ public:
PARSEBOOLOPTION(eOptionIDs::OPT_NAME_ENABLE, NameEnable);
PARSEBOOLOPTION(eOptionIDs::OPT_HEX_PALETTE, HexPalette);
PARSEBOOLOPTION(eOptionIDs::OPT_INSERT_PALETTE, InsertPalette);
PARSEBOOLOPTION(eOptionIDs::OPT_JPEG_COMMENTS, JpegComments);
PARSEBOOLOPTION(eOptionIDs::OPT_PNG_COMMENTS, PngComments);
PARSEBOOLOPTION(eOptionIDs::OPT_ENABLE_COMMENTS, EnableComments);
PARSEBOOLOPTION(eOptionIDs::OPT_WRITE_GENOME, WriteGenome);
PARSEBOOLOPTION(eOptionIDs::OPT_THREADED_WRITE, ThreadedWrite);
PARSEBOOLOPTION(eOptionIDs::OPT_ENCLOSED, Enclosed);
@ -538,7 +537,6 @@ public:
PARSEOPTION(eOptionIDs::OPT_STRIPS, Strips);
PARSEOPTION(eOptionIDs::OPT_SUPERSAMPLE, Supersample);
PARSEOPTION(eOptionIDs::OPT_TEMPSAMPLES, TemporalSamples);
PARSEOPTION(eOptionIDs::OPT_BPC, BitsPerChannel);
PARSEOPTION(eOptionIDs::OPT_PRINT_EDIT_DEPTH, PrintEditDepth);
PARSEOPTION(eOptionIDs::OPT_JPEG, JpegQuality);
PARSEOPTION(eOptionIDs::OPT_BEGIN, FirstFrame);
@ -723,18 +721,18 @@ public:
{
cout << "Usage:\n"
#ifdef _WIN32
"\tEmberRender.exe --in=test.flame [--out=outfile --format=png --verbose --progress --opencl]\n\n";
"\tEmberRender.exe --in=test.flame [--out=outfile --verbose --progress --opencl]\n\n";
#else
"\temberrender --in=test.flame [--out=outfile --format=png --verbose --progress --opencl]\n\n";
"\temberrender --in=test.flame [--out=outfile --verbose --progress --opencl]\n\n";
#endif
}
else if (optUsage == eOptionUse::OPT_USE_ANIMATE)
{
cout << "Usage:\n"
#ifdef _WIN32
"\tEmberAnimate.exe --in=sequence.flame [--format=png --verbose --progress --opencl]\n\n";
"\tEmberAnimate.exe --in=sequence.flame [--verbose --progress --opencl]\n\n";
#else
"\temberanimate --in=sequence.flame [--format=png --verbose --progress --opencl]\n\n";
"\temberanimate --in=sequence.flame [--verbose --progress --opencl]\n\n";
#endif
}
else if (optUsage == eOptionUse::OPT_USE_GENOME)
@ -808,8 +806,7 @@ public:
Eob NameEnable;
Eob HexPalette;
Eob InsertPalette;
Eob JpegComments;
Eob PngComments;
Eob EnableComments;
Eob WriteGenome;
Eob ThreadedWrite;
Eob Enclosed;
@ -828,7 +825,6 @@ public:
Eou Strips;
Eou Supersample;
Eou TemporalSamples;
Eou BitsPerChannel;
Eou Bits;
Eou PrintEditDepth;
Eou JpegQuality;

View File

@ -136,28 +136,28 @@ static bool WritePng(const char* filename, byte* image, size_t width, size_t hei
glm::uint16 testbe = 1;
vector<byte*> rows(height);
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[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[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[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[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[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[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[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());
for (i = 0; i < height; i++)
@ -326,3 +326,50 @@ static bool WriteBmp(const char* filename, byte* image, size_t width, size_t hei
b = SaveBmp(filename, bgrBuf.data(), width, height, newSize);
return b;
}
/// <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;
size_t i, i0, i1, rep, val, frame, frameCount, count = 0;
size_t ftime, firstFrame, lastFrame;
size_t n, tot, totb, totw;
T avgPix, fractionBlack, fractionWhite, blend, spread, mix0, mix1;
double tot;
size_t n, totb, totw;
double avgPix;
T fractionBlack, fractionWhite, blend, spread, mix0, mix1;
string token, filename;
ostringstream os, os2;
vector<Ember<T>> embers, embers2, templateEmbers;
vector<eVariationId> vars, noVars;
vector<byte> finalImage;
vector<v4F> finalImage;
eCrossMode crossMeth;
eMutateMode mutMeth;
Ember<T> orig, save, selp0, selp1, parent0, parent1;
@ -598,7 +600,6 @@ bool EmberGenome(EmberOptions& opt)
renderer->YAxisUp(opt.YAxisUp());
renderer->LockAccum(opt.LockAccum());
renderer->PixelAspectRatio(T(opt.AspectRatio()));
renderer->Transparency(opt.Transparency());
if (opt.Repeat() == 0)
{
@ -786,19 +787,20 @@ bool EmberGenome(EmberOptions& opt)
return false;
}
tot = totb = totw = 0;
tot = 0;
totb = totw = 0;
n = orig.m_FinalRasW * orig.m_FinalRasH;
for (i = 0; i < 3 * n; i += 3)
for (i = 0; i < n; i++)
{
tot += (finalImage[i] + finalImage[i + 1] + finalImage[i + 2]);
tot += (finalImage[i].r + finalImage[i].g + finalImage[i].b);
if (0 == finalImage[i] && 0 == finalImage[i + 1] && 0 == finalImage[i + 2]) totb++;
if (0 == finalImage[i].r && 0 == finalImage[i].g && 0 == finalImage[i].b) totb++;
if (255 == finalImage[i] && 255 == finalImage[i + 1] && 255 == finalImage[i + 2]) totw++;
if (1 == finalImage[i].r && 1 == finalImage[i].g && 1 == finalImage[i].g) totw++;
}
avgPix = (tot / T(3 * n));
avgPix = (tot / (3 * n));
fractionBlack = totb / T(n);
fractionWhite = totw / T(n);

View File

@ -29,17 +29,15 @@ bool EmberRender(EmberOptions& opt)
VerbosePrint("Using " << (sizeof(T) == sizeof(float) ? "single" : "double") << " precision.");
Timing t;
bool writeSuccess = false;
byte* finalImagep;
uint padding;
size_t i, channels;
size_t i;
size_t strips;
size_t iterCount;
string filename;
string inputPath = GetPath(opt.Input());
ostringstream os;
pair<size_t, size_t> p;
vector<Ember<T>> embers;
vector<byte> finalImage;
vector<v4F> finalImage;
EmberStats stats;
EmberReport emberReport;
EmberImageComments comments;
@ -102,12 +100,6 @@ bool EmberRender(EmberOptions& opt)
opt.ThreadCount(1);
renderer->ThreadCount(opt.ThreadCount(), opt.IsaacSeed() != "" ? opt.IsaacSeed().c_str() : nullptr);
if (opt.BitsPerChannel() != 8)
{
cout << "Bits per channel cannot be anything other than 8 with OpenCL, setting to 8.\n";
opt.BitsPerChannel(8);
}
if (opt.InsertPalette())
{
cout << "Inserting palette not supported with OpenCL, insertion will not take place.\n";
@ -115,24 +107,19 @@ bool EmberRender(EmberOptions& opt)
}
}
if (opt.Format() != "jpg" &&
opt.Format() != "png" &&
opt.Format() != "bmp")
if (!Find(opt.Format(), "jpg") &&
!Find(opt.Format(), "png") &&
#ifdef _WIN32
!Find(opt.Format(), "bmp") &&
#endif
!Find(opt.Format(), "exr"))
{
cout << "Format must be jpg, png, or bmp not " << opt.Format() << ". Setting to jpg.\n";
}
channels = opt.Format() == "png" ? 4 : 3;
if (opt.BitsPerChannel() == 16 && opt.Format() != "png")
{
cout << "Support for 16 bits per channel images is only present for the png format. Setting to 8.\n";
opt.BitsPerChannel(8);
}
else if (opt.BitsPerChannel() != 8 && opt.BitsPerChannel() != 16)
{
cout << "Unexpected bits per channel specified " << opt.BitsPerChannel() << ". Setting to 8.\n";
opt.BitsPerChannel(8);
#ifdef _WIN32
cout << "Format must be bmp, jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n";
#else
cout << "Format must be jpg, png, png16 or exr, not " << opt.Format() << ". Setting to png.\n";
#endif
opt.Format("png");
}
if (opt.AspectRatio() < 0)
@ -156,9 +143,6 @@ bool EmberRender(EmberOptions& opt)
renderer->LockAccum(opt.LockAccum());
renderer->InsertPalette(opt.InsertPalette());
renderer->PixelAspectRatio(T(opt.AspectRatio()));
renderer->Transparency(opt.Transparency());
renderer->NumChannels(channels);
renderer->BytesPerChannel(opt.BitsPerChannel() / 8);
renderer->Priority(eThreadPriority(Clamp<intmax_t>(intmax_t(opt.Priority()), intmax_t(eThreadPriority::LOWEST), intmax_t(eThreadPriority::HIGHEST))));
renderer->Callback(opt.DoProgress() ? progress.get() : nullptr);
@ -282,21 +266,6 @@ bool EmberRender(EmberOptions& opt)
//Only write once all strips for this image are finished.
[&](Ember<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,
//so it ends up using the correct value for quality * strips.
iterCount = renderer->TotalIterCount(1);
@ -305,28 +274,116 @@ bool EmberRender(EmberOptions& opt)
os << comments.m_NumIters << " / " << iterCount << " (" << std::fixed << std::setprecision(2) << ((double(stats.m_Iters) / double(iterCount)) * 100) << "%)";
VerbosePrint("\nIters ran/requested: " + os.str());
if (!opt.EmberCL()) VerbosePrint("Bad values: " << stats.m_Badvals);
if (!opt.EmberCL())
VerbosePrint("Bad values: " << stats.m_Badvals);
VerbosePrint("Render time: " + t.Format(stats.m_RenderMs));
VerbosePrint("Pure iter time: " + t.Format(stats.m_IterMs));
VerbosePrint("Iters/sec: " << size_t(stats.m_Iters / (stats.m_IterMs / 1000.0)) << "\n");
VerbosePrint("Writing " + filename);
bool useName = opt.NameEnable() && !finalEmber.m_Name.empty();
auto finalImagep = finalImage.data();
auto size = finalEmber.m_FinalRasW * finalEmber.m_FinalRasH;
bool doBmp = Find(opt.Format(), "bmp");
bool doJpg = Find(opt.Format(), "jpg");
bool doExr = Find(opt.Format(), "exr");
bool doPng8 = Find(opt.Format(), "png");
bool doPng16 = Find(opt.Format(), "png16");
bool doOnlyPng8 = doPng8 && !doPng16;
vector<byte> rgb8Image;
vector<std::thread> writeFileThreads;
writeFileThreads.reserve(5);
if ((opt.Format() == "jpg" || opt.Format() == "bmp") && renderer->NumChannels() == 4)
RgbaToRgb(finalImage, finalImage, renderer->FinalRasW(), renderer->FinalRasH());
if (doBmp || doJpg)
{
rgb8Image.resize(size * 3);
Rgba32ToRgb8(finalImagep, rgb8Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH);
finalImagep = finalImage.data();
writeSuccess = false;
if (doBmp)
{
writeFileThreads.push_back(std::thread([&]()
{
auto filename = MakeSingleFilename(inputPath, opt.Out(), finalEmber.m_Name, opt.Prefix(), opt.Suffix(), "bmp", padding, i, useName);
VerbosePrint("Writing " + filename);
writeSuccess = WriteBmp(filename.c_str(), rgb8Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH);
if (opt.Format() == "png")
writeSuccess = WritePng(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, opt.BitsPerChannel() / 8, opt.PngComments(), comments, opt.Id(), opt.Url(), opt.Nick());
else if (opt.Format() == "jpg")
writeSuccess = WriteJpeg(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, int(opt.JpegQuality()), opt.JpegComments(), comments, opt.Id(), opt.Url(), opt.Nick());
else if (opt.Format() == "bmp")
writeSuccess = WriteBmp(filename.c_str(), finalImagep, finalEmber.m_FinalRasW, finalEmber.m_FinalRasH);
if (!writeSuccess)
cout << "Error writing " << filename << "\n";
}));
}
if (!writeSuccess)
cout << "Error writing " << filename << "\n";
if (doJpg)
{
writeFileThreads.push_back(std::thread([&]()
{
auto filename = MakeSingleFilename(inputPath, opt.Out(), finalEmber.m_Name, opt.Prefix(), opt.Suffix(), "jpg", padding, i, useName);
VerbosePrint("Writing " + filename);
writeSuccess = WriteJpeg(filename.c_str(), rgb8Image.data(), finalEmber.m_FinalRasW, finalEmber.m_FinalRasH, int(opt.JpegQuality()), opt.EnableComments(), comments, opt.Id(), opt.Url(), opt.Nick());
if (!writeSuccess)
cout << "Error writing " << filename << "\n";
}));
}
}
if (doPng8)
{
bool doBothPng = doPng16 && (opt.Format().find("png") != opt.Format().rfind("png"));
if (doBothPng || doOnlyPng8)//8-bit PNG
{
writeFileThreads.push_back(std::thread([&]()
{
auto filename = MakeSingleFilename(inputPath, opt.Out(), finalEmber.m_Name, opt.Prefix(), opt.Suffix(), "png", padding, i, useName);
VerbosePrint("Writing " + filename);
vector<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())

View File

@ -5,6 +5,25 @@
#include <list>
#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>
/// EmberTester is a scratch area used for on the fly testing.
/// It may become a more formalized automated testing system
@ -17,6 +36,43 @@ using namespace EmberCommon;
//#define DO_NVIDIA 1
void writeRgba1(const char filename[],
const Rgba* pixels,
int width,
int height)
{
//
// Write an RGBA image using class RgbaOutputFile.
//
// - open the file
// - describe the memory layout of the pixels
// - store the pixels in the file
//
//auto& chl = file.header().channels();
//chl.findChannel("R")->type = PixelType::FLOAT;
//Header header(width, height);
//header.channels().insert("R", Channel(IMF::FLOAT));
//header.channels().insert("G", Channel(IMF::FLOAT));
//header.channels().insert("B", Channel(IMF::FLOAT));
////header.channels().insert("A", Channel(IMF::FLOAT));
//FrameBuffer frameBuffer;
//frameBuffer.insert("Z", // name
// Slice(IMF::FLOAT, // type
// (char *)zPixels, // base
// sizeof(*zPixels) * 1, // xStride
// sizeof(*zPixels) * width)); // yStride
try
{
RgbaOutputFile file(filename, width, height, WRITE_RGBA);
file.setFrameBuffer(pixels, 1, width);
file.writePixels(height);
}
catch (std::exception e)
{
cout << e.what() << endl;
}
}
template <typename T>
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;
list<Ember<float>> fl;
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();
string line = "title=\"cj_aerie\" smooth=no", delim = " =\"";
auto vec = Split(line, delim, true);
@ -2008,25 +2078,24 @@ int _tmain(int argc, _TCHAR* argv[])
return 1;
*/
//MakeTestAllVarsRegPrePostComboFile("testallvarsout.flame");
/* return 0;
return 0;
/*
TestThreadedKernel();
auto palf = PaletteList<float>::Instance();
Palette<float>* pal = palf->GetRandomPalette();
TestThreadedKernel();
cout << pal->Size() << endl;
auto palf = PaletteList<float>::Instance();
Palette<float>* pal = palf->GetRandomPalette();
double d = 1;
cout << pal->Size() << endl;
for (int i = 0; i < 10; i++)
{
cout << "log10(" << d << ") = " << std::max<uint>(1u, uint(std::log10(d)) + 1u) << endl;
d *= 10;
}
double d = 1;
for (int i = 0; i < 10; i++)
{
cout << "log10(" << d << ") = " << std::max<uint>(1u, uint(std::log10(d)) + 1u) << endl;
d *= 10;
}
return 0;*/
return 0;*/
/*
uint i, iters = (uint)10e7;
size_t total = 0;

View File

@ -58,7 +58,7 @@
<enum>QFrame::NoFrame</enum>
</property>
<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 name="textFormat">
<enum>Qt::RichText</enum>

View File

@ -169,7 +169,7 @@ void DoubleSpinBox::OnTimeout()
/// <returns>false</returns>
bool DoubleSpinBox::eventFilter(QObject* o, QEvent* e)
{
QMouseEvent* me = dynamic_cast<QMouseEvent*>(e);
auto me = dynamic_cast<QMouseEvent*>(e);
if (isEnabled() && me)
{
@ -287,3 +287,124 @@ void DoubleSpinBox::StopTimer()
s_Timer.stop();
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>
class VariationTreeDoubleSpinBox : public DoubleSpinBox
{
public:
/// <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);
}
Q_OBJECT
public:
explicit VariationTreeDoubleSpinBox(QWidget* p, VariationTreeWidgetItem* widgetItem, eVariationId id, const string& param, int h = 16, double step = 0.05);
virtual ~VariationTreeDoubleSpinBox() { }
bool IsParam() { return !m_Param.empty(); }
string ParamName() { return m_Param; }
eVariationId GetVariationId() { return m_Id; }
VariationTreeWidgetItem* WidgetItem() { return m_WidgetItem; }
public slots:
void PiActionTriggered(bool checked = false);
void TwoPiActionTriggered(bool checked = false);
void PiOver2ActionTriggered(bool checked = false);
void PiOver3ActionTriggered(bool checked = false);
void PiOver4ActionTriggered(bool checked = false);
void PiOver6ActionTriggered(bool checked = false);
void OneOverPiActionTriggered(bool checked = false);
void TwoOverPiActionTriggered(bool checked = false);
void ThreeOverPiActionTriggered(bool checked = false);
void FourOverPiActionTriggered(bool checked = false);
void SqrtTwoActionTriggered(bool checked = false);
void SqrtThreeActionTriggered(bool checked = false);
private:
string m_Param;
eVariationId m_Id;
VariationTreeWidgetItem* m_WidgetItem;
};
/// <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()
//{
// qDebug() << "~EmberTreeWidgetItemBase()";
//}
~EmberTreeWidgetItemBase()
{
//qDebug() << "~EmberTreeWidgetItemBase()";
}
/// <summary>
/// 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_PathCellIndex = row;
QStringList comboList;
#ifndef _WIN32
comboList.append("bmp");
#endif
comboList.append("jpg");
comboList.append("png");
comboList.append("exr");
m_Tbcw = new TwoButtonComboWidget("...", "Open", comboList, 22, 40, 22, table);
table->setCellWidget(row, 1, m_Tbcw);
table->item(row++, 1)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
@ -116,6 +120,7 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF
ui.FinalRenderSaveXmlCheckBox->setChecked( m_Settings->FinalSaveXml());
ui.FinalRenderDoAllCheckBox->setChecked( m_Settings->FinalDoAll());
ui.FinalRenderDoSequenceCheckBox->setChecked( m_Settings->FinalDoSequence());
ui.FinalRenderPng16BitCheckBox->setChecked( m_Settings->FinalPng16Bit());
ui.FinalRenderKeepAspectCheckBox->setChecked( m_Settings->FinalKeepAspect());
ui.FinalRenderThreadCountSpin->setValue( m_Settings->FinalThreadCount());
#ifdef _WIN32
@ -136,11 +141,27 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF
m_SupersampleSpin->setValue(m_Settings->FinalSupersample());
m_StripsSpin->setValue(int(m_Settings->FinalStrips()));
Scale(eScaleType(m_Settings->FinalScale()));
int index = 0;
#ifdef _WIN32
if (m_Settings->FinalExt() == "jpg")
m_Tbcw->m_Combo->setCurrentIndex(0);
else
m_Tbcw->m_Combo->setCurrentIndex(1);
if (m_Settings->FinalExt().endsWith("bmp", Qt::CaseInsensitive))
m_Tbcw->m_Combo->setCurrentIndex(index);
index++;
#endif
if (m_Settings->FinalExt().endsWith("jpg", Qt::CaseInsensitive))
m_Tbcw->m_Combo->setCurrentIndex(index);
index++;
if (m_Settings->FinalExt().endsWith("png", Qt::CaseInsensitive))
m_Tbcw->m_Combo->setCurrentIndex(index);
index++;
if (m_Settings->FinalExt().endsWith("exr", Qt::CaseInsensitive))
m_Tbcw->m_Combo->setCurrentIndex(index);
//Explicitly call these to enable/disable the appropriate controls.
OnOpenCLCheckBoxStateChanged(ui.FinalRenderOpenCLCheckBox->isChecked());
@ -150,23 +171,20 @@ FractoriumFinalRenderDialog::FractoriumFinalRenderDialog(QWidget* p, Qt::WindowF
int desktopHeight = qApp->desktop()->availableGeometry().height();
s.setHeight(std::min(s.height(), int(double(desktopHeight * 0.90))));
setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, s, qApp->desktop()->availableGeometry()));
ui.FinalRenderThreadHorizontalLayout->setAlignment(Qt::AlignLeft);
ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadCountSpin, Qt::AlignLeft);
ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadPriorityLabel, Qt::AlignLeft);
ui.FinalRenderThreadHorizontalLayout->setAlignment(ui.FinalRenderThreadPriorityComboBox, Qt::AlignLeft);
QWidget* w = SetTabOrder(this, ui.FinalRenderEarlyClipCheckBox, ui.FinalRenderYAxisUpCheckBox);
//Update these with new controls.
QWidget* w = SetTabOrder(this, ui.FinalRenderEarlyClipCheckBox, ui.FinalRenderYAxisUpCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderTransparencyCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderOpenCLCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderPng16BitCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderDoublePrecisionCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderSaveXmlCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderDoAllCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderDoSequenceCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderCurrentSpin);
w = SetTabOrder(this, w, ui.FinalRenderDeviceTable);
w = SetTabOrder(this, w, ui.FinalRenderApplyToAllCheckBox);
w = SetTabOrder(this, w, ui.FinalRenderThreadCountSpin);
w = SetTabOrder(this, w, ui.FinalRenderThreadPriorityComboBox);
w = SetTabOrder(this, w, ui.FinalRenderApplyToAllCheckBox);
w = SetTabOrder(this, w, m_WidthScaleSpin);
w = SetTabOrder(this, w, m_HeightScaleSpin);
w = SetTabOrder(this, w, ui.FinalRenderScaleNoneRadioButton);
@ -215,9 +233,10 @@ bool FractoriumFinalRenderDialog::Double() { return ui.FinalRenderDoublePrecisio
bool FractoriumFinalRenderDialog::SaveXml() { return ui.FinalRenderSaveXmlCheckBox->isChecked(); }
bool FractoriumFinalRenderDialog::DoAll() { return ui.FinalRenderDoAllCheckBox->isChecked(); }
bool FractoriumFinalRenderDialog::DoSequence() { return ui.FinalRenderDoSequenceCheckBox->isChecked(); }
bool FractoriumFinalRenderDialog::Png16Bit() { return ui.FinalRenderPng16BitCheckBox->isChecked(); }
bool FractoriumFinalRenderDialog::KeepAspect() { return ui.FinalRenderKeepAspectCheckBox->isChecked(); }
bool FractoriumFinalRenderDialog::ApplyToAll() { return ui.FinalRenderApplyToAllCheckBox->isChecked(); }
QString FractoriumFinalRenderDialog::Ext() { return m_Tbcw->m_Combo->currentIndex() == 0 ? "jpg" : "png"; }
QString FractoriumFinalRenderDialog::Ext() { return m_Tbcw->m_Combo->currentText(); }
QString FractoriumFinalRenderDialog::Path() { return ui.FinalRenderParamsTable->item(m_PathCellIndex, 1)->text(); }
void FractoriumFinalRenderDialog::Path(const QString& s) { ui.FinalRenderParamsTable->item(m_PathCellIndex, 1)->setText(s); }
QString FractoriumFinalRenderDialog::Prefix() { return m_PrefixEdit->text(); }
@ -261,6 +280,7 @@ FinalRenderGuiState FractoriumFinalRenderDialog::State()
state.m_SaveXml = SaveXml();
state.m_DoAll = DoAll();
state.m_DoSequence = DoSequence();
state.m_Png16Bit = Png16Bit();
state.m_KeepAspect = KeepAspect();
state.m_Scale = Scale();
state.m_Path = Path();

View File

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

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>519</width>
<height>941</height>
<width>573</width>
<height>751</height>
</rect>
</property>
<property name="sizePolicy">
@ -63,8 +63,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>507</width>
<height>929</height>
<width>561</width>
<height>739</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
@ -83,96 +83,12 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QGridLayout" name="FinalRenderGridLayout" columnstretch="0,0">
<item row="0" column="0">
<widget class="QCheckBox" name="FinalRenderEarlyClipCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: clip colors and gamma correct after density filtering.&lt;/p&gt;&lt;p&gt;Unchecked: do it after spatial filtering.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Early Clip</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="FinalRenderYAxisUpCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: Positive Y direction is up.&lt;/p&gt;&lt;p&gt;Unchecked: Positive Y direction is down.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Positive Y Up</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="FinalRenderOpenCLCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use OpenCL to render if your video card supports it.&lt;/p&gt;&lt;p&gt;This is highly recommended as it will dramatically speed up render time.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use OpenCL</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="FinalRenderTransparencyCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use transparency in the final image.&lt;/p&gt;&lt;p&gt;Only supported for Png format.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Transparency</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="FinalRenderDoublePrecisionCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: use 64-bit double precision numbers (slower, but better image quality).&lt;/p&gt;&lt;p&gt;Unchecked: use 32-bit single precision numbers (faster, but worse image quality).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use Double Precision</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="FinalRenderSaveXmlCheckBox">
<property name="toolTip">
<string>Save an Xml parameter file for each flame rendered</string>
</property>
<property name="text">
<string>Save Xml</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="FinalRenderDoAllCheckBox">
<property name="toolTip">
<string>Render all open flames instead of just the current one</string>
</property>
<property name="text">
<string>Render All</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="FinalRenderDoSequenceCheckBox">
<property name="toolTip">
<string>Use temporal samples value to achieve motion blur effect between flames</string>
</property>
<property name="text">
<string>Render as Animation Sequence</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QGridLayout" name="FinalRenderGridLayout2">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item row="0" column="0">
<item row="0" column="1">
<widget class="QLabel" name="FinalRenderPreviewLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@ -215,7 +131,7 @@
</property>
</widget>
</item>
<item row="0" column="1">
<item row="0" column="2">
<widget class="QSpinBox" name="FinalRenderCurrentSpin">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -237,6 +153,100 @@
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QGridLayout" name="FinalRenderGridLayout" columnstretch="0,0">
<item row="0" column="0">
<widget class="QCheckBox" name="FinalRenderEarlyClipCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: clip colors and gamma correct after density filtering.&lt;/p&gt;&lt;p&gt;Unchecked: do it after spatial filtering.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Early Clip</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QCheckBox" name="FinalRenderYAxisUpCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: Positive Y direction is up.&lt;/p&gt;&lt;p&gt;Unchecked: Positive Y direction is down.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Positive Y Up</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="FinalRenderOpenCLCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use OpenCL to render if your video card supports it.&lt;/p&gt;&lt;p&gt;This is highly recommended as it will dramatically speed up render time.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use OpenCL</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QCheckBox" name="FinalRenderTransparencyCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use transparency in the final image.&lt;/p&gt;&lt;p&gt;Only supported for Png format.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Transparency</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="FinalRenderDoublePrecisionCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Checked: use 64-bit double precision numbers (slower, but better image quality).&lt;/p&gt;&lt;p&gt;Unchecked: use 32-bit single precision numbers (faster, but worse image quality).&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Use Double Precision</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="FinalRenderSaveXmlCheckBox">
<property name="toolTip">
<string>Save an Xml parameter file for each flame rendered</string>
</property>
<property name="text">
<string>Save Xml</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="FinalRenderDoAllCheckBox">
<property name="toolTip">
<string>Render all open flames instead of just the current one</string>
</property>
<property name="text">
<string>Render All</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="FinalRenderDoSequenceCheckBox">
<property name="toolTip">
<string>Use temporal samples value to achieve motion blur effect between flames</string>
</property>
<property name="text">
<string>Render as Animation Sequence</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QCheckBox" name="FinalRenderPng16BitCheckBox">
<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>
</item>
</layout>
</item>
<item>
@ -328,7 +338,10 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="FinalRenderThreadHorizontalLayout" stretch="0,0,0">
<layout class="QHBoxLayout" name="FinalRenderThreadHorizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QSpinBox" name="FinalRenderThreadCountSpin">
<property name="sizePolicy">
@ -405,6 +418,19 @@
</item>
</widget>
</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>
</item>
<item>
@ -928,6 +954,146 @@
</item>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTextEdit" name="FinalRenderTextOutput">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<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>
</widget>
</item>
<item>
<layout class="QGridLayout" name="FinalRenderGridLayout3" rowstretch="0,0,0,0" columnstretch="1,4">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item row="2" column="1">
<widget class="QProgressBar" name="FinalRenderFilteringProgress">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="FinalRenderIterationProgressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Iteration:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="FinalRenderAccumProgressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Final Accumulation:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QProgressBar" name="FinalRenderIterationProgress">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QProgressBar" name="FinalRenderAccumProgress">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="FinalRenderFilteringProgressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Density Filtering:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="FinalRenderTotalProgressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Total Progress:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QProgressBar" name="FinalRenderTotalProgress">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="FinalRenderImageCountLabel">
<property name="text">
@ -938,142 +1104,6 @@
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="FinalRenderGridLayout3" rowstretch="0,0,0,0" columnstretch="1,4">
<property name="sizeConstraint">
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item row="2" column="1">
<widget class="QProgressBar" name="FinalRenderFilteringProgress">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="FinalRenderIterationProgressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Iteration:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="FinalRenderAccumProgressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Final Accumulation:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QProgressBar" name="FinalRenderIterationProgress">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QProgressBar" name="FinalRenderAccumProgress">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="FinalRenderFilteringProgressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Density Filtering:</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="FinalRenderTotalProgressLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Total Progress:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QProgressBar" name="FinalRenderTotalProgress">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTextEdit" name="FinalRenderTextOutput">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<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>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="FinalRenderButtonHBoxLayout">
<property name="spacing">
@ -1160,13 +1190,22 @@
</customwidgets>
<tabstops>
<tabstop>FinalRenderEarlyClipCheckBox</tabstop>
<tabstop>FinalRenderDoublePrecisionCheckBox</tabstop>
<tabstop>FinalRenderYAxisUpCheckBox</tabstop>
<tabstop>FinalRenderSaveXmlCheckBox</tabstop>
<tabstop>FinalRenderTransparencyCheckBox</tabstop>
<tabstop>FinalRenderDoAllCheckBox</tabstop>
<tabstop>FinalRenderOpenCLCheckBox</tabstop>
<tabstop>FinalRenderParamsTable</tabstop>
<tabstop>FinalRenderTextOutput</tabstop>
<tabstop>FinalRenderStartButton</tabstop>
<tabstop>FinalRenderDoSequenceCheckBox</tabstop>
<tabstop>FinalRenderPng16BitCheckBox</tabstop>
<tabstop>FinalRenderCurrentSpin</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>
</tabstops>
<resources/>

View File

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

View File

@ -28,6 +28,7 @@ struct FinalRenderGuiState
bool m_Double;
bool m_SaveXml;
bool m_DoAll;
bool m_Png16Bit;
bool m_DoSequence;
bool m_KeepAspect;
eScaleType m_Scale;
@ -111,7 +112,7 @@ public:
#endif
virtual void SetEmber(size_t index, bool verbatim) override;
virtual bool Render() override;
virtual bool CreateRenderer(eRendererType renderType, const vector<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 size_t Index() const override { return m_Ember->m_Index; }
virtual uint SizeOfT() const override { return sizeof(T); }
@ -134,7 +135,7 @@ public:
protected:
void HandleFinishedProgress();
void SaveCurrentRender(Ember<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, const EmberStats& stats, Timing& renderTimer);
void SyncGuiToEmber(Ember<T>& ember, size_t widthOverride = 0, size_t heightOverride = 0);
@ -158,6 +159,7 @@ class FinalRenderPreviewRenderer : public PreviewRenderer<T>
{
public:
using PreviewRenderer<T>::m_PreviewRun;
using PreviewRenderer<T>::m_PreviewVec;
using PreviewRenderer<T>::m_PreviewEmber;
using PreviewRenderer<T>::m_PreviewRenderer;
using PreviewRenderer<T>::m_PreviewFinalImage;

View File

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

View File

@ -339,18 +339,18 @@ public slots:
void OnSummaryTreeHeaderSectionClicked(int logicalIndex);
//Rendering/progress.
void StartRenderTimer();
void StartRenderTimer(bool updatePreviews);
void IdleTimer();
bool ControllersOk();
void ShowCritical(const QString& title, const QString& text, bool invokeRequired = false);
//Can't have a template function be a slot.
void SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vector<byte>& v, uint w, uint h);
void SetLibraryTreeItemData(EmberTreeWidgetItemBase* item, vv4F& v, uint w, uint h);
public:
//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 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 SetFixedTableHeader(QHeaderView* header, QHeaderView::ResizeMode mode = QHeaderView::Fixed);
@ -422,8 +422,8 @@ private:
void ErrorReportToQTextEdit(const vector<string>& errors, QTextEdit* textEdit, bool clear = true);
//Rendering/progress.
void ShutdownAndRecreateFromOptions();
bool CreateRendererFromOptions();
void ShutdownAndRecreateFromOptions(bool updatePreviews);
bool CreateRendererFromOptions(bool updatePreviews);
bool CreateControllerFromOptions();
void EnableRenderControls(bool enable);
@ -489,22 +489,22 @@ private:
DoubleSpinBox* m_XformDirectColorSpin;
//Xforms Affine.
DoubleSpinBox* m_PreX1Spin;//Pre.
DoubleSpinBox* m_PreX2Spin;
DoubleSpinBox* m_PreY1Spin;
DoubleSpinBox* m_PreY2Spin;
DoubleSpinBox* m_PreO1Spin;
DoubleSpinBox* m_PreO2Spin;
AffineDoubleSpinBox* m_PreX1Spin;//Pre.
AffineDoubleSpinBox* m_PreX2Spin;
AffineDoubleSpinBox* m_PreY1Spin;
AffineDoubleSpinBox* m_PreY2Spin;
AffineDoubleSpinBox* m_PreO1Spin;
AffineDoubleSpinBox* m_PreO2Spin;
DoubleSpinBox* m_PostX1Spin;//Post.
DoubleSpinBox* m_PostX2Spin;
DoubleSpinBox* m_PostY1Spin;
DoubleSpinBox* m_PostY2Spin;
DoubleSpinBox* m_PostO1Spin;
DoubleSpinBox* m_PostO2Spin;
AffineDoubleSpinBox* m_PostX1Spin;//Post.
AffineDoubleSpinBox* m_PostX2Spin;
AffineDoubleSpinBox* m_PostY1Spin;
AffineDoubleSpinBox* m_PostY2Spin;
AffineDoubleSpinBox* m_PostO1Spin;
AffineDoubleSpinBox* m_PostO2Spin;
DoubleSpinBox* m_PreSpins[6];
DoubleSpinBox* m_PostSpins[6];
AffineDoubleSpinBox* m_PreSpins[6];
AffineDoubleSpinBox* m_PostSpins[6];
//Xaos.
DoubleSpinBox* m_XaosSpinBox;
@ -555,6 +555,7 @@ private:
int m_VarSortMode;
int m_PaletteSortMode;
int m_PreviousPaletteRow;
vector<byte> m_PreviewVec;
shared_ptr<OpenCLInfo> m_Info;
unique_ptr<FractoriumEmberControllerBase> m_Controller;
Ui::FractoriumClass ui;

View File

@ -18,7 +18,7 @@ FractoriumEmberControllerBase::FractoriumEmberControllerBase(Fractorium* fractor
m_RenderTimer->setInterval(0);
m_Fractorium->connect(m_RenderTimer.get(), SIGNAL(timeout()), SLOT(IdleTimer()));
m_RenderRestartTimer = make_unique<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>
@ -368,7 +368,6 @@ void TreePreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
auto f = m_Controller->m_Fractorium;
m_PreviewRenderer.EarlyClip(f->m_Settings->EarlyClip());
m_PreviewRenderer.YAxisUp(f->m_Settings->YAxisUp());
m_PreviewRenderer.Transparency(f->m_Settings->Transparency());
m_PreviewRenderer.ThreadCount(std::max(1u, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe.
if (auto top = m_Tree->topLevelItem(0))
@ -394,7 +393,7 @@ void TreePreviewRenderer<T>::PreviewRenderFunc(uint start, uint end)
//until the update is complete.
QMetaObject::invokeMethod(f, "SetLibraryTreeItemData", Qt::BlockingQueuedConnection,
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));
}

View File

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

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="w">The width 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>
/// 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.
/// </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>
void FractoriumEmberController<T>::SyncLibrary(eLibraryUpdate update)
{

View File

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

View File

@ -491,21 +491,38 @@ void Fractorium::SetPaletteFileComboIndex(const string& filename)
}
/// <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.
/// 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.
/// </summary>
/// <param name="i">The index of the curve to be cleared, 0 to clear all.</param>
template <typename T>
void FractoriumEmberController<T>::ClearColorCurves()
void FractoriumEmberController<T>::ClearColorCurves(int i)
{
Update([&]
{
m_Ember.m_Curves.Init();
if (i)
m_Ember.m_Curves.Init(i);
else
m_Ember.m_Curves.Init(0);
}, true, m_Renderer->EarlyClip() ? eProcessAction::FILTER_AND_ACCUM : eProcessAction::ACCUM_ONLY);
FillCurvesControl();
}
void Fractorium::OnResetCurvesButtonClicked(bool checked) { m_Controller->ClearColorCurves(); }
void Fractorium::OnResetCurvesButtonClicked(bool checked)
{
if (ui.CurvesAllRadio->isChecked())
m_Controller->ClearColorCurves(0);
else if (ui.CurvesRedRadio->isChecked())
m_Controller->ClearColorCurves(1);
else if (ui.CurvesGreenRadio->isChecked())
m_Controller->ClearColorCurves(2);
else if (ui.CurvesBlueRadio->isChecked())
m_Controller->ClearColorCurves(3);
else
m_Controller->ClearColorCurves(0);
}
/// <summary>
/// Set the coordinate of the curve point.

View File

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

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

View File

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

View File

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

View File

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

View File

@ -251,7 +251,7 @@ void FractoriumEmberController<T>::AffineSetHelper(double d, int index, bool pre
UpdateXform([&] (Xform<T>* xform)
{
auto& affine = pre ? xform->m_Affine : xform->m_Post;
DoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins;
AffineDoubleSpinBox** spinners = pre ? m_Fractorium->m_PreSpins : m_Fractorium->m_PostSpins;
if (m_Fractorium->ui.PolarAffineCheckBox->isChecked())
{
@ -618,7 +618,7 @@ void FractoriumEmberController<T>::FillBothAffines()
template <typename T>
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;
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="signal">The signal the spinner emits</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->setDecimals(prec);
table->setCellWidget(row, col, spinBox);

View File

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

View File

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

View File

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

View File

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

View File

@ -424,6 +424,16 @@ in interactive mode for each mouse movement</string>
</property>
</widget>
</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>
</widget>
<widget class="QWidget" name="OptionsXmlSavingTab">
@ -846,6 +856,8 @@ in interactive mode for each mouse movement</string>
<tabstop>TransparencyCheckBox</tabstop>
<tabstop>ShowAllXformsCheckBox</tabstop>
<tabstop>ContinuousUpdateCheckBox</tabstop>
<tabstop>ToggleTypeCheckBox</tabstop>
<tabstop>Png16BitCheckBox</tabstop>
<tabstop>RandomCountSpin</tabstop>
<tabstop>ThreadCountSpin</tabstop>
<tabstop>CpuSubBatchSpin</tabstop>

View File

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

View File

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

View File

@ -52,4 +52,5 @@ copy X64\Release\tbb.dll ..\..\..\fractorium\Deps
copy X64\Release\tbb.lib ..\..\..\fractorium\Deps
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