Compare commits

..

31 Commits

Author SHA1 Message Date
Samuele Lorefice
769cb8fd5f Removed last instances of direct Console.Write calls, updated the Linewalker Library. 2025-04-28 16:18:29 +02:00
Samuele Lorefice
359ae02e19 Added ImageSharp license to opensource licenses. 2025-04-24 15:16:42 +02:00
Samuele Lorefice
cdbcabd6a1 Added OpenSource licenses file to be output at build for ILGpu 2025-04-23 17:39:20 +02:00
Samuele Lorefice
780baa6e7a Added License 2025-04-23 16:58:13 +02:00
Samuele Lorefice
eaf1ced1c3 Added tiny logging library. Revised logging. 2025-04-09 03:28:18 +02:00
Samuele Lorefice
93002270d5 Changed blur iterations to reuse buffers. Huge memory savings + speedup 2025-04-08 21:39:06 +02:00
Samuele Lorefice
3e90df9e6e Removed unused properties from record 2025-04-08 20:03:05 +02:00
Samuele Lorefice
687de13d6c Added logline specifying what accelerator has been bound 2025-04-08 19:19:48 +02:00
Samuele Lorefice
2609d231e7 Added missing copy images script run configs 2025-04-08 19:11:43 +02:00
Samuele Lorefice
964eb02adb Added DeviceType switch, removed copy script from run configs (rider bug prevents execution), added CUDA and OpenCL run configs. 2025-04-08 18:53:52 +02:00
Samuele Lorefice
8992a6c33d Added windows version of shell script to build configs 2025-04-07 17:46:17 +02:00
Samuele Lorefice
ebfb083099 Rider updating files when closing is annoying 2025-04-07 10:46:44 +02:00
Samuele Lorefice
de46c11510 fixed debug images being output without debug flag.
Explicitly added quotes ("") to readme command example
2025-04-07 10:40:15 +02:00
Samuele Lorefice
f37c7039e5 Added readme 2025-04-07 10:04:15 +02:00
Samuele Lorefice
1defbd66ff Final cleanup and added last run configs 2025-04-07 09:55:17 +02:00
Samuele Lorefice
f342a2158b Command line argument parsing added, general logging cleanup. Buildchain improvements 2025-04-07 09:45:34 +02:00
Samuele Lorefice
1de6ee2127 Started pre-release cleanup 2025-04-07 08:24:54 +02:00
Samuele Lorefice
f1d1749c40 Merge branch 'ILGpu' 2025-04-07 07:59:37 +02:00
Samuele Lorefice
c65346aeec Added release build configs 2025-04-07 07:56:23 +02:00
Samuele Lorefice
a74ae1167e Updated gitignore 2025-04-07 07:51:39 +02:00
mm00
d7d2091d64 Merge remote-tracking branch 'origin/ILGpu' into ILGpu 2025-04-04 19:23:20 +02:00
mm00
ea28738280 added border to initial image used in final image calculation 2025-04-04 19:23:02 +02:00
Samuele Lorefice
2faab96a7d Merge remote-tracking branch 'origin/ILGpu' into ILGpu 2025-04-04 19:22:26 +02:00
Samuele Lorefice
8c5ca2ce21 Small kernel fixes 2025-04-04 19:22:05 +02:00
mm00
d0fbd31c1d fixed final image masking 2025-04-04 19:20:43 +02:00
mm00
5cf8612699 Fixed directional blur 2025-04-03 20:49:44 +02:00
mm00
dfa2cf3f31 added luma threshould 2025-04-03 19:28:26 +02:00
mm00
64b7eb9dcc added SDF normalization 2025-04-03 19:28:20 +02:00
Samuele Lorefice
d652c63586 Added editor config 2025-04-03 19:14:58 +02:00
Samuele Lorefice
9614283dc8 Cleanup and added formatting rules. Reformatted 2025-04-03 19:02:50 +02:00
mm00
07c00117f1 Added directional blur for final image 2025-04-02 19:45:20 +02:00
35 changed files with 5069 additions and 378 deletions

4012
.editorconfig Normal file

File diff suppressed because it is too large Load Diff

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ obj/
/packages/ /packages/
riderModule.iml riderModule.iml
/_ReSharper.Caches/ /_ReSharper.Caches/
publish/

View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="LocationsWithSilentDeleteHolder">
<option name="locations">
<list>
<option value="$PROJECT_DIR$/SDFMapCreator/publish/win-x64" />
<option value="$PROJECT_DIR$/SDFMapCreator/publish/linux-x64" />
</list>
</option>
</component>
</project>

View File

@@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Copy Images Psh" type="PowerShellRunType" factoryName="PowerShell" activateToolWindowBeforeRun="false" scriptUrl="I:\SDFMapCreator\SDFMapCreator\copyImages.ps1" workingDirectory="I:\SDFMapCreator\SDFMapCreator" executablePath="C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe">
<envs />
<method v="2" />
</configuration>
</component>

View File

@@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Copy Images Sh" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="" />
<option name="INDEPENDENT_SCRIPT_PATH" value="false" />
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/SDFMapCreator/copyImages.sh" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="/bin/bash" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="true" />
<envs />
<method v="2" />
</configuration>
</component>

View File

@@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Release Build Linux-x64" type="DotNetFolderPublish" factoryName="Publish to folder">
<riderPublish configuration="Release" delete_existing_files="true" include_native_libs_for_self_extract="true" platform="Any CPU" produce_single_file="true" runtime="linux-x64" target_folder="$PROJECT_DIR$/SDFMapCreator/publish/linux-x64" target_framework="net8.0" uuid_high="-7972981449231152312" uuid_low="-5206031561210231144" />
<method v="2" />
</configuration>
</component>

View File

@@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Release Build Win-x64" type="DotNetFolderPublish" factoryName="Publish to folder">
<riderPublish configuration="Release" delete_existing_files="true" include_native_libs_for_self_extract="true" platform="Any CPU" produce_single_file="true" runtime="win-x64" target_folder="$PROJECT_DIR$/SDFMapCreator/publish/win-x64" target_framework="net8.0" uuid_high="-7972981449231152312" uuid_low="-5206031561210231144" />
<method v="2" />
</configuration>
</component>

View File

@@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run SDF Tool [8 pic CUDA]" type="DotNetProject" factoryName=".NET Project" focusToolWindowBeforeRun="true">
<option name="EXE_PATH" value="$PROJECT_DIR$/SDFMapCreator/bin/Debug/net8.0/SDFMapTool.exe" />
<option name="PROGRAM_PARAMETERS" value="-D ./images -o &quot;./sdf.png&quot; -i &quot;01.png;02.png;03.png;04.png;05.png;06.png;07.png;08.png&quot; --device CUDA" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/SDFMapCreator/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/SDFMapCreator/SDFMapCreator.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" default="false" projectName="SDFMapCreator" projectPath="$PROJECT_DIR$/SDFMapCreator/SDFMapCreator.csproj" />
</method>
</configuration>
</component>

View File

@@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run SDF Tool [8 pic OpenCL]" type="DotNetProject" factoryName=".NET Project" focusToolWindowBeforeRun="true">
<option name="EXE_PATH" value="$PROJECT_DIR$/SDFMapCreator/bin/Debug/net8.0/SDFMapTool.exe" />
<option name="PROGRAM_PARAMETERS" value="-D ./images -o &quot;./sdf.png&quot; -i &quot;01.png;02.png;03.png;04.png;05.png;06.png;07.png;08.png&quot; --device OpenCL" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/SDFMapCreator/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/SDFMapCreator/SDFMapCreator.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" default="false" projectName="SDFMapCreator" projectPath="$PROJECT_DIR$/SDFMapCreator/SDFMapCreator.csproj" />
</method>
</configuration>
</component>

View File

@@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run SDF Tool [Debug, 8 pic]" type="DotNetProject" factoryName=".NET Project" focusToolWindowBeforeRun="true">
<option name="EXE_PATH" value="$PROJECT_DIR$/SDFMapCreator/bin/Debug/net8.0/SDFMapTool.exe" />
<option name="PROGRAM_PARAMETERS" value="-d -D &quot;./images&quot; -o &quot;./sdf.png&quot; -i &quot;01.png;02.png;03.png;04.png;05.png;06.png;07.png;08.png&quot;" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/SDFMapCreator/bin/Debug/net8.0" />
<option name="PASS_PARENT_ENVS" value="1" />
<option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" />
<option name="PROJECT_PATH" value="$PROJECT_DIR$/SDFMapCreator/SDFMapCreator.csproj" />
<option name="PROJECT_EXE_PATH_TRACKING" value="1" />
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value="net8.0" />
<method v="2">
<option name="Build" default="false" projectName="SDFMapCreator" projectPath="$PROJECT_DIR$/SDFMapCreator/SDFMapCreator.csproj" />
</method>
</configuration>
</component>

201
LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

41
Readme.md Normal file
View File

@@ -0,0 +1,41 @@
# SDF Tool
*A signed distance fields-based tool to generate shadow transition maps*
## Key features
- float16 png output for reduced banding
- Automatic CUDA/OpenCL GPU Acceleration for most of the process
- *nix style command line interface
## How to use
From command line: `SDFMapTool -D <directory_with_images> -i "<image1.png;image2.png;...>" -o <output_file_path.png> ...`
Available options:
```terminal
-d, --debug Enable debug mode.
-D, --imgDirectory Required. Input Images directory.
-i, --images Required. Images file names separated by ';' and in the correct order.
-o, --output Required. Output file path.
-b, --blur How many blur iterations to run.
-r, --radius Blur radius.
-s, --step Blur step size.
--sigma Blur sigma value (weighting).
--device Device to use for computation. (CPU, OpenCL, CUDA)
--help Display this help screen.
--version Display version information.
```

View File

@@ -12,5 +12,6 @@ Global
{915A479D-55CC-4B48-B7C0-75E0B8978698}.Debug|Any CPU.Build.0 = Debug|Any CPU {915A479D-55CC-4B48-B7C0-75E0B8978698}.Debug|Any CPU.Build.0 = Debug|Any CPU
{915A479D-55CC-4B48-B7C0-75E0B8978698}.Release|Any CPU.ActiveCfg = Release|Any CPU {915A479D-55CC-4B48-B7C0-75E0B8978698}.Release|Any CPU.ActiveCfg = Release|Any CPU
{915A479D-55CC-4B48-B7C0-75E0B8978698}.Release|Any CPU.Build.0 = Release|Any CPU {915A479D-55CC-4B48-B7C0-75E0B8978698}.Release|Any CPU.Build.0 = Release|Any CPU
{915A479D-55CC-4B48-B7C0-75E0B8978698}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
EndGlobalSection EndGlobalSection
EndGlobal EndGlobal

View File

@@ -0,0 +1,19 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/CSharpVarKeywordUsage/ForBuiltInTypes/@EntryValue">UseVar</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_FOR_LOCK/@EntryValue">NotRequired</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/BRACES_REDUNDANT/@EntryValue">False</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/DEFAULT_PRIVATE_MODIFIER/@EntryValue">Implicit</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/METHOD_OR_OPERATOR_BODY/@EntryValue">ExpressionBody</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpCodeStyle/MODIFIERS_ORDER/@EntryValue">public private protected internal file new static abstract virtual override readonly sealed extern unsafe volatile async required</s:String>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_EXPR_METHOD_ON_SINGLE_LINE/@EntryValue">IF_OWNER_IS_SINGLE_LINE</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ALLOW_COMMENT_AFTER_LBRACE/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/ANONYMOUS_METHOD_DECLARATION_BRACES/@EntryValue">END_OF_LINE</s:String>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AROUND_SINGLE_LINE_INVOCABLE/@EntryValue">1</s:Int64>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/INDENT_NESTED_FOR_STMT/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_BEFORE_ARROW_WITH_EXPRESSIONS/@EntryValue">True</s:Boolean>
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_LIMIT/@EntryValue">151</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/WRAP_OBJECT_AND_COLLECTION_INITIALIZER_STYLE/@EntryValue">WRAP_IF_LONG</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -1,14 +1,18 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAccelerator_002EAllocations_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fa3e5a0b5353a4a59d3be7bb386db3c46069739eacca1fc6b7323dca1ee7fd_003FAccelerator_002EAllocations_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAccelerator_002EAllocations_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fa3e5a0b5353a4a59d3be7bb386db3c46069739eacca1fc6b7323dca1ee7fd_003FAccelerator_002EAllocations_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACPUMultiprocessor_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fmm00_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F5d1e647f49ea4d7aa141f19476dc7451ae33d6321d7fb675b45f9b836878ca1a_003FCPUMultiprocessor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACPUMultiprocessor_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fmm00_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F5d1e647f49ea4d7aa141f19476dc7451ae33d6321d7fb675b45f9b836878ca1a_003FCPUMultiprocessor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADanywyf_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Ftmp_003FJetBrainsPerUserTemp_002D1000_002D1_003FSandboxFiles_003FLiqequv_003FDanywyf_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntryPointDescription_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F32b5380d8ca1aa7219c1622702a3e927b2bb32c9a53b43e12bb5a4af9a2862d_003FEntryPointDescription_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEntryPointDescription_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F32b5380d8ca1aa7219c1622702a3e927b2bb32c9a53b43e12bb5a4af9a2862d_003FEntryPointDescription_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AImage_007BTPixel_007D_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F98f0ece83ba33754ab932bd7b5c712d12f3a59029f9f14067f553a3a318c8f_003FImage_007BTPixel_007D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AImage_007BTPixel_007D_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F98f0ece83ba33754ab932bd7b5c712d12f3a59029f9f14067f553a3a318c8f_003FImage_007BTPixel_007D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ALogger_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F70a76ec6534f41dfb670b3e0b4505f5f2600_003F8a_003F2e6ec04f_003FLogger_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMonitor_002ECoreCLR_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fmm00_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F8056cd3f452fefb9834f05cdb275b762dd41f27b7766cd71174e78592dc495b_003FMonitor_002ECoreCLR_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AMonitor_002ECoreCLR_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fmm00_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F8056cd3f452fefb9834f05cdb275b762dd41f27b7766cd71174e78592dc495b_003FMonitor_002ECoreCLR_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APixelTypeInfo_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc3cfdca1fb93eb6df5e51a81da5df646adfab8b862fd1a07ee5d247b49c5179_003FPixelTypeInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003APixelTypeInfo_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003Fc3cfdca1fb93eb6df5e51a81da5df646adfab8b862fd1a07ee5d247b49c5179_003FPixelTypeInfo_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASafeFileHandle_002EUnix_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fmm00_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F9cf5f68d759deefc91b9c48c5ac3dd27708bb7dc38d0c485661fff5ce15b82_003FSafeFileHandle_002EUnix_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASafeFileHandle_002EUnix_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fmm00_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F9cf5f68d759deefc91b9c48c5ac3dd27708bb7dc38d0c485661fff5ce15b82_003FSafeFileHandle_002EUnix_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASafeFileHandle_002EWindows_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F261ea83c988816e3d8fe76b15b7ac6c10af64b8f9e739854f83c137c8ba9_003FSafeFileHandle_002EWindows_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASafeFileHandle_002EWindows_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F261ea83c988816e3d8fe76b15b7ac6c10af64b8f9e739854f83c137c8ba9_003FSafeFileHandle_002EWindows_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F2c8e7ca976f350cba9836d5565dac56b11e0b56656fa786460eb1395857a6fa_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AThrowHelper_002Ecs_002Fl_003AC_0021_003FUsers_003Fairon_003FAppData_003FRoaming_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F2c8e7ca976f350cba9836d5565dac56b11e0b56656fa786460eb1395857a6fa_003FThrowHelper_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVector3_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fmm00_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F6edafe13d8727aa238b865f5dc91dbc984b5abfbc60bece3744f6311c2c_003FVector3_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AVector3_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003Fhome_003Fmm00_003F_002Econfig_003FJetBrains_003FRider2024_002E3_003Fresharper_002Dhost_003FSourcesCache_003F6edafe13d8727aa238b865f5dc91dbc984b5abfbc60bece3744f6311c2c_003FVector3_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:Int64 x:Key="/Default/Dpa/Thresholds/=AllocationLoh/@EntryIndexedValue">52428800</s:Int64>
<s:Int64 x:Key="/Default/Dpa/Thresholds/=AllocationTopSoh/@EntryIndexedValue">26214400</s:Int64>
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt; <s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;
&lt;Assembly Path="/mnt/nvme2/Railgun/SDFMapCreator/SDFMapCreator/bin/Debug/net8.0/Magick.NET-Q16-HDRI-OpenMP-x64.dll" /&gt; &lt;Assembly Path="/mnt/nvme2/Railgun/SDFMapCreator/SDFMapCreator/bin/Debug/net8.0/Magick.NET-Q16-HDRI-OpenMP-x64.dll" /&gt;
&lt;/AssemblyExplorer&gt;</s:String></wpf:ResourceDictionary> &lt;/AssemblyExplorer&gt;</s:String></wpf:ResourceDictionary>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,10 +1,29 @@
/*
Copyright 2025 Railgun Entertainment AS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System.Numerics; using System.Numerics;
using LineWalker;
using SixLabors.ImageSharp; using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SDFMapCreator; namespace SDFMapCreator;
public static class ImageUtil { public static class ImageUtil {
static Logger logger = Logger.GetInstance();
public static T[,] LoadImage<T>(string path) where T : struct, IEquatable<T> { public static T[,] LoadImage<T>(string path) where T : struct, IEquatable<T> {
var image = SixLabors.ImageSharp.Image.Load(path); var image = SixLabors.ImageSharp.Image.Load(path);
return image switch { return image switch {
@@ -18,14 +37,14 @@ public static class ImageUtil {
static T[,] ProcessPixelsRgba64<T>(this Image<Rgba64> image) where T : struct, IEquatable<T> { static T[,] ProcessPixelsRgba64<T>(this Image<Rgba64> image) where T : struct, IEquatable<T> {
var max = 65535f; var max = 65535f;
int width = image.Width; var width = image.Width;
int height = image.Height; var height = image.Height;
var result = new T[image.Width, image.Height]; var result = new T[image.Width, image.Height];
image.ProcessPixelRows(accessor => { image.ProcessPixelRows(accessor => {
//we use Y as the row index and X as the column index //we use Y as the row index and X as the column index
for (int y = 0; y < height; y++) { for (var y = 0; y < height; y++) {
var span = accessor.GetRowSpan(y); var span = accessor.GetRowSpan(y);
for (int x = 0; x < width; x++) { for (var x = 0; x < width; x++) {
switch (result) { switch (result) {
case float[,] f: case float[,] f:
f[x, y] = span[x].R / max * (span[x].A / max); f[x, y] = span[x].R / max * (span[x].A / max);
@@ -37,7 +56,7 @@ public static class ImageUtil {
f[x, y] = new Vector3(span[x].R / max, span[x].G / max, span[x].B / max) * new Vector3(span[x].A / max); f[x, y] = new Vector3(span[x].R / max, span[x].G / max, span[x].B / max) * new Vector3(span[x].A / max);
break; break;
case Vector4[,] f: case Vector4[,] f:
f[x, y] = new Vector4(span[x].R / max, span[x].G / max, span[x].B / max, span[x].A / max); f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, span[x].A / max);
break; break;
} }
} }
@@ -48,14 +67,14 @@ public static class ImageUtil {
static T[,] ProcessPixelsRgb24<T>(this Image<Rgb24> image) where T : struct, IEquatable<T> { static T[,] ProcessPixelsRgb24<T>(this Image<Rgb24> image) where T : struct, IEquatable<T> {
var max = 255f; var max = 255f;
int width = image.Width; var width = image.Width;
int height = image.Height; var height = image.Height;
var result = new T[image.Width, image.Height]; var result = new T[image.Width, image.Height];
image.ProcessPixelRows(accessor => { image.ProcessPixelRows(accessor => {
//we use Y as the row index and X as the column index //we use Y as the row index and X as the column index
for (int y = 0; y < height; y++) { for (var y = 0; y < height; y++) {
var span = accessor.GetRowSpan(y); var span = accessor.GetRowSpan(y);
for (int x = 0; x < width; x++) { for (var x = 0; x < width; x++) {
switch (result) { switch (result) {
case float[,] f: case float[,] f:
f[x, y] = span[x].R / max; f[x, y] = span[x].R / max;
@@ -78,14 +97,14 @@ public static class ImageUtil {
static T[,] ProcessPixelsRgba32<T>(this Image<Rgba32> image) where T : struct, IEquatable<T> { static T[,] ProcessPixelsRgba32<T>(this Image<Rgba32> image) where T : struct, IEquatable<T> {
var max = 255f; var max = 255f;
int width = image.Width; var width = image.Width;
int height = image.Height; var height = image.Height;
var result = new T[image.Width, image.Height]; var result = new T[image.Width, image.Height];
image.ProcessPixelRows(accessor => { image.ProcessPixelRows(accessor => {
//we use Y as the row index and X as the column index //we use Y as the row index and X as the column index
for (int y = 0; y < height; y++) { for (var y = 0; y < height; y++) {
var span = accessor.GetRowSpan(y); var span = accessor.GetRowSpan(y);
for (int x = 0; x < width; x++) { for (var x = 0; x < width; x++) {
switch (result) { switch (result) {
case float[,] f: case float[,] f:
f[x, y] = span[x].R / max * (span[x].A / max); f[x, y] = span[x].R / max * (span[x].A / max);
@@ -97,7 +116,7 @@ public static class ImageUtil {
f[x, y] = new Vector3(span[x].R / max, span[x].G / max, span[x].B / max) * new Vector3(span[x].A / max); f[x, y] = new Vector3(span[x].R / max, span[x].G / max, span[x].B / max) * new Vector3(span[x].A / max);
break; break;
case Vector4[,] f: case Vector4[,] f:
f[x, y] = new Vector4(span[x].R / max, span[x].G / max, span[x].B / max, span[x].A / max); f[x, y] = new(span[x].R / max, span[x].G / max, span[x].B / max, span[x].A / max);
break; break;
} }
} }
@@ -108,14 +127,14 @@ public static class ImageUtil {
static T[,] ProcessPixelsRgb48<T>(this Image<Rgb48> image) where T : struct, IEquatable<T> { static T[,] ProcessPixelsRgb48<T>(this Image<Rgb48> image) where T : struct, IEquatable<T> {
var max = 65535f; var max = 65535f;
int width = image.Width; var width = image.Width;
int height = image.Height; var height = image.Height;
var result = new T[image.Width, image.Height]; var result = new T[image.Width, image.Height];
image.ProcessPixelRows(accessor => { image.ProcessPixelRows(accessor => {
//we use Y as the row index and X as the column index //we use Y as the row index and X as the column index
for (int y = 0; y < height; y++) { for (var y = 0; y < height; y++) {
var span = accessor.GetRowSpan(y); var span = accessor.GetRowSpan(y);
for (int x = 0; x < width; x++) { for (var x = 0; x < width; x++) {
switch (result) { switch (result) {
case float[,] f: case float[,] f:
f[x, y] = span[x].R / max; f[x, y] = span[x].R / max;
@@ -146,39 +165,39 @@ public static class ImageUtil {
Vector4[,] => 4, Vector4[,] => 4,
_ => throw new NotSupportedException($"Type {typeof(T)} is not supported.") _ => throw new NotSupportedException($"Type {typeof(T)} is not supported.")
}; };
Console.Write($"Writing image {path}..."); logger.Log($"Writing image {path}...");
using Image<Rgb48> image = new(width, height); using Image<Rgb48> image = new(width, height);
image.ProcessPixelRows(accessor => { image.ProcessPixelRows(accessor => {
for (int y = 0; y < height; y++) { for (var y = 0; y < height; y++) {
var span = accessor.GetRowSpan(y); var span = accessor.GetRowSpan(y);
for (int x = 0; x < width; x++) { for (var x = 0; x < width; x++) {
switch (array) { switch (array) {
case float[,] f: case float[,] f:
span[x] = new Rgb48((ushort)(f[x, y] * 65535), (ushort)(f[x, y] * 65535), (ushort)(f[x,y] * 65535)); span[x] = new((ushort)(f[x, y] * 65535), (ushort)(f[x, y] * 65535), (ushort)(f[x, y] * 65535));
break; break;
case Vector2[,] f: case Vector2[,] f:
span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), 0); span[x] = new((ushort)(f[x, y].X * 65535), (ushort)(f[x, y].Y * 65535), 0);
break; break;
case Vector3[,] f: case Vector3[,] f:
span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), (ushort)(f[x,y].Z * 65535)); span[x] = new((ushort)(f[x, y].X * 65535), (ushort)(f[x, y].Y * 65535), (ushort)(f[x, y].Z * 65535));
break; break;
case Vector4[,] f: case Vector4[,] f:
span[x] = new Rgb48((ushort)(f[x,y].X * 65535), (ushort)(f[x,y].Y * 65535), (ushort)(f[x,y].Z * 65535)); span[x] = new((ushort)(f[x, y].X * 65535), (ushort)(f[x, y].Y * 65535), (ushort)(f[x, y].Z * 65535));
break; break;
} }
} }
} }
}); });
Console.WriteLine($"Done!"); logger.Log($"Writing image {path}...Done", true);
image.Save(path); image.Save(path);
} }
private static void ImageData(SixLabors.ImageSharp.Image image1, string path) { static void ImageData(SixLabors.ImageSharp.Image image1, string path) {
Console.WriteLine( logger.Log(
$""" $"""
Image file: {path} Image file: {path}
Resolution: {image1.Width}x{image1.Height} Resolution: {image1.Width}x{image1.Height}
Total Pixels: {image1.Width*image1.Height} |{"NaN"} channels, {image1.PixelType.BitsPerPixel/4} bits per channel Total Pixels: {image1.Width * image1.Height} |{"NaN"} channels, {image1.PixelType.BitsPerPixel / 4} bits per channel
"""); """);
} }

View File

@@ -0,0 +1,81 @@
********************************************************************************
ILGPU License
********************************************************************************
University of Illinois/NCSA Open Source License
Copyright (c) 2016-2025 ILGPU Project
All rights reserved.
Developed by: Marcel Koester (m4rs@m4rs.net)
www.ilgpu.net
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the documentation
and/or other materials provided with the distribution.
* Neither the names of ILGPU, Marcel Koester, nor the names of its
contributors may be used to endorse or promote products derived from this
Software without specific prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
------------------------------------------------------------------------------------------------------------------------
Six Labors Split License
Version 1.0, June 2022
Copyright (c) Six Labors
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source
code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including
but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" (or "Works") shall mean any Six Labors software made available under the License, as indicated by a
copyright notice that is included in or attached to the work.
"Direct Package Dependency" shall mean any Work in Source or Object form that is installed directly by You.
"Transitive Package Dependency" shall mean any Work in Object form that is installed indirectly by a third party
dependency unrelated to Six Labors.
2. License
Works in Source or Object form are split licensed and may be licensed under the Apache License, Version 2.0 or a
Six Labors Commercial Use License.
Licenses are granted based upon You meeting the qualified criteria as stated. Once granted,
You must reference the granted license only in all documentation.
Works in Source or Object form are licensed to You under the Apache License, Version 2.0 if.
- You are consuming the Work in for use in software licensed under an Open Source or Source Available license.
- You are consuming the Work as a Transitive Package Dependency.
- You are consuming the Work as a Direct Package Dependency in the capacity of a For-profit company/individual with
less than 1M USD annual gross revenue.
- You are consuming the Work as a Direct Package Dependency in the capacity of a Non-profit organization
or Registered Charity.
For all other scenarios, Works in Source or Object form are licensed to You under the Six Labors Commercial License
which may be purchased by visiting https://sixlabors.com/pricing/.

View File

@@ -1,109 +1,181 @@
using System.Diagnostics; /*
Copyright 2025 Railgun Entertainment AS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System.Diagnostics;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SDFMapCreator; using CommandLine;
using LineWalker;
public class Program { namespace SDFMapCreator;
private const float MAX = 1f;
private const float MIN = 0f; public enum DeviceType {
private static readonly int MAX_THREADS = Environment.ProcessorCount - 2; CPU,
private static bool debug = true; OpenCL,
CUDA
}
public class Options {
[Option('d', "debug", Required = false, HelpText = "Enable debug mode.")]
public bool Debug { get; set; } = false;
[Option('D', "imgDirectory", Required = true, HelpText = "Input Images directory.")]
public string ImgDirectory { get; set; } = "images";
[Option('i', "images", Required = true, HelpText = "Images file names separated by ';' and in the correct order.")]
public string Images { get; set; } = "";
[Option('o', "output", Required = true, HelpText = "Output file path.")]
public string OutputFile { get; set; } = "output.png";
[Option('b', "blur", Required = false, HelpText = "How many blur iterations to run.")]
public int BlurIterations { get; set; } = 10;
[Option('r', "radius", Required = false, HelpText = "Blur radius.")]
public float BlurRadius { get; set; } = 100f;
[Option('s', "step", Required = false, HelpText = "Blur step size.")]
public float BlurStep { get; set; } = 0.5f;
[Option("sigma", Required = false, HelpText = "Blur sigma value (weighting).")]
public float BlurSigma { get; set; } = 1f;
[Option("device", Required = false, HelpText = "Device to use for computation. (CPU, OpenCL, CUDA)")]
public DeviceType? Device { get; set; } = null;
}
public static class Program {
#region Magic Values
const float MAX = 1f;
const float MIN = 0f;
static readonly int MAX_THREADS = Environment.ProcessorCount - 2;
static readonly ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = MAX_THREADS }; static readonly ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = MAX_THREADS };
static char PSep => Path.DirectorySeparatorChar;
#endregion
static Logger logger = Logger.GetInstance();
static Options options = null!;
static List<Image> Images = new(); static List<Image> Images = new();
static List<MaskData> ImageMasks = new(); static List<MaskData> ImageMasks = new();
static List<TransitionMaskData> TransitionMasks = new(); static List<TransitionMaskData> TransitionMasks = new();
static List<SDFData> SDFs = new(); static List<SDFData> SDFs = new();
static List<Vector3[,]> Gradients = new(); static List<Vector3[,]> Gradients = new();
static readonly SdfKernels kernels = new(); static SdfKernels kernels;
static uint width; static uint width;
static uint height; static uint height;
static void ConsoleUpdateLine(string s) => Console.Write("\r" + s);
public static void Main(string[] args) { public static void Main(string[] args) {
Console.WriteLine("Reading images..."); var parser = Parser.Default;
if(debug) { parser.ParseArguments<Options>(args)
if (!Directory.Exists("Debug")) Directory.CreateDirectory("Debug"); .WithParsed(o => options = o)
Console.WriteLine("Debug mode enabled."); .WithNotParsed(_ => {
Environment.Exit(1);
});
if(options.Device != null) kernels = new (options.Device.Value);
else kernels = new();
string debugPath = $"{Environment.CurrentDirectory}{PSep}Debug";
if (options.Debug) {
if (!Directory.Exists(debugPath)) Directory.CreateDirectory(debugPath);
logger.Log("Debug mode enabled.", LogLevel.Debug);
} }
var imagesPath = "images"; logger.Log("Reading images...");
for (int i = 0; i < 8; i++) { var imageNames = options.Images.Split(';');
var pixels = ImageUtil.LoadImage<Vector3>($"./{imagesPath}{Path.DirectorySeparatorChar}{i + 1:00}.png"); for (var i = 0; i < imageNames.GetLength(0); i++) {
string imgPath = $"{options.ImgDirectory}{PSep}{imageNames[i]}";
logger.Log($"Reading image {imgPath}", updatePrevious:true);
var pixels = ImageUtil.LoadImage<Vector3>(imgPath);
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1))); Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
} }
/*
var pixels = ImageUtil.LoadImage<Vector3>($"./sphereempty.png");
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
pixels = ImageUtil.LoadImage<Vector3>($"./spherehalf.png");
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
pixels = ImageUtil.LoadImage<Vector3>($"./spherecut.png");
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
pixels = ImageUtil.LoadImage<Vector3>($"./spherefull.png");
Images.Add(new(pixels, pixels.GetLength(0), pixels.GetLength(1)));
*/
//check if all the images in Images are the same resolution //check if all the images in Images are the same resolution
if (Images.Select(img => (img.Width, img.Height)).Distinct().Count() > 1) { if (Images.Select(img => (img.Width, img.Height)).Distinct().Count() > 1) {
Console.WriteLine("Error: Not all images have the same resolution."); logger.Log("Error: Not all images have the same resolution.", LogLevel.Critical);
Environment.Exit(1); Environment.Exit(1);
} }
width = (uint)Images[0].Width; width = (uint)Images[0].Width;
height = (uint)Images[0].Height; height = (uint)Images[0].Height;
Console.WriteLine("Creating masks..."); // sum all images together
for (int i = 0; i < Images.Count; i++) { //for each image pair, create a mask var sumPath = $"{debugPath}{PSep}Sum";
if(options.Debug) Images[0].Pixels.SaveImage($"{sumPath}0.png");
for (int i = 1; i < Images.Count; i++) {
for (int x = 0; x < Images[i].Width; x++) {
for (int y = 0; y < Images[i].Height; y++) {
Images[i].Pixels[x, y].X = MathF.Min(Images[i - 1].Pixels[x, y].X + Images[i].Pixels[x, y].X, MAX);
Images[i].Pixels[x, y].Y = MathF.Min(Images[i - 1].Pixels[x, y].Y + Images[i].Pixels[x, y].X, MAX);
Images[i].Pixels[x, y].Z = MathF.Min(Images[i - 1].Pixels[x, y].Z + Images[i].Pixels[x, y].X, MAX);
}
}
if(options.Debug)Images[i].Pixels.SaveImage($"{sumPath}{i}.png");
}
logger.Log("Creating masks...");
for (var i = 0; i < Images.Count; i++) { //for each image pair, create a mask
logger.Log($"Creating mask {i}...", updatePrevious:true);
var selfMask = SelfMask(Images[i].Pixels); var selfMask = SelfMask(Images[i].Pixels);
ImageMasks.Add(new(selfMask, Images[i], new())); ImageMasks.Add(new(selfMask, Images[i], new()));
if (debug) selfMask.SaveImage($"Debug/selfMask{i}.png"); if (options.Debug) selfMask.SaveImage($"{debugPath}{PSep}selfMask{i}.png");
if (i >= Images.Count - 1) continue; if (i >= Images.Count - 1) continue;
ConsoleUpdateLine($"Creating mask {i}...");
var mask = GetABMask(Images[i].Pixels, Images[i + 1].Pixels); var mask = GetABMask(Images[i].Pixels, Images[i + 1].Pixels);
TransitionMasks.Add(new(mask, Images[i], Images[i + 1])); TransitionMasks.Add(new(mask));
if (debug) mask.SaveImage($"Debug/mask{i}.png");
} }
//EdgeDetect all masks //EdgeDetect all masks
Console.WriteLine("\nEdge detecting masks..."); logger.Log("Edge detecting masks...");
foreach (var mask in ImageMasks) { foreach (var mask in ImageMasks) {
ConsoleUpdateLine($"Edge detecting mask {ImageMasks.IndexOf(mask)}...");
EdgeDetect(mask); EdgeDetect(mask);
} }
if (debug) for (int i = 0; i < TransitionMasks.Count; i++) ImageMasks[i].Mask.SaveImage($"Debug/mask{i}.png");
Console.WriteLine("Creating SDFs..."); if (options.Debug)
for (var i = 0; i < TransitionMasks.Count; i++)
ImageMasks[i].Mask.SaveImage($"{debugPath}{PSep}mask{i}.png");
logger.Log("Creating SDFs...");
for (var i = 0; i < ImageMasks.Count; i++) { for (var i = 0; i < ImageMasks.Count; i++) {
var mask = ImageMasks[i]; var mask = ImageMasks[i];
SDFs.Add(SDF(mask)); SDFs.Add(SDF(mask));
if (debug) SDFs[i].SDF.SaveImage($"Debug/sdf{i}.png"); if (options.Debug) SDFs[i].SDF.SaveImage($"{debugPath}{PSep}sdf{i}.png");
} }
Console.WriteLine("Creating gradients..."); logger.Log("Creating gradients...");
for (var i = 0; i < TransitionMasks.Count; i++) { for (var i = 0; i < TransitionMasks.Count; i++) {
ConsoleUpdateLine($"Generating gradient {i}...");
var gradientData = Gradient(TransitionMasks[i], SDFs[i], SDFs[i + 1]); var gradientData = Gradient(TransitionMasks[i], SDFs[i], SDFs[i + 1]);
Gradients.Add(gradientData); Gradients.Add(gradientData);
if (debug) gradientData.SaveImage($"Debug/gradient{i}.png"); if (options.Debug) gradientData.SaveImage($"{debugPath}{PSep}gradient{i}.png");
} }
// generate final image // generate final image
var finalImage = new Vector3[width, height]; var finalImage = new Vector3[width, height];
var currStep = 0f; var currStep = 0f;
var stepIncrement = MAX / (Gradients.Count); var stepIncrement = MAX / Gradients.Count;
for (int x = 0; x < width; x++)
for (int y = 0; y < height; y++) for (var x = 0; x < width; x++)
finalImage[x, y] = new Vector3(Images[0].Pixels[x, y].X); for (var y = 0; y < height; y++)
finalImage[x, y] = new(ImageMasks[0].Mask[x, y].X != 0 || ImageMasks[0].Mask[x, y].Y > 0 ? MAX : MIN);
for (var i = 0; i < Gradients.Count; i++) { for (var i = 0; i < Gradients.Count; i++) {
var mask = ImageMasks[i + 1]; var mask = ImageMasks[i + 1];
var gradient = Gradients[i]; var gradient = Gradients[i];
Console.WriteLine($"Applying gradient {i}..., {currStep} -> {currStep + stepIncrement}"); logger.Log($"Applying gradient {i}..., Step: {currStep:F2} -> Next: {(currStep + stepIncrement):F2}");
for (var x = 0; x < mask.Mask.GetLength(0); x++) { for (var x = 0; x < mask.Mask.GetLength(0); x++) {
for (var y = 0; y < mask.Mask.GetLength(1); y++) { for (var y = 0; y < mask.Mask.GetLength(1); y++) {
if (gradient[x, y].X == 0) continue; if (mask.Mask[x,y].X == 0) continue;
var pixel = new Vector3(Remap(gradient[x, y].X, MIN, MAX, 1.0f - currStep, var pixel = new Vector3(Remap(gradient[x, y].X, MIN, MAX, 1.0f - currStep,
1.0f - (currStep + stepIncrement))); 1.0f - (currStep + stepIncrement)));
if (pixel.X > finalImage[x, y].X) finalImage[x, y] = pixel; if (pixel.X > finalImage[x, y].X) finalImage[x, y] = pixel;
@@ -111,26 +183,26 @@ public class Program {
} }
currStep += stepIncrement; currStep += stepIncrement;
} }
if(options.Debug) finalImage.SaveImage($"{debugPath}{options.OutputFile}");
// apply directional blur // apply directional blur
var iterations = 10; var iterations = options.BlurIterations;
var radius = 3f; var radius = options.BlurRadius;
var step = .5f; var step = options.BlurStep;
var sigma = 1f; var sigma = options.BlurSigma;
for (int i = 0; i < iterations; i++) var totalMask = SelfMask(Images[^1].Pixels);
{ if(options.Debug) totalMask.SaveImage($"{debugPath}{PSep}TotalMask.png");
Console.WriteLine($"Applying directional blur {i + 1}/{iterations}...");
finalImage = DirectionalBlur(finalImage, ImageMasks[0].Mask, radius, step, sigma);
}
finalImage.SaveImage("final.png"); finalImage = DirectionalBlur(finalImage, totalMask, iterations, radius, step, sigma);
Console.WriteLine("Done!");
finalImage.SaveImage(options.OutputFile);
logger.Log("Done!");
Logger.Shutdown();
} }
private static void EdgeDetect(MaskData maskData) { static void EdgeDetect(MaskData maskData) {
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
Console.WriteLine("Running edge detection...");
kernels.EdgeDetect(maskData.Mask, out var temp); kernels.EdgeDetect(maskData.Mask, out var temp);
maskData.Mask = temp; maskData.Mask = temp;
Parallel.For(0, width * height, parallelOptions, (i) => { Parallel.For(0, width * height, parallelOptions, (i) => {
@@ -140,73 +212,56 @@ public class Program {
}); });
sw.Stop(); sw.Stop();
Console.WriteLine( string logString = $"Edge detecting mask {ImageMasks.IndexOf(maskData)}..." +
$"\nEdge pixels: {maskData.Edges.Count} | {width*height / (sw.ElapsedMilliseconds + 1)} pixels/s\n Time: {sw.Elapsed.TotalSeconds:F4}s"); Environment.NewLine + $"Edge pixels: {maskData.Edges.Count} | {width * height / (sw.ElapsedMilliseconds + 1)} pixels/s"+
Environment.NewLine + $"Time: {sw.Elapsed.TotalSeconds:F4}s";
logger.Log(logString, updatePrevious:true);
} }
static Vector3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) { static Vector3[,] Gradient(TransitionMaskData mask, SDFData sdfA, SDFData sdfB) {
int iterCount = 0;
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
/*Vector3[,] temp = new Vector3[width, height];
var min = MAX;
var max = MIN;
Console.WriteLine("Running gradient generation...");
Parallel.For(0, width * height, parallelOptions, (i) =>
{
int x = (int)(i % width);
int y = (int)(i / width);
if (mask.Mask[x, y].X == 0 || mask.Mask[x, y].Y > 0) return;
var a = sdfA.SDF[x, y].X;
var b = sdfB.SDF[x, y].X;
var gradient = a / (a + b);
temp[x, y] = new(Remap(gradient, 0, 1, MIN, MAX));
if (gradient < min) min = gradient;
if (gradient > max) max = gradient;
});*/
kernels.Gradient(mask.Mask, sdfA.SDF, sdfB.SDF, out var temp); kernels.Gradient(mask.Mask, sdfA.SDF, sdfB.SDF, out var temp);
Console.WriteLine($"Gradient Time: {sw.Elapsed.TotalSeconds:N4}s ({width*height / (float)sw.Elapsed.TotalSeconds:N0} pixels/s)"); logger.Log($"Gradient Time: {sw.Elapsed.TotalSeconds:N4}s ({width * height / (float)sw.Elapsed.TotalSeconds:N0} pixels/s)");
return temp; return temp;
} }
static SDFData SDF(MaskData mask) { static SDFData SDF(MaskData mask) {
int iterCount = 0;
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
kernels.Sdf(mask.Edges.ToArray(), (int)width, (int)height, out var temp); kernels.Sdf(mask.Edges.ToArray(), (int)width, (int)height, out var temp);
string logString = $"SDF Generation Time: {sw.Elapsed.TotalSeconds:N4}s ({width * height / sw.Elapsed.TotalSeconds:N0} pixels/s)";
sw.Restart();
var absMax = 0f;
Parallel.ForEach(temp.Cast<Vector3>(), pixel => {
var localMax = pixel.X;
Interlocked.Exchange(ref absMax, MathF.Max(absMax, localMax));
});
Console.WriteLine($"\nSDF Generation Time: {sw.Elapsed.TotalSeconds:N4}s ({width*height / sw.Elapsed.TotalSeconds:N0} pixels/s)"); Parallel.For(0, width * height, parallelOptions, (i) => {
//convert 1D index to 2D index
var x = (int)(i % width);
var y = (int)(i / width);
temp[x, y] = new(Remap(temp[x, y].X, 0, absMax, MIN, MAX));
});
sw.Stop();
logString += Environment.NewLine +
$"SDF Normalization Time: {sw.Elapsed.TotalSeconds:N4}s ({width * height / sw.Elapsed.TotalSeconds:N0} pixels/s)";
logger.Log(logString, updatePrevious:true);
return new(temp); return new(temp);
} }
static Vector3[,] DirectionalBlur(Vector3[,] image, Vector3[,] mask, float radius = 3f, float step = .5f, float sigma = 1f) { static Vector3[,] DirectionalBlur(Vector3[,] image, Vector3[,] mask, int iterations, float radius = 3f, float step = .5f, float sigma = 1f) {
var width = (uint)image.GetLength(0);
var height = (uint)image.GetLength(1);
var output = new Vector3[width, height];
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
kernels.DirectionalBlur(image, mask, out var temp, iterations, radius, step, sigma);
kernels.DirectionalBlur(image, mask, out var temp, radius, step, sigma); logger.Log($"Directional Blur Time: {sw.Elapsed.TotalSeconds:N4}s ({width * height * iterations / sw.Elapsed.TotalSeconds:N0} pixels/s)");
Console.WriteLine(
$"Directional Blur Time: {sw.Elapsed.TotalSeconds:N4}s ({width * height / sw.Elapsed.TotalSeconds:N0} pixels/s)");
return temp; return temp;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static float EuclideanDistance(Vector2 a, Vector2 b) => static float EuclideanDistance(Vector2 a, Vector2 b) => MathF.Sqrt(MathF.Pow(a.X - b.X, 2) + MathF.Pow(a.Y - b.Y, 2));
MathF.Sqrt(MathF.Pow(a.X - b.X, 2) + MathF.Pow(a.Y - b.Y, 2));
static Vector3[,] GetABMask(Vector3[,] A, Vector3[,] B) { static Vector3[,] GetABMask(Vector3[,] A, Vector3[,] B) {
kernels.ABMask(A, B, out var temp); kernels.ABMask(A, B, out var temp);
@@ -222,7 +277,9 @@ public class Program {
where T : INumber<T>, IMultiplyOperators<T, float, T>, IAdditionOperators<T, T, T> where T : INumber<T>, IMultiplyOperators<T, float, T>, IAdditionOperators<T, T, T>
=> a * (1 - t) + b * t; => a * (1 - t) + b * t;
public static T Remap<T>(T value, T min, T max, T newMin, T newMax) static Vector3 Lerp(Vector3 a, Vector3 b, float t) => a * (1 - t) + b * t;
static T Remap<T>(T value, T min, T max, T newMin, T newMax)
where T : INumber<T>, ISubtractionOperators<T, T, T>, IMultiplyOperators<T, T, T>, IAdditionOperators<T, T, T> where T : INumber<T>, ISubtractionOperators<T, T, T>, IMultiplyOperators<T, T, T>, IAdditionOperators<T, T, T>
=> (value - min) / (max - min) * (newMax - newMin) + newMin; => (value - min) / (max - min) * (newMax - newMin) + newMin;
} }

View File

@@ -1,12 +1,30 @@
/*
Copyright 2025 Railgun Entertainment AS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System.Numerics; using System.Numerics;
namespace SDFMapCreator; namespace SDFMapCreator;
public record Image(Vector3[,] Pixels, int Width, int Height); public record Image(Vector3[,] Pixels, int Width, int Height);
public record MaskData(Vector3[,] Mask, Image Image, List<Vector2> Edges) { public record MaskData(Vector3[,] Mask, Image Image, List<Vector2> Edges) {
public Vector3[,] Mask { get; set; } = Mask; public Vector3[,] Mask { get; set; } = Mask;
public List<Vector2> Edges { get; set; } = Edges; public List<Vector2> Edges { get; set; } = Edges;
} }
public record SDFData(Vector3[,] SDF); public record SDFData(Vector3[,] SDF);
public record TransitionMaskData(Vector3[,] Mask, Image ImageA, Image ImageB);
public record TransitionMaskData(Vector3[,] Mask);

View File

@@ -5,70 +5,71 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<AssemblyName>SDFMapTool</AssemblyName>
<Company>Circle2Labs</Company>
<Product>SDFMapTool</Product>
<AssemblyVersion>1.0</AssemblyVersion>
<FileVersion>1.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ILGPU" Version="1.5.2" /> <PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="ILGPU.Algorithms" Version="1.5.2" /> <PackageReference Include="ILGPU" Version="1.5.2"/>
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" /> <PackageReference Include="ILGPU.Algorithms" Version="1.5.2"/>
<PackageReference Include="System.Numerics.Vectors" Version="4.6.1" /> <PackageReference Include="LineWalker" Version="0.1.2-alpha" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7"/>
<PackageReference Include="System.Numerics.Vectors" Version="4.6.1"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="1.png"> <None Update="images\01.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="2.png"> <None Update="images\02.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="TestPattern.png"> <None Update="images\03.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="images\01.png"> <None Update="images\04.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="images\02.png"> <None Update="images\05.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="images\03.png"> <None Update="images\06.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="images\04.png"> <None Update="images\07.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="images\05.png"> <None Update="images\08.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="images\06.png"> <None Update="images\spherecut.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="images\07.png"> <None Update="images\spherecut32.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="images\08.png"> <None Update="images\sphereempty.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="spherecut.png"> <None Update="images\spherefull.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="spherefull.png"> <None Update="images\spherefull32.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="spherehalf.png"> <None Update="images\spherehalf.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="spherehalf32.png"> <None Update="images\spherehalf32.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Never</CopyToOutputDirectory>
</None> </None>
<None Update="spherefull32.png"> <None Update="OpenSourceLicenses.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="spherecut32.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="sphereempty.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,3 +1,19 @@
/*
Copyright 2025 Railgun Entertainment AS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System.Numerics; using System.Numerics;
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
@@ -5,63 +21,68 @@ using ILGPU.Runtime;
namespace SDFMapCreator; namespace SDFMapCreator;
public partial class SdfKernels { public partial class SdfKernels {
private static void SelfMaskKernel(Index2D index, ArrayView2D<Vector3, Stride2D.DenseX> input, ArrayView2D<Vector3, Stride2D.DenseX> mask) {
int x = index.X; private const float LUMA_THRESHOLD = 0.0f;
int y = index.Y; static void SelfMaskKernel(Index2D index, ArrayView2D<Vector3, Stride2D.DenseX> input, ArrayView2D<Vector3, Stride2D.DenseX> mask) {
Vector3 value = input[x, y]; var x = index.X;
float lumaA = (value.X + value.Y + value.Z)/3f; var y = index.Y;
float r = lumaA > 0.99f ? 1f : 0f; var value = input[x, y];
mask[x,y] = new (r, 0f, 0f); var lumaA = value.X;
var r = lumaA > LUMA_THRESHOLD ? 1f : 0f;
mask[x, y] = new(r, 0f, 0f);
} }
private static void ABMaskKernel(Index2D index, static void ABMaskKernel(Index2D index,
ArrayView2D<Vector3, Stride2D.DenseX> A, ArrayView2D<Vector3, Stride2D.DenseX> A,
ArrayView2D<Vector3, Stride2D.DenseX> B, ArrayView2D<Vector3, Stride2D.DenseX> B,
ArrayView2D<Vector3, Stride2D.DenseX> mask ArrayView2D<Vector3, Stride2D.DenseX> mask
) { ) {
int x = index.X; var x = index.X;
int y = index.Y; var y = index.Y;
Vector3 valueA = A[x, y]; var valueA = A[x, y];
Vector3 valueB = B[x, y]; var valueB = B[x, y];
float lumaA = (valueA.X + valueA.Y + valueA.Z) / 3f; var lumaA = valueA.X;
float lumaB = (valueB.X + valueB.Y + valueB.Z) / 3f; var lumaB = valueB.X;
float r = lumaA > 0.99f && lumaB > 0.99f ? 1f : 0f; var r = lumaB - lumaA > LUMA_THRESHOLD ? 1f : 0f;
mask[x, y] = new (r, 0f, 0f); mask[x, y] = new(r, 0f, 0f);
} }
private static void EdgeKernel(Index2D index, static void EdgeKernel(Index2D index,
ArrayView2D<Vector3, Stride2D.DenseX> mask, ArrayView2D<Vector3, Stride2D.DenseX> mask,
uint width, uint height uint width, uint height
) { // early exit if not on mask ) { // early exit if not on mask
if (mask[index].X == 0f) return; if (mask[index].X == 0f) return;
int x = index.X; var x = index.X;
int y = index.Y; var y = index.Y;
//if we are on the edge of the image, return false //if we are on the edge of the image, return false
if (x == 0 || y == 0 || x == width - 1 || y == height - 1) return; if (x == 0 || y == 0 || x == width - 1 || y == height - 1) {
mask[index].Y = 1f; //set the edge flag
return;
}
//check the 3x3 kernel //check the 3x3 kernel
for (int xi = x - 1; xi <= x + 1; xi++) { for (var xi = x - 1; xi <= x + 1; xi++) {
for (int yi = y - 1; yi <= y + 1; yi++) { for (var yi = y - 1; yi <= y + 1; yi++) {
if (xi < 0 || xi >= width || yi < 0 || yi >= height) if (xi < 0 || xi >= width || yi < 0 || yi >= height)
continue; //skip out of bounds pixels continue; //skip out of bounds pixels
if (mask[xi, yi].X == 0f) if (mask[xi, yi].X == 0f)
mask[index].Y = 1f; //if we find a black pixel, return true mask[index].Y = 1f; //if we find a black pixel, return true
} }
} }
} }
private static void SdfKernel(Index2D index, static void SdfKernel(Index2D index,
ArrayView2D<Vector3, Stride2D.DenseX> sdf, ArrayView2D<Vector3, Stride2D.DenseX> sdf,
ArrayView1D<Vector2, Stride1D.Dense> edges, ArrayView1D<Vector2, Stride1D.Dense> edges,
int width, int height int width, int height
) { ) {
Vector2 pos = new((float)index.X / width, (float)index.Y / height); Vector2 pos = new((float)index.X / width, (float)index.Y / height);
float minDist = 2f; var minDist = 2f;
int count = edges.IntExtent.Size; var count = edges.IntExtent.Size;
for (int i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
Vector2 edgeNrm = new (edges[i].X/width, edges[i].Y/height); Vector2 edgeNrm = new(edges[i].X / width, edges[i].Y / height);
float dist = Vector2.Distance(pos, edgeNrm); var dist = Vector2.Distance(pos, edgeNrm);
if (dist < minDist) minDist = dist; if (dist < minDist) minDist = dist;
} }
@@ -70,70 +91,90 @@ public partial class SdfKernels {
sdf[index] = new(minDist); sdf[index] = new(minDist);
} }
private static void GradientKernel(Index2D index, static void GradientKernel(Index2D index,
ArrayView2D<Vector3, Stride2D.DenseX> mask, ArrayView2D<Vector3, Stride2D.DenseX> mask,
ArrayView2D<Vector3, Stride2D.DenseX> sdfa, ArrayView2D<Vector3, Stride2D.DenseX> sdfa,
ArrayView2D<Vector3, Stride2D.DenseX> sdfb, ArrayView2D<Vector3, Stride2D.DenseX> sdfb,
ArrayView2D<Vector3, Stride2D.DenseX> gradient ArrayView2D<Vector3, Stride2D.DenseX> gradient
) { //early exit if not on mask ) { //early exit if not on mask
if(mask[index].X == 0f || mask[index].Y == 0f) return; if (mask[index].X == 0f) return;
float a = sdfa[index].X; float b = sdfb[index].X; var a = sdfa[index].X;
gradient[index] = new (a / (a + b)); var b = sdfb[index].X;
gradient[index] = new(a / (a + b));
}
private static void DirectionalBlurKernel(Index2D index, static Vector3 SampleBilinear(ArrayView2D<Vector3, Stride2D.DenseX> image, float x, float y) {
int width = image.IntExtent.X;
int height = image.IntExtent.Y;
var x0 = (int)x;
var y0 = (int)y;
var x1 = x0 + 1;
var y1 = y0 + 1;
if (x0 < 0 || x1 >= width || y0 < 0 || y1 >= height) return Vector3.Zero;
var a = new Vector2(x - x0, y - y0);
var b = new Vector2(1f - a.X, 1f - a.Y);
return Vector3.Lerp(
Vector3.Lerp(image[x0, y0], image[x1, y0], a.X),
Vector3.Lerp(image[x0, y1], image[x1, y1], a.X),
a.Y
);
}
static void DirectionalBlurKernel(Index2D index,
ArrayView2D<Vector3, Stride2D.DenseX> image, ArrayView2D<Vector3, Stride2D.DenseX> image,
ArrayView2D<Vector3, Stride2D.DenseX> mask, ArrayView2D<Vector3, Stride2D.DenseX> mask,
ArrayView2D<Vector3, Stride2D.DenseX> output, ArrayView2D<Vector3, Stride2D.DenseX> output,
float radius, float step, float sigma, float radius, float step, float sigma,
int width, int height int width, int height
) ) {
{ var x = index.X;
int x = index.X; var y = index.Y;
int y = index.Y; var value = image[x, y];
Vector3 value = image[x, y]; var maskValue = mask[x, y];
Vector3 maskValue = mask[x, y]; if (maskValue.X == 0f) {
if (maskValue.X == 0f)
{
output[x, y] = value; output[x, y] = value;
return; return;
} }
var gradient = Vector2.Zero; var gradient = Vector2.Zero;
for (int dx = -1; dx <= 1; dx++) // calculate the gradient
{ for (int i = -1; i <= 1; i++) {
for (int dy = -1; dy <= 1; dy++) if (x + i < 0 || x + i >= width || y + i < 0 || y + i >= height) continue;
{ gradient.X += i * image[x + i, y].X;
if (x + dx < 0 || x + dx >= width || y + dy < 0 || y + dy >= height) continue; gradient.Y += i * image[x, y + i].X;
gradient += new Vector2(dx, dy) * image[x + dx, y + dy].X;
}
} }
if (gradient == Vector2.Zero) if (gradient == Vector2.Zero) {
{
output[x, y] = value; output[x, y] = value;
return; return;
} }
//clean up the buffer in case
output[index] = new(0f, 0f, 0f);
gradient = Vector2.Normalize(gradient); gradient = Vector2.Normalize(gradient);
float sum = 0; float sum = 0;
// now we follow the direction line and sample the image for length; // now we follow the direction line and sample the image for length;
for (float l = -radius; l <= radius; l += step) for (var l = -radius; l <= radius; l += step) {
{ var xOffset = (gradient.X * l);
int xOffset = (int)(gradient.X * l); var yOffset = (gradient.Y * l);
int yOffset = (int)(gradient.Y * l); var xSample = x + xOffset;
int xSample = x + xOffset; var ySample = y + yOffset;
int ySample = y + yOffset;
if (xSample < 0 || xSample >= width || ySample < 0 || ySample >= height) continue; if (xSample < 0 || xSample >= width || ySample < 0 || ySample >= height) continue;
Vector3 sampleValue = image[xSample, ySample]; var sampleValue = SampleBilinear(image, xSample, ySample);
float weight = MathF.Exp(-l * l / (2f * sigma * sigma)); var weight = MathF.Exp(-(l * l) / (2f * sigma * sigma));
output[x, y] += sampleValue * weight; output[x, y] += sampleValue * weight;
sum += weight; sum += weight;
} }
output[x, y] /= sum; output[x, y] = output[x, y] / sum;
} }
} }

View File

@@ -1,63 +1,98 @@
using System; /*
Copyright 2025 Railgun Entertainment AS
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using System.Numerics; using System.Numerics;
using ILGPU; using ILGPU;
using ILGPU.Runtime; using ILGPU.Runtime;
using ILGPU.Runtime.CPU; using ILGPU.Runtime.CPU;
using ILGPU.Runtime.Cuda; using ILGPU.Runtime.Cuda;
using ILGPU.Runtime.OpenCL; using ILGPU.Runtime.OpenCL;
using ILGPU.Algorithms; using LineWalker;
namespace SDFMapCreator; namespace SDFMapCreator;
public partial class SdfKernels { public partial class SdfKernels {
private Context gpuContext; Logger logger = Logger.GetInstance();
Context gpuContext;
Accelerator accelerator;
public SdfKernels() { public SdfKernels() {
// Initialize the GPU context // Initialize the GPU context
gpuContext = Context.Create(builder => builder gpuContext = Context.Create(builder => builder
.OpenCL()
.Cuda() .Cuda()
.OpenCL()
.CPU() .CPU()
.Math(MathMode.Fast32BitOnly) .Math(MathMode.Fast32BitOnly)
.EnableAlgorithms() .EnableAlgorithms()
); );
Console.WriteLine("Reading available accelerators (CUDA only)..."); logger.Log("Reading available accelerators (CUDA only)...");
foreach (var device in gpuContext.GetCudaDevices()) { foreach (var device in gpuContext.GetCudaDevices()) {
using Accelerator accelerator = device.CreateAccelerator(gpuContext); accelerator = device.CreateAccelerator(gpuContext);
Console.WriteLine($"{GetInfoString(accelerator)}"); logger.Log($"Found accelerator: {accelerator.Name} ({accelerator.AcceleratorType})");
} }
Console.WriteLine("Reading available accelerators (OpenCL only)..."); logger.Log("Reading available accelerators (OpenCL only)...");
foreach (var device in gpuContext.GetCLDevices()) { foreach (var device in gpuContext.GetCLDevices()) {
using Accelerator accelerator = device.CreateAccelerator(gpuContext); accelerator = device.CreateAccelerator(gpuContext);
Console.WriteLine($"{GetInfoString(accelerator)}"); logger.Log($"Found accelerator: {accelerator.Name} ({accelerator.AcceleratorType})");
} }
Console.WriteLine("Reading available accelerators (CPU only)..."); logger.Log("Reading available accelerators (CPU only)...");
foreach (var device in gpuContext.GetCPUDevices()) { foreach (var device in gpuContext.GetCPUDevices()) {
using Accelerator accelerator = device.CreateAccelerator(gpuContext); accelerator = device.CreateAccelerator(gpuContext);
Console.WriteLine($"{GetInfoString(accelerator)}"); logger.Log($"Found accelerator: {accelerator.Name} ({accelerator.AcceleratorType})");
} }
accelerator = gpuContext.GetPreferredDevice(false).CreateAccelerator(gpuContext);
logger.Log($"Using accelerator: {accelerator.Name} ({accelerator.AcceleratorType})");
}
public SdfKernels(DeviceType device) {
logger.Log($"Looking up for {device.ToString()} accelerators...");
var builder = Context.Create();
switch (device) {
case DeviceType.CPU: builder.CPU(); break;
case DeviceType.CUDA: builder.Cuda(); break;
case DeviceType.OpenCL: builder.OpenCL(); break;
}
gpuContext = builder
.Math(MathMode.Fast32BitOnly)
.EnableAlgorithms()
.ToContext();
accelerator = gpuContext.GetPreferredDevice(false).CreateAccelerator(gpuContext);
logger.Log($"Using accelerator: {accelerator.Name} ({accelerator.AcceleratorType})");
} }
public void SelfMask(Vector3[,] input, out Vector3[,] mask) { public void SelfMask(Vector3[,] input, out Vector3[,] mask) {
var dev = gpuContext.GetPreferredDevice(preferCPU:false); var width = input.GetLength(0);
int width = input.GetLength(0); var height = input.GetLength(1);
int height = input.GetLength(1);
mask = new Vector3[width, height]; mask = new Vector3[width, height];
using Accelerator accelerator = dev.CreateAccelerator(gpuContext);
using var buffer = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var buffer = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
buffer.CopyFromCPU(input); buffer.CopyFromCPU(input);
using var maskBuffer = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var maskBuffer = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
var selfMaskKernel = accelerator.LoadAutoGroupedStreamKernel<Index2D, ArrayView2D<Vector3, Stride2D.DenseX>, var selfMaskKernel = accelerator.LoadAutoGroupedStreamKernel<Index2D, ArrayView2D<Vector3, Stride2D.DenseX>,
ArrayView2D<Vector3, Stride2D.DenseX>>(SelfMaskKernel); ArrayView2D<Vector3, Stride2D.DenseX>>(SelfMaskKernel);
selfMaskKernel(new (width, height), buffer.View, maskBuffer.View); selfMaskKernel(new(width, height), buffer.View, maskBuffer.View);
accelerator.Synchronize(); accelerator.Synchronize();
@@ -65,26 +100,24 @@ public partial class SdfKernels {
} }
public void ABMask(Vector3[,] A, Vector3[,] B, out Vector3[,] mask) { public void ABMask(Vector3[,] A, Vector3[,] B, out Vector3[,] mask) {
var dev = gpuContext.GetPreferredDevice(preferCPU:false); var width = A.GetLength(0);
int width = A.GetLength(0); var height = A.GetLength(1);
int height = A.GetLength(1);
mask = new Vector3[width, height]; mask = new Vector3[width, height];
using Accelerator accelerator = dev.CreateAccelerator(gpuContext);
using var bufferA = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var bufferA = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
bufferA.CopyFromCPU(A); bufferA.CopyFromCPU(A);
using var bufferB = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var bufferB = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
bufferB.CopyFromCPU(B); bufferB.CopyFromCPU(B);
using var maskBuffer = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var maskBuffer = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
var abMaskKernel = accelerator.LoadAutoGroupedStreamKernel<Index2D, var abMaskKernel = accelerator.LoadAutoGroupedStreamKernel<Index2D,
ArrayView2D<Vector3, Stride2D.DenseX>, ArrayView2D<Vector3, Stride2D.DenseX>,
ArrayView2D<Vector3, Stride2D.DenseX>, ArrayView2D<Vector3, Stride2D.DenseX>,
ArrayView2D<Vector3, Stride2D.DenseX>>(ABMaskKernel); ArrayView2D<Vector3, Stride2D.DenseX>>(ABMaskKernel);
abMaskKernel(new (width, height), bufferA.View, bufferB.View, maskBuffer.View); abMaskKernel(new(width, height), bufferA.View, bufferB.View, maskBuffer.View);
accelerator.Synchronize(); accelerator.Synchronize();
@@ -92,19 +125,17 @@ public partial class SdfKernels {
} }
public void EdgeDetect(Vector3[,] mask, out Vector3[,] edge) { public void EdgeDetect(Vector3[,] mask, out Vector3[,] edge) {
var dev = gpuContext.GetPreferredDevice(preferCPU:false); var width = mask.GetLength(0);
int width = mask.GetLength(0); var height = mask.GetLength(1);
int height = mask.GetLength(1);
edge = new Vector3[width, height]; edge = new Vector3[width, height];
using Accelerator accelerator = dev.CreateAccelerator(gpuContext);
using var buffer = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var buffer = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
buffer.CopyFromCPU(mask); buffer.CopyFromCPU(mask);
var edgeKernel = accelerator.LoadAutoGroupedStreamKernel<Index2D, var edgeKernel = accelerator.LoadAutoGroupedStreamKernel<Index2D,
ArrayView2D<Vector3, Stride2D.DenseX>, uint, uint>(EdgeKernel); ArrayView2D<Vector3, Stride2D.DenseX>, uint, uint>(EdgeKernel);
edgeKernel(new (width, height), buffer.View, (uint)width, (uint)height); edgeKernel(new(width, height), buffer.View, (uint)width, (uint)height);
accelerator.Synchronize(); accelerator.Synchronize();
@@ -112,10 +143,7 @@ public partial class SdfKernels {
} }
public void Sdf(Vector2[] edges, int width, int height, out Vector3[,] sdf) { public void Sdf(Vector2[] edges, int width, int height, out Vector3[,] sdf) {
var dev = gpuContext.GetPreferredDevice(preferCPU:false); using var buffer = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
using Accelerator accelerator = dev.CreateAccelerator(gpuContext);
using var buffer = accelerator.Allocate2DDenseX<Vector3>(new (width, height));
using var edgeBuffer = accelerator.Allocate1D<Vector2>(edges.Length); using var edgeBuffer = accelerator.Allocate1D<Vector2>(edges.Length);
edgeBuffer.CopyFromCPU(edges); edgeBuffer.CopyFromCPU(edges);
@@ -125,7 +153,7 @@ public partial class SdfKernels {
ArrayView1D<Vector2, Stride1D.Dense>, ArrayView1D<Vector2, Stride1D.Dense>,
int, int>(SdfKernel); int, int>(SdfKernel);
sdfKernel(new (width, height), buffer.View, edgeBuffer.View, width, height); sdfKernel(new(width, height), buffer.View, edgeBuffer.View, width, height);
accelerator.Synchronize(); accelerator.Synchronize();
@@ -133,22 +161,20 @@ public partial class SdfKernels {
} }
public void Gradient(Vector3[,] mask, Vector3[,] sdfa, Vector3[,] sdfb, out Vector3[,] gradient) { public void Gradient(Vector3[,] mask, Vector3[,] sdfa, Vector3[,] sdfb, out Vector3[,] gradient) {
var dev = gpuContext.GetPreferredDevice(preferCPU:false); var width = mask.GetLength(0);
int width = mask.GetLength(0); var height = mask.GetLength(1);
int height = mask.GetLength(1);
gradient = new Vector3[width, height]; gradient = new Vector3[width, height];
using Accelerator accelerator = dev.CreateAccelerator(gpuContext);
using var bufferMask = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var bufferMask = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
bufferMask.CopyFromCPU(mask); bufferMask.CopyFromCPU(mask);
using var bufferSdfa = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var bufferSdfa = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
bufferSdfa.CopyFromCPU(sdfa); bufferSdfa.CopyFromCPU(sdfa);
using var bufferSdfb = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var bufferSdfb = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
bufferSdfb.CopyFromCPU(sdfb); bufferSdfb.CopyFromCPU(sdfb);
using var bufferGradient = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var bufferGradient = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
var gradientKernel = accelerator.LoadAutoGroupedStreamKernel<Index2D, var gradientKernel = accelerator.LoadAutoGroupedStreamKernel<Index2D,
ArrayView2D<Vector3, Stride2D.DenseX>, ArrayView2D<Vector3, Stride2D.DenseX>,
@@ -156,26 +182,26 @@ public partial class SdfKernels {
ArrayView2D<Vector3, Stride2D.DenseX>, ArrayView2D<Vector3, Stride2D.DenseX>,
ArrayView2D<Vector3, Stride2D.DenseX>>(GradientKernel); ArrayView2D<Vector3, Stride2D.DenseX>>(GradientKernel);
gradientKernel(new (width, height), bufferMask.View, bufferSdfa.View, bufferSdfb.View, bufferGradient.View); gradientKernel(new(width, height), bufferMask.View, bufferSdfa.View, bufferSdfb.View, bufferGradient.View);
accelerator.Synchronize(); accelerator.Synchronize();
gradient = bufferGradient.GetAsArray2D(); gradient = bufferGradient.GetAsArray2D();
}
public void DirectionalBlur(Vector3[,] image, Vector3[,] mask, out Vector3[,] output, float radius = 3f, float step = .5f, float sigma = 1f) { public void DirectionalBlur(Vector3[,] image, Vector3[,] mask, out Vector3[,] output, int iterations, float radius = 3f,
var dev = gpuContext.GetPreferredDevice(preferCPU:false); float step = .5f, float sigma = 1f) {
int width = image.GetLength(0); var width = image.GetLength(0);
int height = image.GetLength(1); var height = image.GetLength(1);
output = new Vector3[width, height]; output = new Vector3[width, height];
using Accelerator accelerator = dev.CreateAccelerator(gpuContext);
using var imageBuffer = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var imageBuffer = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
imageBuffer.CopyFromCPU(image); imageBuffer.CopyFromCPU(image);
using var maskBuffer = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var maskBuffer = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
maskBuffer.CopyFromCPU(mask); maskBuffer.CopyFromCPU(mask);
using var outputBuffer = accelerator.Allocate2DDenseX<Vector3>(new (width, height)); using var outputBuffer = accelerator.Allocate2DDenseX<Vector3>(new(width, height));
var blurKernel = accelerator.LoadAutoGroupedStreamKernel<Index2D, var blurKernel = accelerator.LoadAutoGroupedStreamKernel<Index2D,
ArrayView2D<Vector3, Stride2D.DenseX>, ArrayView2D<Vector3, Stride2D.DenseX>,
@@ -183,18 +209,24 @@ public partial class SdfKernels {
ArrayView2D<Vector3, Stride2D.DenseX>, ArrayView2D<Vector3, Stride2D.DenseX>,
float, float, float, int, int>(DirectionalBlurKernel); float, float, float, int, int>(DirectionalBlurKernel);
blurKernel(new (width, height), imageBuffer.View, maskBuffer.View, outputBuffer.View, var stream = accelerator.DefaultStream;
radius, step, sigma, width, height); for (int i = 0; i < iterations; i++) {
if (i > 0) outputBuffer.CopyTo(stream, imageBuffer);
accelerator.Synchronize(); blurKernel(new(width, height), imageBuffer.View, maskBuffer.View, outputBuffer.View, radius, step, sigma, width, height);
accelerator.Synchronize();
}
output = outputBuffer.GetAsArray2D(); output = outputBuffer.GetAsArray2D();
} }
private static string GetInfoString(Accelerator a) static string GetInfoString(Accelerator a) {
{ var infoString = new StringWriter();
StringWriter infoString = new StringWriter();
a.PrintInformation(infoString); a.PrintInformation(infoString);
return infoString.ToString(); return infoString.ToString();
} }
~SdfKernels() {
accelerator?.Dispose();
gpuContext?.Dispose();
}
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,29 @@
# Copyright 2025 Railgun Entertainment AS
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
$imagesPath = "./images/*"
if (Test-Path "./bin/Debug/net8.0/")
{
Write-Output "Copying images to Debug folder"
Copy-Item -Path $imagesPath -Destination "./bin/Debug/net8.0/images/"
}
if(Test-Path "./bin/Release/net8.0/")
{
Write-Output "Copying images to Release folder"
Copy-Item -Path $imagesPath -Destination "./bin/Release/net8.0/images/"
}
return 0

View File

@@ -0,0 +1,23 @@
# Copyright 2025 Railgun Entertainment AS
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if [ -d ./bin/Debug/net8.0/ ]; then
echo "Copying images to bin/Debug"
cp -r ./images ./bin/Debug/net8.0/
fi
if [ -d ./bin/Release/net8.0/ ]; then
echo "Copying images to bin/Release"
cp -r ./images ./bin/Release/net8.0/
fi

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 680 B

After

Width:  |  Height:  |  Size: 680 B

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 693 B

After

Width:  |  Height:  |  Size: 693 B

View File

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Before

Width:  |  Height:  |  Size: 540 B

After

Width:  |  Height:  |  Size: 540 B