diff --git a/build/ComputeRasterizer.sln b/build/ComputeRasterizer.sln index 93dcce0..fa7d573 100644 --- a/build/ComputeRasterizer.sln +++ b/build/ComputeRasterizer.sln @@ -1,34 +1,59 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29728.190 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ComputeRasterizer", "ComputeRasterizer.vcxproj", "{6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}" + ProjectSection(ProjectDependencies) = postProject + {57010B68-87C5-33AE-8633-D479ABB84636} = {57010B68-87C5-33AE-8633-D479ABB84636} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "laszip", "laszip.vcxproj", "{57010B68-87C5-33AE-8633-D479ABB84636}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + MinSizeRel|x64 = MinSizeRel|x64 + MinSizeRel|x86 = MinSizeRel|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 + RelWithDebInfo|x64 = RelWithDebInfo|x64 + RelWithDebInfo|x86 = RelWithDebInfo|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.Debug|x64.ActiveCfg = Debug|x64 {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.Debug|x64.Build.0 = Debug|x64 {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.Debug|x86.ActiveCfg = Debug|Win32 {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.Debug|x86.Build.0 = Debug|Win32 + {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.MinSizeRel|x64.ActiveCfg = Release|x64 + {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.MinSizeRel|x64.Build.0 = Release|x64 + {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.MinSizeRel|x86.ActiveCfg = Release|Win32 + {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.MinSizeRel|x86.Build.0 = Release|Win32 {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.Release|x64.ActiveCfg = Release|x64 {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.Release|x64.Build.0 = Release|x64 {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.Release|x86.ActiveCfg = Release|Win32 {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.Release|x86.Build.0 = Release|Win32 - {90F2F7C7-E01B-4BC5-9A67-088352B9BCE9}.Debug|x64.ActiveCfg = Debug|x64 - {90F2F7C7-E01B-4BC5-9A67-088352B9BCE9}.Debug|x64.Build.0 = Debug|x64 - {90F2F7C7-E01B-4BC5-9A67-088352B9BCE9}.Debug|x86.ActiveCfg = Debug|Win32 - {90F2F7C7-E01B-4BC5-9A67-088352B9BCE9}.Debug|x86.Build.0 = Debug|Win32 - {90F2F7C7-E01B-4BC5-9A67-088352B9BCE9}.Release|x64.ActiveCfg = Release|x64 - {90F2F7C7-E01B-4BC5-9A67-088352B9BCE9}.Release|x64.Build.0 = Release|x64 - {90F2F7C7-E01B-4BC5-9A67-088352B9BCE9}.Release|x86.ActiveCfg = Release|Win32 - {90F2F7C7-E01B-4BC5-9A67-088352B9BCE9}.Release|x86.Build.0 = Release|Win32 + {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.RelWithDebInfo|x64.Build.0 = Release|x64 + {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.RelWithDebInfo|x86.ActiveCfg = Release|Win32 + {6E98D902-2BE0-4AB8-97B8-1EBD66E95CA5}.RelWithDebInfo|x86.Build.0 = Release|Win32 + {57010B68-87C5-33AE-8633-D479ABB84636}.Debug|x64.ActiveCfg = Debug|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.Debug|x64.Build.0 = Debug|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.Debug|x86.ActiveCfg = Debug|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.Debug|x86.Build.0 = Debug|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.MinSizeRel|x64.ActiveCfg = MinSizeRel|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.MinSizeRel|x64.Build.0 = MinSizeRel|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.MinSizeRel|x86.ActiveCfg = MinSizeRel|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.MinSizeRel|x86.Build.0 = MinSizeRel|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.Release|x64.ActiveCfg = Release|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.Release|x64.Build.0 = Release|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.Release|x86.ActiveCfg = Release|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.Release|x86.Build.0 = Release|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.RelWithDebInfo|x64.ActiveCfg = RelWithDebInfo|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.RelWithDebInfo|x64.Build.0 = RelWithDebInfo|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.RelWithDebInfo|x86.ActiveCfg = RelWithDebInfo|x64 + {57010B68-87C5-33AE-8633-D479ABB84636}.RelWithDebInfo|x86.Build.0 = RelWithDebInfo|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/build/ComputeRasterizer.vcxproj b/build/ComputeRasterizer.vcxproj index 0c81f1a..53fdbc0 100644 --- a/build/ComputeRasterizer.vcxproj +++ b/build/ComputeRasterizer.vcxproj @@ -25,19 +25,6 @@ 10.0 - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - Application true @@ -140,14 +127,14 @@ NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;WIN32;%(PreprocessorDefinitions) true stdcpplatest - $(SolutionDir)..\include;$(SolutionDir)..\libs\glfw\include;$(SolutionDir)..\libs\glew\include;$(SolutionDir)..\libs\imgui\;$(SolutionDir)..\libs\implot\;$(SolutionDir)..\libs\imgui\backends;$(SolutionDir)..\libs\glm;$(SolutionDir)..\modules;$(SolutionDir)..\libs\json;$(SolutionDir)..\libs\openvr\headers;$(CUDA_PATH)\include;%(AdditionalIncludeDirectories) + $(SolutionDir)..\include;$(SolutionDir)..\libs\glfw\include;$(SolutionDir)..\libs\glew\include;$(SolutionDir)..\libs\imgui\;$(SolutionDir)..\libs\implot\;$(SolutionDir)..\libs\imgui\backends;$(SolutionDir)..\libs\glm;$(SolutionDir)..\modules;$(SolutionDir)..\libs\json;$(SolutionDir)..\libs\openvr\headers;$(CUDA_PATH)\include;$(SolutionDir)..\libs\laszip;%(AdditionalIncludeDirectories) Console true true true - $(SolutionDir)..\libs\glfw\lib\msvc2017_x64\glfw3.lib;opengl32.lib;$(SolutionDir)..\libs\openvr\lib\win64\openvr_api.lib;$(CUDA_PATH)\lib\x64\cuda.lib;$(CUDA_PATH)\lib\x64\nvrtc.lib;%(AdditionalDependencies) + $(SolutionDir)..\libs\glfw\lib\msvc2017_x64\glfw3.lib;opengl32.lib;$(SolutionDir)..\libs\openvr\lib\win64\openvr_api.lib;$(CUDA_PATH)\lib\x64\cuda.lib;$(CUDA_PATH)\lib\x64\nvrtc.lib;$(SolutionDir)Release\laszip.lib;%(AdditionalDependencies) copy /Y "$(SolutionDir)..\libs\openvr\openvr_api.dll" "$(OutDir)" @@ -167,6 +154,7 @@ + diff --git a/build/ComputeRasterizer.vcxproj.filters b/build/ComputeRasterizer.vcxproj.filters index 6f620e9..1f90fb5 100644 --- a/build/ComputeRasterizer.vcxproj.filters +++ b/build/ComputeRasterizer.vcxproj.filters @@ -59,6 +59,9 @@ headers + + source + diff --git a/build/Release/laszip.exp b/build/Release/laszip.exp new file mode 100644 index 0000000..6e938e0 Binary files /dev/null and b/build/Release/laszip.exp differ diff --git a/build/Release/laszip.lib b/build/Release/laszip.lib new file mode 100644 index 0000000..f025ee6 Binary files /dev/null and b/build/Release/laszip.lib differ diff --git a/build/laszip.vcxproj b/build/laszip.vcxproj new file mode 100644 index 0000000..409c9e4 --- /dev/null +++ b/build/laszip.vcxproj @@ -0,0 +1,331 @@ + + + + x64 + + + + Debug + x64 + + + Release + x64 + + + MinSizeRel + x64 + + + RelWithDebInfo + x64 + + + + {57010B68-87C5-33AE-8633-D479ABB84636} + 10.0 + Win32Proj + x64 + laszip + NoUpgrade + + + + DynamicLibrary + MultiByte + v142 + + + DynamicLibrary + MultiByte + v143 + + + DynamicLibrary + MultiByte + v142 + + + DynamicLibrary + MultiByte + v142 + + + + + + + + + + <_ProjectFileVersion>10.0.20506.1 + $(SolutionDir)bin\$(Configuration)_$(Platform) + laszip.dir\Debug\ + laszip + .dll + true + true + $(SolutionDir)bin\$(Configuration)_$(Platform) + $(SolutionDir)obj\$(Configuration)_$(Platform) + laszip + .dll + false + true + $(SolutionDir)..\build_laszip\MinSizeRel\ + laszip.dir\MinSizeRel\ + laszip + .dll + false + true + $(SolutionDir)..\build_laszip\RelWithDebInfo\ + laszip.dir\RelWithDebInfo\ + laszip + .dll + true + true + + + + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + $(IntDir) + EnableFastChecks + ProgramDatabase + Sync + Disabled + stdcpplatest + Disabled + NotUsing + MultiThreadedDebugDLL + true + false + WIN32;_WINDOWS;OPENCV_VERSION=;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNING;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;LASZIP_DLL_EXPORT=1;LASZIPDLL_EXPORTS;UNORDERED;HAVE_UNORDERED_MAP=1;CMAKE_INTDIR="Debug";laszip_EXPORTS;%(PreprocessorDefinitions) + $(IntDir) + + + WIN32;_DEBUG;_WINDOWS;OPENCV_VERSION=;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNING;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;LASZIP_DLL_EXPORT=1;LASZIPDLL_EXPORTS;UNORDERED;HAVE_UNORDERED_MAP=1;CMAKE_INTDIR=\"Debug\";laszip_EXPORTS;%(PreprocessorDefinitions) + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + + + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + %(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + true + %(IgnoreSpecificDefaultLibraries) + $(SolutionDir)Debug/laszip.lib + $(SolutionDir)Debug/laszip.pdb + Console + + + false + + + + + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + $(IntDir) + Sync + AnySuitable + stdcpplatest + MaxSpeed + NotUsing + MultiThreadedDLL + true + false + WIN32;_WINDOWS;NDEBUG;OPENCV_VERSION=;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNING;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;LASZIP_DLL_EXPORT=1;LASZIPDLL_EXPORTS;UNORDERED;HAVE_UNORDERED_MAP=1;CMAKE_INTDIR="Release";laszip_EXPORTS;%(PreprocessorDefinitions) + $(IntDir) + + + + + WIN32;_WINDOWS;NDEBUG;OPENCV_VERSION=;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNING;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;LASZIP_DLL_EXPORT=1;LASZIPDLL_EXPORTS;UNORDERED;HAVE_UNORDERED_MAP=1;CMAKE_INTDIR=\"Release\";laszip_EXPORTS;%(PreprocessorDefinitions) + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + + + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + %(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + false + %(IgnoreSpecificDefaultLibraries) + $(SolutionDir)Release/laszip.lib + $(SolutionDir)Release/laszip.pdb + Console + + + false + + + + + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + $(IntDir) + Sync + OnlyExplicitInline + stdcpplatest + MinSpace + NotUsing + MultiThreadedDLL + true + false + WIN32;_WINDOWS;NDEBUG;OPENCV_VERSION=;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNING;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;LASZIP_DLL_EXPORT=1;LASZIPDLL_EXPORTS;UNORDERED;HAVE_UNORDERED_MAP=1;CMAKE_INTDIR="MinSizeRel";laszip_EXPORTS;%(PreprocessorDefinitions) + $(IntDir) + + + + + WIN32;_WINDOWS;NDEBUG;OPENCV_VERSION=;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNING;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;LASZIP_DLL_EXPORT=1;LASZIPDLL_EXPORTS;UNORDERED;HAVE_UNORDERED_MAP=1;CMAKE_INTDIR=\"MinSizeRel\";laszip_EXPORTS;%(PreprocessorDefinitions) + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + + + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + %(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + false + %(IgnoreSpecificDefaultLibraries) + $(SolutionDir)MinSizeRel/laszip.lib + $(SolutionDir)MinSizeRel/laszip.pdb + Console + + + false + + + + + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + $(IntDir) + ProgramDatabase + Sync + OnlyExplicitInline + stdcpplatest + MaxSpeed + NotUsing + MultiThreadedDLL + true + false + WIN32;_WINDOWS;NDEBUG;OPENCV_VERSION=;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNING;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;LASZIP_DLL_EXPORT=1;LASZIPDLL_EXPORTS;UNORDERED;HAVE_UNORDERED_MAP=1;CMAKE_INTDIR="RelWithDebInfo";laszip_EXPORTS;%(PreprocessorDefinitions) + $(IntDir) + + + WIN32;_WINDOWS;NDEBUG;OPENCV_VERSION=;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNING;_SCL_SECURE_NO_WARNINGS;NOMINMAX;WIN32_LEAN_AND_MEAN;LASZIP_DLL_EXPORT=1;LASZIPDLL_EXPORTS;UNORDERED;HAVE_UNORDERED_MAP=1;CMAKE_INTDIR=\"RelWithDebInfo\";laszip_EXPORTS;%(PreprocessorDefinitions) + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + + + $(SolutionDir)..\libs\laszip\.\dll;$(SolutionDir)..\libs\laszip\.\src;$(SolutionDir)..\libs\laszip\..;%(AdditionalIncludeDirectories) + $(ProjectDir)/$(IntDir) + %(Filename).h + %(Filename).tlb + %(Filename)_i.c + %(Filename)_p.c + + + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib + %(AdditionalLibraryDirectories) + %(AdditionalOptions) /machine:x64 + true + %(IgnoreSpecificDefaultLibraries) + $(SolutionDir)RelWithDebInfo/laszip.lib + $(SolutionDir)RelWithDebInfo/laszip.pdb + Console + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/build/laszip.vcxproj.filters b/build/laszip.vcxproj.filters new file mode 100644 index 0000000..8de7ecc --- /dev/null +++ b/build/laszip.vcxproj.filters @@ -0,0 +1,213 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + {A525AD03-DF1D-3DA0-9B3D-ACFFDAEB7A07} + + + {A64DCB35-0E11-3333-9B3E-001D80484A38} + + + diff --git a/build/laszip.vcxproj.user b/build/laszip.vcxproj.user new file mode 100644 index 0000000..0f14913 --- /dev/null +++ b/build/laszip.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/imgui.ini b/imgui.ini index 3b89977..8f4e391 100644 --- a/imgui.ini +++ b/imgui.ini @@ -9,7 +9,7 @@ Size=490,340 Collapsed=0 [Window][State] -Pos=2080,10 +Pos=2180,10 Size=370,320 Collapsed=0 @@ -19,17 +19,17 @@ Size=236,313 Collapsed=0 [Window][Settings] -Pos=2080,340 +Pos=2180,340 Size=370,370 Collapsed=0 [Window][Data Sets] Pos=10,610 -Size=490,160 +Size=490,260 Collapsed=0 [Window][Debug Stuff] -Pos=10,780 +Pos=10,880 Size=490,200 Collapsed=0 diff --git a/libs/laszip/CHANGES.txt b/libs/laszip/CHANGES.txt new file mode 100644 index 0000000..29a8287 --- /dev/null +++ b/libs/laszip/CHANGES.txt @@ -0,0 +1,51 @@ + 9 November 2019 -- upped version to 3.4 revision 3 for selective decompression extra bytes fix + 9 November 2019 -- fix for selective decompression of more than 16 extra bytes in new point types 6 or higher + 9 November 2019 -- moved UTF8toUTF16() function from laszip.hpp to mydefs.hpp and added new mydefs.cpp +16 October 2019 -- unicode support added for Windows LASzip.dll via in new UTF8toUTF16() function +11 August 2019 -- added CMake for DLL build and minor fixes to allow 64 bit Windows compile of LASzip.dll +11 April 2019 -- increase AC_BUFFER_SIZE from 1024 to 4096 to lower chance of ultra-rare propagate_carry() overflow +10 April 2019 -- fix potential memory leaks found by Connor Manning using valgrind +31 March 2019 -- better license terms for core arithmetic coder thanks to Amir Said. upgrade to version 3.4 rev 0 +20 March 2019 -- upped to 3.3 r1 for checking consistent legacy and extended classification for new point types +19 March 2019 -- bug fix when decompressing new point types: zero "legacy classification" if "classification" > 31 + 7 March 2019 -- upped to 3.3 r0 because hobu was suggesting it for the next release +21 February 2019 -- bug fix when writing 4,294,967,296 or more points uncompressed to LAS +28 December 2018 -- fix for LASzip v4 decompression of WavePacket part of PRDF 9 and 10 +27 December 2018 -- upped to 3.2 r9 for bug fix in multi-channel NIR decompression +7 November 2018 -- laszip DLL: upped to 3.2 r8 for identical legacy and extended flags check +20 October 2018 -- fixed rare bug in LASinterval::merge_intervals() +5 October 2018 -- laszip DLL: upped to 3.2 r6 for corrected 'is_empty' return value in laszip_inside_rectangle() +28 September 2018 -- laszip DLL: tiny bug fix for writing extended classifications via DLL and updated examples +17 September 2018 -- laszip DLL: no more support for deprecated LASattributes (aka "extra bytes") with dimensions 2 or 3 +30 July 2018 -- bug fix in selective decompression of "extra_bytes" for point types 6 and higher +29 March 2018 -- add LASlib only fields to some structs to avoid future mix-up for "native" LAS 1.4 +9 February 2018 -- minor version increment to 3.2 as POINT14_v4 fixes context inefficiency bug +28 December 2017 -- prepare to correct 'context switch' bug reported by Wanwannodao on some future date +15 September 2017 -- new C++ istream / ostream interface completed and released +22 August 2017 -- new C++ istream / ostream interface +18 July 2017 -- bug fix for spatially-indexed reading from native compressed LAS 1.4 files +28 May 2017 -- support for "selective decompression" of compressed LAS 1.4 points added into DLL API +25 April 2017 -- enable "native LAS 1.4 extension" in LASzip DLL via 'request_native_extension' +30 March 2017 -- alpha-release of "native LAS 1.4 extension" for LASzip compression +11 January 2017 -- new DLL/API function 'laszip_set_chunk_size()' to change chunk size +8 January 2017 -- changed file names from "laszip_dll.h" to "laszip_api.h" for hobu +7 January 2017 -- set reserved field in LASzip VLR from 0xAABB to 0x0 +7 January 2017 -- make scan angle quantization in compatibility mode consistent with LASlib +7 January 2017 -- compatibility mode *decompression* fix for points with waveforms +23 September 2015 -- correct update of bounding box and counters from inventory on closing +23 September 2015 -- correct update of bounding box and counters from inventory on closing +22 September 2015 -- bug fix for not overwriting description of pre-existing "extra bytes" +5 September 201 -- the "LAS 1.4 compatibility mode" now allows pre-existing "extra bytes" +31 July 2015 -- new DLL (incompatible with prior version) supports "LAS 1.4 compatibility mode" +4 April 2015 -- added DLL functions for creation and exploitation of spatial indexing LAX files +3 April 2015 -- moved spatial indexing (LAX file generation) from LASlib to LASzip +16 November 2014 -- improved detection & reporting of file truncation and/or LAZ bit-errors +6 September 2014 -- removal of (unused) EntropyEncoder and EntropyDecoder purely virtual classes +24 August 2014 -- when reading LAZ chunk table read is delayed until first read() or seek() is called +18 September 2013 -- fixed small memory leak +24 August 2013 -- fixed bug with explicit cast from LASitem:type to unsigned short and vice versa +11 August 2013 -- laszipdllexample: new EXAMPLE_THREE shows export of geo-referenced LAZ +8 August 2013 -- LASzip: new DLL calls laszip_get_coordinates() and laszip_set_coordinates() +6 August 2013 -- LASzip: new DLL calls laszip_auto_offset() and laszip_check_for_integer_overflow() +1 August 2013 -- LASzip: new DLL calls unload_dll() and get_point_count() for FUSION integration +29 July 2013 -- LASzip: created an easy-to-use DLL interface for LASzip integration diff --git a/libs/laszip/CMakeLists.txt b/libs/laszip/CMakeLists.txt new file mode 100644 index 0000000..0f37668 --- /dev/null +++ b/libs/laszip/CMakeLists.txt @@ -0,0 +1,56 @@ +cmake_minimum_required(VERSION 3.16) +set(CMAKE_SUPPRESS_REGENERATION true) + +project(LASzip LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + + +set(ROOT_DIR "${PROJECT_SOURCE_DIR}") + +# the next line is the ONLY place in the entire laszip system where +# the version info is hard-coded +set(LASZIP_API_VERSION_STRING "3.4.1" CACHE STRING "LASzip version" FORCE) +set(LASZIP_API_VERSION ${LASZIP_API_VERSION_MAJOR}.${LASZIP_API_VERSION_MINOR}.${LASZIP_API_VERSION_PATCH}) + +file(GLOB source_files + "src/*.h" + "src/*.hpp" + "src/*.cpp" + "dll/*.c" + "dll/*.h" +) + +list(FILTER source_files EXCLUDE REGEX ".*src/demzip_dll.cpp$") +list(FILTER source_files EXCLUDE REGEX ".*dll/demzip_api.c$") +list(FILTER source_files EXCLUDE REGEX ".*dll/demzip_api.h$") +list(FILTER source_files EXCLUDE REGEX ".*dll/laszip_api.h$") +list(FILTER source_files EXCLUDE REGEX ".*dll/laszip_api.c$") + +#message("${source_files}") + + +add_library(laszip SHARED ${source_files}) +target_include_directories(laszip PRIVATE + ./dll + ./src + ../ +) + +set(CMAKE_BUILD_TYPE "Release" CACHE STRING "" FORCE) + +add_compile_definitions(OPENCV_VERSION=${OpenCV_VERSION}) +add_compile_definitions(_CRT_SECURE_NO_DEPRECATE) +add_compile_definitions(_CRT_SECURE_NO_WARNINGS) +add_compile_definitions(_CRT_NONSTDC_NO_WARNING) +add_compile_definitions(_SCL_SECURE_NO_WARNINGS) +add_compile_definitions(NOMINMAX) +add_compile_definitions(WIN32_LEAN_AND_MEAN) +add_compile_definitions(LASZIP_DLL_EXPORT=1) +add_compile_definitions(LASZIPDLL_EXPORTS) +add_compile_definitions(UNORDERED) +add_compile_definitions(HAVE_UNORDERED_MAP=1) \ No newline at end of file diff --git a/libs/laszip/COPYING b/libs/laszip/COPYING new file mode 100644 index 0000000..0c226ad --- /dev/null +++ b/libs/laszip/COPYING @@ -0,0 +1,511 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/libs/laszip/README.txt b/libs/laszip/README.txt new file mode 100644 index 0000000..ae3340c --- /dev/null +++ b/libs/laszip/README.txt @@ -0,0 +1,34 @@ +-------------------------------------------------------------------------- + + LASzip LiDAR compression DLL + +-------------------------------------------------------------------------- + + The LASzip LiDAR compressor packaged as a simple stand-alone DLL for + easy inclusion of compression and decompression functionality into + other software. It compresses LAS files into much more compact LAZ + files and vice versa. The resulting files are around 7 - 20 percent + of their original size. Follow the code in laszipdllexample.cpp for + a simple example how to use the DLL. + + To get LASzip just download the LAStools distribution which contains + the LASlib as well as the LASzip sources together with the binaries + of the popular LAStools software and some small example LAZ files. + + http://laszip.org/ + http://lastools.org/ + http://rapidlasso.com/LAStools/ + +-------------------------------------------------------------------------- + + PROGRAMMERS: + + martin@rapidlasso.com + +-------------------------------------------------------------------------- + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + +-------------------------------------------------------------------------- diff --git a/libs/laszip/dll/laszip_api.c b/libs/laszip/dll/laszip_api.c new file mode 100644 index 0000000..8c0776f --- /dev/null +++ b/libs/laszip/dll/laszip_api.c @@ -0,0 +1,1206 @@ +/* +=============================================================================== + + FILE: laszip_api.c + + CONTENTS: + + A simple set of linkable function signatures for the DLL of LASzip + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see header file + +=============================================================================== +*/ + +#include + +// DLL function definitions + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*---------------------------------------------------------------------------*/ +/*---------------- DLL functions to manage the LASzip DLL -------------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_get_version_def) +( + laszip_U8* version_major + , laszip_U8* version_minor + , laszip_U16* version_revision + , laszip_U32* version_build +); +laszip_get_version_def laszip_get_version_ptr = 0; +LASZIP_API laszip_I32 +laszip_get_version +( + laszip_U8* version_major + , laszip_U8* version_minor + , laszip_U16* version_revision + , laszip_U32* version_build +) +{ + if (laszip_get_version_ptr) + { + return (*laszip_get_version_ptr)(version_major, version_minor, version_revision, version_build); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_create_def) +( + laszip_POINTER* pointer +); +laszip_create_def laszip_create_ptr = 0; +LASZIP_API laszip_I32 +laszip_create +( + laszip_POINTER* pointer +) +{ + if (laszip_create_ptr) + { + return (*laszip_create_ptr)(pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_clean_def) +( + laszip_POINTER pointer +); +laszip_clean_def laszip_clean_ptr = 0; +LASZIP_API laszip_I32 +laszip_clean +( + laszip_POINTER pointer +) +{ + if (laszip_clean_ptr) + { + return (*laszip_clean_ptr)(pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_get_error_def) +( + laszip_POINTER pointer + , laszip_CHAR** error +); +laszip_get_error_def laszip_get_error_ptr = 0; +LASZIP_API laszip_I32 +laszip_get_error +( + laszip_POINTER pointer + , laszip_CHAR** error +) +{ + if (laszip_get_error_ptr) + { + return (*laszip_get_error_ptr)(pointer, error); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_get_warning_def) +( + laszip_POINTER pointer + , laszip_CHAR** warning +); +laszip_get_warning_def laszip_get_warning_ptr = 0; +LASZIP_API laszip_I32 +laszip_get_warning +( + laszip_POINTER pointer + , laszip_CHAR** warning +) +{ + if (laszip_get_warning_ptr) + { + return (*laszip_get_warning_ptr)(pointer, warning); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_destroy_def) +( + laszip_POINTER pointer +); +laszip_destroy_def laszip_destroy_ptr = 0; +LASZIP_API laszip_I32 +laszip_destroy +( + laszip_POINTER pointer +) +{ + if (laszip_destroy_ptr) + { + return (*laszip_destroy_ptr)(pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +/*---------- DLL functions to write and read LAS and LAZ files --------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_get_header_pointer_def) +( + laszip_POINTER pointer + , laszip_header_struct** header_pointer +); +laszip_get_header_pointer_def laszip_get_header_pointer_ptr = 0; +LASZIP_API laszip_I32 +laszip_get_header_pointer +( + laszip_POINTER pointer + , laszip_header_struct** header_pointer +) +{ + if (laszip_get_header_pointer_ptr) + { + return (*laszip_get_header_pointer_ptr)(pointer, header_pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_get_point_pointer_def) +( + laszip_POINTER pointer + , laszip_point_struct** point_pointer +); +laszip_get_point_pointer_def laszip_get_point_pointer_ptr = 0; +LASZIP_API laszip_I32 +laszip_get_point_pointer +( + laszip_POINTER pointer + , laszip_point_struct** point_pointer +) +{ + if (laszip_get_point_pointer_ptr) + { + return (*laszip_get_point_pointer_ptr)(pointer, point_pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_get_point_count_def) +( + laszip_POINTER pointer + , laszip_I64* point_count +); +laszip_get_point_count_def laszip_get_point_count_ptr = 0; +LASZIP_API laszip_I32 +laszip_get_point_count +( + laszip_POINTER pointer + , laszip_I64* point_count +) +{ + if (laszip_get_point_count_ptr) + { + return (*laszip_get_point_count_ptr)(pointer, point_count); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_set_header_def) +( + laszip_POINTER pointer + , const laszip_header_struct* header +); +laszip_set_header_def laszip_set_header_ptr = 0; +LASZIP_API laszip_I32 +laszip_set_header +( + laszip_POINTER pointer + , const laszip_header_struct* header +) +{ + if (laszip_set_header_ptr) + { + return (*laszip_set_header_ptr)(pointer, header); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_set_point_type_and_size_def) +( + laszip_POINTER pointer + , laszip_U8 point_type + , laszip_U16 point_size +); +laszip_set_point_type_and_size_def laszip_set_point_type_and_size_ptr = 0; +LASZIP_API laszip_I32 +laszip_set_point_type_and_size +( + laszip_POINTER pointer + , laszip_U8 point_type + , laszip_U16 point_size +) +{ + if (laszip_set_point_type_and_size_ptr) + { + return (*laszip_set_point_type_and_size_ptr)(pointer, point_type, point_size); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_check_for_integer_overflow_def) +( + laszip_POINTER pointer +); +laszip_check_for_integer_overflow_def laszip_check_for_integer_overflow_ptr = 0; +LASZIP_API laszip_I32 +laszip_check_for_integer_overflow +( + laszip_POINTER pointer +) +{ + if (laszip_check_for_integer_overflow_ptr) + { + return (*laszip_check_for_integer_overflow_ptr)(pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_auto_offset_def) +( + laszip_POINTER pointer +); +laszip_auto_offset_def laszip_auto_offset_ptr = 0; +LASZIP_API laszip_I32 +laszip_auto_offset +( + laszip_POINTER pointer +) +{ + if (laszip_auto_offset_ptr) + { + return (*laszip_auto_offset_ptr)(pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_set_point_def) +( + laszip_POINTER pointer + , const laszip_point_struct* point +); +laszip_set_point_def laszip_set_point_ptr = 0; +LASZIP_API laszip_I32 +laszip_set_point +( + laszip_POINTER pointer + , const laszip_point_struct* point +) +{ + if (laszip_set_point_ptr) + { + return (*laszip_set_point_ptr)(pointer, point); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_set_coordinates_def) +( + laszip_POINTER pointer + , const laszip_F64* coordinates +); +laszip_set_coordinates_def laszip_set_coordinates_ptr = 0; +LASZIP_API laszip_I32 +laszip_set_coordinates +( + laszip_POINTER pointer + , const laszip_F64* coordinates +) +{ + if (laszip_set_coordinates_ptr) + { + return (*laszip_set_coordinates_ptr)(pointer, coordinates); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_get_coordinates_def) +( + laszip_POINTER pointer + , laszip_F64* coordinates +); +laszip_get_coordinates_def laszip_get_coordinates_ptr = 0; +LASZIP_API laszip_I32 +laszip_get_coordinates +( + laszip_POINTER pointer + , laszip_F64* coordinates +) +{ + if (laszip_get_coordinates_ptr) + { + return (*laszip_get_coordinates_ptr)(pointer, coordinates); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_set_geokeys_def) +( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_geokey_struct* key_entries +); +laszip_set_geokeys_def laszip_set_geokeys_ptr = 0; +LASZIP_API laszip_I32 +laszip_set_geokeys +( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_geokey_struct* key_entries +) +{ + if (laszip_set_geokeys_ptr) + { + return (*laszip_set_geokeys_ptr)(pointer, number, key_entries); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_set_geodouble_params_def) +( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_F64* geodouble_params +); +laszip_set_geodouble_params_def laszip_set_geodouble_params_ptr = 0; +LASZIP_API laszip_I32 +laszip_set_geodouble_params +( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_F64* geodouble_params +) +{ + if (laszip_set_geodouble_params_ptr) + { + return (*laszip_set_geodouble_params_ptr)(pointer, number, geodouble_params); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_set_geoascii_params_def) +( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_CHAR* geoascii_params +); +laszip_set_geoascii_params_def laszip_set_geoascii_params_ptr = 0; +LASZIP_API laszip_I32 +laszip_set_geoascii_params +( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_CHAR* geoascii_params +) +{ + if (laszip_set_geoascii_params_ptr) + { + return (*laszip_set_geoascii_params_ptr)(pointer, number, geoascii_params); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_add_attribute_def) +( + laszip_POINTER pointer + , laszip_U32 type + , const laszip_CHAR* name + , const laszip_CHAR* description + , laszip_F64 scale + , laszip_F64 offset +); +laszip_add_attribute_def laszip_add_attribute_ptr = 0; +LASZIP_API laszip_I32 +laszip_add_attribute +( + laszip_POINTER pointer + , laszip_U32 type + , const laszip_CHAR* name + , const laszip_CHAR* description + , laszip_F64 scale + , laszip_F64 offset +) +{ + if (laszip_add_attribute_ptr) + { + return (*laszip_add_attribute_ptr)(pointer, type, name, description, scale, offset); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_add_vlr_def) +( + laszip_POINTER pointer + , const laszip_CHAR* user_id + , laszip_U16 record_id + , laszip_U16 record_length_after_header + , const laszip_CHAR* description + , const laszip_U8* data +); +laszip_add_vlr_def laszip_add_vlr_ptr = 0; +LASZIP_API laszip_I32 +laszip_add_vlr +( + laszip_POINTER pointer + , const laszip_CHAR* user_id + , laszip_U16 record_id + , laszip_U16 record_length_after_header + , const laszip_CHAR* description + , const laszip_U8* data +) +{ + if (laszip_add_vlr_ptr) + { + return (*laszip_add_vlr_ptr)(pointer, user_id, record_id, record_length_after_header, description, data); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_remove_vlr_def) +( + laszip_POINTER pointer + , const laszip_CHAR* user_id + , laszip_U16 record_id +); +laszip_remove_vlr_def laszip_remove_vlr_ptr = 0; +LASZIP_API laszip_I32 +laszip_remove_vlr +( + laszip_POINTER pointer + , const laszip_CHAR* user_id + , laszip_U16 record_id +) +{ + if (laszip_remove_vlr_ptr) + { + return (*laszip_remove_vlr_ptr)(pointer, user_id, record_id); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_create_spatial_index_def) +( + laszip_POINTER pointer + , const laszip_BOOL create + , const laszip_BOOL append +); +laszip_create_spatial_index_def laszip_create_spatial_index_ptr = 0; +LASZIP_API laszip_I32 +laszip_create_spatial_index +( + laszip_POINTER pointer + , const laszip_BOOL create + , const laszip_BOOL append +) +{ + if (laszip_create_spatial_index_ptr) + { + return (*laszip_create_spatial_index_ptr)(pointer, create, append); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_preserve_generating_software_def) +( + laszip_POINTER pointer + , const laszip_BOOL preserve +); +laszip_preserve_generating_software_def laszip_preserve_generating_software_ptr = 0; +LASZIP_API laszip_I32 +laszip_preserve_generating_software +( + laszip_POINTER pointer + , const laszip_BOOL preserve +) +{ + if (laszip_preserve_generating_software_ptr) + { + return (*laszip_preserve_generating_software_ptr)(pointer, preserve); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_request_native_extension_def) +( + laszip_POINTER pointer + , const laszip_BOOL request +); +laszip_request_native_extension_def laszip_request_native_extension_ptr = 0; +LASZIP_API laszip_I32 +laszip_request_native_extension +( + laszip_POINTER pointer + , const laszip_BOOL request +) +{ + if (laszip_request_native_extension_ptr) + { + return (*laszip_request_native_extension_ptr)(pointer, request); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_request_compatibility_mode_def) +( + laszip_POINTER pointer + , const laszip_BOOL request +); +laszip_request_compatibility_mode_def laszip_request_compatibility_mode_ptr = 0; +LASZIP_API laszip_I32 +laszip_request_compatibility_mode +( + laszip_POINTER pointer + , const laszip_BOOL request +) +{ + if (laszip_request_compatibility_mode_ptr) + { + return (*laszip_request_compatibility_mode_ptr)(pointer, request); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_set_chunk_size_def) +( + laszip_POINTER pointer + , const laszip_U32 chunk_size +); +laszip_set_chunk_size_def laszip_set_chunk_size_ptr = 0; +LASZIP_API laszip_I32 +laszip_set_chunk_size +( + laszip_POINTER pointer + , const laszip_U32 chunk_size +) +{ + if (laszip_set_chunk_size_ptr) + { + return (*laszip_set_chunk_size_ptr)(pointer, chunk_size); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_open_writer_def) +( + laszip_POINTER pointer + , const laszip_CHAR* file_name + , laszip_BOOL compress +); +laszip_open_writer_def laszip_open_writer_ptr = 0; +LASZIP_API laszip_I32 +laszip_open_writer +( + laszip_POINTER pointer + , const laszip_CHAR* file_name + , laszip_BOOL compress +) +{ + if (laszip_open_writer_ptr) + { + return (*laszip_open_writer_ptr)(pointer, file_name, compress); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_write_point_def) +( + laszip_POINTER pointer +); +laszip_write_point_def laszip_write_point_ptr = 0; +LASZIP_API laszip_I32 +laszip_write_point +( + laszip_POINTER pointer +) +{ + if (laszip_write_point_ptr) + { + return (*laszip_write_point_ptr)(pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_write_indexed_point_def) +( + laszip_POINTER pointer +); +laszip_write_indexed_point_def laszip_write_indexed_point_ptr = 0; +LASZIP_API laszip_I32 +laszip_write_indexed_point +( + laszip_POINTER pointer +) +{ + if (laszip_write_indexed_point_ptr) + { + return (*laszip_write_indexed_point_ptr)(pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_update_inventory_def) +( + laszip_POINTER pointer +); +laszip_update_inventory_def laszip_update_inventory_ptr = 0; +LASZIP_API laszip_I32 +laszip_update_inventory +( + laszip_POINTER pointer +) +{ + if (laszip_update_inventory_ptr) + { + return (*laszip_update_inventory_ptr)(pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_close_writer_def) +( + laszip_POINTER pointer +); +laszip_close_writer_def laszip_close_writer_ptr = 0; +LASZIP_API laszip_I32 +laszip_close_writer +( + laszip_POINTER pointer +) +{ + if (laszip_close_writer_ptr) + { + return (*laszip_close_writer_ptr)(pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_exploit_spatial_index_def) +( + laszip_POINTER pointer + , const laszip_BOOL exploit +); +laszip_exploit_spatial_index_def laszip_exploit_spatial_index_ptr = 0; +LASZIP_API laszip_I32 +laszip_exploit_spatial_index +( + laszip_POINTER pointer + , const laszip_BOOL exploit +) +{ + if (laszip_exploit_spatial_index_ptr) + { + return (*laszip_exploit_spatial_index_ptr)(pointer, exploit); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_decompress_selective_def) +( + laszip_POINTER pointer + , const laszip_U32 decompress_selective +); +laszip_decompress_selective_def laszip_decompress_selective_ptr = 0; +LASZIP_API laszip_I32 +laszip_decompress_selective +( + laszip_POINTER pointer + , const laszip_U32 decompress_selective +) +{ + if (laszip_decompress_selective_ptr) + { + return (*laszip_decompress_selective_ptr)(pointer, decompress_selective); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_open_reader_def) +( + laszip_POINTER pointer + , const laszip_CHAR* file_name + , laszip_BOOL* is_compressed +); +laszip_open_reader_def laszip_open_reader_ptr = 0; +LASZIP_API laszip_I32 +laszip_open_reader +( + laszip_POINTER pointer + , const laszip_CHAR* file_name + , laszip_BOOL* is_compressed +) +{ + if (laszip_open_reader_ptr) + { + return (*laszip_open_reader_ptr)(pointer, file_name, is_compressed); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_has_spatial_index_def) +( + laszip_POINTER pointer + , laszip_BOOL* is_compressed + , laszip_BOOL* is_appended +); +laszip_has_spatial_index_def laszip_has_spatial_index_ptr = 0; +LASZIP_API laszip_I32 +laszip_has_spatial_index +( + laszip_POINTER pointer + , laszip_BOOL* is_compressed + , laszip_BOOL* is_appended +) +{ + if (laszip_has_spatial_index_ptr) + { + return (*laszip_has_spatial_index_ptr)(pointer, is_compressed, is_appended); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_inside_rectangle_def) +( + laszip_POINTER pointer + , laszip_F64 r_min_x + , laszip_F64 r_min_y + , laszip_F64 r_max_x + , laszip_F64 r_max_y + , laszip_BOOL* is_empty +); +laszip_inside_rectangle_def laszip_inside_rectangle_ptr = 0; +LASZIP_API laszip_I32 +laszip_inside_rectangle +( + laszip_POINTER pointer + , laszip_F64 r_min_x + , laszip_F64 r_min_y + , laszip_F64 r_max_x + , laszip_F64 r_max_y + , laszip_BOOL* is_empty +) +{ + if (laszip_inside_rectangle_ptr) + { + return (*laszip_inside_rectangle_ptr)(pointer, r_min_x, r_min_y, r_max_x, r_max_y, is_empty); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_seek_point_def) +( + laszip_POINTER pointer + , laszip_I64 index +); +laszip_seek_point_def laszip_seek_point_ptr = 0; +LASZIP_API laszip_I32 +laszip_seek_point( + laszip_POINTER pointer + , laszip_I64 index +) +{ + if (laszip_seek_point_ptr) + { + return (*laszip_seek_point_ptr)(pointer, index); + } + return 1; +} + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_read_point_def) +( + laszip_POINTER pointer +); +laszip_read_point_def laszip_read_point_ptr = 0; +LASZIP_API laszip_I32 +laszip_read_point( + laszip_POINTER pointer +) +{ + if (laszip_read_point_ptr) + { + return (*laszip_read_point_ptr)(pointer); + } + return 1; +} + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_read_inside_point_def) +( + laszip_POINTER pointer + , laszip_BOOL* is_done +); +laszip_read_inside_point_def laszip_read_inside_point_ptr = 0; +LASZIP_API laszip_I32 +laszip_read_inside_point( + laszip_POINTER pointer + , laszip_BOOL* is_done +) +{ + if (laszip_read_inside_point_ptr) + { + return (*laszip_read_inside_point_ptr)(pointer, is_done); + } + return 1; +} + +/*---------------------------------------------------------------------------*/ +typedef laszip_I32 (*laszip_close_reader_def) +( + laszip_POINTER pointer +); +laszip_close_reader_def laszip_close_reader_ptr = 0; +LASZIP_API laszip_I32 +laszip_close_reader +( + laszip_POINTER pointer +) +{ + if (laszip_close_reader_ptr) + { + return (*laszip_close_reader_ptr)(pointer); + } + return 1; +}; + +/*---------------------------------------------------------------------------*/ +/*---------------- DLL functions to load and unload LASzip ------------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +#ifdef _WIN32 + #include +#define FreeLibraryZeroMeansFail 1 +#else + #include + typedef void* HINSTANCE; +#ifndef NULL +#define NULL 0 +#endif +#define LoadLibrary dlopen +#define GetProcAddress dlsym +#define FreeLibrary dlclose +#define FreeLibraryZeroMeansFail 0 +#define TEXT +#endif +static HINSTANCE laszip_HINSTANCE = NULL; +laszip_I32 laszip_load_dll() +{ + // Assure DLL not yet loaded + if (laszip_HINSTANCE != NULL) { + return 1; + } + // Load DLL file +#ifdef _WIN32 +#ifdef _WIN64 + laszip_HINSTANCE = LoadLibrary(TEXT("LASzip64.dll")); +#else + laszip_HINSTANCE = LoadLibrary(TEXT("LASzip.dll")); +#endif // _WIN64 +#elif __APPLE__ + laszip_HINSTANCE = LoadLibrary("liblaszip.dylib", RTLD_NOW); +#else + laszip_HINSTANCE = LoadLibrary("liblaszip.so", RTLD_NOW); +#endif + if (laszip_HINSTANCE == NULL) { + return 1; + } + // Get function pointers + laszip_get_version_ptr = (laszip_get_version_def)GetProcAddress(laszip_HINSTANCE, "laszip_get_version"); + if (laszip_get_version_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_create_ptr = (laszip_create_def)GetProcAddress(laszip_HINSTANCE, "laszip_create"); + if (laszip_create_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_clean_ptr = (laszip_clean_def)GetProcAddress(laszip_HINSTANCE, "laszip_clean"); + if (laszip_clean_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_get_error_ptr = (laszip_get_error_def)GetProcAddress(laszip_HINSTANCE, "laszip_get_error"); + if (laszip_get_error_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_get_warning_ptr = (laszip_get_warning_def)GetProcAddress(laszip_HINSTANCE, "laszip_get_warning"); + if (laszip_get_warning_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_destroy_ptr = (laszip_destroy_def)GetProcAddress(laszip_HINSTANCE, "laszip_destroy"); + if (laszip_destroy_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_get_header_pointer_ptr = (laszip_get_header_pointer_def)GetProcAddress(laszip_HINSTANCE, "laszip_get_header_pointer"); + if (laszip_get_header_pointer_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_get_point_pointer_ptr = (laszip_get_point_pointer_def)GetProcAddress(laszip_HINSTANCE, "laszip_get_point_pointer"); + if (laszip_get_point_pointer_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_get_point_count_ptr = (laszip_get_point_count_def)GetProcAddress(laszip_HINSTANCE, "laszip_get_point_count"); + if (laszip_get_point_count_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_set_header_ptr = (laszip_set_header_def)GetProcAddress(laszip_HINSTANCE, "laszip_set_header"); + if (laszip_set_header_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_set_point_type_and_size_ptr = (laszip_set_point_type_and_size_def)GetProcAddress(laszip_HINSTANCE, "laszip_set_point_type_and_size"); + if (laszip_set_point_type_and_size_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_check_for_integer_overflow_ptr = (laszip_check_for_integer_overflow_def)GetProcAddress(laszip_HINSTANCE, "laszip_check_for_integer_overflow"); + if (laszip_check_for_integer_overflow_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_auto_offset_ptr = (laszip_auto_offset_def)GetProcAddress(laszip_HINSTANCE, "laszip_auto_offset"); + if (laszip_auto_offset_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_set_point_ptr = (laszip_set_point_def)GetProcAddress(laszip_HINSTANCE, "laszip_set_point"); + if (laszip_set_point_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_set_coordinates_ptr = (laszip_set_coordinates_def)GetProcAddress(laszip_HINSTANCE, "laszip_set_coordinates"); + if (laszip_set_coordinates_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_get_coordinates_ptr = (laszip_get_coordinates_def)GetProcAddress(laszip_HINSTANCE, "laszip_get_coordinates"); + if (laszip_get_coordinates_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_set_geokeys_ptr = (laszip_set_geokeys_def)GetProcAddress(laszip_HINSTANCE, "laszip_set_geokeys"); + if (laszip_set_geokeys_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_set_geodouble_params_ptr = (laszip_set_geodouble_params_def)GetProcAddress(laszip_HINSTANCE, "laszip_set_geodouble_params"); + if (laszip_set_geodouble_params_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_set_geoascii_params_ptr = (laszip_set_geoascii_params_def)GetProcAddress(laszip_HINSTANCE, "laszip_set_geoascii_params"); + if (laszip_set_geoascii_params_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_add_attribute_ptr = (laszip_add_attribute_def)GetProcAddress(laszip_HINSTANCE, "laszip_add_attribute"); + if (laszip_add_attribute_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_add_vlr_ptr = (laszip_add_vlr_def)GetProcAddress(laszip_HINSTANCE, "laszip_add_vlr"); + if (laszip_add_vlr_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_remove_vlr_ptr = (laszip_remove_vlr_def)GetProcAddress(laszip_HINSTANCE, "laszip_remove_vlr"); + if (laszip_remove_vlr_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_create_spatial_index_ptr = (laszip_create_spatial_index_def)GetProcAddress(laszip_HINSTANCE, "laszip_create_spatial_index"); + if (laszip_create_spatial_index_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_preserve_generating_software_ptr = (laszip_preserve_generating_software_def)GetProcAddress(laszip_HINSTANCE, "laszip_preserve_generating_software"); + if (laszip_preserve_generating_software_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_request_native_extension_ptr = (laszip_request_native_extension_def)GetProcAddress(laszip_HINSTANCE, "laszip_request_native_extension"); + if (laszip_request_native_extension_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_request_compatibility_mode_ptr = (laszip_request_compatibility_mode_def)GetProcAddress(laszip_HINSTANCE, "laszip_request_compatibility_mode"); + if (laszip_request_compatibility_mode_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_set_chunk_size_ptr = (laszip_set_chunk_size_def)GetProcAddress(laszip_HINSTANCE, "laszip_set_chunk_size"); + if (laszip_set_chunk_size_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_open_writer_ptr = (laszip_open_writer_def)GetProcAddress(laszip_HINSTANCE, "laszip_open_writer"); + if (laszip_open_writer_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_write_point_ptr = (laszip_write_point_def)GetProcAddress(laszip_HINSTANCE, "laszip_write_point"); + if (laszip_write_point_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_write_indexed_point_ptr = (laszip_write_indexed_point_def)GetProcAddress(laszip_HINSTANCE, "laszip_write_indexed_point"); + if (laszip_write_indexed_point_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_update_inventory_ptr = (laszip_update_inventory_def)GetProcAddress(laszip_HINSTANCE, "laszip_update_inventory"); + if (laszip_update_inventory_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_close_writer_ptr = (laszip_close_writer_def)GetProcAddress(laszip_HINSTANCE, "laszip_close_writer"); + if (laszip_close_writer_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_exploit_spatial_index_ptr = (laszip_exploit_spatial_index_def)GetProcAddress(laszip_HINSTANCE, "laszip_exploit_spatial_index"); + if (laszip_exploit_spatial_index_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_decompress_selective_ptr = (laszip_decompress_selective_def)GetProcAddress(laszip_HINSTANCE, "laszip_decompress_selective"); + if (laszip_decompress_selective_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_open_reader_ptr = (laszip_open_reader_def)GetProcAddress(laszip_HINSTANCE, "laszip_open_reader"); + if (laszip_open_reader_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_has_spatial_index_ptr = (laszip_has_spatial_index_def)GetProcAddress(laszip_HINSTANCE, "laszip_has_spatial_index"); + if (laszip_has_spatial_index_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_inside_rectangle_ptr = (laszip_inside_rectangle_def)GetProcAddress(laszip_HINSTANCE, "laszip_inside_rectangle"); + if (laszip_inside_rectangle_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_seek_point_ptr = (laszip_seek_point_def)GetProcAddress(laszip_HINSTANCE, "laszip_seek_point"); + if (laszip_seek_point_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_read_point_ptr = (laszip_read_point_def)GetProcAddress(laszip_HINSTANCE, "laszip_read_point"); + if (laszip_read_point_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_read_inside_point_ptr = (laszip_read_inside_point_def)GetProcAddress(laszip_HINSTANCE, "laszip_read_inside_point"); + if (laszip_read_inside_point_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + laszip_close_reader_ptr = (laszip_close_reader_def)GetProcAddress(laszip_HINSTANCE, "laszip_close_reader"); + if (laszip_close_reader_ptr == NULL) { + FreeLibrary(laszip_HINSTANCE); + return 1; + } + return 0; +}; + +/*---------------------------------------------------------------------------*/ +laszip_I32 laszip_unload_dll() +{ + if (laszip_HINSTANCE == NULL) { + return 1; + } + if (FreeLibraryZeroMeansFail) + { + if (!FreeLibrary(laszip_HINSTANCE)) { + return 1; + } + } + else + { + if (FreeLibrary(laszip_HINSTANCE)) { + return 1; + } + } + laszip_HINSTANCE = NULL; + return 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/libs/laszip/example/5points_14.las b/libs/laszip/example/5points_14.las new file mode 100644 index 0000000..ea67ccc Binary files /dev/null and b/libs/laszip/example/5points_14.las differ diff --git a/libs/laszip/example/Makefile b/libs/laszip/example/Makefile new file mode 100644 index 0000000..da48b29 --- /dev/null +++ b/libs/laszip/example/Makefile @@ -0,0 +1,9 @@ +# makefile for cleanup of LAStools\LASzip\example +# + +clean: + rm -rf *.o + +clobber: + rm -rf *.o + rm -rf *~ diff --git a/libs/laszip/example/laszipdllexample.cpp b/libs/laszip/example/laszipdllexample.cpp new file mode 100644 index 0000000..59dc595 --- /dev/null +++ b/libs/laszip/example/laszipdllexample.cpp @@ -0,0 +1,4205 @@ +/* +=============================================================================== + + FILE: laszipdllexample.cpp + + CONTENTS: + + This source code implements several different easy-to-follow examples on + how to use the LASzip DLL. The first and the second examples implement a + small compression and decompression utilitity. The third example shows + how to use the DLL to export points to a proper geo-referenced LAZ file. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2018, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the LICENSE.txt file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 7 September 2018 -- introduced the LASCopyString macro to replace _strdup + 28 May 2017 -- 14th example reads compressed LAS 1.4 with "selective decompression" + 25 April 2017 -- 13th example writes LAS 1.4 using new "native LAS 1.4 extension" + 11 January 2017 -- 12th example changes the default chunk size from 50000 to 5000 + 8 January 2017 -- changed from "laszip_dll.h" to "laszip_api.h" because of hobu + 23 September 2015 -- 11th example writes without a-priori bounding box or counters + 22 September 2015 -- 10th upconverts to LAS 1.4 with pre-existing "extra bytes" + 5 September 2015 -- eighth and nineth example show pre-existing "extra bytes" + 19 July 2015 -- sixth and seventh example show LAS 1.4 compatibility mode + 2 April 2015 -- fourth and fifth example with integrated spatially indexing + 11 August 2013 -- added third example for exporting geo-referenced points + 29 July 2013 -- created for the LASzip DLL after returning to Sommerhausen + +=============================================================================== +*/ + +#include +#include +#include +#include + +#include "laszip_api.h" + +#if defined(_MSC_VER) && \ + (_MSC_FULL_VER >= 150000000) +#define LASCopyString _strdup +#else +#define LASCopyString strdup +#endif + +void usage(bool wait=false) +{ + fprintf(stderr,"usage:\n"); + fprintf(stderr,"laszipdllexample\n"); + fprintf(stderr,"laszipdllexample in.las out.laz\n"); + fprintf(stderr,"laszipdllexample in.laz out.las\n"); + fprintf(stderr,"laszipdllexample in.las out.las\n"); + fprintf(stderr,"laszipdllexample in.laz out.laz\n"); + fprintf(stderr,"laszipdllexample -h\n"); + if (wait) + { + fprintf(stderr,"\n"); + getc(stdin); + } + exit(1); +} + +static void dll_error(laszip_POINTER laszip) +{ + if (laszip) + { + laszip_CHAR* error; + if (laszip_get_error(laszip, &error)) + { + fprintf(stderr,"DLL ERROR: getting error messages\n"); + } + fprintf(stderr,"DLL ERROR MESSAGE: %s\n", error); + } +} + +static void byebye(bool error=false, bool wait=false, laszip_POINTER laszip=0) +{ + if (error) + { + dll_error(laszip); + } + if (wait) + { + fprintf(stderr,"\n"); + getc(stdin); + } + exit(error); +} + +static double taketime() +{ + return (double)(clock())/CLOCKS_PER_SEC; +} + +#define EXAMPLE_ONE 1 +#define EXAMPLE_TWO 2 +#define EXAMPLE_THREE 3 +#define EXAMPLE_FOUR 4 +#define EXAMPLE_FIVE 5 +#define EXAMPLE_SIX 6 +#define EXAMPLE_SEVEN 7 +#define EXAMPLE_EIGHT 8 +#define EXAMPLE_NINE 9 +#define EXAMPLE_TEN 10 +#define EXAMPLE_ELEVEN 11 +#define EXAMPLE_TWELVE 12 +#define EXAMPLE_THIRTEEN 13 +#define EXAMPLE_FOURTEEN 14 +#define EXAMPLE_FIFTEEN 15 +#define EXAMPLE_SIXTEEN 16 + +#define EXAMPLE EXAMPLE_EIGHT + +int main(int argc, char *argv[]) +{ + laszip_U32 i; + double start_time = 0.0; + char* file_name_in = 0; + char* file_name_out = 0; + + // load LASzip DLL + + if (laszip_load_dll()) + { + fprintf(stderr,"DLL ERROR: loading LASzip DLL\n"); + byebye(true, argc==1); + } + + // get version of LASzip DLL + + laszip_U8 version_major; + laszip_U8 version_minor; + laszip_U16 version_revision; + laszip_U32 version_build; + + if (laszip_get_version(&version_major, &version_minor, &version_revision, &version_build)) + { + fprintf(stderr,"DLL ERROR: getting LASzip DLL version number\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"LASzip DLL v%d.%d r%d (build %d)\n", (int)version_major, (int)version_minor, (int)version_revision, (int)version_build); + + if (argc == 1) + { + char file_name[256]; + fprintf(stderr,"%s is better run in the command line\n", argv[0]); + fprintf(stderr,"enter input file%s: ", ((EXAMPLE == EXAMPLE_THREE) ? " (not used)" : "")); fgets(file_name, 256, stdin); + file_name[strlen(file_name)-1] = '\0'; + file_name_in = LASCopyString(file_name); + fprintf(stderr,"enter output file: "); fgets(file_name, 256, stdin); + file_name[strlen(file_name)-1] = '\0'; + file_name_out = LASCopyString(file_name); + } + else if (argc == 3) + { + file_name_in = LASCopyString(argv[1]); + file_name_out = LASCopyString(argv[2]); + } + else + { + if ((argc != 2) || (strcmp(argv[1], "-h") != 0)) + { + fprintf(stderr, "ERROR: cannot understand arguments\n"); + } + usage(); + } + + start_time = taketime(); + + if (EXAMPLE == EXAMPLE_ONE) + { + fprintf(stderr,"running EXAMPLE_ONE (reading *without* and writing *without* compatibility mode)\n"); + + // create the reader + + laszip_POINTER laszip_reader; + if (laszip_create(&laszip_reader)) + { + fprintf(stderr,"DLL ERROR: creating laszip reader\n"); + byebye(true, argc==1); + } + + // open the reader + + laszip_BOOL is_compressed = 0; + if (laszip_open_reader(laszip_reader, file_name_in, &is_compressed)) + { + fprintf(stderr,"DLL ERROR: opening laszip reader for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' is %scompressed\n", file_name_in, (is_compressed ? "" : "un")); + + // get a pointer to the header of the reader that was just populated + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_reader, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // how many points does the file have + + laszip_I64 npoints = (header->number_of_point_records ? header->number_of_point_records : header->extended_number_of_point_records); + + // report how many points the file has + + fprintf(stderr,"file '%s' contains %I64d points\n", file_name_in, npoints); + + // get a pointer to the points that will be read + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_reader, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // initialize the header for the writer using the header of the reader + + if (laszip_set_header(laszip_writer, header)) + { + fprintf(stderr,"DLL ERROR: setting header for laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // open the writer + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // read the points + + laszip_I64 p_count = 0; + + while (p_count < npoints) + { + // read a point + + if (laszip_read_point(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: reading point %I64d\n", p_count); + byebye(true, argc==1, laszip_reader); + } + + // copy the point + + if (laszip_set_point(laszip_writer, point)) + { + fprintf(stderr,"DLL ERROR: setting point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // write the point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + } + + fprintf(stderr,"successfully read and written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + // close the reader + + if (laszip_close_reader(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: closing laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // destroy the reader + + if (laszip_destroy(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: destroying laszip reader\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for reading %scompressed and writing %scompressed\n", taketime()-start_time, (is_compressed ? "" : "un"), (compress ? "" : "un")); + + } // end of EXAMPLE_ONE + + if (EXAMPLE == EXAMPLE_TWO) + { + fprintf(stderr,"running EXAMPLE_TWO (another way of reading *without* and writing *without* compatibility mode)\n"); + + // create the reader + + laszip_POINTER laszip_reader; + if (laszip_create(&laszip_reader)) + { + fprintf(stderr,"DLL ERROR: creating laszip reader\n"); + byebye(true, argc==1); + } + + // open the reader + + laszip_BOOL is_compressed = 0; + if (laszip_open_reader(laszip_reader, file_name_in, &is_compressed)) + { + fprintf(stderr,"DLL ERROR: opening laszip reader for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' is %scompressed\n", file_name_in, (is_compressed ? "" : "un")); + + // get a pointer to the header of the reader that was just populated + + laszip_header* header_read; + + if (laszip_get_header_pointer(laszip_reader, &header_read)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // how many points does the file have + + laszip_I64 npoints = (header_read->number_of_point_records ? header_read->number_of_point_records : header_read->extended_number_of_point_records); + + // report how many points the file has + + fprintf(stderr,"file '%s' contains %I64d points\n", file_name_in, npoints); + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // get a pointer to the header of the writer so we can populate it + + laszip_header* header_write; + + if (laszip_get_header_pointer(laszip_writer, &header_write)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // copy entries from the reader header to the writer header + + header_write->file_source_ID = header_read->file_source_ID; + header_write->global_encoding = header_read->global_encoding; + header_write->project_ID_GUID_data_1 = header_read->project_ID_GUID_data_1; + header_write->project_ID_GUID_data_2 = header_read->project_ID_GUID_data_2; + header_write->project_ID_GUID_data_3 = header_read->project_ID_GUID_data_3; + memcpy(header_write->project_ID_GUID_data_4, header_read->project_ID_GUID_data_4, 8); + header_write->version_major = header_read->version_major; + header_write->version_minor = header_read->version_minor; + memcpy(header_write->system_identifier, header_read->system_identifier, 32); + memcpy(header_write->generating_software, header_read->generating_software, 32); + header_write->file_creation_day = header_read->file_creation_day; + header_write->file_creation_year = header_read->file_creation_year; + header_write->header_size = header_read->header_size; + header_write->offset_to_point_data = header_read->header_size; /* note !!! */ + header_write->number_of_variable_length_records = header_read->number_of_variable_length_records; + header_write->point_data_format = header_read->point_data_format; + header_write->point_data_record_length = header_read->point_data_record_length; + header_write->number_of_point_records = header_read->number_of_point_records; + for (i = 0; i < 5; i++) + { + header_write->number_of_points_by_return[i] = header_read->number_of_points_by_return[i]; + } + header_write->x_scale_factor = header_read->x_scale_factor; + header_write->y_scale_factor = header_read->y_scale_factor; + header_write->z_scale_factor = header_read->z_scale_factor; + header_write->x_offset = header_read->x_offset; + header_write->y_offset = header_read->y_offset; + header_write->z_offset = header_read->z_offset; + header_write->max_x = header_read->max_x; + header_write->min_x = header_read->min_x; + header_write->max_y = header_read->max_y; + header_write->min_y = header_read->min_y; + header_write->max_z = header_read->max_z; + header_write->min_z = header_read->min_z; + + // LAS 1.3 and higher only + header_write->start_of_waveform_data_packet_record = header_read->start_of_waveform_data_packet_record; + + // LAS 1.4 and higher only + header_write->start_of_first_extended_variable_length_record = header_read->start_of_first_extended_variable_length_record; + header_write->number_of_extended_variable_length_records = header_read->number_of_extended_variable_length_records; + header_write->extended_number_of_point_records = header_read->extended_number_of_point_records; + for (i = 0; i < 15; i++) + { + header_write->extended_number_of_points_by_return[i] = header_read->extended_number_of_points_by_return[i]; + } + + // we may modify output because we omit any user defined data that may be ** the header + + if (header_read->user_data_in_header_size) + { + header_write->header_size -= header_read->user_data_in_header_size; + header_write->offset_to_point_data -= header_read->user_data_in_header_size; + fprintf(stderr,"omitting %d bytes of user_data_in_header\n", header_read->user_data_after_header_size); + } + + // add all the VLRs + + if (header_read->number_of_variable_length_records) + { + fprintf(stderr,"offset_to_point_data before adding %u VLRs is : %d\n", header_read->number_of_variable_length_records, (laszip_I32)header_write->offset_to_point_data); + for (i = 0; i < header_read->number_of_variable_length_records; i++) + { + if (laszip_add_vlr(laszip_writer, header_read->vlrs[i].user_id, header_read->vlrs[i].record_id, header_read->vlrs[i].record_length_after_header, header_read->vlrs[i].description, header_read->vlrs[i].data)) + { + fprintf(stderr,"DLL ERROR: adding VLR %u of %u to the header of the laszip writer\n", i+i, header_read->number_of_variable_length_records); + byebye(true, argc==1, laszip_writer); + } + fprintf(stderr,"offset_to_point_data after adding VLR number %u is : %d\n", i+1, (laszip_I32)header_write->offset_to_point_data); + } + } + + // we may modify output because we omit any user defined data that may be *after* the header + + if (header_read->user_data_after_header_size) + { + fprintf(stderr,"omitting %d bytes of user_data_after_header\n", header_read->user_data_after_header_size); + } + + // open the writer + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // get a pointer to the point of the reader will be read + + laszip_point* point_read; + + if (laszip_get_point_pointer(laszip_reader, &point_read)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // get a pointer to the point of the writer that we will populate and write + + laszip_point* point_write; + + if (laszip_get_point_pointer(laszip_writer, &point_write)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // read the points + + laszip_I64 p_count = 0; + + while (p_count < npoints) + { + // read a point + + if (laszip_read_point(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: reading point %I64d\n", p_count); + byebye(true, argc==1, laszip_reader); + } + + // copy the point + + point_write->X = point_read->X; + point_write->Y = point_read->Y; + point_write->Z = point_read->Z; + point_write->intensity = point_read->intensity; + point_write->return_number = point_read->return_number; + point_write->number_of_returns = point_read->number_of_returns; + point_write->scan_direction_flag = point_read->scan_direction_flag; + point_write->edge_of_flight_line = point_read->edge_of_flight_line; + point_write->classification = point_read->classification; + point_write->withheld_flag = point_read->withheld_flag; + point_write->keypoint_flag = point_read->keypoint_flag; + point_write->synthetic_flag = point_read->synthetic_flag; + point_write->scan_angle_rank = point_read->scan_angle_rank; + point_write->user_data = point_read->user_data; + point_write->point_source_ID = point_read->point_source_ID; + + point_write->gps_time = point_read->gps_time; + memcpy(point_write->rgb, point_read->rgb, 8); + memcpy(point_write->wave_packet, point_read->wave_packet, 29); + + // LAS 1.4 only + point_write->extended_scanner_channel = point_read->extended_scanner_channel; + point_write->extended_classification_flags = point_read->extended_classification_flags; + point_write->extended_classification = point_read->extended_classification; + point_write->extended_return_number = point_read->extended_return_number; + point_write->extended_number_of_returns = point_read->extended_number_of_returns; + point_write->extended_scan_angle = point_read->extended_scan_angle; + + if (point_read->num_extra_bytes) + { + memcpy(point_write->extra_bytes, point_read->extra_bytes, point_read->num_extra_bytes); + } + + // write the point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + } + + fprintf(stderr,"successfully read and written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + // close the reader + + if (laszip_close_reader(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: closing laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // destroy the reader + + if (laszip_destroy(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: destroying laszip reader\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for reading %scompressed and writing %scompressed\n", taketime()-start_time, (is_compressed ? "" : "un"), (compress ? "" : "un")); + + } // end of EXAMPLE_TWO + + if (EXAMPLE == EXAMPLE_THREE) + { + fprintf(stderr,"running EXAMPLE_THREE (writing five points of type 1 to LAS 1.2 file)\n"); + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // get a pointer to the header of the writer so we can populate it + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_writer, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // populate the header + + header->file_source_ID = 4711; + header->global_encoding = (1<<0); // see LAS specification for details + header->version_major = 1; + header->version_minor = 2; + strncpy(header->system_identifier, "LASzip DLL example 3", 32); + header->file_creation_day = 120; + header->file_creation_year = 2013; + header->point_data_format = 1; + header->point_data_record_length = 28; + header->number_of_point_records = 5; + header->number_of_points_by_return[0] = 3; + header->number_of_points_by_return[1] = 2; + header->max_x = 630499.95; + header->min_x = 630498.56; + header->max_y = 4834749.66; + header->min_y = 4834748.73; + header->max_z = 63.68; + header->min_z = 61.33; + + // optional: use the bounding box and the scale factor to create a "good" offset + + if (laszip_auto_offset(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: during automatic offset creation\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data before adding funny VLR is : %d\n", (laszip_I32)header->offset_to_point_data); + + // add some funny VLR + + if (laszip_add_vlr(laszip_writer, "funny", 12345, 0, "just a funny VLR", 0)) + { + fprintf(stderr,"DLL ERROR: adding funny VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + // create the geokeys with the projection information + + laszip_geokey_struct key_entries[5]; + + // projected coordinates + key_entries[0].key_id = 1024; // GTModelTypeGeoKey + key_entries[0].tiff_tag_location = 0; + key_entries[0].count = 1; + key_entries[0].value_offset = 1; // ModelTypeProjected + + // projection + key_entries[1].key_id = 3072; // ProjectedCSTypeGeoKey + key_entries[1].tiff_tag_location = 0; + key_entries[1].count = 1; + key_entries[1].value_offset = 32613; // PCS_WGS84_UTM_zone_13N + + // horizontal units + key_entries[2].key_id = 3076; // ProjLinearUnitsGeoKey + key_entries[2].tiff_tag_location = 0; + key_entries[2].count = 1; + key_entries[2].value_offset = 9001; // meters + + // vertical units + key_entries[3].key_id = 4099; // VerticalUnitsGeoKey + key_entries[3].tiff_tag_location = 0; + key_entries[3].count = 1; + key_entries[3].value_offset = 9001; // meters + + // vertical datum + key_entries[4].key_id = 4096; // VerticalCSTypeGeoKey + key_entries[4].tiff_tag_location = 0; + key_entries[4].count = 1; + key_entries[4].value_offset = 5030; // WGS84 + + // add the geokeys (create or replace the appropriate VLR) + + fprintf(stderr,"offset_to_point_data before adding projection VLR : %d\n", (laszip_I32)header->offset_to_point_data); + + if (laszip_set_geokeys(laszip_writer, 5, key_entries)) + { + fprintf(stderr,"DLL ERROR: adding funny VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data after adding two VLRs : %d\n", (laszip_I32)header->offset_to_point_data); + + // open the writer + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // get a pointer to the point of the writer that we will populate and write + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_writer, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // write five points + + laszip_I64 p_count = 0; + laszip_F64 coordinates[3]; + + // populate the first point + + coordinates[0] = 630499.95; + coordinates[1] = 4834749.17; + coordinates[2] = 62.15; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 60; + point->return_number = 2; + point->number_of_returns = 2; + point->classification = 2; + point->scan_angle_rank = 21; + point->gps_time = 413162.560400; + + // write the first point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the second point + + coordinates[0] = 630499.83; + coordinates[1] = 4834748.88; + coordinates[2] = 62.68; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 90; + point->return_number = 1; + point->number_of_returns = 1; + point->classification = 1; + point->scan_angle_rank = 21; + point->gps_time = 413162.563600; + + // write the second point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the third point + + coordinates[0] = 630499.54; + coordinates[1] = 4834749.66; + coordinates[2] = 62.66; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 70; + point->return_number = 1; + point->number_of_returns = 1; + point->classification = 1; + point->scan_angle_rank = 22; + point->gps_time = 413162.566800; + + // write the third point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the fourth point + + coordinates[0] = 630498.56; + coordinates[1] = 4834749.41; + coordinates[2] = 63.68; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 20; + point->return_number = 1; + point->number_of_returns = 2; + point->classification = 3; + point->scan_angle_rank = 22; + point->gps_time = 413162.580200; + + // write the fourth point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the fifth point + + coordinates[0] = 630498.80; + coordinates[1] = 4834748.73; + coordinates[2] = 62.16; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 110; + point->return_number = 2; + point->number_of_returns = 2; + point->classification = 2; + point->scan_angle_rank = 22; + point->gps_time = 413162.580200; + + // write the fifth point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // get the number of points written so far + + if (laszip_get_point_count(laszip_writer, &p_count)) + { + fprintf(stderr,"DLL ERROR: getting point count\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"successfully written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for writing %scompressed\n", taketime()-start_time, (compress ? "" : "un")); + + } // end of EXAMPLE_THREE + + if (EXAMPLE == EXAMPLE_FOUR) + { + fprintf(stderr,"running EXAMPLE_FOUR (reading area-of-interest from a file exploiting possibly existing spatial indexing information)\n"); + + // create the reader + + laszip_POINTER laszip_reader; + if (laszip_create(&laszip_reader)) + { + fprintf(stderr,"DLL ERROR: creating laszip reader\n"); + byebye(true, argc==1); + } + + // signal that spatial queries are coming + + laszip_BOOL exploit = 1; + if (laszip_exploit_spatial_index(laszip_reader, exploit)) + { + fprintf(stderr,"DLL ERROR: signaling laszip reader that spatial queries are coming for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + // open the reader + + laszip_BOOL is_compressed = 0; + if (laszip_open_reader(laszip_reader, file_name_in, &is_compressed)) + { + fprintf(stderr,"DLL ERROR: opening laszip reader for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' is %scompressed\n", file_name_in, (is_compressed ? "" : "un")); + + // check whether spatial indexing information is available + + laszip_BOOL is_indexed = 0; + laszip_BOOL is_appended = 0; + if (laszip_has_spatial_index(laszip_reader, &is_indexed, &is_appended)) + { + fprintf(stderr,"DLL ERROR: checking laszip reader whether spatial indexing information is present for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' does %shave spatial indexing information\n", file_name_in, (is_indexed ? "" : "not ")); + + // get a pointer to the header of the reader that was just populated + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_reader, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // how many points does the file have + + laszip_I64 npoints = (header->number_of_point_records ? header->number_of_point_records : header->extended_number_of_point_records); + + // report how many points the file has + + fprintf(stderr,"file '%s' contains %I64d points\n", file_name_in, npoints); + + // create a rectangular box enclosing a subset of points at the center of the full bounding box + + const laszip_F64 sub = 0.05; + + laszip_F64 mid_x = (header->min_x + header->max_x) / 2; + laszip_F64 mid_y = (header->min_y + header->max_y) / 2; + + laszip_F64 range_x = header->max_x - header->min_x; + laszip_F64 range_y = header->max_y - header->min_y; + + laszip_F64 sub_min_x = mid_x - sub * range_x; + laszip_F64 sub_min_y = mid_y - sub * range_y; + + laszip_F64 sub_max_x = mid_x + sub * range_x; + laszip_F64 sub_max_y = mid_y + sub * range_y; + + // request the reader to only read this specified rectangular subset of points + + laszip_BOOL is_empty = 0; + if (laszip_inside_rectangle(laszip_reader, sub_min_x, sub_min_y, sub_max_x, sub_max_y, &is_empty)) + { + fprintf(stderr,"DLL ERROR: requesting points inside of rectangle [%g,%g] (%g,%g) from laszip reader\n", sub_min_x, sub_min_y, sub_max_x, sub_max_y); + byebye(true, argc==1, laszip_reader); + } + + // get a pointer to the points that will be read + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_reader, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // initialize the header for the writer using the header of the reader + + if (laszip_set_header(laszip_writer, header)) + { + fprintf(stderr,"DLL ERROR: setting header for laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // open the writer + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // read the points + + laszip_BOOL is_done = 0; + laszip_I64 p_count = 0; + + while (p_count < npoints) + { + // read a point + + if (laszip_read_inside_point(laszip_reader, &is_done)) + { + fprintf(stderr,"DLL ERROR: reading point %I64d\n", p_count); + byebye(true, argc==1, laszip_reader); + } + + // are we done reading + + if (is_done) + { + break; + } + + // copy the point + + if (laszip_set_point(laszip_writer, point)) + { + fprintf(stderr,"DLL ERROR: setting point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // write the point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // update the inventory + + if (laszip_update_inventory(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: updating inventory for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + } + + fprintf(stderr,"successfully read and written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + // close the reader + + if (laszip_close_reader(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: closing laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // destroy the reader + + if (laszip_destroy(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: destroying laszip reader\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for reading %scompressed and writing %scompressed\n", taketime()-start_time, (is_compressed ? "" : "un"), (compress ? "" : "un")); + + } // end of EXAMPLE_FOUR + + if (EXAMPLE == EXAMPLE_FIVE) + { + fprintf(stderr,"running EXAMPLE_FIVE (reading from one file and writing to another file while simultaneously generating a spatial index)\n"); + + // create the reader + + laszip_POINTER laszip_reader; + if (laszip_create(&laszip_reader)) + { + fprintf(stderr,"DLL ERROR: creating laszip reader\n"); + byebye(true, argc==1); + } + + // open the reader + + laszip_BOOL is_compressed = 0; + if (laszip_open_reader(laszip_reader, file_name_in, &is_compressed)) + { + fprintf(stderr,"DLL ERROR: opening laszip reader for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' is %scompressed\n", file_name_in, (is_compressed ? "" : "un")); + + // get a pointer to the header of the reader that was just populated + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_reader, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // how many points does the file have + + laszip_I64 npoints = (header->number_of_point_records ? header->number_of_point_records : header->extended_number_of_point_records); + + // report how many points the file has + + fprintf(stderr,"file '%s' contains %I64d points\n", file_name_in, npoints); + + // get a pointer to the points that will be read + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_reader, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // initialize the header for the writer using the header of the reader + + if (laszip_set_header(laszip_writer, header)) + { + fprintf(stderr,"DLL ERROR: setting header for laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // enable the creation of spatial indices + + laszip_BOOL create = 1; + laszip_BOOL append = 0; /* not supported yet */ + + if (laszip_create_spatial_index(laszip_writer, create, append)) + { + fprintf(stderr,"DLL ERROR: signaling laszip writer to create spatial indexing information\n"); + byebye(true, argc==1, laszip_writer); + } + + // open the writer + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' spatially indexed and %scompressed\n", file_name_out, (compress ? "" : "un")); + + // read the points + + laszip_I64 p_count = 0; + + while (p_count < npoints) + { + // read a point + + if (laszip_read_point(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: reading point %I64d\n", p_count); + byebye(true, argc==1, laszip_reader); + } + + // copy the point + + if (laszip_set_point(laszip_writer, point)) + { + fprintf(stderr,"DLL ERROR: setting point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // write the point + + if (laszip_write_indexed_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing indexed point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + } + + fprintf(stderr,"successfully read and written %I64d indexed points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + // close the reader + + if (laszip_close_reader(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: closing laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // destroy the reader + + if (laszip_destroy(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: destroying laszip reader\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for reading %scompressed and writing indexed & %scompressed\n", taketime()-start_time, (is_compressed ? "" : "un"), (compress ? "" : "un")); + + } // end of EXAMPLE_FIVE + + if (EXAMPLE == EXAMPLE_SIX) + { + fprintf(stderr,"running EXAMPLE_SIX (writing five points of type 6 to LAS 1.4 without compatibility)\n"); + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // get a pointer to the header of the writer so we can populate it + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_writer, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // populate the header + + header->file_source_ID = 4711; + header->global_encoding = (1<<0) | (1<<4); // see LAS specification for details + header->version_major = 1; + header->version_minor = 4; + strncpy(header->system_identifier, "LASzip DLL example 6", 32); + header->file_creation_day = 30; + header->file_creation_year = 2015; + header->header_size = 375; + header->offset_to_point_data = 375; + header->point_data_format = 6; + header->point_data_record_length = 30; + header->number_of_point_records = 0; // legacy 32-bit counters should be zero for new point types > 5 + for (i = 0; i < 5; i++) + { + header->number_of_points_by_return[i] = 0; + } + header->extended_number_of_point_records = 5; + header->extended_number_of_points_by_return[0] = 1; + header->extended_number_of_points_by_return[1] = 2; + header->extended_number_of_points_by_return[7] = 1; + header->extended_number_of_points_by_return[8] = 1; + header->max_x = 630499.95; + header->min_x = 630498.56; + header->max_y = 4834749.66; + header->min_y = 4834748.73; + header->max_z = 63.68; + header->min_z = 61.33; + + // optional: use the bounding box and the scale factor to create a "good" offset + + if (laszip_auto_offset(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: during automatic offset creation\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data before adding empty OGC WKT VLR is : %d\n", (laszip_I32)header->offset_to_point_data); + + // add intentionally empty OGC WKT + + if (laszip_add_vlr(laszip_writer, "LASF_Projection", 2112, 0, "intentionally empty OGC WKT", 0)) + { + fprintf(stderr,"DLL ERROR: adding intentionally empty OGC WKT VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data before adding funny VLR is : %d\n", (laszip_I32)header->offset_to_point_data); + + // add some funny VLR + + if (laszip_add_vlr(laszip_writer, "funny", 12345, 0, "just a funny VLR", 0)) + { + fprintf(stderr,"DLL ERROR: adding funny VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data after adding VLRs : %d\n", (laszip_I32)header->offset_to_point_data); + + // open the writer + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + // this should fail if compress is true + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // get a pointer to the point of the writer that we will populate and write + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_writer, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // write five points + + laszip_I64 p_count = 0; + laszip_F64 coordinates[3]; + + // populate the first point + + coordinates[0] = 630499.95; + coordinates[1] = 4834749.17; + coordinates[2] = 62.15; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 60; + point->extended_return_number = 2; + point->extended_number_of_returns = 2; + point->classification = 2; // it must be set because it "fits" in 5 bits + point->extended_classification = 2; + point->extended_scan_angle = 3500; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 8; // overflag flag is set + point->gps_time = 53413162.560400; + + // write the first point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the second point + + coordinates[0] = 630499.83; + coordinates[1] = 4834748.88; + coordinates[2] = 62.68; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 90; + point->extended_return_number = 8; + point->extended_number_of_returns = 9; + point->classification = 0; // it must be set to zero as the real value is stored in the extended field + point->extended_classification = 41; + point->extended_scan_angle = 3567; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.563600; + + // write the second point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the third point + + coordinates[0] = 630499.54; + coordinates[1] = 4834749.66; + coordinates[2] = 62.66; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 70; + point->extended_return_number = 9; + point->extended_number_of_returns = 9; + point->classification = 0; // it must be set to zero as the real value is stored in the extended field + point->extended_classification = 42; + point->extended_scan_angle = 3633; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.566800; + + // write the third point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the fourth point + + coordinates[0] = 630498.56; + coordinates[1] = 4834749.41; + coordinates[2] = 63.68; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 20; + point->extended_return_number = 1; + point->extended_number_of_returns = 2; + point->classification = 5; // it must be set because it "fits" in 5 bits + point->extended_classification = 5; + point->extended_scan_angle = 3700; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.580200; + + // write the fourth point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the fifth point + + coordinates[0] = 630498.80; + coordinates[1] = 4834748.73; + coordinates[2] = 62.16; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 110; + point->extended_return_number = 2; + point->extended_number_of_returns = 2; + point->classification = 2; // it must be set because it "fits" in 5 bits + point->extended_classification = 2; + point->extended_scan_angle = 3767; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.580200; + + // write the fifth point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // get the number of points written so far + + if (laszip_get_point_count(laszip_writer, &p_count)) + { + fprintf(stderr,"DLL ERROR: getting point count\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"successfully written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for writing %scompressed\n", taketime()-start_time, (compress ? "" : "un")); + + } // end of EXAMPLE_SIX + + if (EXAMPLE == EXAMPLE_SEVEN) // CHECK + { + fprintf(stderr,"running EXAMPLE_SEVEN (writing five points of type 6 to LAS 1.4 *with* compatibility to compressed LAZ *and* also uncompressed LAS)\n"); + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // get a pointer to the header of the writer so we can populate it + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_writer, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // populate the header + + header->file_source_ID = 4711; + header->global_encoding = (1<<0) | (1<<4); // see LAS specification for details + header->version_major = 1; + header->version_minor = 4; + strncpy(header->system_identifier, "LASzip DLL example 7", 32); + header->file_creation_day = 30; + header->file_creation_year = 2015; + header->header_size = 375; + header->offset_to_point_data = 375; + header->point_data_format = 6; + header->point_data_record_length = 30; + header->number_of_point_records = 0; // legacy 32-bit counters should be zero for new point types > 5 + for (i = 0; i < 5; i++) + { + header->number_of_points_by_return[i] = 0; + } + header->extended_number_of_point_records = 5; + header->extended_number_of_points_by_return[0] = 1; + header->extended_number_of_points_by_return[1] = 2; + header->extended_number_of_points_by_return[7] = 1; + header->extended_number_of_points_by_return[8] = 1; + header->max_x = 630499.95; + header->min_x = 630498.56; + header->max_y = 4834749.66; + header->min_y = 4834748.73; + header->max_z = 63.68; + header->min_z = 61.33; + + // optional: use the bounding box and the scale factor to create a "good" offset + + if (laszip_auto_offset(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: during automatic offset creation\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data before adding empty OGC WKT VLR is : %d\n", (laszip_I32)header->offset_to_point_data); + + // add intentionally empty OGC WKT + + if (laszip_add_vlr(laszip_writer, "LASF_Projection", 2112, 0, "intentionally empty OGC WKT", 0)) + { + fprintf(stderr,"DLL ERROR: adding intentionally empty OGC WKT VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data before adding funny VLR is : %d\n", (laszip_I32)header->offset_to_point_data); + + // add some funny VLR + + if (laszip_add_vlr(laszip_writer, "funny", 12345, 0, "just a funny VLR", 0)) + { + fprintf(stderr,"DLL ERROR: adding funny VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data after adding VLRs : %d\n", (laszip_I32)header->offset_to_point_data); + + // enable the compatibility mode + + laszip_BOOL request = 1; + if (laszip_request_compatibility_mode(laszip_writer, request)) + { + fprintf(stderr,"DLL ERROR: enabling laszip LAS 1.4 compatibility mode\n"); + byebye(true, argc==1, laszip_writer); + } + + // open the writer + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // get a pointer to the point of the writer that we will populate and write + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_writer, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // write five points + + laszip_I64 p_count = 0; + laszip_F64 coordinates[3]; + + // populate the first point + + coordinates[0] = 630499.95; + coordinates[1] = 4834749.17; + coordinates[2] = 62.15; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 60; + point->extended_return_number = 2; + point->extended_number_of_returns = 2; + point->classification = 2; // it must be set because it "fits" in 5 bits + point->extended_classification = 2; + point->extended_scan_angle = 3500; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 8; // overflag flag is set + point->gps_time = 53413162.560400; + + // write the first point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the second point + + coordinates[0] = 630499.83; + coordinates[1] = 4834748.88; + coordinates[2] = 62.68; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 90; + point->extended_return_number = 8; + point->extended_number_of_returns = 9; + point->classification = 0; // it must be set to zero as the real value is stored in the extended field + point->extended_classification = 41; + point->extended_scan_angle = 3567; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.563600; + + // write the second point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the third point + + coordinates[0] = 630499.54; + coordinates[1] = 4834749.66; + coordinates[2] = 62.66; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 70; + point->extended_return_number = 9; + point->extended_number_of_returns = 9; + point->classification = 0; // it must be set to zero as the real value is stored in the extended field + point->extended_classification = 42; + point->extended_scan_angle = 3633; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.566800; + + // write the third point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the fourth point + + coordinates[0] = 630498.56; + coordinates[1] = 4834749.41; + coordinates[2] = 63.68; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 20; + point->extended_return_number = 1; + point->extended_number_of_returns = 2; + point->classification = 5; // it must be set because it "fits" in 5 bits + point->extended_classification = 5; + point->extended_scan_angle = 3700; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.580200; + + // write the fourth point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the fifth point + + coordinates[0] = 630498.80; + coordinates[1] = 4834748.73; + coordinates[2] = 62.16; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 110; + point->extended_return_number = 2; + point->extended_number_of_returns = 2; + point->classification = 2; // it must be set because it "fits" in 5 bits + point->extended_classification = 2; + point->extended_scan_angle = 3767; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.580200; + + // write the fifth point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + + // get the number of points written so far + + if (laszip_get_point_count(laszip_writer, &p_count)) + { + fprintf(stderr,"DLL ERROR: getting point count\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"successfully written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for writing %scompressed\n", taketime()-start_time, (compress ? "" : "un")); + + } // end of EXAMPLE_SEVEN + + if (EXAMPLE == EXAMPLE_EIGHT) + { + fprintf(stderr,"running EXAMPLE_EIGHT (always *with* compatibility mode when reading but when writing *only* for compressed output)\n"); + + // create the reader + + laszip_POINTER laszip_reader; + if (laszip_create(&laszip_reader)) + { + fprintf(stderr,"DLL ERROR: creating laszip reader\n"); + byebye(true, argc==1); + } + + // request compatibility mode for the reader + + laszip_BOOL request_reader = 1; + if (laszip_request_compatibility_mode(laszip_reader, request_reader)) + { + fprintf(stderr,"DLL ERROR: enabling laszip LAS 1.4 compatibility mode for the reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // open the reader + + laszip_BOOL is_compressed = 0; + if (laszip_open_reader(laszip_reader, file_name_in, &is_compressed)) + { + fprintf(stderr,"DLL ERROR: opening laszip reader for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' is %scompressed\n", file_name_in, (is_compressed ? "" : "un")); + + // get a pointer to the header of the reader that was just populated + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_reader, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // how many points does the file have + + laszip_I64 npoints = (header->number_of_point_records ? header->number_of_point_records : header->extended_number_of_point_records); + + // report how many points the file has + + fprintf(stderr,"file '%s' contains %I64d points\n", file_name_in, npoints); + + // get a pointer to the points that will be read + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_reader, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // check if the output is compressed + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + // *only* enable the compatibility mode for the writer for compressed output + + if (compress) + { + laszip_BOOL request_writer = 1; + if (laszip_request_compatibility_mode(laszip_writer, request_writer)) + { + fprintf(stderr,"DLL ERROR: enabling laszip LAS 1.4 compatibility mode for the writer\n"); + byebye(true, argc==1, laszip_writer); + } + } + + // initialize the header for the writer using the header of the reader + + if (laszip_set_header(laszip_writer, header)) + { + fprintf(stderr,"DLL ERROR: setting header for laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // open the writer + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // read the points + + laszip_I64 p_count = 0; + + while (p_count < npoints) + { + // read a point + + if (laszip_read_point(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: reading point %I64\n", p_count); + byebye(true, argc==1, laszip_reader); + } + + // copy the point + + if (laszip_set_point(laszip_writer, point)) + { + fprintf(stderr,"DLL ERROR: setting point %I64\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // write the point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + } + + fprintf(stderr,"successfully read and written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + // close the reader + + if (laszip_close_reader(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: closing laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // destroy the reader + + if (laszip_destroy(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: destroying laszip reader\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for reading %scompressed and writing %scompressed\n", taketime()-start_time, (is_compressed ? "" : "un"), (compress ? "" : "un")); + + } // end of EXAMPLE_EIGHT + + if (EXAMPLE == EXAMPLE_NINE) + { + fprintf(stderr,"running EXAMPLE_NINE (writing LAS 1.4 points with \"extra bytes\" *with* compatibility to compressed LAZ *and* also uncompressed LAS)\n"); + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // get a pointer to the header of the writer so we can populate it + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_writer, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // populate the header + + header->file_source_ID = 4711; + header->global_encoding = (1<<0) | (1<<4); // see LAS specification for details + header->version_major = 1; + header->version_minor = 4; + strncpy(header->system_identifier, "LASzip DLL example 9", 32); + header->file_creation_day = 30; + header->file_creation_year = 2015; + header->header_size = 375; + header->offset_to_point_data = 375; + header->point_data_format = 6; + header->point_data_record_length = 30 + 2 + 1; // three "extra bytes" per point store two additional attributes + header->number_of_point_records = 0; // legacy 32-bit counters should be zero for new point types > 5 + for (i = 0; i < 5; i++) + { + header->number_of_points_by_return[i] = 0; + } + header->extended_number_of_point_records = 5; + header->extended_number_of_points_by_return[0] = 1; + header->extended_number_of_points_by_return[1] = 2; + header->extended_number_of_points_by_return[7] = 1; + header->extended_number_of_points_by_return[8] = 1; + header->max_x = 630499.95; + header->min_x = 630498.56; + header->max_y = 4834749.66; + header->min_y = 4834748.73; + header->max_z = 63.68; + header->min_z = 61.33; + + // optional: use the bounding box and the scale factor to create a "good" offset + + if (laszip_auto_offset(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: during automatic offset creation\n"); + byebye(true, argc==1, laszip_writer); + } + + // add description for the two attributes in the three "extra bytes" + + fprintf(stderr,"offset_to_point_data before adding 'height above ground' is : %d\n", (laszip_I32)header->offset_to_point_data); + + if (laszip_add_attribute(laszip_writer, 3, "height above ground", "quantized to 5 cm above 1s SRTM", 0.05, 0.0)) + { + fprintf(stderr,"DLL ERROR: adding 'height above ground' attribute\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data before adding 'coverage count' is : %d\n", (laszip_I32)header->offset_to_point_data); + + if (laszip_add_attribute(laszip_writer, 0, "coverage count", "by 0.5 m radius of high returns", 1.0, 0.0)) + { + fprintf(stderr,"DLL ERROR: adding 'coverage count' attribute\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data before adding empty OGC WKT VLR is : %d\n", (laszip_I32)header->offset_to_point_data); + + // add intentionally empty OGC WKT + + if (laszip_add_vlr(laszip_writer, "LASF_Projection", 2112, 0, "intentionally empty OGC WKT", 0)) + { + fprintf(stderr,"DLL ERROR: adding intentionally empty OGC WKT VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data before adding funny VLR is : %d\n", (laszip_I32)header->offset_to_point_data); + + // add some funny VLR + + if (laszip_add_vlr(laszip_writer, "funny", 12345, 0, "just a funny VLR", 0)) + { + fprintf(stderr,"DLL ERROR: adding funny VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data after adding VLRs : %d\n", (laszip_I32)header->offset_to_point_data); + + // enable the compatibility mode + + laszip_BOOL request = 1; + if (laszip_request_compatibility_mode(laszip_writer, request)) + { + fprintf(stderr,"DLL ERROR: enabling laszip LAS 1.4 compatibility mode\n"); + byebye(true, argc==1, laszip_writer); + } + + // open the writer + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // get a pointer to the point of the writer that we will populate and write + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_writer, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // write five points + + laszip_I64 p_count = 0; + laszip_F64 coordinates[3]; + + // populate the first point + + coordinates[0] = 630499.95; + coordinates[1] = 4834749.17; + coordinates[2] = 62.15; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 60; + point->extended_return_number = 2; + point->extended_number_of_returns = 2; + point->classification = 2; // it must be set because it "fits" in 5 bits + point->extended_classification = 2; + point->extended_scan_angle = 3500; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 8; // overflag flag is set + point->gps_time = 53413162.560400; + + // set attribute 'height above ground' quantized to 0.05 m + *((laszip_I16*)(point->extra_bytes + 0)) = (laszip_I16)(12.50 / 0.05); + + // set attribute 'coverage count' + *((laszip_U8*)(point->extra_bytes + 2)) = 3; + + // write the first point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the second point + + coordinates[0] = 630499.83; + coordinates[1] = 4834748.88; + coordinates[2] = 62.68; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 90; + point->extended_return_number = 8; + point->extended_number_of_returns = 9; + point->classification = 0; // it must be set to zero as the real value is stored in the extended field + point->extended_classification = 41; + point->extended_scan_angle = 3567; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.563600; + + // set attribute 'height above ground' quantized to 0.05 m + *((laszip_I16*)(point->extra_bytes + 0)) = (laszip_I16)(9.32 / 0.05); + + // set attribute 'coverage count' + *((laszip_U8*)(point->extra_bytes + 2)) = 5; + + // write the second point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the third point + + coordinates[0] = 630499.54; + coordinates[1] = 4834749.66; + coordinates[2] = 62.66; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 70; + point->extended_return_number = 9; + point->extended_number_of_returns = 9; + point->classification = 0; // it must be set to zero as the real value is stored in the extended field + point->extended_classification = 42; + point->extended_scan_angle = 3633; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.566800; + + // set attribute 'height above ground' quantized to 0.05 m + *((laszip_I16*)(point->extra_bytes + 0)) = (laszip_I16)(23.50 / 0.05); + + // set attribute 'coverage count' + *((laszip_U8*)(point->extra_bytes + 2)) = 0; + + // write the third point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the fourth point + + coordinates[0] = 630498.56; + coordinates[1] = 4834749.41; + coordinates[2] = 63.68; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 20; + point->extended_return_number = 1; + point->extended_number_of_returns = 2; + point->classification = 5; // it must be set because it "fits" in 5 bits + point->extended_classification = 5; + point->extended_scan_angle = 3700; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.580200; + + // set attribute 'height above ground' quantized to 0.05 m + *((laszip_I16*)(point->extra_bytes + 0)) = (laszip_I16)(8.65 / 0.05); + + // set attribute 'coverage count' + *((laszip_U8*)(point->extra_bytes + 2)) = 6; + + // write the fourth point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the fifth point + + coordinates[0] = 630498.80; + coordinates[1] = 4834748.73; + coordinates[2] = 62.16; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 110; + point->extended_return_number = 2; + point->extended_number_of_returns = 2; + point->classification = 2; // it must be set because it "fits" in 5 bits + point->extended_classification = 2; + point->extended_scan_angle = 3767; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.580200; + + // set attribute 'height above ground' quantized to 0.05 m + *((laszip_I16*)(point->extra_bytes + 0)) = (laszip_I16)(16.13 / 0.05); + + // set attribute 'coverage count' + *((laszip_U8*)(point->extra_bytes + 2)) = 2; + + // write the fifth point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // get the number of points written so far + + if (laszip_get_point_count(laszip_writer, &p_count)) + { + fprintf(stderr,"DLL ERROR: getting point count\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"successfully written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for writing %scompressed\n", taketime()-start_time, (compress ? "" : "un")); + + } // end of EXAMPLE_NINE + + if (EXAMPLE == EXAMPLE_TEN) + { + fprintf(stderr,"running EXAMPLE_TEN (read LAS 1.0-1.3 file, upconvert old to new point types, write LAS 1.4 compatibility mode *only* for compressed output)\n"); + + // create the reader + + laszip_POINTER laszip_reader; + if (laszip_create(&laszip_reader)) + { + fprintf(stderr,"DLL ERROR: creating laszip reader\n"); + byebye(true, argc==1); + } + + // enable the compatibility mode for the reader + + laszip_BOOL request_reader = 1; + if (laszip_request_compatibility_mode(laszip_reader, request_reader)) + { + fprintf(stderr,"DLL ERROR: enabling laszip LAS 1.4 compatibility mode for the reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // open the reader + + laszip_BOOL is_compressed = 0; + if (laszip_open_reader(laszip_reader, file_name_in, &is_compressed)) + { + fprintf(stderr,"DLL ERROR: opening laszip reader for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' is %scompressed\n", file_name_in, (is_compressed ? "" : "un")); + + // get a pointer to the header of the reader that was just populated + + laszip_header* header_read; + + if (laszip_get_header_pointer(laszip_reader, &header_read)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // make sure it is LAS 1.0, LAS 1.1, LAS 1.2, or LAS 1.3 + + if (header_read->version_minor > 3) + { + fprintf(stderr,"USER ERROR: input should be LAS 1.0 to LAS 1.3\n"); + byebye(true, argc==1); + } + + // how many points does the LAS 1.x (x < 4) file have + + laszip_I64 npoints = header_read->number_of_point_records; + + // report how many points the file has + + fprintf(stderr,"file '%s' contains %I64d points\n", file_name_in, npoints); + + // get a pointer to the points that will be read + + laszip_point* point_read; + + if (laszip_get_point_pointer(laszip_reader, &point_read)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // check if the output is compressed + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + // *only* enable the compatibility mode for the writer for compressed output + + if (compress) + { + laszip_BOOL request_writer = 1; + if (laszip_request_compatibility_mode(laszip_writer, request_writer)) + { + fprintf(stderr,"DLL ERROR: enabling laszip LAS 1.4 compatibility mode for the writer\n"); + byebye(true, argc==1, laszip_writer); + } + } + + // get a pointer to the header of the writer so we can populate it + + laszip_header* header_write; + + if (laszip_get_header_pointer(laszip_writer, &header_write)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // copy entries from the reader header to the writer header + + header_write->file_source_ID = header_read->file_source_ID; + header_write->global_encoding = header_read->global_encoding; + header_write->project_ID_GUID_data_1 = header_read->project_ID_GUID_data_1; + header_write->project_ID_GUID_data_2 = header_read->project_ID_GUID_data_2; + header_write->project_ID_GUID_data_3 = header_read->project_ID_GUID_data_3; + memcpy(header_write->project_ID_GUID_data_4, header_read->project_ID_GUID_data_4, 8); + header_write->version_major = header_read->version_major; + header_write->version_minor = header_read->version_minor; + memcpy(header_write->system_identifier, header_read->system_identifier, 32); + memcpy(header_write->generating_software, header_read->generating_software, 32); + header_write->file_creation_day = header_read->file_creation_day; + header_write->file_creation_year = header_read->file_creation_year; + header_write->header_size = header_read->header_size; + header_write->offset_to_point_data = header_read->header_size; /* note !!! */ + header_write->number_of_variable_length_records = header_read->number_of_variable_length_records; + header_write->point_data_format = header_read->point_data_format; + header_write->point_data_record_length = header_read->point_data_record_length; + header_write->number_of_point_records = header_read->number_of_point_records; + for (i = 0; i < 5; i++) + { + header_write->number_of_points_by_return[i] = header_read->number_of_points_by_return[i]; + } + header_write->x_scale_factor = header_read->x_scale_factor; + header_write->y_scale_factor = header_read->y_scale_factor; + header_write->z_scale_factor = header_read->z_scale_factor; + header_write->x_offset = header_read->x_offset; + header_write->y_offset = header_read->y_offset; + header_write->z_offset = header_read->z_offset; + header_write->max_x = header_read->max_x; + header_write->min_x = header_read->min_x; + header_write->max_y = header_read->max_y; + header_write->min_y = header_read->min_y; + header_write->max_z = header_read->max_z; + header_write->min_z = header_read->min_z; + + // LAS 1.3 and higher only + header_write->start_of_waveform_data_packet_record = header_read->start_of_waveform_data_packet_record; + + // we may modify output because we omit any user defined data that may be *before* the header + + header_write->user_data_in_header_size = 0; + if (header_read->user_data_in_header_size) + { + header_write->header_size -= header_read->user_data_in_header_size; + header_write->offset_to_point_data -= header_read->user_data_in_header_size; + fprintf(stderr,"omitting %d bytes of user_data_in_header\n", header_read->user_data_after_header_size); + } + + // add all the VLRs + + if (header_read->number_of_variable_length_records) + { + fprintf(stderr,"offset_to_point_data before adding %u VLRs is : %d\n", header_read->number_of_variable_length_records, (laszip_I32)header_write->offset_to_point_data); + for (i = 0; i < header_read->number_of_variable_length_records; i++) + { + if (laszip_add_vlr(laszip_writer, header_read->vlrs[i].user_id, header_read->vlrs[i].record_id, header_read->vlrs[i].record_length_after_header, header_read->vlrs[i].description, header_read->vlrs[i].data)) + { + fprintf(stderr,"DLL ERROR: adding VLR %u of %u to the header of the laszip writer\n", i+i, header_read->number_of_variable_length_records); + byebye(true, argc==1, laszip_writer); + } + fprintf(stderr,"offset_to_point_data after adding VLR number %u is : %d\n", i+1, (laszip_I32)header_write->offset_to_point_data); + } + } + + // we may modify output because we omit any user defined data that may be *after* the header + + header_write->user_data_after_header_size = 0; + if (header_read->user_data_after_header_size) + { + fprintf(stderr,"omitting %d bytes of user_data_after_header\n", header_read->user_data_after_header_size); + } + + // upgrade the header and the points to LAS 1.4 + + header_write->version_minor = 4; + if (header_read->version_minor == 3) + { + header_write->header_size += 140; + header_write->offset_to_point_data += 140; + } + else + { + header_write->header_size += 148; + header_write->offset_to_point_data += 148; + } + + if (header_read->point_data_format == 0) + { + header_write->point_data_format = 6; + header_write->point_data_record_length += 10; + } + else if (header_read->point_data_format == 1) + { + header_write->point_data_format = 6; + header_write->point_data_record_length += 2; + } + else if (header_read->point_data_format == 2) + { + header_write->point_data_format = 7; + header_write->point_data_record_length += 10; + } + else if (header_read->point_data_format == 3) + { + header_write->point_data_format = 7; + header_write->point_data_record_length += 2; + } + else + { + fprintf(stderr,"USER ERROR: input point type should be 0 to 3 and not %d\n", header_read->point_data_format); + byebye(true, argc==1); + } + + // we do not add EVLRs + header_write->start_of_first_extended_variable_length_record = 0; + header_write->number_of_extended_variable_length_records = 0; + + // zero the legacy counters + header_write->number_of_point_records = 0; + for (i = 0; i < 5; i++) + { + header_write->number_of_points_by_return[i] = 0; + } + + // populate the extended counters + header_write->extended_number_of_point_records = header_read->number_of_point_records; + for (i = 0; i < 5; i++) + { + header_write->extended_number_of_points_by_return[i] = header_read->number_of_points_by_return[i]; + } + for (i = 5; i < 15; i++) + { + header_write->extended_number_of_points_by_return[i] = 0; + } + + // open the writer + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // get a pointer to the point of the writer that we will populate and write + + laszip_point* point_write; + + if (laszip_get_point_pointer(laszip_writer, &point_write)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // read the points + + laszip_I64 p_count = 0; + + while (p_count < npoints) + { + // read a point + + if (laszip_read_point(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: reading point %I64\n", p_count); + byebye(true, argc==1, laszip_reader); + } + + // copy the point + + point_write->X = point_read->X; + point_write->Y = point_read->Y; + point_write->Z = point_read->Z; + point_write->intensity = point_read->intensity; + point_write->scan_direction_flag = point_read->scan_direction_flag; + point_write->edge_of_flight_line = point_read->edge_of_flight_line; + point_write->user_data = point_read->user_data; + point_write->point_source_ID = point_read->point_source_ID; + + point_write->gps_time = point_read->gps_time; + memcpy(point_write->rgb, point_read->rgb, 8); + + point_write->extended_scanner_channel = 0; + point_write->extended_classification_flags = (point_read->withheld_flag << 2) | (point_read->keypoint_flag << 1) | (point_read->synthetic_flag << 0);; + point_write->extended_classification = point_read->classification; + point_write->extended_return_number = point_read->return_number; + point_write->extended_number_of_returns = point_read->number_of_returns; + point_write->extended_scan_angle = (laszip_I16)( (point_read->scan_angle_rank > 0) ? ((1.0 / 0.006 * point_read->scan_angle_rank) + 0.5) : ((1.0 / 0.006 * point_read->scan_angle_rank) - 0.5) ); + + if (point_read->num_extra_bytes) + { + memcpy(point_write->extra_bytes, point_read->extra_bytes, point_read->num_extra_bytes); + } + + // write the point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + } + + fprintf(stderr,"successfully read and written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + // close the reader + + if (laszip_close_reader(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: closing laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // destroy the reader + + if (laszip_destroy(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: destroying laszip reader\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for reading %scompressed and writing %scompressed\n", taketime()-start_time, (is_compressed ? "" : "un"), (compress ? "" : "un")); + + } // end of EXAMPLE_TEN + + if (EXAMPLE == EXAMPLE_ELEVEN) + { + fprintf(stderr,"running EXAMPLE_ELEVEN (writing points to LAS 1.4 without a-priori knowlegde of bounding box or point count (compatibility only for LAZ))\n"); + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // get a pointer to the header of the writer so we can populate it + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_writer, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // populate the header + + header->file_source_ID = 4711; + header->global_encoding = (1<<0) | (1<<4); // see LAS specification for details + header->version_major = 1; + header->version_minor = 4; + strncpy(header->system_identifier, "LASzip DLL example 7", 32); + header->file_creation_day = 30; + header->file_creation_year = 2015; + header->header_size = 375; + header->offset_to_point_data = 375; + header->point_data_format = 6; + header->point_data_record_length = 30; + header->number_of_point_records = 0; // legacy 32-bit counters should be zero for new point types > 5 + for (i = 0; i < 5; i++) + { + header->number_of_points_by_return[i] = 0; // legacy 32-bit counters should be zero for new point types > 5 + } + header->extended_number_of_point_records = 0; // a-priori unknown number of points + for (i = 0; i < 15; i++) + { + header->extended_number_of_points_by_return[i] = 0; + } + header->max_x = 0.0; // a-priori unknown bounding box + header->min_x = 0.0; + header->max_y = 0.0; + header->min_y = 0.0; + header->max_z = 0.0; + header->min_z = 0.0; + + fprintf(stderr,"offset_to_point_data before adding empty OGC WKT VLR is : %d\n", (laszip_I32)header->offset_to_point_data); + + // add intentionally empty OGC WKT + + if (laszip_add_vlr(laszip_writer, "LASF_Projection", 2112, 0, "intentionally empty OGC WKT", 0)) + { + fprintf(stderr,"DLL ERROR: adding intentionally empty OGC WKT VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data before adding funny VLR is : %d\n", (laszip_I32)header->offset_to_point_data); + + // add some funny VLR + + if (laszip_add_vlr(laszip_writer, "funny", 12345, 0, "just a funny VLR", 0)) + { + fprintf(stderr,"DLL ERROR: adding funny VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data after adding VLRs : %d\n", (laszip_I32)header->offset_to_point_data); + + // compressed output or not? + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + // if compressed output was requested enable the compatibility mode + + if (compress) + { + laszip_BOOL request = 1; + if (laszip_request_compatibility_mode(laszip_writer, request)) + { + fprintf(stderr,"DLL ERROR: enabling laszip LAS 1.4 compatibility mode\n"); + byebye(true, argc==1, laszip_writer); + } + } + + // open the writer + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // get a pointer to the point of the writer that we will populate and write + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_writer, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // write five points + + laszip_I64 p_count = 0; + laszip_F64 coordinates[3]; + + // populate the first point + + coordinates[0] = 630499.95; + coordinates[1] = 4834749.17; + coordinates[2] = 62.15; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 60; + point->extended_return_number = 2; + point->extended_number_of_returns = 2; + point->classification = 2; // it must be set because it "fits" in 5 bits + point->extended_classification = 2; + point->extended_scan_angle = 3500; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 8; // overflag flag is set + point->gps_time = 53413162.560400; + + // write the first point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // update inventory with first point + + if (laszip_update_inventory(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: updating inventory for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + + // populate the second point + + coordinates[0] = 630499.83; + coordinates[1] = 4834748.88; + coordinates[2] = 62.68; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 90; + point->extended_return_number = 8; + point->extended_number_of_returns = 9; + point->classification = 0; // it must be set to zero as the real value is stored in the extended field + point->extended_classification = 41; + point->extended_scan_angle = 3567; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.563600; + + // write the second point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // update inventory with second point + + if (laszip_update_inventory(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: updating inventory for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + + // populate the third point + + coordinates[0] = 630499.54; + coordinates[1] = 4834749.66; + coordinates[2] = 62.66; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 70; + point->extended_return_number = 9; + point->extended_number_of_returns = 9; + point->classification = 0; // it must be set to zero as the real value is stored in the extended field + point->extended_classification = 42; + point->extended_scan_angle = 3633; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.566800; + + // write the third point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // update inventory with third point + + if (laszip_update_inventory(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: updating inventory for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + + // populate the fourth point + + coordinates[0] = 630498.56; + coordinates[1] = 4834749.41; + coordinates[2] = 63.68; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 20; + point->extended_return_number = 1; + point->extended_number_of_returns = 2; + point->classification = 5; // it must be set because it "fits" in 5 bits + point->extended_classification = 5; + point->extended_scan_angle = 3700; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.580200; + + // write the fourth point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // update inventory with fourth point + + if (laszip_update_inventory(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: updating inventory for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + + // populate the fifth point + + coordinates[0] = 630498.80; + coordinates[1] = 4834748.73; + coordinates[2] = 62.16; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 110; + point->extended_return_number = 2; + point->extended_number_of_returns = 2; + point->classification = 2; // it must be set because it "fits" in 5 bits + point->extended_classification = 2; + point->extended_scan_angle = 3767; + point->extended_scanner_channel = 1; + point->extended_classification_flags = 0; // no flag is not set + point->gps_time = 53413162.580200; + + // write the fifth point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // update inventory with fifth point + + if (laszip_update_inventory(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: updating inventory for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + + // get the number of points written so far + + if (laszip_get_point_count(laszip_writer, &p_count)) + { + fprintf(stderr,"DLL ERROR: getting point count\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"successfully written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for writing %scompressed\n", taketime()-start_time, (compress ? "" : "un")); + + } // end of EXAMPLE_ELEVEN + + if (EXAMPLE == EXAMPLE_TWELVE) + { + fprintf(stderr,"running EXAMPLE_TWELVE (changing chunk size to 5000 *with* compatibility when writing *only* for compressed output)\n"); + + // create the reader + + laszip_POINTER laszip_reader; + if (laszip_create(&laszip_reader)) + { + fprintf(stderr,"DLL ERROR: creating laszip reader\n"); + byebye(true, argc==1); + } + + // enable the compatibility mode for the reader + + laszip_BOOL request_reader = 1; + if (laszip_request_compatibility_mode(laszip_reader, request_reader)) + { + fprintf(stderr,"DLL ERROR: enabling laszip LAS 1.4 compatibility mode for the reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // open the reader + + laszip_BOOL is_compressed = 0; + if (laszip_open_reader(laszip_reader, file_name_in, &is_compressed)) + { + fprintf(stderr,"DLL ERROR: opening laszip reader for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' is %scompressed\n", file_name_in, (is_compressed ? "" : "un")); + + // get a pointer to the header of the reader that was just populated + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_reader, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // how many points does the file have + + laszip_I64 npoints = (header->number_of_point_records ? header->number_of_point_records : header->extended_number_of_point_records); + + // report how many points the file has + + fprintf(stderr,"file '%s' contains %I64d points\n", file_name_in, npoints); + + // get a pointer to the points that will be read + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_reader, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // check if the output is compressed + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + // *only* enable the compatibility mode for the writer for compressed output + + if (compress) + { + laszip_BOOL request_writer = 1; + if (laszip_request_compatibility_mode(laszip_writer, request_writer)) + { + fprintf(stderr,"DLL ERROR: enabling laszip LAS 1.4 compatibility mode for the writer\n"); + byebye(true, argc==1, laszip_writer); + } + } + + // initialize the header for the writer using the header of the reader + + if (laszip_set_header(laszip_writer, header)) + { + fprintf(stderr,"DLL ERROR: setting header for laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // change the chunk size from the default value to 50000 + + if (laszip_set_chunk_size(laszip_writer, 5000)) + { + fprintf(stderr,"DLL ERROR: setting chunk size 5000 for laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // open the writer + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // read the points + + laszip_I64 p_count = 0; + + while (p_count < npoints) + { + // read a point + + if (laszip_read_point(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: reading point %I64\n", p_count); + byebye(true, argc==1, laszip_reader); + } + + // copy the point + + if (laszip_set_point(laszip_writer, point)) + { + fprintf(stderr,"DLL ERROR: setting point %I64\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // write the point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + } + + fprintf(stderr,"successfully read and written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + // close the reader + + if (laszip_close_reader(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: closing laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // destroy the reader + + if (laszip_destroy(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: destroying laszip reader\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for reading %scompressed and writing %scompressed\n", taketime()-start_time, (is_compressed ? "" : "un"), (compress ? "" : "un")); + + } // end of EXAMPLE_TWELVE + + if (EXAMPLE == EXAMPLE_THIRTEEN) + { + fprintf(stderr,"running EXAMPLE_THIRTEEN (*with* compatibility mode when reading compressed LAS 1.4 and *with* native extension when writing compressed LAS 1.4)\n"); + + // create the reader + + laszip_POINTER laszip_reader; + if (laszip_create(&laszip_reader)) + { + fprintf(stderr,"DLL ERROR: creating laszip reader\n"); + byebye(true, argc==1); + } + + // request compatibility mode for the reader + + laszip_BOOL request_compatibility = 1; + if (laszip_request_compatibility_mode(laszip_reader, request_compatibility)) + { + fprintf(stderr,"DLL ERROR: requesting LAS 1.4 compatibility mode for the reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // open the reader + + laszip_BOOL is_compressed = 0; + if (laszip_open_reader(laszip_reader, file_name_in, &is_compressed)) + { + fprintf(stderr,"DLL ERROR: opening laszip reader for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' is %scompressed\n", file_name_in, (is_compressed ? "" : "un")); + + // get a pointer to the header of the reader that was just populated + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_reader, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // how many points does the file have + + laszip_I64 npoints = (header->number_of_point_records ? header->number_of_point_records : header->extended_number_of_point_records); + + // report how many points the file has + + fprintf(stderr,"file '%s' contains %I64d points\n", file_name_in, npoints); + + // get a pointer to the points that will be read + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_reader, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // request native extension for the writer + + laszip_BOOL request_native = 1; + if (laszip_request_native_extension(laszip_writer, request_native)) + { + fprintf(stderr,"DLL ERROR: requesting native LAS 1.4 extension for the writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // initialize the header for the writer using the header of the reader + + if (laszip_set_header(laszip_writer, header)) + { + fprintf(stderr,"DLL ERROR: setting header for laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // check if the output is compressed + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + // open the writer + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // read the points + + laszip_I64 p_count = 0; + + while (p_count < npoints) + { + // read a point + + if (laszip_read_point(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: reading point %I64\n", p_count); + byebye(true, argc==1, laszip_reader); + } + + // copy the point + + if (laszip_set_point(laszip_writer, point)) + { + fprintf(stderr,"DLL ERROR: setting point %I64\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // write the point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + } + + fprintf(stderr,"successfully read and written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + // close the reader + + if (laszip_close_reader(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: closing laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // destroy the reader + + if (laszip_destroy(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: destroying laszip reader\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for reading %scompressed and writing %scompressed\n", taketime()-start_time, (is_compressed ? "" : "un"), (compress ? "" : "un")); + + } // end of EXAMPLE_THIRTEEN + + if (EXAMPLE == EXAMPLE_FOURTEEN) + { + fprintf(stderr,"running EXAMPLE_FOURTEEN (selective decompression of XYZ when reading native compressed LAS 1.4\n"); + + // create the reader + + laszip_POINTER laszip_reader; + if (laszip_create(&laszip_reader)) + { + fprintf(stderr,"DLL ERROR: creating laszip reader\n"); + byebye(true, argc==1); + } + + // request compatibility mode for the reader + + laszip_BOOL request_compatibility = 1; + if (laszip_request_compatibility_mode(laszip_reader, request_compatibility)) + { + fprintf(stderr,"DLL ERROR: requesting LAS 1.4 compatibility mode for the reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // request compatibility mode for the reader + + laszip_U32 decompress_selective = laszip_DECOMPRESS_SELECTIVE_CHANNEL_RETURNS_XY | laszip_DECOMPRESS_SELECTIVE_Z; + if (laszip_decompress_selective(laszip_reader, decompress_selective)) + { + fprintf(stderr,"DLL ERROR: decompressing XYZ selectively for the reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // open the reader + + laszip_BOOL is_compressed = 0; + if (laszip_open_reader(laszip_reader, file_name_in, &is_compressed)) + { + fprintf(stderr,"DLL ERROR: opening laszip reader for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' is %scompressed\n", file_name_in, (is_compressed ? "" : "un")); + + // get a pointer to the header of the reader that was just populated + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_reader, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // how many points does the file have + + laszip_I64 npoints = (header->number_of_point_records ? header->number_of_point_records : header->extended_number_of_point_records); + + // report how many points the file has + + fprintf(stderr,"file '%s' contains %I64d points\n", file_name_in, npoints); + + // get a pointer to the points that will be read + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_reader, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // request native extension for the writer + + laszip_BOOL request_native = 1; + if (laszip_request_native_extension(laszip_writer, request_native)) + { + fprintf(stderr,"DLL ERROR: requesting native LAS 1.4 extension for the writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // initialize the header for the writer using the header of the reader + + if (laszip_set_header(laszip_writer, header)) + { + fprintf(stderr,"DLL ERROR: setting header for laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // check if the output is compressed + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + // open the writer + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // read the points + + laszip_I64 p_count = 0; + + while (p_count < npoints) + { + // read a point + + if (laszip_read_point(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: reading point %I64\n", p_count); + byebye(true, argc==1, laszip_reader); + } + + // copy the point + + if (laszip_set_point(laszip_writer, point)) + { + fprintf(stderr,"DLL ERROR: setting point %I64\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + // write the point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + } + + fprintf(stderr,"successfully read and written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + // close the reader + + if (laszip_close_reader(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: closing laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // destroy the reader + + if (laszip_destroy(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: destroying laszip reader\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for reading %scompressed and writing %scompressed\n", taketime()-start_time, (is_compressed ? "" : "un"), (compress ? "" : "un")); + + } // end of EXAMPLE_FOURTEEN + + if (EXAMPLE == EXAMPLE_FIFTEEN) + { + fprintf(stderr,"running EXAMPLE_FIFETEEN (reading and writing native compressed LAS 1.4 by copying the points)\n"); + + // create the reader + + laszip_POINTER laszip_reader; + if (laszip_create(&laszip_reader)) + { + fprintf(stderr,"DLL ERROR: creating laszip reader\n"); + byebye(true, argc==1); + } + + // request compatibility mode for the reader + + laszip_BOOL request_compatibility = 1; + if (laszip_request_compatibility_mode(laszip_reader, request_compatibility)) + { + fprintf(stderr,"DLL ERROR: requesting LAS 1.4 compatibility mode for the reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // open the reader + + laszip_BOOL is_compressed = 0; + if (laszip_open_reader(laszip_reader, file_name_in, &is_compressed)) + { + fprintf(stderr,"DLL ERROR: opening laszip reader for '%s'\n", file_name_in); + byebye(true, argc==1, laszip_reader); + } + + fprintf(stderr,"file '%s' is %scompressed\n", file_name_in, (is_compressed ? "" : "un")); + + // get a pointer to the header of the reader that was just populated + + laszip_header* header_read; + + if (laszip_get_header_pointer(laszip_reader, &header_read)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // how many points does the file have + + laszip_I64 npoints = (header_read->number_of_point_records ? header_read->number_of_point_records : header_read->extended_number_of_point_records); + + // report how many points the file has + + fprintf(stderr,"file '%s' contains %I64d points\n", file_name_in, npoints); + + // get a pointer to the points that will be read + + laszip_point* point_read; + + if (laszip_get_point_pointer(laszip_reader, &point_read)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // get a pointer to the header of the writer so we can populate it + + laszip_header* header_write; + + if (laszip_get_header_pointer(laszip_writer, &header_write)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // populate the header + + header_write->file_source_ID = header_read->file_source_ID; + header_write->global_encoding = header_read->global_encoding; + header_write->version_major = header_read->version_major; + header_write->version_minor = header_read->version_minor; + strncpy(header_write->system_identifier, "LASzip DLL example 15", 32); + header_write->file_creation_day = header_read->file_creation_day; + header_write->file_creation_year = header_read->file_creation_year; + header_write->header_size = header_read->header_size; + header_write->offset_to_point_data = header_read->header_size; // real offset_to_point_data is calculated when adding VLRs + header_write->point_data_format = header_read->point_data_format; + header_write->point_data_record_length = header_read->point_data_record_length; + if ((header_read->point_data_format > 5) || (header_read->extended_number_of_point_records > (2<<32-1))) + { + // legacy 32-bit counters should be zero for new point types > 5 or if there are more than 2<<32-1 points + header_write->number_of_point_records = 0; + for (i = 0; i < 5; i++) + { + header_write->number_of_points_by_return[i] = 0; + } + } + else + { + // legacy 32-bit counters should be populated + header_write->number_of_point_records = (header_read->number_of_point_records ? header_read->number_of_point_records : (laszip_U32)(header_read->extended_number_of_point_records)); + for (i = 0; i < 5; i++) + { + header_write->number_of_points_by_return[i] = (header_read->number_of_points_by_return[i] ? header_read->number_of_points_by_return[i] : (laszip_U32)(header_read->extended_number_of_points_by_return[i])); + } + } + header_write->extended_number_of_point_records = header_read->extended_number_of_point_records; + for (i = 0; i < 15; i++) + { + header_write->extended_number_of_points_by_return[i] = header_read->extended_number_of_points_by_return[i]; + } + header_write->x_scale_factor = header_read->x_scale_factor; + header_write->y_scale_factor = header_read->y_scale_factor; + header_write->z_scale_factor = header_read->z_scale_factor; + header_write->x_offset = header_read->x_offset; + header_write->y_offset = header_read->y_offset; + header_write->z_offset = header_read->z_offset; + header_write->max_x = header_read->max_x; + header_write->min_x = header_read->min_x; + header_write->max_y = header_read->max_y; + header_write->min_y = header_read->min_y; + header_write->max_z = header_read->max_z; + header_write->min_z = header_read->min_z; + + // extended VLRs or Waveforms are not supported yet + + header_write->start_of_waveform_data_packet_record = 0; + header_write->number_of_extended_variable_length_records = 0; + header_write->start_of_first_extended_variable_length_record = 0; + + // add VLRs + + for (i = 0; i < header_read->number_of_variable_length_records; i++) + { + if (laszip_add_vlr(laszip_writer, header_read->vlrs[i].user_id, header_read->vlrs[i].record_id, header_read->vlrs[i].record_length_after_header, header_read->vlrs[i].description, header_read->vlrs[i].data)) + { + fprintf(stderr,"DLL ERROR: adding VLR[%d] to the header\n", i); + byebye(true, argc==1, laszip_writer); + } + fprintf(stderr,"offset_to_point_data after adding VLR[%d] : %d\n", i, (laszip_I32)header_write->offset_to_point_data); + } + + // open the writer + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // get a pointer to the point of the writer that we will populate and write + + laszip_point* point_write; + + if (laszip_get_point_pointer(laszip_writer, &point_write)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // read and write all the points + + laszip_I64 p_count = 0; + + while (p_count < npoints) + { + // read a point + + if (laszip_read_point(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: reading point %I64\n", p_count); + byebye(true, argc==1, laszip_reader); + } + + // copy the point + + point_write->X = point_read->X; + point_write->Y = point_read->Y; + point_write->Z = point_read->Z; + point_write->intensity = point_read->intensity; + point_write->scan_direction_flag = point_read->scan_direction_flag; + point_write->edge_of_flight_line = point_read->edge_of_flight_line; + point_write->withheld_flag = point_read->withheld_flag; + point_write->keypoint_flag = point_read->keypoint_flag; + point_write->synthetic_flag = point_read->synthetic_flag; + point_write->classification = point_read->classification; + point_write->user_data = point_read->user_data; + point_write->point_source_ID = point_read->point_source_ID; + + point_write->gps_time = point_read->gps_time; + memcpy(point_write->rgb, point_read->rgb, 8); + + if (point_write->extended_point_type) + { + point_write->extended_scanner_channel = point_read->extended_scanner_channel; + point_write->extended_classification_flags = point_read->extended_classification_flags; + point_write->extended_classification = point_read->extended_classification; + point_write->extended_return_number = point_read->extended_return_number; + point_write->extended_number_of_returns = point_read->extended_number_of_returns; + point_write->extended_scan_angle = point_read->extended_scan_angle; + } + else + { + point_write->return_number = point_read->return_number; + point_write->number_of_returns = point_read->number_of_returns; + point_write->scan_angle_rank = point_read->scan_angle_rank; + } + + if (point_read->num_extra_bytes) + { + memcpy(point_write->extra_bytes, point_read->extra_bytes, point_read->num_extra_bytes); + } + + // write the point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + p_count++; + } + + fprintf(stderr,"successfully read and written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + // close the reader + + if (laszip_close_reader(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: closing laszip reader\n"); + byebye(true, argc==1, laszip_reader); + } + + // destroy the reader + + if (laszip_destroy(laszip_reader)) + { + fprintf(stderr,"DLL ERROR: destroying laszip reader\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for reading %scompressed and writing %scompressed\n", taketime()-start_time, (is_compressed ? "" : "un"), (compress ? "" : "un")); + + } // end of EXAMPLE_FIFTEEN + + if (EXAMPLE == EXAMPLE_SIXTEEN) + { + fprintf(stderr,"running EXAMPLE_SIXTEEN (writing three points of type 6 to LAS 1.4 file)\n"); + + // create the writer + + laszip_POINTER laszip_writer; + if (laszip_create(&laszip_writer)) + { + fprintf(stderr,"DLL ERROR: creating laszip writer\n"); + byebye(true, argc==1); + } + + // get a pointer to the header of the writer so we can populate it + + laszip_header* header; + + if (laszip_get_header_pointer(laszip_writer, &header)) + { + fprintf(stderr,"DLL ERROR: getting header pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // populate the header + + header->file_source_ID = 4711; + header->global_encoding = 0x0001; // time stamps are in adjusted standard GPS time + header->version_major = 1; + header->version_minor = 4; + strncpy(header->system_identifier, "LASzip DLL example 16", 32); + header->file_creation_day = 120; + header->file_creation_year = 2018; + header->header_size = 375; // must be 375 for LAS 1.4 + header->offset_to_point_data = 375; // must be at least 375 for LAS 1.4 + header->number_of_variable_length_records = 0; + header->point_data_format = 6; + header->point_data_record_length = 30; + header->number_of_point_records = 0; // must be zero for point type 6 or higher + header->number_of_points_by_return[0] = 0; // must be zero for point type 6 or higher + header->number_of_points_by_return[1] = 0; // must be zero for point type 6 or higher + header->number_of_points_by_return[2] = 0; // must be zero for point type 6 or higher + header->number_of_points_by_return[3] = 0; // must be zero for point type 6 or higher + header->number_of_points_by_return[4] = 0; // must be zero for point type 6 or higher + header->max_x = 630499.95; + header->min_x = 630498.56; + header->max_y = 4834749.66; + header->min_y = 4834748.73; + header->max_z = 63.68; + header->min_z = 61.33; + header->extended_number_of_point_records = 3; + header->extended_number_of_points_by_return[0] = 2; + header->extended_number_of_points_by_return[1] = 1; + header->extended_number_of_points_by_return[2] = 0; + header->extended_number_of_points_by_return[3] = 0; + header->extended_number_of_points_by_return[4] = 0; + header->extended_number_of_points_by_return[5] = 0; + header->extended_number_of_points_by_return[6] = 0; + header->extended_number_of_points_by_return[7] = 0; + header->extended_number_of_points_by_return[8] = 0; + header->extended_number_of_points_by_return[9] = 0; + header->extended_number_of_points_by_return[10] = 0; + header->extended_number_of_points_by_return[11] = 0; + header->extended_number_of_points_by_return[12] = 0; + header->extended_number_of_points_by_return[13] = 0; + header->extended_number_of_points_by_return[14] = 0; + + // optional: use the bounding box and the scale factor to create a "good" offset + + if (laszip_auto_offset(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: during automatic offset creation\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data before adding funny VLR is : %d\n", (laszip_I32)header->offset_to_point_data); + + // add some funny VLR + + if (laszip_add_vlr(laszip_writer, "funny", 12345, 0, "just a funny VLR", 0)) + { + fprintf(stderr,"DLL ERROR: adding funny VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + // create the geokeys with the projection information + + laszip_geokey_struct key_entries[5]; + + // projected coordinates + key_entries[0].key_id = 1024; // GTModelTypeGeoKey + key_entries[0].tiff_tag_location = 0; + key_entries[0].count = 1; + key_entries[0].value_offset = 1; // ModelTypeProjected + + // projection + key_entries[1].key_id = 3072; // ProjectedCSTypeGeoKey + key_entries[1].tiff_tag_location = 0; + key_entries[1].count = 1; + key_entries[1].value_offset = 32613; // PCS_WGS84_UTM_zone_13N + + // horizontal units + key_entries[2].key_id = 3076; // ProjLinearUnitsGeoKey + key_entries[2].tiff_tag_location = 0; + key_entries[2].count = 1; + key_entries[2].value_offset = 9001; // meters + + // vertical units + key_entries[3].key_id = 4099; // VerticalUnitsGeoKey + key_entries[3].tiff_tag_location = 0; + key_entries[3].count = 1; + key_entries[3].value_offset = 9001; // meters + + // vertical datum + key_entries[4].key_id = 4096; // VerticalCSTypeGeoKey + key_entries[4].tiff_tag_location = 0; + key_entries[4].count = 1; + key_entries[4].value_offset = 5030; // WGS84 + + // add the geokeys (create or replace the appropriate VLR) + + fprintf(stderr,"offset_to_point_data before adding projection VLR : %d\n", (laszip_I32)header->offset_to_point_data); + + if (laszip_set_geokeys(laszip_writer, 5, key_entries)) + { + fprintf(stderr,"DLL ERROR: adding funny VLR to the header\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"offset_to_point_data after adding two VLRs : %d\n", (laszip_I32)header->offset_to_point_data); + + // open the writer + + laszip_BOOL compress = (strstr(file_name_out, ".laz") != 0); + + if (laszip_open_writer(laszip_writer, file_name_out, compress)) + { + fprintf(stderr,"DLL ERROR: opening laszip writer for '%s'\n", file_name_out); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"writing file '%s' %scompressed\n", file_name_out, (compress ? "" : "un")); + + // get a pointer to the point of the writer that we will populate and write + + laszip_point* point; + + if (laszip_get_point_pointer(laszip_writer, &point)) + { + fprintf(stderr,"DLL ERROR: getting point pointer from laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // write three points + + laszip_I64 p_count = 0; + laszip_F64 coordinates[3]; + + // populate the first point + + coordinates[0] = 630499.95; + coordinates[1] = 4834749.17; + coordinates[2] = 63.15; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 60; + point->extended_return_number = 1; + point->extended_number_of_returns = 2; + point->extended_classification = 1; + point->extended_classification_flags = 0; // none + point->extended_scan_angle = (laszip_I16)((21.0/0.006) + 0.5); + point->gps_time = 1132762996.478024; + + // write the first point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the second point + + coordinates[0] = 630499.83; + coordinates[1] = 4834748.88; + coordinates[2] = 62.18; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 90; + point->extended_return_number = 2; + point->extended_number_of_returns = 2; + point->extended_classification = 1; + point->extended_classification_flags = 0x4 | 0x2 | 0x1; // withheld, keypoint, synthetic flag + point->extended_scan_angle = (laszip_I16)((21.0/0.006) + 0.5); + point->gps_time = 1132762996.478024; + + // write the second point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // populate the third point + + coordinates[0] = 630499.54; + coordinates[1] = 4834749.66; + coordinates[2] = 62.66; + + if (laszip_set_coordinates(laszip_writer, coordinates)) + { + fprintf(stderr,"DLL ERROR: setting coordinates for point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + + point->intensity = 70; + point->extended_return_number = 1; + point->extended_number_of_returns = 1; + point->extended_classification = 2; + point->extended_classification_flags = 0x8 | 0x4 | 0x2 | 0x1; // overlap, withheld, keypoint, synthetic flag + point->extended_scan_angle = (laszip_I16)((21.25/0.006) + 0.5); + point->gps_time = 1132762996.476224; + + // write the third point + + if (laszip_write_point(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: writing point %I64d\n", p_count); + byebye(true, argc==1, laszip_writer); + } + p_count++; + + // get the number of points written so far + + if (laszip_get_point_count(laszip_writer, &p_count)) + { + fprintf(stderr,"DLL ERROR: getting point count\n"); + byebye(true, argc==1, laszip_writer); + } + + fprintf(stderr,"successfully written %I64d points\n", p_count); + + // close the writer + + if (laszip_close_writer(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: closing laszip writer\n"); + byebye(true, argc==1, laszip_writer); + } + + // destroy the writer + + if (laszip_destroy(laszip_writer)) + { + fprintf(stderr,"DLL ERROR: destroying laszip writer\n"); + byebye(true, argc==1); + } + + fprintf(stderr,"total time: %g sec for writing %scompressed\n", taketime()-start_time, (compress ? "" : "un")); + + } // end of EXAMPLE_SIXTEEN + + // unload LASzip DLL + + if (laszip_unload_dll()) + { + fprintf(stderr,"DLL ERROR: unloading LASzip DLL\n"); + byebye(true, argc==1); + } + + return 0; +} diff --git a/libs/laszip/example/laszipdllexample.dsp b/libs/laszip/example/laszipdllexample.dsp new file mode 100644 index 0000000..2b3e284 --- /dev/null +++ b/libs/laszip/example/laszipdllexample.dsp @@ -0,0 +1,120 @@ +# Microsoft Developer Studio Project File - Name="laszipdllexample" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=laszipdllexample - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "laszipdllexample.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "laszipdllexample.mak" CFG="laszipdllexample - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "laszipdllexample - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "laszipdllexample - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "laszipdllexample - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /w /W0 /GX /O2 /I "..\dll" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=copy Release\laszipdllexample.exe ..\dll\laszipdllexample.exe +# End Special Build Tool + +!ELSEIF "$(CFG)" == "laszipdllexample - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\dll" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# Begin Special Build Tool +SOURCE="$(InputPath)" +PostBuild_Cmds=copy Debug\laszipdllexample.exe ..\dll\laszipdllexample.exe +# End Special Build Tool + +!ENDIF + +# Begin Target + +# Name "laszipdllexample - Win32 Release" +# Name "laszipdllexample - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\dll\laszip_api.c +# End Source File +# Begin Source File + +SOURCE=.\laszipdllexample.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\dll\laszip_api.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/libs/laszip/laszip_api.h b/libs/laszip/laszip_api.h new file mode 100644 index 0000000..7336e8e --- /dev/null +++ b/libs/laszip/laszip_api.h @@ -0,0 +1,621 @@ +/* +=============================================================================== + + FILE: laszip_api.h + + CONTENTS: + + A simple DLL interface to LASzip + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 22 August 2017 -- Add version info. + 4 August 2017 -- 'laszip_set_point_type_and_size()' as minimal setup for ostream writer + 3 August 2017 -- new 'laszip_create_laszip_vlr()' gets VLR as C++ std::vector + 29 July 2017 -- integrating minimal stream-based reading/writing into branch + 20 July 2017 -- Andrew Bell adds support for stream-based reading/writing. + 28 May 2017 -- support for "LAS 1.4 selective decompression" added into DLL API + 25 April 2017 -- adding initial support for new "native LAS 1.4 extension" + 8 January 2017 -- name change from 'laszip_dll.h' and integration Hobu's changes for Unix + 7 January 2017 -- set reserved field in LASzip VLR from 0xAABB to 0x0 + 7 January 2017 -- make scan angle quantization in compatibility mode consistent with LIB + 7 January 2017 -- compatibility mode *decompression* fix for points with waveforms + 23 September 2015 -- correct update of bounding box and counters from inventory on closing + 22 September 2015 -- bug fix for not overwriting description of pre-existing "extra bytes" + 5 September 2015 -- "LAS 1.4 compatibility mode" now allows pre-existing "extra bytes" + 3 August 2015 -- incompatible DLL change for QSI-sponsored "LAS 1.4 compatibility mode" + 8 July 2015 -- adding support for NOAA-sponsored "LAS 1.4 compatibility mode" + 1 April 2015 -- adding exploitation and creation of spatial indexing information + 8 August 2013 -- added laszip_get_coordinates() and laszip_set_coordinates() + 6 August 2013 -- added laszip_auto_offset() and laszip_check_for_integer_overflow() + 31 July 2013 -- added laszip_get_point_count() for FUSION integration + 29 July 2013 -- reorganized to create an easy to use LASzip DLL + +=============================================================================== +*/ + +#ifndef LASZIP_API_H +#define LASZIP_API_H + +#ifdef LASZIP_API_VERSION +#include +#endif + +#ifdef _WIN32 +# ifdef LASZIP_DYN_LINK +# ifdef LASZIP_SOURCE +# define LASZIP_API __declspec(dllexport) +# else +# define LASZIP_API __declspec(dllimport) +# endif +# else +# define LASZIP_API +# endif +#else +# define LASZIP_API +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +/*---------------------------------------------------------------------------*/ +/*--------------- DLL variables to pass data to/from LASzip -----------------*/ +/*---------------------------------------------------------------------------*/ + +#ifdef _WIN32 +typedef int laszip_BOOL; +typedef unsigned char laszip_U8; +typedef unsigned short laszip_U16; +typedef unsigned int laszip_U32; +typedef unsigned __int64 laszip_U64; +typedef char laszip_I8; +typedef short laszip_I16; +typedef int laszip_I32; +typedef __int64 laszip_I64; +typedef char laszip_CHAR; +typedef float laszip_F32; +typedef double laszip_F64; +typedef void* laszip_POINTER; +#else +#include +typedef int laszip_BOOL; +typedef uint8_t laszip_U8; +typedef uint16_t laszip_U16; +typedef uint32_t laszip_U32; +typedef uint64_t laszip_U64; +typedef int8_t laszip_I8; +typedef int16_t laszip_I16; +typedef int32_t laszip_I32; +typedef int64_t laszip_I64; +typedef char laszip_CHAR; +typedef float laszip_F32; +typedef double laszip_F64; +typedef void* laszip_POINTER; +#endif + +typedef struct laszip_geokey +{ + laszip_U16 key_id; + laszip_U16 tiff_tag_location; + laszip_U16 count; + laszip_U16 value_offset; +} laszip_geokey_struct; + +typedef struct laszip_vlr +{ + laszip_U16 reserved; + laszip_CHAR user_id[16]; + laszip_U16 record_id; + laszip_U16 record_length_after_header; + laszip_CHAR description[32]; + laszip_U8* data; +} laszip_vlr_struct; + +typedef struct laszip_header +{ + laszip_U16 file_source_ID; + laszip_U16 global_encoding; + laszip_U32 project_ID_GUID_data_1; + laszip_U16 project_ID_GUID_data_2; + laszip_U16 project_ID_GUID_data_3; + laszip_CHAR project_ID_GUID_data_4[8]; + laszip_U8 version_major; + laszip_U8 version_minor; + laszip_CHAR system_identifier[32]; + laszip_CHAR generating_software[32]; + laszip_U16 file_creation_day; + laszip_U16 file_creation_year; + laszip_U16 header_size; + laszip_U32 offset_to_point_data; + laszip_U32 number_of_variable_length_records; + laszip_U8 point_data_format; + laszip_U16 point_data_record_length; + laszip_U32 number_of_point_records; + laszip_U32 number_of_points_by_return[5]; + laszip_F64 x_scale_factor; + laszip_F64 y_scale_factor; + laszip_F64 z_scale_factor; + laszip_F64 x_offset; + laszip_F64 y_offset; + laszip_F64 z_offset; + laszip_F64 max_x; + laszip_F64 min_x; + laszip_F64 max_y; + laszip_F64 min_y; + laszip_F64 max_z; + laszip_F64 min_z; + + // LAS 1.3 and higher only + laszip_U64 start_of_waveform_data_packet_record; + + // LAS 1.4 and higher only + laszip_U64 start_of_first_extended_variable_length_record; + laszip_U32 number_of_extended_variable_length_records; + laszip_U64 extended_number_of_point_records; + laszip_U64 extended_number_of_points_by_return[15]; + + // optional + laszip_U32 user_data_in_header_size; + laszip_U8* user_data_in_header; + + // optional VLRs + laszip_vlr_struct* vlrs; + + // optional + laszip_U32 user_data_after_header_size; + laszip_U8* user_data_after_header; + +} laszip_header_struct; + +typedef struct laszip_point +{ + laszip_I32 X; + laszip_I32 Y; + laszip_I32 Z; + laszip_U16 intensity; + laszip_U8 return_number : 3; + laszip_U8 number_of_returns : 3; + laszip_U8 scan_direction_flag : 1; + laszip_U8 edge_of_flight_line : 1; + laszip_U8 classification : 5; + laszip_U8 synthetic_flag : 1; + laszip_U8 keypoint_flag : 1; + laszip_U8 withheld_flag : 1; + laszip_I8 scan_angle_rank; + laszip_U8 user_data; + laszip_U16 point_source_ID; + + // LAS 1.4 only + laszip_I16 extended_scan_angle; + laszip_U8 extended_point_type : 2; + laszip_U8 extended_scanner_channel : 2; + laszip_U8 extended_classification_flags : 4; + laszip_U8 extended_classification; + laszip_U8 extended_return_number : 4; + laszip_U8 extended_number_of_returns : 4; + + // for 8 byte alignment of the GPS time + laszip_U8 dummy[7]; + + laszip_F64 gps_time; + laszip_U16 rgb[4]; + laszip_U8 wave_packet[29]; + + laszip_I32 num_extra_bytes; + laszip_U8* extra_bytes; + +} laszip_point_struct; + +/*---------------------------------------------------------------------------*/ +/*------ DLL constants for selective decompression via LASzip DLL -----------*/ +/*---------------------------------------------------------------------------*/ + +#define laszip_DECOMPRESS_SELECTIVE_ALL 0xFFFFFFFF + +#define laszip_DECOMPRESS_SELECTIVE_CHANNEL_RETURNS_XY 0x00000000 +#define laszip_DECOMPRESS_SELECTIVE_Z 0x00000001 +#define laszip_DECOMPRESS_SELECTIVE_CLASSIFICATION 0x00000002 +#define laszip_DECOMPRESS_SELECTIVE_FLAGS 0x00000004 +#define laszip_DECOMPRESS_SELECTIVE_INTENSITY 0x00000008 +#define laszip_DECOMPRESS_SELECTIVE_SCAN_ANGLE 0x00000010 +#define laszip_DECOMPRESS_SELECTIVE_USER_DATA 0x00000020 +#define laszip_DECOMPRESS_SELECTIVE_POINT_SOURCE 0x00000040 +#define laszip_DECOMPRESS_SELECTIVE_GPS_TIME 0x00000080 +#define laszip_DECOMPRESS_SELECTIVE_RGB 0x00000100 +#define laszip_DECOMPRESS_SELECTIVE_NIR 0x00000200 +#define laszip_DECOMPRESS_SELECTIVE_WAVEPACKET 0x00000400 +#define laszip_DECOMPRESS_SELECTIVE_BYTE0 0x00010000 +#define laszip_DECOMPRESS_SELECTIVE_BYTE1 0x00020000 +#define laszip_DECOMPRESS_SELECTIVE_BYTE2 0x00040000 +#define laszip_DECOMPRESS_SELECTIVE_BYTE3 0x00080000 +#define laszip_DECOMPRESS_SELECTIVE_BYTE4 0x00100000 +#define laszip_DECOMPRESS_SELECTIVE_BYTE5 0x00200000 +#define laszip_DECOMPRESS_SELECTIVE_BYTE6 0x00400000 +#define laszip_DECOMPRESS_SELECTIVE_BYTE7 0x00800000 +#define laszip_DECOMPRESS_SELECTIVE_EXTRA_BYTES 0xFFFF0000 + +/*---------------------------------------------------------------------------*/ +/*---------------- DLL functions to manage the LASzip DLL -------------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_version +( + laszip_U8* version_major + , laszip_U8* version_minor + , laszip_U16* version_revision + , laszip_U32* version_build +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_create( + laszip_POINTER* pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_error +( + laszip_POINTER pointer + , laszip_CHAR** error +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_warning +( + laszip_POINTER pointer + , laszip_CHAR** warning +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_clean( + laszip_POINTER pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_destroy( + laszip_POINTER pointer +); + +/*---------------------------------------------------------------------------*/ +/*---------- DLL functions to write and read LAS and LAZ files --------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_header_pointer( + laszip_POINTER pointer + , laszip_header_struct** header_pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_point_pointer( + laszip_POINTER pointer + , laszip_point_struct** point_pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_point_count( + laszip_POINTER pointer + , laszip_I64* count +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_header( + laszip_POINTER pointer + , const laszip_header_struct* header +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_point_type_and_size( + laszip_POINTER pointer + , laszip_U8 point_type + , laszip_U16 point_size +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_check_for_integer_overflow( + laszip_POINTER pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_auto_offset( + laszip_POINTER pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_point( + laszip_POINTER pointer + , const laszip_point_struct* point +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_coordinates( + laszip_POINTER pointer + , const laszip_F64* coordinates +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_coordinates( + laszip_POINTER pointer + , laszip_F64* coordinates +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_geokeys( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_geokey_struct* key_entries +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_geodouble_params( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_F64* geodouble_params +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_geoascii_params( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_CHAR* geoascii_params +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_add_attribute( + laszip_POINTER pointer + , laszip_U32 type + , const laszip_CHAR* name + , const laszip_CHAR* description + , laszip_F64 scale + , laszip_F64 offset +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_add_vlr( + laszip_POINTER pointer + , const laszip_CHAR* user_id + , laszip_U16 record_id + , laszip_U16 record_length_after_header + , const laszip_CHAR* description + , const laszip_U8* data +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_remove_vlr( + laszip_POINTER pointer + , const laszip_CHAR* user_id + , laszip_U16 record_id +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_create_spatial_index( + laszip_POINTER pointer + , const laszip_BOOL create + , const laszip_BOOL append +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_preserve_generating_software( + laszip_POINTER pointer + , const laszip_BOOL preserve +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_request_native_extension( + laszip_POINTER pointer + , const laszip_BOOL request +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_request_compatibility_mode( + laszip_POINTER pointer + , const laszip_BOOL request +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_chunk_size( + laszip_POINTER pointer + , const laszip_U32 chunk_size +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_open_writer( + laszip_POINTER pointer + , const laszip_CHAR* file_name + , laszip_BOOL compress +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_write_point( + laszip_POINTER pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_write_indexed_point( + laszip_POINTER pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_update_inventory( + laszip_POINTER pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_close_writer( + laszip_POINTER pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_exploit_spatial_index( + laszip_POINTER pointer + , const laszip_BOOL exploit +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_decompress_selective( + laszip_POINTER pointer + , const laszip_U32 decompress_selective +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_open_reader( + laszip_POINTER pointer + , const laszip_CHAR* file_name + , laszip_BOOL* is_compressed +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_has_spatial_index( + laszip_POINTER pointer + , laszip_BOOL* is_indexed + , laszip_BOOL* is_appended +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_inside_rectangle( + laszip_POINTER pointer + , laszip_F64 min_x + , laszip_F64 min_y + , laszip_F64 max_x + , laszip_F64 max_y + , laszip_BOOL* is_empty +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_seek_point( + laszip_POINTER pointer + , laszip_I64 index +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_read_point( + laszip_POINTER pointer +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_read_inside_point( + laszip_POINTER pointer + , laszip_BOOL* is_done +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_close_reader( + laszip_POINTER pointer +); + +/*---------------------------------------------------------------------------*/ +/*---------------- DLL functions to load and unload LASzip ------------------*/ +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_load_dll +( +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_unload_dll +( +); + +#ifdef __cplusplus +} // extern "C" + +#if defined(_MSC_VER) && (_MSC_VER < 1300) +#include +#else +#include +#include +using namespace std; +#endif + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_open_reader_stream( + laszip_POINTER pointer + , istream& stream + , laszip_BOOL* is_compressed +); + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_open_writer_stream( + laszip_POINTER pointer + , ostream& stream + , laszip_BOOL compress + , laszip_BOOL do_not_write_header +); + +/*---------------------------------------------------------------------------*/ +// make LASzip VLR for point type and point size already specified earlier +LASZIP_API laszip_I32 +laszip_create_laszip_vlr( + laszip_POINTER pointer + , laszip_U8** vlr + , laszip_U32* vlr_size +); + +#endif // __cplusplus + +#endif /* LASZIP_API_H */ diff --git a/libs/laszip/laszip_api_version.h.in b/libs/laszip/laszip_api_version.h.in new file mode 100644 index 0000000..642db75 --- /dev/null +++ b/libs/laszip/laszip_api_version.h.in @@ -0,0 +1,44 @@ +/* +=============================================================================== + + FILE: laszip_api_version.h + + CONTENTS: + + Version information for LASzip API interface + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 22 August 2017 -- Created + +=============================================================================== +*/ + +#ifndef LASZIP_API_VERSION_H +#define LASZIP_API_VERSION_H + +/* + * version settings + */ +#define LASZIP_API_VERSION_MAJOR @LASZIP_API_VERSION_MAJOR@ +#define LASZIP_API_VERSION_MINOR @LASZIP_API_VERSION_MINOR@ +#define LASZIP_API_VERSION_PATCH @LASZIP_API_VERSION_PATCH@ + +#define HAVE_UNORDERED_MAP @HAVE_UNORDERED_MAP@ + +#endif /* LASZIP_API_VERSION_H */ diff --git a/libs/laszip/src/CMakeLists.txt b/libs/laszip/src/CMakeLists.txt new file mode 100644 index 0000000..e6255c1 --- /dev/null +++ b/libs/laszip/src/CMakeLists.txt @@ -0,0 +1,37 @@ +############################################################################### +# +# src/CMakeLists.txt controls building of laszip library +# +# Copyright (c) 2009 Mateusz Loskot +# +############################################################################### +set(LASZIP_SOURCES + arithmeticdecoder.cpp + arithmeticencoder.cpp + arithmeticmodel.cpp + integercompressor.cpp + lasindex.cpp + lasinterval.cpp + lasquadtree.cpp + lasreaditemcompressed_v1.cpp + lasreaditemcompressed_v2.cpp + lasreaditemcompressed_v3.cpp + lasreaditemcompressed_v4.cpp + lasreadpoint.cpp + laswriteitemcompressed_v1.cpp + laswriteitemcompressed_v2.cpp + laswriteitemcompressed_v3.cpp + laswriteitemcompressed_v4.cpp + laswritepoint.cpp + laszip.cpp + laszip_dll.cpp + mydefs.cpp +) + +add_definitions(-DLASZIPDLL_EXPORTS) +add_definitions(-DUNORDERED) + +if(HAVE_UNORDERED_MAP) + add_definitions(-DHAVE_UNORDERED_MAP=1) +endif(HAVE_UNORDERED_MAP) +LASZIP_ADD_LIBRARY(${LASZIP_BASE_LIB_NAME} ${LASZIP_SOURCES}) diff --git a/libs/laszip/src/arithmeticdecoder.cpp b/libs/laszip/src/arithmeticdecoder.cpp new file mode 100644 index 0000000..23a19f2 --- /dev/null +++ b/libs/laszip/src/arithmeticdecoder.cpp @@ -0,0 +1,342 @@ +/* +=============================================================================== + + FILE: arithmeticdecoder.cpp + + CONTENTS: + + A modular C++ wrapper for an adapted version of Amir Said's FastAC Code. + see: http://www.cipr.rpi.edu/~said/FastAC.html + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2005-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see header file + +=============================================================================== +*/ + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// **************************** - +// ARITHMETIC CODING EXAMPLES - +// **************************** - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// Fast arithmetic coding implementation - +// -> 32-bit variables, 32-bit product, periodic updates, table decoding - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// Version 1.00 - April 25, 2004 - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// WARNING - +// ========= - +// - +// The only purpose of this program is to demonstrate the basic principles - +// of arithmetic coding. The original version of this code can be found in - +// Digital Signal Compression: Principles and Practice - +// (Cambridge University Press, 2011, ISBN: 9780511984655) - +// - +// Copyright (c) 2019 by Amir Said (said@ieee.org) & - +// William A. Pearlman (pearlw@ecse.rpi.edu) - +// - +// Redistribution and use in source and binary forms, with or without - +// modification, are permitted provided that the following conditions are - +// met: - +// - +// 1. Redistributions of source code must retain the above copyright notice, - +// this list of conditions and the following disclaimer. - +// - +// 2. Redistributions in binary form must reproduce the above copyright - +// notice, this list of conditions and the following disclaimer in the - +// documentation and/or other materials provided with the distribution. - +// - +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - +// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// A description of the arithmetic coding method used here is available in - +// - +// Lossless Compression Handbook, ed. K. Sayood - +// Chapter 5: Arithmetic Coding (A. Said), pp. 101-152, Academic Press, 2003 - +// - +// A. Said, Introduction to Arithetic Coding Theory and Practice - +// HP Labs report HPL-2004-76 - http://www.hpl.hp.com/techreports/ - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#include "arithmeticdecoder.hpp" + +#include +#include + +#include "arithmeticmodel.hpp" + +ArithmeticDecoder::ArithmeticDecoder() +{ + instream = 0; +} + +BOOL ArithmeticDecoder::init(ByteStreamIn* instream, BOOL really_init) +{ + if (instream == 0) return FALSE; + this->instream = instream; + length = AC__MaxLength; + if (really_init) + { + value = (instream->getByte() << 24); + value |= (instream->getByte() << 16); + value |= (instream->getByte() << 8); + value |= (instream->getByte()); + } + return TRUE; +} + +void ArithmeticDecoder::done() +{ + instream = 0; +} + +ArithmeticBitModel* ArithmeticDecoder::createBitModel() +{ + ArithmeticBitModel* m = new ArithmeticBitModel(); + return m; +} + +void ArithmeticDecoder::initBitModel(ArithmeticBitModel* m) +{ + m->init(); +} + +void ArithmeticDecoder::destroyBitModel(ArithmeticBitModel* m) +{ + delete m; +} + +ArithmeticModel* ArithmeticDecoder::createSymbolModel(U32 n) +{ + ArithmeticModel* m = new ArithmeticModel(n, FALSE); + return m; +} + +void ArithmeticDecoder::initSymbolModel(ArithmeticModel* m, U32 *table) +{ + m->init(table); +} + +void ArithmeticDecoder::destroySymbolModel(ArithmeticModel* m) +{ + delete m; +} + +U32 ArithmeticDecoder::decodeBit(ArithmeticBitModel* m) +{ + assert(m); + + U32 x = m->bit_0_prob * (length >> BM__LengthShift); // product l x p0 + U32 sym = (value >= x); // decision + // update & shift interval + if (sym == 0) { + length = x; + ++m->bit_0_count; + } + else { + value -= x; // shifted interval base = 0 + length -= x; + } + + if (length < AC__MinLength) renorm_dec_interval(); // renormalization + if (--m->bits_until_update == 0) m->update(); // periodic model update + + return sym; // return data bit value +} + +U32 ArithmeticDecoder::decodeSymbol(ArithmeticModel* m) +{ + U32 n, sym, x, y = length; + + if (m->decoder_table) { // use table look-up for faster decoding + + unsigned dv = value / (length >>= DM__LengthShift); + unsigned t = dv >> m->table_shift; + + sym = m->decoder_table[t]; // initial decision based on table look-up + n = m->decoder_table[t+1] + 1; + + while (n > sym + 1) { // finish with bisection search + U32 k = (sym + n) >> 1; + if (m->distribution[k] > dv) n = k; else sym = k; + } + // compute products + x = m->distribution[sym] * length; + if (sym != m->last_symbol) y = m->distribution[sym+1] * length; + } + + else { // decode using only multiplications + + x = sym = 0; + length >>= DM__LengthShift; + U32 k = (n = m->symbols) >> 1; + // decode via bisection search + do { + U32 z = length * m->distribution[k]; + if (z > value) { + n = k; + y = z; // value is smaller + } + else { + sym = k; + x = z; // value is larger or equal + } + } while ((k = (sym + n) >> 1) != sym); + } + + value -= x; // update interval + length = y - x; + + if (length < AC__MinLength) renorm_dec_interval(); // renormalization + + ++m->symbol_count[sym]; + if (--m->symbols_until_update == 0) m->update(); // periodic model update + + assert(sym < m->symbols); + + return sym; +} + +U32 ArithmeticDecoder::readBit() +{ + U32 sym = value / (length >>= 1); // decode symbol, change length + value -= length * sym; // update interval + + if (length < AC__MinLength) renorm_dec_interval(); // renormalization + + if (sym >= 2) + { + throw 4711; + } + + return sym; +} + +U32 ArithmeticDecoder::readBits(U32 bits) +{ + assert(bits && (bits <= 32)); + + if (bits > 19) + { + U32 tmp = readShort(); + bits = bits - 16; + U32 tmp1 = readBits(bits) << 16; + return (tmp1|tmp); + } + + U32 sym = value / (length >>= bits);// decode symbol, change length + value -= length * sym; // update interval + + if (length < AC__MinLength) renorm_dec_interval(); // renormalization + + if (sym >= (1u<>= 8); // decode symbol, change length + value -= length * sym; // update interval + + if (length < AC__MinLength) renorm_dec_interval(); // renormalization + + if (sym >= (1u<<8)) + { + throw 4711; + } + + return (U8)sym; +} + +U16 ArithmeticDecoder::readShort() +{ + U32 sym = value / (length >>= 16); // decode symbol, change length + value -= length * sym; // update interval + + if (length < AC__MinLength) renorm_dec_interval(); // renormalization + + if (sym >= (1u<<16)) + { + throw 4711; + } + + return (U16)sym; +} + +U32 ArithmeticDecoder::readInt() +{ + U32 lowerInt = readShort(); + U32 upperInt = readShort(); + return (upperInt<<16)|lowerInt; +} + +F32 ArithmeticDecoder::readFloat() /* danger in float reinterpretation */ +{ + U32I32F32 u32i32f32; + u32i32f32.u32 = readInt(); + return u32i32f32.f32; +} + +U64 ArithmeticDecoder::readInt64() +{ + U64 lowerInt = readInt(); + U64 upperInt = readInt(); + return (upperInt<<32)|lowerInt; +} + +F64 ArithmeticDecoder::readDouble() /* danger in float reinterpretation */ +{ + U64I64F64 u64i64f64; + u64i64f64.u64 = readInt64(); + return u64i64f64.f64; +} + +ArithmeticDecoder::~ArithmeticDecoder() +{ +} + +inline void ArithmeticDecoder::renorm_dec_interval() +{ + do { // read least-significant byte + value = (value << 8) | instream->getByte(); + } while ((length <<= 8) < AC__MinLength); // length multiplied by 256 +} diff --git a/libs/laszip/src/arithmeticdecoder.hpp b/libs/laszip/src/arithmeticdecoder.hpp new file mode 100644 index 0000000..9608450 --- /dev/null +++ b/libs/laszip/src/arithmeticdecoder.hpp @@ -0,0 +1,109 @@ +/* +=============================================================================== + + FILE: arithmeticdecoder.hpp + + CONTENTS: + + A modular C++ wrapper for an adapted version of Amir Said's FastAC Code. + see: http://www.cipr.rpi.edu/~said/FastAC.html + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 22 August 2016 -- can be used as init dummy by "native LAS 1.4 compressor" + 13 November 2014 -- integrity check in readBits(), readByte(), readShort() + 6 September 2014 -- removed the (unused) inheritance from EntropyDecoder + 10 January 2011 -- licensing change for LGPL release and liblas integration + 8 December 2010 -- unified framework for all entropy coders + 30 October 2009 -- refactoring Amir Said's FastAC code + +=============================================================================== +*/ +#ifndef ARITHMETIC_DECODER_HPP +#define ARITHMETIC_DECODER_HPP + +#include "mydefs.hpp" +#include "bytestreamin.hpp" + +class ArithmeticModel; +class ArithmeticBitModel; + +class ArithmeticDecoder +{ +public: + +/* Constructor & Destructor */ + ArithmeticDecoder(); + ~ArithmeticDecoder(); + +/* Manage decoding */ + BOOL init(ByteStreamIn* instream, BOOL really_init = TRUE); + void done(); + +/* Manage an entropy model for a single bit */ + ArithmeticBitModel* createBitModel(); + void initBitModel(ArithmeticBitModel* model); + void destroyBitModel(ArithmeticBitModel* model); + +/* Manage an entropy model for n symbols (table optional) */ + ArithmeticModel* createSymbolModel(U32 n); + void initSymbolModel(ArithmeticModel* model, U32* table=0); + void destroySymbolModel(ArithmeticModel* model); + +/* Decode a bit with modelling */ + U32 decodeBit(ArithmeticBitModel* model); + +/* Decode a symbol with modelling */ + U32 decodeSymbol(ArithmeticModel* model); + +/* Decode a bit without modelling */ + U32 readBit(); + +/* Decode bits without modelling */ + U32 readBits(U32 bits); + +/* Decode an unsigned char without modelling */ + U8 readByte(); + +/* Decode an unsigned short without modelling */ + U16 readShort(); + +/* Decode an unsigned int without modelling */ + U32 readInt(); + +/* Decode a float without modelling */ + F32 readFloat(); + +/* Decode an unsigned 64 bit int without modelling */ + U64 readInt64(); + +/* Decode a double without modelling */ + F64 readDouble(); + +/* Only read from instream if ArithmeticDecoder is dummy */ + ByteStreamIn* getByteStreamIn() const { return instream; }; + +private: + + ByteStreamIn* instream; + + void renorm_dec_interval(); + U32 value, length; +}; + +#endif diff --git a/libs/laszip/src/arithmeticencoder.cpp b/libs/laszip/src/arithmeticencoder.cpp new file mode 100644 index 0000000..4ad8c8b --- /dev/null +++ b/libs/laszip/src/arithmeticencoder.cpp @@ -0,0 +1,355 @@ +/* +=============================================================================== + + FILE: arithmeticencoder.cpp + + CONTENTS: + + A modular C++ wrapper for an adapted version of Amir Said's FastAC Code. + see: http://www.cipr.rpi.edu/~said/FastAC.html + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2005-2014, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see header file + +=============================================================================== +*/ + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// **************************** - +// ARITHMETIC CODING EXAMPLES - +// **************************** - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// Fast arithmetic coding implementation - +// -> 32-bit variables, 32-bit product, periodic updates, table decoding - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// Version 1.00 - April 25, 2004 - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// WARNING - +// ========= - +// - +// The only purpose of this program is to demonstrate the basic principles - +// of arithmetic coding. The original version of this code can be found in - +// Digital Signal Compression: Principles and Practice - +// (Cambridge University Press, 2011, ISBN: 9780511984655) - +// - +// Copyright (c) 2019 by Amir Said (said@ieee.org) & - +// William A. Pearlman (pearlw@ecse.rpi.edu) - +// - +// Redistribution and use in source and binary forms, with or without - +// modification, are permitted provided that the following conditions are - +// met: - +// - +// 1. Redistributions of source code must retain the above copyright notice, - +// this list of conditions and the following disclaimer. - +// - +// 2. Redistributions in binary form must reproduce the above copyright - +// notice, this list of conditions and the following disclaimer in the - +// documentation and/or other materials provided with the distribution. - +// - +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - +// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// A description of the arithmetic coding method used here is available in - +// - +// Lossless Compression Handbook, ed. K. Sayood - +// Chapter 5: Arithmetic Coding (A. Said), pp. 101-152, Academic Press, 2003 - +// - +// A. Said, Introduction to Arithetic Coding Theory and Practice - +// HP Labs report HPL-2004-76 - http://www.hpl.hp.com/techreports/ - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#include "arithmeticencoder.hpp" + +#include +#include + +#include + +FILE* file = 0; + +#include "arithmeticmodel.hpp" + +ArithmeticEncoder::ArithmeticEncoder() +{ + outstream = 0; + + outbuffer = (U8*)malloc(sizeof(U8)*2*AC_BUFFER_SIZE); + endbuffer = outbuffer + 2 * AC_BUFFER_SIZE; +} + +ArithmeticEncoder::~ArithmeticEncoder() +{ + free(outbuffer); +} + +BOOL ArithmeticEncoder::init(ByteStreamOut* outstream) +{ + if (outstream == 0) return FALSE; + this->outstream = outstream; + base = 0; + length = AC__MaxLength; + outbyte = outbuffer; + endbyte = endbuffer; + return TRUE; +} + +void ArithmeticEncoder::done() +{ + U32 init_base = base; // done encoding: set final data bytes + BOOL another_byte = TRUE; + + if (length > 2 * AC__MinLength) { + base += AC__MinLength; // base offset + length = AC__MinLength >> 1; // set new length for 1 more byte + } + else { + base += AC__MinLength >> 1; // base offset + length = AC__MinLength >> 9; // set new length for 2 more bytes + another_byte = FALSE; + } + + if (init_base > base) propagate_carry(); // overflow = carry + renorm_enc_interval(); // renormalization = output last bytes + + if (endbyte != endbuffer) + { + assert(outbyte < outbuffer + AC_BUFFER_SIZE); + outstream->putBytes(outbuffer + AC_BUFFER_SIZE, AC_BUFFER_SIZE); + } + U32 buffer_size = (U32)(outbyte - outbuffer); + if (buffer_size) outstream->putBytes(outbuffer, buffer_size); + + // write two or three zero bytes to be in sync with the decoder's byte reads + outstream->putByte(0); + outstream->putByte(0); + if (another_byte) outstream->putByte(0); + + outstream = 0; +} + +ArithmeticBitModel* ArithmeticEncoder::createBitModel() +{ + ArithmeticBitModel* m = new ArithmeticBitModel(); + return m; +} + +void ArithmeticEncoder::initBitModel(ArithmeticBitModel* m) +{ + m->init(); +} + +void ArithmeticEncoder::destroyBitModel(ArithmeticBitModel* m) +{ + delete m; +} + +ArithmeticModel* ArithmeticEncoder::createSymbolModel(U32 n) +{ + ArithmeticModel* m = new ArithmeticModel(n, true); + return m; +} + +void ArithmeticEncoder::initSymbolModel(ArithmeticModel* m, U32* table) +{ + m->init(table); +} + +void ArithmeticEncoder::destroySymbolModel(ArithmeticModel* m) +{ + delete m; +} + +void ArithmeticEncoder::encodeBit(ArithmeticBitModel* m, U32 sym) +{ + assert(m && (sym <= 1)); + + U32 x = m->bit_0_prob * (length >> BM__LengthShift); // product l x p0 + // update interval + if (sym == 0) { + length = x; + ++m->bit_0_count; + } + else { + U32 init_base = base; + base += x; + length -= x; + if (init_base > base) propagate_carry(); // overflow = carry + } + + if (length < AC__MinLength) renorm_enc_interval(); // renormalization + if (--m->bits_until_update == 0) m->update(); // periodic model update +} + +void ArithmeticEncoder::encodeSymbol(ArithmeticModel* m, U32 sym) +{ + assert(m && (sym <= m->last_symbol)); + + U32 x, init_base = base; + // compute products + if (sym == m->last_symbol) { + x = m->distribution[sym] * (length >> DM__LengthShift); + base += x; // update interval + length -= x; // no product needed + } + else { + x = m->distribution[sym] * (length >>= DM__LengthShift); + base += x; // update interval + length = m->distribution[sym+1] * length - x; + } + + if (init_base > base) propagate_carry(); // overflow = carry + if (length < AC__MinLength) renorm_enc_interval(); // renormalization + + ++m->symbol_count[sym]; + if (--m->symbols_until_update == 0) m->update(); // periodic model update +} + +void ArithmeticEncoder::writeBit(U32 sym) +{ + assert(sym < 2); + + U32 init_base = base; + base += sym * (length >>= 1); // new interval base and length + + if (init_base > base) propagate_carry(); // overflow = carry + if (length < AC__MinLength) renorm_enc_interval(); // renormalization +} + +void ArithmeticEncoder::writeBits(U32 bits, U32 sym) +{ + assert(bits && (bits <= 32) && (sym < (1u< 19) + { + writeShort(sym&U16_MAX); + sym = sym >> 16; + bits = bits - 16; + } + + U32 init_base = base; + base += sym * (length >>= bits); // new interval base and length + + if (init_base > base) propagate_carry(); // overflow = carry + if (length < AC__MinLength) renorm_enc_interval(); // renormalization +} + +void ArithmeticEncoder::writeByte(U8 sym) +{ + U32 init_base = base; + base += (U32)(sym) * (length >>= 8); // new interval base and length + + if (init_base > base) propagate_carry(); // overflow = carry + if (length < AC__MinLength) renorm_enc_interval(); // renormalization +} + +void ArithmeticEncoder::writeShort(U16 sym) +{ + U32 init_base = base; + base += (U32)(sym) * (length >>= 16); // new interval base and length + + if (init_base > base) propagate_carry(); // overflow = carry + if (length < AC__MinLength) renorm_enc_interval(); // renormalization +} + +void ArithmeticEncoder::writeInt(U32 sym) +{ + writeShort((U16)(sym & 0xFFFF)); // lower 16 bits + writeShort((U16)(sym >> 16)); // UPPER 16 bits +} + +void ArithmeticEncoder::writeFloat(F32 sym) /* danger in float reinterpretation */ +{ + U32I32F32 u32i32f32; + u32i32f32.f32 = sym; + writeInt(u32i32f32.u32); +} + +void ArithmeticEncoder::writeInt64(U64 sym) +{ + writeInt((U32)(sym & 0xFFFFFFFF)); // lower 32 bits + writeInt((U32)(sym >> 32)); // UPPER 32 bits +} + +void ArithmeticEncoder::writeDouble(F64 sym) /* danger in float reinterpretation */ +{ + U64I64F64 u64i64f64; + u64i64f64.f64 = sym; + writeInt64(u64i64f64.u64); +} + +inline void ArithmeticEncoder::propagate_carry() +{ + U8 * p; + if (outbyte == outbuffer) + p = endbuffer - 1; + else + p = outbyte - 1; + while (*p == 0xFFU) + { + *p = 0; + if (p == outbuffer) + p = endbuffer - 1; + else + p--; + assert(outbuffer <= p); + assert(p < endbuffer); + assert(outbyte < endbuffer); + } + ++*p; +} + +inline void ArithmeticEncoder::renorm_enc_interval() +{ + do { // output and discard top byte + assert(outbuffer <= outbyte); + assert(outbyte < endbuffer); + assert(outbyte < endbyte); + *outbyte++ = (U8)(base >> 24); + if (outbyte == endbyte) manage_outbuffer(); + base <<= 8; + } while ((length <<= 8) < AC__MinLength); // length multiplied by 256 +} + +inline void ArithmeticEncoder::manage_outbuffer() +{ + if (outbyte == endbuffer) outbyte = outbuffer; + outstream->putBytes(outbyte, AC_BUFFER_SIZE); + endbyte = outbyte + AC_BUFFER_SIZE; + assert(endbyte > outbyte); + assert(outbyte < endbuffer); +} diff --git a/libs/laszip/src/arithmeticencoder.hpp b/libs/laszip/src/arithmeticencoder.hpp new file mode 100644 index 0000000..4891611 --- /dev/null +++ b/libs/laszip/src/arithmeticencoder.hpp @@ -0,0 +1,114 @@ +/* +=============================================================================== + + FILE: arithmeticencoder.hpp + + CONTENTS: + + A modular C++ wrapper for an adapted version of Amir Said's FastAC Code. + see: http://www.cipr.rpi.edu/~said/FastAC.html + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 1 July 2016 -- can be used as init dummy by "native LAS 1.4 compressor" + 6 September 2014 -- removed the (unused) inheritance from EntropyEncoder + 10 January 2011 -- licensing change for LGPL release and liblas integration + 8 December 2010 -- unified framework for all entropy coders + 30 October 2009 -- refactoring Amir Said's FastAC code + +=============================================================================== +*/ +#ifndef ARITHMETIC_ENCODER_HPP +#define ARITHMETIC_ENCODER_HPP + +#include "mydefs.hpp" +#include "bytestreamout.hpp" + +class ArithmeticModel; +class ArithmeticBitModel; + +class ArithmeticEncoder +{ +public: + +/* Constructor & Destructor */ + ArithmeticEncoder(); + ~ArithmeticEncoder(); + +/* Manage encoding */ + BOOL init(ByteStreamOut* outstream); + void done(); + +/* Manage an entropy model for a single bit */ + ArithmeticBitModel* createBitModel(); + void initBitModel(ArithmeticBitModel* model); + void destroyBitModel(ArithmeticBitModel* model); + +/* Manage an entropy model for n symbols (table optional) */ + ArithmeticModel* createSymbolModel(U32 n); + void initSymbolModel(ArithmeticModel* model, U32 *table=0); + void destroySymbolModel(ArithmeticModel* model); + +/* Encode a bit with modelling */ + void encodeBit(ArithmeticBitModel* model, U32 sym); + +/* Encode a symbol with modelling */ + void encodeSymbol(ArithmeticModel* model, U32 sym); + +/* Encode a bit without modelling */ + void writeBit(U32 sym); + +/* Encode bits without modelling */ + void writeBits(U32 bits, U32 sym); + +/* Encode an unsigned char without modelling */ + void writeByte(U8 sym); + +/* Encode an unsigned short without modelling */ + void writeShort(U16 sym); + +/* Encode an unsigned int without modelling */ + void writeInt(U32 sym); + +/* Encode a float without modelling */ + void writeFloat(F32 sym); + +/* Encode an unsigned 64 bit int without modelling */ + void writeInt64(U64 sym); + +/* Encode a double without modelling */ + void writeDouble(F64 sym); + +/* Only write to outstream if ArithmeticEncoder is dummy */ + ByteStreamOut* getByteStreamOut() const { return outstream; }; + +private: + + ByteStreamOut* outstream; + + void propagate_carry(); + void renorm_enc_interval(); + void manage_outbuffer(); + U8* outbuffer; + U8* endbuffer; + U8* outbyte; + U8* endbyte; + U32 base, length; +}; + +#endif diff --git a/libs/laszip/src/arithmeticmodel.cpp b/libs/laszip/src/arithmeticmodel.cpp new file mode 100644 index 0000000..6d88b83 --- /dev/null +++ b/libs/laszip/src/arithmeticmodel.cpp @@ -0,0 +1,212 @@ +/* +=============================================================================== + + FILE: arithmeticmodel.cpp + + CONTENTS: + + A modular C++ wrapper for an adapted version of Amir Said's FastAC Code. + see: http://www.cipr.rpi.edu/~said/FastAC.html + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2005-2014, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see header file + +=============================================================================== +*/ + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// Fast arithmetic coding implementation - +// -> 32-bit variables, 32-bit product, periodic updates, table decoding - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// Version 1.00 - April 25, 2004 - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// WARNING - +// ========= - +// - +// The only purpose of this program is to demonstrate the basic principles - +// of arithmetic coding. It is provided as is, without any express or - +// implied warranty, without even the warranty of fitness for any particular - +// purpose, or that the implementations are correct. - +// - +// Permission to copy and redistribute this code is hereby granted, provided - +// that this warning and copyright notices are not removed or altered. - +// - +// Copyright (c) 2004 by Amir Said (said@ieee.org) & - +// William A. Pearlman (pearlw@ecse.rpi.edu) - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// - +// A description of the arithmetic coding method used here is available in - +// - +// Lossless Compression Handbook, ed. K. Sayood - +// Chapter 5: Arithmetic Coding (A. Said), pp. 101-152, Academic Press, 2003 - +// - +// A. Said, Introduction to Arithetic Coding Theory and Practice - +// HP Labs report HPL-2004-76 - http://www.hpl.hp.com/techreports/ - +// - +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +#include "arithmeticmodel.hpp" + +#include +#include + +ArithmeticModel::ArithmeticModel(U32 symbols, BOOL compress) +{ + this->symbols = symbols; + this->compress = compress; + distribution = 0; +} + +ArithmeticModel::~ArithmeticModel() +{ + if (distribution) delete [] distribution; +} + +I32 ArithmeticModel::init(U32* table) +{ + if (distribution == 0) + { + if ( (symbols < 2) || (symbols > (1 << 11)) ) + { + return -1; // invalid number of symbols + } + last_symbol = symbols - 1; + if ((!compress) && (symbols > 16)) + { + U32 table_bits = 3; + while (symbols > (1U << (table_bits + 2))) ++table_bits; + table_size = 1 << table_bits; + table_shift = DM__LengthShift - table_bits; + distribution = new U32[2*symbols+table_size+2]; + decoder_table = distribution + 2 * symbols; + } + else // small alphabet: no table needed + { + decoder_table = 0; + table_size = table_shift = 0; + distribution = new U32[2*symbols]; + } + if (distribution == 0) + { + return -1; // "cannot allocate model memory"); + } + symbol_count = distribution + symbols; + } + + total_count = 0; + update_cycle = symbols; + if (table) + for (U32 k = 0; k < symbols; k++) symbol_count[k] = table[k]; + else + for (U32 k = 0; k < symbols; k++) symbol_count[k] = 1; + + update(); + symbols_until_update = update_cycle = (symbols + 6) >> 1; + + return 0; +} + +void ArithmeticModel::update() +{ + // halve counts when a threshold is reached + if ((total_count += update_cycle) > DM__MaxCount) + { + total_count = 0; + for (U32 n = 0; n < symbols; n++) + { + total_count += (symbol_count[n] = (symbol_count[n] + 1) >> 1); + } + } + + // compute cumulative distribution, decoder table + U32 k, sum = 0, s = 0; + U32 scale = 0x80000000U / total_count; + + if (compress || (table_size == 0)) + { + for (k = 0; k < symbols; k++) + { + distribution[k] = (scale * sum) >> (31 - DM__LengthShift); + sum += symbol_count[k]; + } + } + else + { + for (k = 0; k < symbols; k++) + { + distribution[k] = (scale * sum) >> (31 - DM__LengthShift); + sum += symbol_count[k]; + U32 w = distribution[k] >> table_shift; + while (s < w) decoder_table[++s] = k - 1; + } + decoder_table[0] = 0; + while (s <= table_size) decoder_table[++s] = symbols - 1; + } + + // set frequency of model updates + update_cycle = (5 * update_cycle) >> 2; + U32 max_cycle = (symbols + 6) << 3; + if (update_cycle > max_cycle) update_cycle = max_cycle; + symbols_until_update = update_cycle; +} + +ArithmeticBitModel::ArithmeticBitModel() +{ + init(); +} + +ArithmeticBitModel::~ArithmeticBitModel() +{ +} + +void ArithmeticBitModel::init() +{ + // initialization to equiprobable model + bit_0_count = 1; + bit_count = 2; + bit_0_prob = 1U << (BM__LengthShift - 1); + // start with frequent updates + update_cycle = bits_until_update = 4; +} + +void ArithmeticBitModel::update() +{ + // halve counts when a threshold is reached + if ((bit_count += update_cycle) > BM__MaxCount) + { + bit_count = (bit_count + 1) >> 1; + bit_0_count = (bit_0_count + 1) >> 1; + if (bit_0_count == bit_count) ++bit_count; + } + + // compute scaled bit 0 probability + U32 scale = 0x80000000U / bit_count; + bit_0_prob = (bit_0_count * scale) >> (31 - BM__LengthShift); + + // set frequency of model updates + update_cycle = (5 * update_cycle) >> 2; + if (update_cycle > 64) update_cycle = 64; + bits_until_update = update_cycle; +} diff --git a/libs/laszip/src/arithmeticmodel.hpp b/libs/laszip/src/arithmeticmodel.hpp new file mode 100644 index 0000000..36cbe52 --- /dev/null +++ b/libs/laszip/src/arithmeticmodel.hpp @@ -0,0 +1,92 @@ +/* +=============================================================================== + + FILE: arithmeticmodel.hpp + + CONTENTS: + + A modular C++ wrapper for an adapted version of Amir Said's FastAC Code. + see: http://www.cipr.rpi.edu/~said/FastAC.html + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2019, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 11 April 2019 -- 1024 AC_BUFFER_SIZE to 4096 for propagate_carry() overflow + 10 January 2011 -- licensing change for LGPL release and liblas integration + 8 December 2010 -- unified framework for all entropy coders + 30 October 2009 -- refactoring Amir Said's FastAC code + +=============================================================================== +*/ +#ifndef ARITHMETIC_MODEL_HPP +#define ARITHMETIC_MODEL_HPP + +#include +#include + +#include "mydefs.hpp" + +/* this header byte needs to change in case incompatible change happen */ +#define AC_HEADER_BYTE 2 +#define AC_BUFFER_SIZE 4096 + +const U32 AC__MinLength = 0x01000000U; // threshold for renormalization +const U32 AC__MaxLength = 0xFFFFFFFFU; // maximum AC interval length + + // Maximum values for binary models +const U32 BM__LengthShift = 13; // length bits discarded before mult. +const U32 BM__MaxCount = 1 << BM__LengthShift; // for adaptive models + + // Maximum values for general models +const U32 DM__LengthShift = 15; // length bits discarded before mult. +const U32 DM__MaxCount = 1 << DM__LengthShift; // for adaptive models + +class ArithmeticModel +{ +public: + ArithmeticModel(U32 symbols, BOOL compress); + ~ArithmeticModel(); + + I32 init(U32* table=0); + +private: + void update(); + U32 * distribution, * symbol_count, * decoder_table; + U32 total_count, update_cycle, symbols_until_update; + U32 symbols, last_symbol, table_size, table_shift; + BOOL compress; + friend class ArithmeticEncoder; + friend class ArithmeticDecoder; +}; + +class ArithmeticBitModel +{ +public: + ArithmeticBitModel(); + ~ArithmeticBitModel(); + + void init(); + +private: + void update(); + U32 update_cycle, bits_until_update; + U32 bit_0_prob, bit_0_count, bit_count; + friend class ArithmeticEncoder; + friend class ArithmeticDecoder; +}; + +#endif diff --git a/libs/laszip/src/bytestreamin.hpp b/libs/laszip/src/bytestreamin.hpp new file mode 100644 index 0000000..b11509f --- /dev/null +++ b/libs/laszip/src/bytestreamin.hpp @@ -0,0 +1,92 @@ +/* +=============================================================================== + + FILE: bytestreamin.hpp + + CONTENTS: + + Abstract base class for input streams with endian handling. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2012, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 2 January 2013 -- new functions for reading a stream of groups of bits + 1 October 2011 -- added 64 bit file support in MSVC 6.0 at McCafe at Hbf Linz + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- created from ByteStreamOutFile after Howard got pushy (-; + +=============================================================================== +*/ +#ifndef BYTE_STREAM_IN_HPP +#define BYTE_STREAM_IN_HPP + +#include "mydefs.hpp" + +class ByteStreamIn +{ +public: +/* write single bits */ + inline U32 getBits(U32 num_bits) + { + if (num_buffer < num_bits) + { + U32 input_bits; + get32bitsLE((U8*)&input_bits); + bit_buffer = bit_buffer | (((U64)input_bits) << num_buffer); + num_buffer = num_buffer + 32; + } + U32 new_bits = (U32)(bit_buffer & ((1 << num_bits) - 1)); + bit_buffer = bit_buffer >> num_bits; + num_buffer = num_buffer - num_bits; + return new_bits; + }; +/* read a single byte */ + virtual U32 getByte() = 0; +/* read an array of bytes */ + virtual void getBytes(U8* bytes, const U32 num_bytes) = 0; +/* read 16 bit low-endian field */ + virtual void get16bitsLE(U8* bytes) = 0; +/* read 32 bit low-endian field */ + virtual void get32bitsLE(U8* bytes) = 0; +/* read 64 bit low-endian field */ + virtual void get64bitsLE(U8* bytes) = 0; +/* read 16 bit big-endian field */ + virtual void get16bitsBE(U8* bytes) = 0; +/* read 32 bit big-endian field */ + virtual void get32bitsBE(U8* bytes) = 0; +/* read 64 bit big-endian field */ + virtual void get64bitsBE(U8* bytes) = 0; +/* is the stream seekable (e.g. stdin is not) */ + virtual BOOL isSeekable() const = 0; +/* get current position of stream */ + virtual I64 tell() const = 0; +/* seek to this position in the stream */ + virtual BOOL seek(const I64 position) = 0; +/* seek to the end of the file */ + virtual BOOL seekEnd(const I64 distance=0) = 0; +/* seek to the end of the file */ + virtual BOOL skipBytes(const U32 num_bytes) { I64 curr = tell(); return seek(curr + num_bytes); }; +/* constructor */ + inline ByteStreamIn() { bit_buffer = 0; num_buffer = 0; }; +/* destructor */ + virtual ~ByteStreamIn() {}; +private: + U64 bit_buffer; + U32 num_buffer; +}; + +#endif diff --git a/libs/laszip/src/bytestreamin_array.hpp b/libs/laszip/src/bytestreamin_array.hpp new file mode 100644 index 0000000..0b43d17 --- /dev/null +++ b/libs/laszip/src/bytestreamin_array.hpp @@ -0,0 +1,292 @@ +/* +=============================================================================== + + FILE: bytestreamin_array.hpp + + CONTENTS: + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 23 June 2016 -- alternative init option for "native LAS 1.4 compressor" + 19 July 2015 -- moved from LASlib to LASzip for "compatibility mode" in DLL + 9 April 2012 -- created after cooking Zuccini/Onion/Potatoe dinner for Mara + +=============================================================================== +*/ +#ifndef BYTE_STREAM_IN_ARRAY_H +#define BYTE_STREAM_IN_ARRAY_H + +#include "bytestreamin.hpp" + +#include +#include + +class ByteStreamInArray : public ByteStreamIn +{ +public: + ByteStreamInArray(); + ByteStreamInArray(const U8* data, I64 size); +/* init the array */ + BOOL init(const U8* data, I64 size); +/* read a single byte */ + U32 getByte(); +/* read an array of bytes */ + void getBytes(U8* bytes, const U32 num_bytes); +/* is the stream seekable (e.g. stdin is not) */ + BOOL isSeekable() const; +/* get current position of stream */ + I64 tell() const; +/* seek to this position in the stream */ + BOOL seek(const I64 position); +/* seek to the end of the stream */ + BOOL seekEnd(const I64 distance=0); +/* destructor */ + ~ByteStreamInArray(){}; +protected: + const U8* data; + I64 size; + I64 curr; +}; + +class ByteStreamInArrayLE : public ByteStreamInArray +{ +public: + ByteStreamInArrayLE(); + ByteStreamInArrayLE(const U8* data, I64 size); +/* read 16 bit low-endian field */ + void get16bitsLE(U8* bytes); +/* read 32 bit low-endian field */ + void get32bitsLE(U8* bytes); +/* read 64 bit low-endian field */ + void get64bitsLE(U8* bytes); +/* read 16 bit big-endian field */ + void get16bitsBE(U8* bytes); +/* read 32 bit big-endian field */ + void get32bitsBE(U8* bytes); +/* read 64 bit big-endian field */ + void get64bitsBE(U8* bytes); +private: + U8 swapped[8]; +}; + +class ByteStreamInArrayBE : public ByteStreamInArray +{ +public: + ByteStreamInArrayBE(); + ByteStreamInArrayBE(const U8* data, I64 size); +/* read 16 bit low-endian field */ + void get16bitsLE(U8* bytes); +/* read 32 bit low-endian field */ + void get32bitsLE(U8* bytes); +/* read 64 bit low-endian field */ + void get64bitsLE(U8* bytes); +/* read 16 bit big-endian field */ + void get16bitsBE(U8* bytes); +/* read 32 bit big-endian field */ + void get32bitsBE(U8* bytes); +/* read 64 bit big-endian field */ + void get64bitsBE(U8* bytes); +private: + U8 swapped[8]; +}; + +inline ByteStreamInArray::ByteStreamInArray() +{ + this->data = 0; + this->size = 0; + this->curr = 0; +} + +inline ByteStreamInArray::ByteStreamInArray(const U8* data, I64 size) +{ + init(data, size); +} + +inline BOOL ByteStreamInArray::init(const U8* data, I64 size) +{ + this->curr = 0; + if (data) + { + this->data = data; + this->size = size; + } + else + { + this->data = 0; + this->size = 0; + if (size) return FALSE; + } + return TRUE; +} + +inline U32 ByteStreamInArray::getByte() +{ + if (curr == size) + { + throw EOF; + } + U32 byte = data[curr]; + curr++; + return byte; +} + +inline void ByteStreamInArray::getBytes(U8* bytes, const U32 num_bytes) +{ + if ((curr + num_bytes) > size) + { + throw EOF; + } + memcpy((void*)bytes, (void*)(data+curr), num_bytes); + curr += num_bytes; +} + +inline BOOL ByteStreamInArray::isSeekable() const +{ + return TRUE; +} + +inline I64 ByteStreamInArray::tell() const +{ + return curr; +} + +inline BOOL ByteStreamInArray::seek(const I64 position) +{ + if ((0 <= position) && (position <= size)) + { + curr = position; + return TRUE; + } + return FALSE; +} + +inline BOOL ByteStreamInArray::seekEnd(const I64 distance) +{ + if ((0 <= distance) && (distance <= size)) + { + curr = size - distance; + return TRUE; + } + return FALSE; +} + +inline ByteStreamInArrayLE::ByteStreamInArrayLE() : ByteStreamInArray() +{ +} + +inline ByteStreamInArrayLE::ByteStreamInArrayLE(const U8* data, I64 size) : ByteStreamInArray(data, size) +{ +} + +inline void ByteStreamInArrayLE::get16bitsLE(U8* bytes) +{ + getBytes(bytes, 2); +} + +inline void ByteStreamInArrayLE::get32bitsLE(U8* bytes) +{ + getBytes(bytes, 4); +} + +inline void ByteStreamInArrayLE::get64bitsLE(U8* bytes) +{ + getBytes(bytes, 8); +} + +inline void ByteStreamInArrayLE::get16bitsBE(U8* bytes) +{ + getBytes(swapped, 2); + bytes[0] = swapped[1]; + bytes[1] = swapped[0]; +} + +inline void ByteStreamInArrayLE::get32bitsBE(U8* bytes) +{ + getBytes(swapped, 4); + bytes[0] = swapped[3]; + bytes[1] = swapped[2]; + bytes[2] = swapped[1]; + bytes[3] = swapped[0]; +} + +inline void ByteStreamInArrayLE::get64bitsBE(U8* bytes) +{ + getBytes(swapped, 8); + bytes[0] = swapped[7]; + bytes[1] = swapped[6]; + bytes[2] = swapped[5]; + bytes[3] = swapped[4]; + bytes[4] = swapped[3]; + bytes[5] = swapped[2]; + bytes[6] = swapped[1]; + bytes[7] = swapped[0]; +} + +inline ByteStreamInArrayBE::ByteStreamInArrayBE() : ByteStreamInArray() +{ +} + +inline ByteStreamInArrayBE::ByteStreamInArrayBE(const U8* data, I64 size) : ByteStreamInArray(data, size) +{ +} + +inline void ByteStreamInArrayBE::get16bitsLE(U8* bytes) +{ + getBytes(swapped, 2); + bytes[0] = swapped[1]; + bytes[1] = swapped[0]; +} + +inline void ByteStreamInArrayBE::get32bitsLE(U8* bytes) +{ + getBytes(swapped, 4); + bytes[0] = swapped[3]; + bytes[1] = swapped[2]; + bytes[2] = swapped[1]; + bytes[3] = swapped[0]; +} + +inline void ByteStreamInArrayBE::get64bitsLE(U8* bytes) +{ + getBytes(swapped, 8); + bytes[0] = swapped[7]; + bytes[1] = swapped[6]; + bytes[2] = swapped[5]; + bytes[3] = swapped[4]; + bytes[4] = swapped[3]; + bytes[5] = swapped[2]; + bytes[6] = swapped[1]; + bytes[7] = swapped[0]; +} + +inline void ByteStreamInArrayBE::get16bitsBE(U8* bytes) +{ + getBytes(bytes, 2); +} + +inline void ByteStreamInArrayBE::get32bitsBE(U8* bytes) +{ + getBytes(bytes, 4); +} + +inline void ByteStreamInArrayBE::get64bitsBE(U8* bytes) +{ + getBytes(bytes, 8); +} + +#endif diff --git a/libs/laszip/src/bytestreamin_file.hpp b/libs/laszip/src/bytestreamin_file.hpp new file mode 100644 index 0000000..a403eb5 --- /dev/null +++ b/libs/laszip/src/bytestreamin_file.hpp @@ -0,0 +1,268 @@ +/* +=============================================================================== + + FILE: bytestreamin_file.hpp + + CONTENTS: + + Class for FILE*-based input streams with endian handling. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2012, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 1 October 2011 -- added 64 bit file support in MSVC 6.0 at McCafe at Hbf Linz + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- created from ByteStreamOutFile after Howard got pushy (-; + +=============================================================================== +*/ +#ifndef BYTE_STREAM_IN_FILE_H +#define BYTE_STREAM_IN_FILE_H + +#include "bytestreamin.hpp" + +#include + +#if defined(_MSC_VER) && (_MSC_VER < 1300) +extern "C" __int64 _cdecl _ftelli64(FILE*); +extern "C" int _cdecl _fseeki64(FILE*, __int64, int); +#endif + +class ByteStreamInFile : public ByteStreamIn +{ +public: + ByteStreamInFile(FILE* file); +/* read a single byte */ + U32 getByte(); +/* read an array of bytes */ + void getBytes(U8* bytes, const U32 num_bytes); +/* is the stream seekable (e.g. stdin is not) */ + BOOL isSeekable() const; +/* get current position of stream */ + I64 tell() const; +/* seek to this position in the stream */ + BOOL seek(const I64 position); +/* seek to the end of the file */ + BOOL seekEnd(const I64 distance=0); +/* destructor */ + ~ByteStreamInFile(){}; +protected: + FILE* file; +}; + +class ByteStreamInFileLE : public ByteStreamInFile +{ +public: + ByteStreamInFileLE(FILE* file); +/* read 16 bit low-endian field */ + void get16bitsLE(U8* bytes); +/* read 32 bit low-endian field */ + void get32bitsLE(U8* bytes); +/* read 64 bit low-endian field */ + void get64bitsLE(U8* bytes); +/* read 16 bit big-endian field */ + void get16bitsBE(U8* bytes); +/* read 32 bit big-endian field */ + void get32bitsBE(U8* bytes); +/* read 64 bit big-endian field */ + void get64bitsBE(U8* bytes); +private: + U8 swapped[8]; +}; + +class ByteStreamInFileBE : public ByteStreamInFile +{ +public: + ByteStreamInFileBE(FILE* file); +/* read 16 bit low-endian field */ + void get16bitsLE(U8* bytes); +/* read 32 bit low-endian field */ + void get32bitsLE(U8* bytes); +/* read 64 bit low-endian field */ + void get64bitsLE(U8* bytes); +/* read 16 bit big-endian field */ + void get16bitsBE(U8* bytes); +/* read 32 bit big-endian field */ + void get32bitsBE(U8* bytes); +/* read 64 bit big-endian field */ + void get64bitsBE(U8* bytes); +private: + U8 swapped[8]; +}; + +inline ByteStreamInFile::ByteStreamInFile(FILE* file) +{ + this->file = file; +} + +inline U32 ByteStreamInFile::getByte() +{ + int byte = getc(file); + if (byte == EOF) + { + throw EOF; + } + return (U32)byte; +} + +inline void ByteStreamInFile::getBytes(U8* bytes, const U32 num_bytes) +{ + if (fread(bytes, 1, num_bytes, file) != num_bytes) + { + throw EOF; + } +} + +inline BOOL ByteStreamInFile::isSeekable() const +{ + return (file != stdin); +} + +inline I64 ByteStreamInFile::tell() const +{ +#if defined _WIN32 && ! defined (__MINGW32__) + return _ftelli64(file); +#elif defined (__MINGW32__) + return (I64)ftello64(file); +#else + return (I64)ftello(file); +#endif +} + +inline BOOL ByteStreamInFile::seek(const I64 position) +{ + if (tell() != position) + { +#if defined _WIN32 && ! defined (__MINGW32__) + return !(_fseeki64(file, position, SEEK_SET)); +#elif defined (__MINGW32__) + return !(fseeko64(file, (off_t)position, SEEK_SET)); +#else + return !(fseeko(file, (off_t)position, SEEK_SET)); +#endif + } + return TRUE; +} + +inline BOOL ByteStreamInFile::seekEnd(const I64 distance) +{ +#if defined _WIN32 && ! defined (__MINGW32__) + return !(_fseeki64(file, -distance, SEEK_END)); +#elif defined (__MINGW32__) + return !(fseeko64(file, (off_t)-distance, SEEK_END)); +#else + return !(fseeko(file, (off_t)-distance, SEEK_END)); +#endif +} + +inline ByteStreamInFileLE::ByteStreamInFileLE(FILE* file) : ByteStreamInFile(file) +{ +} + +inline void ByteStreamInFileLE::get16bitsLE(U8* bytes) +{ + getBytes(bytes, 2); +} + +inline void ByteStreamInFileLE::get32bitsLE(U8* bytes) +{ + getBytes(bytes, 4); +} + +inline void ByteStreamInFileLE::get64bitsLE(U8* bytes) +{ + getBytes(bytes, 8); +} + +inline void ByteStreamInFileLE::get16bitsBE(U8* bytes) +{ + getBytes(swapped, 2); + bytes[0] = swapped[1]; + bytes[1] = swapped[0]; +} + +inline void ByteStreamInFileLE::get32bitsBE(U8* bytes) +{ + getBytes(swapped, 4); + bytes[0] = swapped[3]; + bytes[1] = swapped[2]; + bytes[2] = swapped[1]; + bytes[3] = swapped[0]; +} + +inline void ByteStreamInFileLE::get64bitsBE(U8* bytes) +{ + getBytes(swapped, 8); + bytes[0] = swapped[7]; + bytes[1] = swapped[6]; + bytes[2] = swapped[5]; + bytes[3] = swapped[4]; + bytes[4] = swapped[3]; + bytes[5] = swapped[2]; + bytes[6] = swapped[1]; + bytes[7] = swapped[0]; +} + +inline ByteStreamInFileBE::ByteStreamInFileBE(FILE* file) : ByteStreamInFile(file) +{ +} + +inline void ByteStreamInFileBE::get16bitsLE(U8* bytes) +{ + getBytes(swapped, 2); + bytes[0] = swapped[1]; + bytes[1] = swapped[0]; +} + +inline void ByteStreamInFileBE::get32bitsLE(U8* bytes) +{ + getBytes(swapped, 4); + bytes[0] = swapped[3]; + bytes[1] = swapped[2]; + bytes[2] = swapped[1]; + bytes[3] = swapped[0]; +} + +inline void ByteStreamInFileBE::get64bitsLE(U8* bytes) +{ + getBytes(swapped, 8); + bytes[0] = swapped[7]; + bytes[1] = swapped[6]; + bytes[2] = swapped[5]; + bytes[3] = swapped[4]; + bytes[4] = swapped[3]; + bytes[5] = swapped[2]; + bytes[6] = swapped[1]; + bytes[7] = swapped[0]; +} + +inline void ByteStreamInFileBE::get16bitsBE(U8* bytes) +{ + getBytes(bytes, 2); +} + +inline void ByteStreamInFileBE::get32bitsBE(U8* bytes) +{ + getBytes(bytes, 4); +} + +inline void ByteStreamInFileBE::get64bitsBE(U8* bytes) +{ + getBytes(bytes, 8); +} + +#endif diff --git a/libs/laszip/src/bytestreamin_istream.hpp b/libs/laszip/src/bytestreamin_istream.hpp new file mode 100644 index 0000000..577b504 --- /dev/null +++ b/libs/laszip/src/bytestreamin_istream.hpp @@ -0,0 +1,250 @@ +/* +=============================================================================== + + FILE: bytestreamin_istream.hpp + + CONTENTS: + + Class for istream-based input streams with endian handling. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2018, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 10 July 2018 -- because it's hard to determine seek-ability, user must set it + 1 October 2011 -- added 64 bit file support in MSVC 6.0 at McCafe at Hbf Linz + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- created from ByteStreamOutFile after Howard got pushy (-; + +=============================================================================== +*/ +#ifndef BYTE_STREAM_IN_ISTREAM_H +#define BYTE_STREAM_IN_ISTREAM_H + +#include "bytestreamin.hpp" + +#ifdef LZ_WIN32_VC6 +#include +#else +#include +#include +using namespace std; +#endif + +class ByteStreamInIstream : public ByteStreamIn +{ +public: + ByteStreamInIstream(istream& stream, BOOL seekable=TRUE); +/* read a single byte */ + U32 getByte(); +/* read an array of bytes */ + void getBytes(U8* bytes, const U32 num_bytes); +/* is the stream seekable (e.g. stdin is not) */ + BOOL isSeekable() const { return seekable; }; +/* get current position of stream */ + I64 tell() const; +/* seek to this position in the stream */ + BOOL seek(const I64 position); +/* seek to the end of the file */ + BOOL seekEnd(const I64 distance=0); +/* destructor */ + ~ByteStreamInIstream(){}; +protected: + istream& stream; + BOOL seekable; +}; + +class ByteStreamInIstreamLE : public ByteStreamInIstream +{ +public: + ByteStreamInIstreamLE(istream& stream, BOOL seekable=TRUE); +/* read 16 bit low-endian field */ + void get16bitsLE(U8* bytes); +/* read 32 bit low-endian field */ + void get32bitsLE(U8* bytes); +/* read 64 bit low-endian field */ + void get64bitsLE(U8* bytes); +/* read 16 bit big-endian field */ + void get16bitsBE(U8* bytes); +/* read 32 bit big-endian field */ + void get32bitsBE(U8* bytes); +/* read 64 bit big-endian field */ + void get64bitsBE(U8* bytes); +private: + U8 swapped[8]; +}; + +class ByteStreamInIstreamBE : public ByteStreamInIstream +{ +public: + ByteStreamInIstreamBE(istream& stream, BOOL seekable=TRUE); +/* read 16 bit low-endian field */ + void get16bitsLE(U8* bytes); +/* read 32 bit low-endian field */ + void get32bitsLE(U8* bytes); +/* read 64 bit low-endian field */ + void get64bitsLE(U8* bytes); +/* read 16 bit big-endian field */ + void get16bitsBE(U8* bytes); +/* read 32 bit big-endian field */ + void get32bitsBE(U8* bytes); +/* read 64 bit big-endian field */ + void get64bitsBE(U8* bytes); +private: + U8 swapped[8]; +}; + +inline ByteStreamInIstream::ByteStreamInIstream(istream& stream_param, BOOL seekable_param) : stream(stream_param), seekable(seekable_param) +{ +} + +inline U32 ByteStreamInIstream::getByte() +{ + int byte = stream.get(); + if (stream.eof()) + { + throw EOF; + } + return (U32)byte; +} + +inline void ByteStreamInIstream::getBytes(U8* bytes, const U32 num_bytes) +{ + stream.read((char*)bytes, num_bytes); + if (!stream.good()) + { + throw EOF; + } +} + +inline I64 ByteStreamInIstream::tell() const +{ + return (I64)stream.tellg(); +} + +inline BOOL ByteStreamInIstream::seek(const I64 position) +{ + if (tell() != position) + { + stream.seekg(static_cast(position)); + return stream.good(); + } + return TRUE; +} + +inline BOOL ByteStreamInIstream::seekEnd(const I64 distance) +{ + stream.seekg(static_cast(-distance), ios::end); + return stream.good(); +} + +inline ByteStreamInIstreamLE::ByteStreamInIstreamLE(istream& stream, BOOL seekable) : ByteStreamInIstream(stream, seekable) +{ +} + +inline void ByteStreamInIstreamLE::get16bitsLE(U8* bytes) +{ + getBytes(bytes, 2); +} + +inline void ByteStreamInIstreamLE::get32bitsLE(U8* bytes) +{ + getBytes(bytes, 4); +} + +inline void ByteStreamInIstreamLE::get64bitsLE(U8* bytes) +{ + getBytes(bytes, 8); +} + +inline void ByteStreamInIstreamLE::get16bitsBE(U8* bytes) +{ + getBytes(swapped, 2); + bytes[0] = swapped[1]; + bytes[1] = swapped[0]; +} + +inline void ByteStreamInIstreamLE::get32bitsBE(U8* bytes) +{ + getBytes(swapped, 4); + bytes[0] = swapped[3]; + bytes[1] = swapped[2]; + bytes[2] = swapped[1]; + bytes[3] = swapped[0]; +} + +inline void ByteStreamInIstreamLE::get64bitsBE(U8* bytes) +{ + getBytes(swapped, 8); + bytes[0] = swapped[7]; + bytes[1] = swapped[6]; + bytes[2] = swapped[5]; + bytes[3] = swapped[4]; + bytes[4] = swapped[3]; + bytes[5] = swapped[2]; + bytes[6] = swapped[1]; + bytes[7] = swapped[0]; +} + +inline ByteStreamInIstreamBE::ByteStreamInIstreamBE(istream& stream, BOOL seekable) : ByteStreamInIstream(stream, seekable) +{ +} + +inline void ByteStreamInIstreamBE::get16bitsLE(U8* bytes) +{ + getBytes(swapped, 2); + bytes[0] = swapped[1]; + bytes[1] = swapped[0]; +} + +inline void ByteStreamInIstreamBE::get32bitsLE(U8* bytes) +{ + getBytes(swapped, 4); + bytes[0] = swapped[3]; + bytes[1] = swapped[2]; + bytes[2] = swapped[1]; + bytes[3] = swapped[0]; +} + +inline void ByteStreamInIstreamBE::get64bitsLE(U8* bytes) +{ + getBytes(swapped, 8); + bytes[0] = swapped[7]; + bytes[1] = swapped[6]; + bytes[2] = swapped[5]; + bytes[3] = swapped[4]; + bytes[4] = swapped[3]; + bytes[5] = swapped[2]; + bytes[6] = swapped[1]; + bytes[7] = swapped[0]; +} + +inline void ByteStreamInIstreamBE::get16bitsBE(U8* bytes) +{ + getBytes(bytes, 2); +} + +inline void ByteStreamInIstreamBE::get32bitsBE(U8* bytes) +{ + getBytes(bytes, 4); +} + +inline void ByteStreamInIstreamBE::get64bitsBE(U8* bytes) +{ + getBytes(bytes, 8); +} + +#endif diff --git a/libs/laszip/src/bytestreaminout.hpp b/libs/laszip/src/bytestreaminout.hpp new file mode 100644 index 0000000..475981b --- /dev/null +++ b/libs/laszip/src/bytestreaminout.hpp @@ -0,0 +1,45 @@ +/* +=============================================================================== + + FILE: bytestreaminout.hpp + + CONTENTS: + + Abstract base class for streams that both input and output with endian handling. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2013, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 29 December 2013 -- created after helping a client to QA their Optech LiDAR + +=============================================================================== +*/ +#ifndef BYTE_STREAM_INOUT_HPP +#define BYTE_STREAM_INOUT_HPP + +#include "mydefs.hpp" +#include "bytestreamin.hpp" +#include "bytestreamout.hpp" + +class ByteStreamInOut : public ByteStreamIn, ByteStreamOut +{ +public: +/* destructor */ + virtual ~ByteStreamInOut() {}; +}; + +#endif diff --git a/libs/laszip/src/bytestreaminout_file.hpp b/libs/laszip/src/bytestreaminout_file.hpp new file mode 100644 index 0000000..88d43e3 --- /dev/null +++ b/libs/laszip/src/bytestreaminout_file.hpp @@ -0,0 +1,57 @@ +/* +=============================================================================== + + FILE: bytestreaminout_file.hpp + + CONTENTS: + + Class for FILE*-based streams that both input and output with endian handling. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2013, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 29 December 2013 -- created after helping a client to QA their Optech LiDAR + +=============================================================================== +*/ +#ifndef BYTE_STREAM_INOUT_FILE_HPP +#define BYTE_STREAM_INOUT_FILE_HPP + +#include "bytestreamin_file.hpp" +#include "bytestreamout_file.hpp" + +class ByteStreamInOutFileLE : public ByteStreamInFileLE, public ByteStreamOutFileLE +{ +public: + ByteStreamInOutFileLE(FILE* file); +}; + +class ByteStreamInOutFileBE : public ByteStreamInFileBE, public ByteStreamOutFileBE +{ +public: + ByteStreamInOutFileBE(FILE* file); +}; + +inline ByteStreamInOutFileLE::ByteStreamInOutFileLE(FILE* file) : ByteStreamInFileLE(file), ByteStreamOutFileLE(file) +{ +} + +inline ByteStreamInOutFileBE::ByteStreamInOutFileBE(FILE* file) : ByteStreamInFileBE(file), ByteStreamOutFileBE(file) +{ +} + +#endif diff --git a/libs/laszip/src/bytestreamout.hpp b/libs/laszip/src/bytestreamout.hpp new file mode 100644 index 0000000..d809314 --- /dev/null +++ b/libs/laszip/src/bytestreamout.hpp @@ -0,0 +1,103 @@ +/* +=============================================================================== + + FILE: bytestreamout.hpp + + CONTENTS: + + Abstract base class for output streams with endian handling. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2013, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 2 January 2013 -- new functions for writing a stream of groups of bits + 1 October 2011 -- added 64 bit file support in MSVC 6.0 at McCafe at Hbf Linz + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- created from ByteStreamOutFile after Howard got pushy (-; + +=============================================================================== +*/ +#ifndef BYTE_STREAM_OUT_HPP +#define BYTE_STREAM_OUT_HPP + +#include "mydefs.hpp" + +class ByteStreamOut +{ +public: +/* write single bits */ + inline BOOL putBits(U32 bits, U32 num_bits) + { + U64 new_bits = bits; + bit_buffer |= (new_bits << num_buffer); + num_buffer += num_bits; + if (num_buffer >= 32) + { + U32 output_bits = (U32)(bit_buffer & U32_MAX); + bit_buffer = bit_buffer >> 32; + num_buffer = num_buffer - 32; + return put32bitsLE((U8*)&output_bits); + } + return TRUE; + }; +/* called after writing bits before closing or writing bytes */ + inline BOOL flushBits() + { + if (num_buffer) + { + U32 num_zero_bits = 32 - num_buffer; + U32 output_bits = (U32)(bit_buffer >> num_zero_bits); + bit_buffer = 0; + num_buffer = 0; + return put32bitsLE((U8*)&output_bits); + } + return TRUE; + }; +/* write a single byte */ + virtual BOOL putByte(U8 byte) = 0; +/* write an array of bytes */ + virtual BOOL putBytes(const U8* bytes, U32 num_bytes) = 0; +/* write 16 bit low-endian field */ + virtual BOOL put16bitsLE(const U8* bytes) = 0; +/* write 32 bit low-endian field */ + virtual BOOL put32bitsLE(const U8* bytes) = 0; +/* write 64 bit low-endian field */ + virtual BOOL put64bitsLE(const U8* bytes) = 0; +/* write 16 bit big-endian field */ + virtual BOOL put16bitsBE(const U8* bytes) = 0; +/* write 32 bit big-endian field */ + virtual BOOL put32bitsBE(const U8* bytes) = 0; +/* write 64 bit big-endian field */ + virtual BOOL put64bitsBE(const U8* bytes) = 0; +/* is the stream seekable (e.g. standard out is not) */ + virtual BOOL isSeekable() const = 0; +/* get current position of stream */ + virtual I64 tell() const = 0; +/* seek to this position in the stream */ + virtual BOOL seek(const I64 position) = 0; +/* seek to the end of the file */ + virtual BOOL seekEnd() = 0; +/* constructor */ + inline ByteStreamOut() { bit_buffer = 0; num_buffer = 0; }; +/* destructor */ + virtual ~ByteStreamOut() {}; +private: + U64 bit_buffer; + U32 num_buffer; +}; + +#endif diff --git a/libs/laszip/src/bytestreamout_array.hpp b/libs/laszip/src/bytestreamout_array.hpp new file mode 100644 index 0000000..f2ca2d1 --- /dev/null +++ b/libs/laszip/src/bytestreamout_array.hpp @@ -0,0 +1,284 @@ +/* +=============================================================================== + + FILE: bytestreamout_array.hpp + + CONTENTS: + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2019, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 11 April 2019 -- increase default alloc from 1024 bytes to 4096 bytes + 10 April 2019 -- fix potential memory leak found by Connor Manning's valgrind + 22 June 2016 -- access to current size for "native LAS 1.4 compressor" + 19 July 2015 -- moved from LASlib to LASzip for "compatibility mode" in DLL + 9 April 2012 -- created after cooking Zuccini/Onion/Potatoe dinner for Mara + +=============================================================================== +*/ +#ifndef BYTE_STREAM_OUT_ARRAY_HPP +#define BYTE_STREAM_OUT_ARRAY_HPP + +#include "bytestreamout.hpp" + +#include +#include + +class ByteStreamOutArray : public ByteStreamOut +{ +public: + ByteStreamOutArray(I64 alloc=4096); +/* write a single byte */ + BOOL putByte(U8 byte); +/* write an array of bytes */ + BOOL putBytes(const U8* bytes, U32 num_bytes); +/* is the stream seekable (e.g. standard out is not) */ + BOOL isSeekable() const; +/* get current position of stream */ + I64 tell() const; +/* seek to this position in the stream */ + BOOL seek(const I64 position); +/* seek to the end of the file */ + BOOL seekEnd(); +/* destructor */ + ~ByteStreamOutArray() { if (data) free(data); }; +/* get access to data */ + inline I64 getSize() const { return size; }; + inline I64 getCurr() const { return curr; }; + inline const U8* getData() const { return data; }; + inline U8* takeData() { U8* d = data; data = 0; alloc = 0; size = 0; curr = 0; return d; }; +protected: + U8* data; + I64 alloc; + I64 size; + I64 curr; +}; + +class ByteStreamOutArrayLE : public ByteStreamOutArray +{ +public: + ByteStreamOutArrayLE(I64 alloc=4096); +/* write 16 bit low-endian field */ + BOOL put16bitsLE(const U8* bytes); +/* write 32 bit low-endian field */ + BOOL put32bitsLE(const U8* bytes); +/* write 64 bit low-endian field */ + BOOL put64bitsLE(const U8* bytes); +/* write 16 bit big-endian field */ + BOOL put16bitsBE(const U8* bytes); +/* write 32 bit big-endian field */ + BOOL put32bitsBE(const U8* bytes); +/* write 64 bit big-endian field */ + BOOL put64bitsBE(const U8* bytes); +private: + U8 swapped[8]; +}; + +class ByteStreamOutArrayBE : public ByteStreamOutArray +{ +public: + ByteStreamOutArrayBE(I64 alloc=4096); +/* write 16 bit low-endian field */ + BOOL put16bitsLE(const U8* bytes); +/* write 32 bit low-endian field */ + BOOL put32bitsLE(const U8* bytes); +/* write 64 bit low-endian field */ + BOOL put64bitsLE(const U8* bytes); +/* write 16 bit big-endian field */ + BOOL put16bitsBE(const U8* bytes); +/* write 32 bit big-endian field */ + BOOL put32bitsBE(const U8* bytes); +/* write 64 bit big-endian field */ + BOOL put64bitsBE(const U8* bytes); +private: + U8 swapped[8]; +}; + +inline ByteStreamOutArray::ByteStreamOutArray(I64 alloc) +{ + this->data = (U8*)malloc((U32)alloc); + this->alloc = alloc; + this->size = 0; + this->curr = 0; +} + +inline BOOL ByteStreamOutArray::putByte(U8 byte) +{ + if (curr == alloc) + { + alloc += 4096; + data = (U8*)realloc(data, (U32)alloc); + if (data == 0) + { + return FALSE; + } + } + data[curr] = byte; + if (curr == size) size++; + curr++; + return TRUE; +} + +inline BOOL ByteStreamOutArray::putBytes(const U8* bytes, U32 num_bytes) +{ + if ((curr+num_bytes) > alloc) + { + alloc += (4096+num_bytes); + data = (U8*)realloc(data, (U32)alloc); + if (data == 0) + { + return FALSE; + } + } + memcpy((void*)(data+curr), bytes, num_bytes); + curr += num_bytes; + if (curr > size) size = curr; + return TRUE; +} + +inline BOOL ByteStreamOutArray::isSeekable() const +{ + return TRUE; +} + +inline I64 ByteStreamOutArray::tell() const +{ + return curr; +} + +inline BOOL ByteStreamOutArray::seek(I64 position) +{ + if ((0 <= position) && (position <= size)) + { + curr = position; + return TRUE; + } + return FALSE; +} + +inline BOOL ByteStreamOutArray::seekEnd() +{ + curr = size; + return TRUE; + +/* + if ((0 <= distance) && (distance <= size)) + { + curr = size - distance; + return TRUE; + } + return FALSE; +*/ +} + +inline ByteStreamOutArrayLE::ByteStreamOutArrayLE(I64 alloc) : ByteStreamOutArray(alloc) +{ +} + +inline BOOL ByteStreamOutArrayLE::put16bitsLE(const U8* bytes) +{ + return putBytes(bytes, 2); +} + +inline BOOL ByteStreamOutArrayLE::put32bitsLE(const U8* bytes) +{ + return putBytes(bytes, 4); +} + +inline BOOL ByteStreamOutArrayLE::put64bitsLE(const U8* bytes) +{ + return putBytes(bytes, 8); +} + +inline BOOL ByteStreamOutArrayLE::put16bitsBE(const U8* bytes) +{ + swapped[0] = bytes[1]; + swapped[1] = bytes[0]; + return putBytes(swapped, 2); +} + +inline BOOL ByteStreamOutArrayLE::put32bitsBE(const U8* bytes) +{ + swapped[0] = bytes[3]; + swapped[1] = bytes[2]; + swapped[2] = bytes[1]; + swapped[3] = bytes[0]; + return putBytes(swapped, 4); +} + +inline BOOL ByteStreamOutArrayLE::put64bitsBE(const U8* bytes) +{ + swapped[0] = bytes[7]; + swapped[1] = bytes[6]; + swapped[2] = bytes[5]; + swapped[3] = bytes[4]; + swapped[4] = bytes[3]; + swapped[5] = bytes[2]; + swapped[6] = bytes[1]; + swapped[7] = bytes[0]; + return putBytes(swapped, 8); +} + +inline ByteStreamOutArrayBE::ByteStreamOutArrayBE(I64 alloc) : ByteStreamOutArray(alloc) +{ +} + +inline BOOL ByteStreamOutArrayBE::put16bitsLE(const U8* bytes) +{ + swapped[0] = bytes[1]; + swapped[1] = bytes[0]; + return putBytes(swapped, 2); +} + +inline BOOL ByteStreamOutArrayBE::put32bitsLE(const U8* bytes) +{ + swapped[0] = bytes[3]; + swapped[1] = bytes[2]; + swapped[2] = bytes[1]; + swapped[3] = bytes[0]; + return putBytes(swapped, 4); +} + +inline BOOL ByteStreamOutArrayBE::put64bitsLE(const U8* bytes) +{ + swapped[0] = bytes[7]; + swapped[1] = bytes[6]; + swapped[2] = bytes[5]; + swapped[3] = bytes[4]; + swapped[4] = bytes[3]; + swapped[5] = bytes[2]; + swapped[6] = bytes[1]; + swapped[7] = bytes[0]; + return putBytes(swapped, 8); +} + +inline BOOL ByteStreamOutArrayBE::put16bitsBE(const U8* bytes) +{ + return putBytes(bytes, 2); +} + +inline BOOL ByteStreamOutArrayBE::put32bitsBE(const U8* bytes) +{ + return putBytes(bytes, 4); +} + +inline BOOL ByteStreamOutArrayBE::put64bitsBE(const U8* bytes) +{ + return putBytes(bytes, 8); +} + +#endif diff --git a/libs/laszip/src/bytestreamout_file.hpp b/libs/laszip/src/bytestreamout_file.hpp new file mode 100644 index 0000000..d8c5132 --- /dev/null +++ b/libs/laszip/src/bytestreamout_file.hpp @@ -0,0 +1,265 @@ +/* +=============================================================================== + + FILE: bytestreamout_file.hpp + + CONTENTS: + + Class for FILE*-based output streams with endian handling. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2013, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 1 October 2011 -- added 64 bit file support in MSVC 6.0 at McCafe at Hbf Linz + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- created from ByteStreamOutFile after Howard got pushy (-; + +=============================================================================== +*/ +#ifndef BYTE_STREAM_OUT_FILE_H +#define BYTE_STREAM_OUT_FILE_H + +#include "bytestreamout.hpp" + +#include + +#if defined(_MSC_VER) && (_MSC_VER < 1300) +extern "C" int _cdecl _fseeki64(FILE*, __int64, int); +extern "C" __int64 _cdecl _ftelli64(FILE*); +#endif + +class ByteStreamOutFile : public ByteStreamOut +{ +public: + ByteStreamOutFile(FILE* file); +/* replace a closed FILE* with a reopened FILE* in "ab" mode */ + BOOL refile(FILE* file); +/* write a single byte */ + BOOL putByte(U8 byte); +/* write an array of bytes */ + BOOL putBytes(const U8* bytes, U32 num_bytes); +/* is the stream seekable (e.g. standard out is not) */ + BOOL isSeekable() const; +/* get current position of stream */ + I64 tell() const; +/* seek to this position in the stream */ + BOOL seek(const I64 position); +/* seek to the end of the file */ + BOOL seekEnd(); +/* destructor */ + ~ByteStreamOutFile(){}; +protected: + FILE* file; +}; + +class ByteStreamOutFileLE : public ByteStreamOutFile +{ +public: + ByteStreamOutFileLE(FILE* file); +/* write 16 bit low-endian field */ + BOOL put16bitsLE(const U8* bytes); +/* write 32 bit low-endian field */ + BOOL put32bitsLE(const U8* bytes); +/* write 64 bit low-endian field */ + BOOL put64bitsLE(const U8* bytes); +/* write 16 bit big-endian field */ + BOOL put16bitsBE(const U8* bytes); +/* write 32 bit big-endian field */ + BOOL put32bitsBE(const U8* bytes); +/* write 64 bit big-endian field */ + BOOL put64bitsBE(const U8* bytes); +private: + U8 swapped[8]; +}; + +class ByteStreamOutFileBE : public ByteStreamOutFile +{ +public: + ByteStreamOutFileBE(FILE* file); +/* write 16 bit low-endian field */ + BOOL put16bitsLE(const U8* bytes); +/* write 32 bit low-endian field */ + BOOL put32bitsLE(const U8* bytes); +/* write 64 bit low-endian field */ + BOOL put64bitsLE(const U8* bytes); +/* write 16 bit big-endian field */ + BOOL put16bitsBE(const U8* bytes); +/* write 32 bit big-endian field */ + BOOL put32bitsBE(const U8* bytes); +/* write 64 bit big-endian field */ + BOOL put64bitsBE(const U8* bytes); +private: + U8 swapped[8]; +}; + +inline ByteStreamOutFile::ByteStreamOutFile(FILE* file) +{ + this->file = file; +} + +inline BOOL ByteStreamOutFile::refile(FILE* file) +{ + if (file == 0) return FALSE; + this->file = file; + return TRUE; +} + +inline BOOL ByteStreamOutFile::putByte(U8 byte) +{ + return (fputc(byte, file) == byte); +} + +inline BOOL ByteStreamOutFile::putBytes(const U8* bytes, U32 num_bytes) +{ + return (fwrite(bytes, 1, num_bytes, file) == num_bytes); +} + +inline BOOL ByteStreamOutFile::isSeekable() const +{ + return (file != stdout); +} + +inline I64 ByteStreamOutFile::tell() const +{ +#if defined _WIN32 && ! defined (__MINGW32__) + return _ftelli64(file); +#elif defined (__MINGW32__) + return (I64)ftello64(file); +#else + return (I64)ftello(file); +#endif +} + +inline BOOL ByteStreamOutFile::seek(I64 position) +{ +#if defined _WIN32 && ! defined (__MINGW32__) + return !(_fseeki64(file, position, SEEK_SET)); +#elif defined (__MINGW32__) + return !(fseeko64(file, (off_t)position, SEEK_SET)); +#else + return !(fseeko(file, (off_t)position, SEEK_SET)); +#endif +} + +inline BOOL ByteStreamOutFile::seekEnd() +{ +#if defined _WIN32 && ! defined (__MINGW32__) + return !(_fseeki64(file, 0, SEEK_END)); +#elif defined (__MINGW32__) + return !(fseeko64(file, (off_t)0, SEEK_END)); +#else + return !(fseeko(file, (off_t)0, SEEK_END)); +#endif +} + +inline ByteStreamOutFileLE::ByteStreamOutFileLE(FILE* file) : ByteStreamOutFile(file) +{ +} + +inline BOOL ByteStreamOutFileLE::put16bitsLE(const U8* bytes) +{ + return putBytes(bytes, 2); +} + +inline BOOL ByteStreamOutFileLE::put32bitsLE(const U8* bytes) +{ + return putBytes(bytes, 4); +} + +inline BOOL ByteStreamOutFileLE::put64bitsLE(const U8* bytes) +{ + return putBytes(bytes, 8); +} + +inline BOOL ByteStreamOutFileLE::put16bitsBE(const U8* bytes) +{ + swapped[0] = bytes[1]; + swapped[1] = bytes[0]; + return putBytes(swapped, 2); +} + +inline BOOL ByteStreamOutFileLE::put32bitsBE(const U8* bytes) +{ + swapped[0] = bytes[3]; + swapped[1] = bytes[2]; + swapped[2] = bytes[1]; + swapped[3] = bytes[0]; + return putBytes(swapped, 4); +} + +inline BOOL ByteStreamOutFileLE::put64bitsBE(const U8* bytes) +{ + swapped[0] = bytes[7]; + swapped[1] = bytes[6]; + swapped[2] = bytes[5]; + swapped[3] = bytes[4]; + swapped[4] = bytes[3]; + swapped[5] = bytes[2]; + swapped[6] = bytes[1]; + swapped[7] = bytes[0]; + return putBytes(swapped, 8); +} + +inline ByteStreamOutFileBE::ByteStreamOutFileBE(FILE* file) : ByteStreamOutFile(file) +{ +} + +inline BOOL ByteStreamOutFileBE::put16bitsLE(const U8* bytes) +{ + swapped[0] = bytes[1]; + swapped[1] = bytes[0]; + return putBytes(swapped, 2); +} + +inline BOOL ByteStreamOutFileBE::put32bitsLE(const U8* bytes) +{ + swapped[0] = bytes[3]; + swapped[1] = bytes[2]; + swapped[2] = bytes[1]; + swapped[3] = bytes[0]; + return putBytes(swapped, 4); +} + +inline BOOL ByteStreamOutFileBE::put64bitsLE(const U8* bytes) +{ + swapped[0] = bytes[7]; + swapped[1] = bytes[6]; + swapped[2] = bytes[5]; + swapped[3] = bytes[4]; + swapped[4] = bytes[3]; + swapped[5] = bytes[2]; + swapped[6] = bytes[1]; + swapped[7] = bytes[0]; + return putBytes(swapped, 8); +} + +inline BOOL ByteStreamOutFileBE::put16bitsBE(const U8* bytes) +{ + return putBytes(bytes, 2); +} + +inline BOOL ByteStreamOutFileBE::put32bitsBE(const U8* bytes) +{ + return putBytes(bytes, 4); +} + +inline BOOL ByteStreamOutFileBE::put64bitsBE(const U8* bytes) +{ + return putBytes(bytes, 8); +} + +#endif diff --git a/libs/laszip/src/bytestreamout_nil.hpp b/libs/laszip/src/bytestreamout_nil.hpp new file mode 100644 index 0000000..cd28a7d --- /dev/null +++ b/libs/laszip/src/bytestreamout_nil.hpp @@ -0,0 +1,141 @@ +/* +=============================================================================== + + FILE: bytestreamout_.hpilp + + CONTENTS: + + Class for a black hole that only counts the bytes. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2012, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 1 October 2011 -- added 64 bit file support in MSVC 6.0 at McCafe at Hbf Linz + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- created from ByteStreamOutFile after Howard got pushy (-; + +=============================================================================== +*/ +#ifndef BYTE_STREAM_OUT_NIL_H +#define BYTE_STREAM_OUT_NIL_H + +#include "bytestreamout.hpp" + +#include + +class ByteStreamOutNil : public ByteStreamOut +{ +public: + ByteStreamOutNil(); +/* write a single byte */ + BOOL putByte(U8 byte); +/* write an array of bytes */ + BOOL putBytes(const U8* bytes, U32 num_bytes); +/* write 16 bit low-endian field */ + BOOL put16bitsLE(const U8* bytes); +/* write 32 bit low-endian field */ + BOOL put32bitsLE(const U8* bytes); +/* write 64 bit low-endian field */ + BOOL put64bitsLE(const U8* bytes); +/* write 16 bit big-endian field */ + BOOL put16bitsBE(const U8* bytes); +/* write 32 bit big-endian field */ + BOOL put32bitsBE(const U8* bytes); +/* write 64 bit big-endian field */ + BOOL put64bitsBE(const U8* bytes); +/* is the stream seekable (e.g. standard out is not) */ + BOOL isSeekable() const; +/* get current position of stream */ + I64 tell() const; +/* seek to this position in the stream */ + BOOL seek(const I64 position); +/* seek to the end of the file */ + BOOL seekEnd(); +/* destructor */ + ~ByteStreamOutNil(){}; +private: + I64 num_bytes; +}; + +inline ByteStreamOutNil::ByteStreamOutNil() +{ + num_bytes = 0; +} + +inline BOOL ByteStreamOutNil::putByte(U8 byte) +{ + num_bytes++; + return TRUE; +} + +inline BOOL ByteStreamOutNil::putBytes(const U8* bytes, U32 num_bytes) +{ + this->num_bytes += num_bytes; + return TRUE; +} + +inline BOOL ByteStreamOutNil::put16bitsLE(const U8* bytes) +{ + return putBytes(bytes, 2); +} + +inline BOOL ByteStreamOutNil::put32bitsLE(const U8* bytes) +{ + return putBytes(bytes, 4); +} + +inline BOOL ByteStreamOutNil::put64bitsLE(const U8* bytes) +{ + return putBytes(bytes, 8); +} + +inline BOOL ByteStreamOutNil::put16bitsBE(const U8* bytes) +{ + return putBytes(bytes, 2); +} + +inline BOOL ByteStreamOutNil::put32bitsBE(const U8* bytes) +{ + return putBytes(bytes, 4); +} + +inline BOOL ByteStreamOutNil::put64bitsBE(const U8* bytes) +{ + return putBytes(bytes, 8); +} + +inline BOOL ByteStreamOutNil::isSeekable() const +{ + return TRUE; +} + +inline I64 ByteStreamOutNil::tell() const +{ + return num_bytes; +} + +inline BOOL ByteStreamOutNil::seek(I64 position) +{ + return TRUE; +} + +inline BOOL ByteStreamOutNil::seekEnd() +{ + return TRUE; +} + +#endif diff --git a/libs/laszip/src/bytestreamout_ostream.hpp b/libs/laszip/src/bytestreamout_ostream.hpp new file mode 100644 index 0000000..a138319 --- /dev/null +++ b/libs/laszip/src/bytestreamout_ostream.hpp @@ -0,0 +1,241 @@ +/* +=============================================================================== + + FILE: bytestreamout_ostream.hpp + + Class for ostream-based output streams with endian handling. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2012, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 1 October 2011 -- added 64 bit file support in MSVC 6.0 at McCafe at Hbf Linz + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- created from ByteStreamOutFile after Howard got pushy (-; + +=============================================================================== +*/ +#ifndef BYTE_STREAM_OUT_OSTREAM_H +#define BYTE_STREAM_OUT_OSTREAM_H + +#include "bytestreamout.hpp" + +#ifdef LZ_WIN32_VC6 +#include +#else +#include +#include +using namespace std; +#endif + +class ByteStreamOutOstream : public ByteStreamOut +{ +public: + ByteStreamOutOstream(ostream& stream); +/* write a single byte */ + BOOL putByte(U8 byte); +/* write an array of bytes */ + BOOL putBytes(const U8* bytes, U32 num_bytes); +/* is the stream seekable (e.g. standard out is not) */ + BOOL isSeekable() const; +/* get current position of stream */ + I64 tell() const; +/* seek to this position in the stream */ + BOOL seek(const I64 position); +/* seek to the end of the file */ + BOOL seekEnd(); +/* destructor */ + ~ByteStreamOutOstream(){}; +protected: + ostream& stream; +}; + +class ByteStreamOutOstreamLE : public ByteStreamOutOstream +{ +public: + ByteStreamOutOstreamLE(ostream& stream); +/* write 16 bit low-endian field */ + BOOL put16bitsLE(const U8* bytes); +/* write 32 bit low-endian field */ + BOOL put32bitsLE(const U8* bytes); +/* write 64 bit low-endian field */ + BOOL put64bitsLE(const U8* bytes); +/* write 16 bit big-endian field */ + BOOL put16bitsBE(const U8* bytes); +/* write 32 bit big-endian field */ + BOOL put32bitsBE(const U8* bytes); +/* write 64 bit big-endian field */ + BOOL put64bitsBE(const U8* bytes); +private: + U8 swapped[8]; +}; + +class ByteStreamOutOstreamBE : public ByteStreamOutOstream +{ +public: + ByteStreamOutOstreamBE(ostream& stream); +/* write 16 bit low-endian field */ + BOOL put16bitsLE(const U8* bytes); +/* write 32 bit low-endian field */ + BOOL put32bitsLE(const U8* bytes); +/* write 64 bit low-endian field */ + BOOL put64bitsLE(const U8* bytes); +/* write 16 bit big-endian field */ + BOOL put16bitsBE(const U8* bytes); +/* write 32 bit big-endian field */ + BOOL put32bitsBE(const U8* bytes); +/* write 64 bit big-endian field */ + BOOL put64bitsBE(const U8* bytes); +private: + U8 swapped[8]; +}; + +inline ByteStreamOutOstream::ByteStreamOutOstream(ostream& stream_param) : + stream(stream_param) +{ +} + +inline BOOL ByteStreamOutOstream::putByte(U8 byte) +{ + stream.put(byte); + return stream.good(); +} + +inline BOOL ByteStreamOutOstream::putBytes(const U8* bytes, U32 num_bytes) +{ + stream.write((const char*)bytes, num_bytes); + return stream.good(); +} + +inline BOOL ByteStreamOutOstream::isSeekable() const +{ + return !!(static_cast(stream)); +} + +inline I64 ByteStreamOutOstream::tell() const +{ + return (I64)stream.tellp(); +} + +inline BOOL ByteStreamOutOstream::seek(I64 position) +{ + stream.seekp(static_cast(position)); + return stream.good(); +} + +inline BOOL ByteStreamOutOstream::seekEnd() +{ + stream.seekp(0, ios::end); + return stream.good(); +} + +inline ByteStreamOutOstreamLE::ByteStreamOutOstreamLE(ostream& stream) : ByteStreamOutOstream(stream) +{ +} + +inline BOOL ByteStreamOutOstreamLE::put16bitsLE(const U8* bytes) +{ + return putBytes(bytes, 2); +} + +inline BOOL ByteStreamOutOstreamLE::put32bitsLE(const U8* bytes) +{ + return putBytes(bytes, 4); +} + +inline BOOL ByteStreamOutOstreamLE::put64bitsLE(const U8* bytes) +{ + return putBytes(bytes, 8); +} + +inline BOOL ByteStreamOutOstreamLE::put16bitsBE(const U8* bytes) +{ + swapped[0] = bytes[1]; + swapped[1] = bytes[0]; + return putBytes(swapped, 2); +} + +inline BOOL ByteStreamOutOstreamLE::put32bitsBE(const U8* bytes) +{ + swapped[0] = bytes[3]; + swapped[1] = bytes[2]; + swapped[2] = bytes[1]; + swapped[3] = bytes[0]; + return putBytes(swapped, 4); +} + +inline BOOL ByteStreamOutOstreamLE::put64bitsBE(const U8* bytes) +{ + swapped[0] = bytes[7]; + swapped[1] = bytes[6]; + swapped[2] = bytes[5]; + swapped[3] = bytes[4]; + swapped[4] = bytes[3]; + swapped[5] = bytes[2]; + swapped[6] = bytes[1]; + swapped[7] = bytes[0]; + return putBytes(swapped, 8); +} + +inline ByteStreamOutOstreamBE::ByteStreamOutOstreamBE(ostream& stream) : ByteStreamOutOstream(stream) +{ +} + +inline BOOL ByteStreamOutOstreamBE::put16bitsLE(const U8* bytes) +{ + swapped[0] = bytes[1]; + swapped[1] = bytes[0]; + return putBytes(swapped, 2); +} + +inline BOOL ByteStreamOutOstreamBE::put32bitsLE(const U8* bytes) +{ + swapped[0] = bytes[3]; + swapped[1] = bytes[2]; + swapped[2] = bytes[1]; + swapped[3] = bytes[0]; + return putBytes(swapped, 4); +} + +inline BOOL ByteStreamOutOstreamBE::put64bitsLE(const U8* bytes) +{ + swapped[0] = bytes[7]; + swapped[1] = bytes[6]; + swapped[2] = bytes[5]; + swapped[3] = bytes[4]; + swapped[4] = bytes[3]; + swapped[5] = bytes[2]; + swapped[6] = bytes[1]; + swapped[7] = bytes[0]; + return putBytes(swapped, 8); +} + +inline BOOL ByteStreamOutOstreamBE::put16bitsBE(const U8* bytes) +{ + return putBytes(bytes, 2); +} + +inline BOOL ByteStreamOutOstreamBE::put32bitsBE(const U8* bytes) +{ + return putBytes(bytes, 4); +} + +inline BOOL ByteStreamOutOstreamBE::put64bitsBE(const U8* bytes) +{ + return putBytes(bytes, 8); +} + +#endif diff --git a/libs/laszip/src/endian.hpp b/libs/laszip/src/endian.hpp new file mode 100644 index 0000000..d4332ec --- /dev/null +++ b/libs/laszip/src/endian.hpp @@ -0,0 +1,105 @@ +/****************************************************************************** + * $Id$ + * + * Project: liblas - http://liblas.org - A BSD library for LAS format data. + * Purpose: Endian macros + * Author: Mateusz Loskot, mateusz@loskot.net + * + * This file has been stolen from and + * modified for libLAS purposes. + * + * (C) Copyright Mateusz Loskot 2007, mateusz@loskot.net + * (C) Copyright Caleb Epstein 2005 + * (C) Copyright John Maddock 2006 + * Distributed under the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * Revision History + * 06 Feb 2006 - Initial Revision + * 09 Nov 2006 - fixed variant and version bits for v4 guids + * 13 Nov 2006 - added serialization + * 17 Nov 2006 - added name-based guid creation + * 20 Nov 2006 - add fixes for gcc (from Tim Blechmann) + * 07 Mar 2007 - converted to header only + * 20 Jan 2008 - removed dependency of Boost and modified for LASZIP (by Mateusz Loskot) + ****************************************************************************** + * + * Copyright (c) 1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * + * + * Copyright notice reproduced from , from + * which this code was originally taken. + * + * Modified by Caleb Epstein to use with GNU libc and to + * defined the BOOST_ENDIAN macro. + ****************************************************************************/ + +#ifndef LASZIP_DETAIL_ENDIAN_HPP_INCLUDED +#define LASZIP_DETAIL_ENDIAN_HPP_INCLUDED + +// GNU libc offers the helpful header which defines +// __BYTE_ORDER + +#if defined (__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define LASZIP_LITTLE_ENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define LASZIP_BIG_ENDIAN +# elif (__BYTE_ORDER == __PDP_ENDIAN) +# define LASZIP_PDP_ENDIAN +# else +# error Unknown machine endianness detected. +# endif +# define LASZIP_BYTE_ORDER __BYTE_ORDER +#elif defined(_BIG_ENDIAN) +# define LASZIP_BIG_ENDIAN +# define LASZIP_BYTE_ORDER 4321 +#elif defined(_LITTLE_ENDIAN) +# define LASZIP_LITTLE_ENDIAN +# define LASZIP_BYTE_ORDER 1234 + +#elif defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN) +# define LASZIP_LITTLE_ENDIAN +# define LASZIP_BYTE_ORDER 1234 + +#elif defined(__sparc) || defined(__sparc__) \ + || defined(_POWER) || defined(__powerpc__) \ + || defined(__ppc__) || defined(__hpux) \ + || defined(_MIPSEB) || defined(_POWER) \ + || defined(__s390__) +# define LASZIP_BIG_ENDIAN +# define LASZIP_BYTE_ORDER 4321 +#elif defined(__i386__) || defined(__alpha__) \ + || defined(__ia64) || defined(__ia64__) \ + || defined(_M_IX86) || defined(_M_IA64) \ + || defined(_M_ALPHA) || defined(__amd64) \ + || defined(__amd64__) || defined(_M_AMD64) \ + || defined(__x86_64) || defined(__x86_64__) \ + || defined(_M_X64) + +# define LASZIP_LITTLE_ENDIAN +# define LASZIP_BYTE_ORDER 1234 +#else +# error The file src/endian.hpp needs to be set up for your CPU type. +#endif + + +#if defined(LASZIP_BIG_ENDIAN) +# error LASzip is not currently big endian safe. Please contact the authors on the liblas-devel mailing list for more information +#endif + + +#endif // LASZIP_DETAIL_ENDIAN_HPP_INCLUDED + diff --git a/libs/laszip/src/integercompressor.cpp b/libs/laszip/src/integercompressor.cpp new file mode 100644 index 0000000..25bc640 --- /dev/null +++ b/libs/laszip/src/integercompressor.cpp @@ -0,0 +1,548 @@ +/* +=============================================================================== + + FILE: integercompressor.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2005-2014, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ +#include "integercompressor.hpp" + +#define COMPRESS_ONLY_K +#undef COMPRESS_ONLY_K + +#define CREATE_HISTOGRAMS +#undef CREATE_HISTOGRAMS + +#include +#include + +#ifdef CREATE_HISTOGRAMS +#include +#endif + +IntegerCompressor::IntegerCompressor(ArithmeticEncoder* enc, U32 bits, U32 contexts, U32 bits_high, U32 range) +{ + assert(enc); + this->enc = enc; + this->dec = 0; + this->bits = bits; + this->contexts = contexts; + this->bits_high = bits_high; + this->range = range; + + if (range) // the corrector's significant bits and range + { + corr_bits = 0; + corr_range = range; + while (range) + { + range = range >> 1; + corr_bits++; + } + if (corr_range == (1u << (corr_bits-1))) + { + corr_bits--; + } + // the corrector must fall into this interval + corr_min = -((I32)(corr_range/2)); + corr_max = corr_min + corr_range - 1; + } + else if (bits && bits < 32) + { + corr_bits = bits; + corr_range = 1u << bits; + // the corrector must fall into this interval + corr_min = -((I32)(corr_range/2)); + corr_max = corr_min + corr_range - 1; + } + else + { + corr_bits = 32; + corr_range = 0; + // the corrector must fall into this interval + corr_min = I32_MIN; + corr_max = I32_MAX; + } + + k = 0; + + mBits = 0; + mCorrector = 0; + +#ifdef CREATE_HISTOGRAMS + corr_histogram = (int**)malloc(sizeof(int*) * (corr_bits+1)); + for (int k = 0; k <= corr_bits; k++) + { + corr_histogram[k] = (int*)malloc(sizeof(int) * ((1<enc = 0; + this->dec = dec; + this->bits = bits; + this->contexts = contexts; + this->bits_high = bits_high; + this->range = range; + + if (range) // the corrector's significant bits and range + { + corr_bits = 0; + corr_range = range; + while (range) + { + range = range >> 1; + corr_bits++; + } + if (corr_range == (1u << (corr_bits-1))) + { + corr_bits--; + } + // the corrector must fall into this interval + corr_min = -((I32)(corr_range/2)); + corr_max = corr_min + corr_range - 1; + } + else if (bits && bits < 32) + { + corr_bits = bits; + corr_range = 1u << bits; + // the corrector must fall into this interval + corr_min = -((I32)(corr_range/2)); + corr_max = corr_min + corr_range - 1; + } + else + { + corr_bits = 32; + corr_range = 0; + // the corrector must fall into this interval + corr_min = I32_MIN; + corr_max = I32_MAX; + } + + k = 0; + + mBits = 0; + mCorrector = 0; +} + +IntegerCompressor::~IntegerCompressor() +{ + U32 i; + if (mBits) + { + for (i = 0; i < contexts; i++) + { + if (enc) enc->destroySymbolModel(mBits[i]); + else dec->destroySymbolModel(mBits[i]); + } + delete [] mBits; + } +#ifndef COMPRESS_ONLY_K + if (mCorrector) + { + if (enc) enc->destroyBitModel((ArithmeticBitModel*)mCorrector[0]); + else dec->destroyBitModel((ArithmeticBitModel*)mCorrector[0]); + for (i = 1; i <= corr_bits; i++) + { + if (enc) enc->destroySymbolModel(mCorrector[i]); + else dec->destroySymbolModel(mCorrector[i]); + } + delete [] mCorrector; + } +#endif + +#ifdef CREATE_HISTOGRAMS + if (end) + { + int total_number = 0; + double total_entropy = 0.0f; + double total_raw = 0.0f; + for (int k = 0; k <= corr_bits; k++) + { + int number = 0; + int different = 0; + for (int c = 0; c <= (1<createSymbolModel(corr_bits+1); + } +#ifndef COMPRESS_ONLY_K + mCorrector = new ArithmeticModel*[corr_bits+1]; + mCorrector[0] = (ArithmeticModel*)enc->createBitModel(); + for (i = 1; i <= corr_bits; i++) + { + if (i <= bits_high) + { + mCorrector[i] = enc->createSymbolModel(1<createSymbolModel(1<initSymbolModel(mBits[i]); + } +#ifndef COMPRESS_ONLY_K + enc->initBitModel((ArithmeticBitModel*)mCorrector[0]); + for (i = 1; i <= corr_bits; i++) + { + enc->initSymbolModel(mCorrector[i]); + } +#endif +} + +void IntegerCompressor::compress(I32 pred, I32 real, U32 context) +{ + assert(enc); + // the corrector will be within the interval [ - (corr_range - 1) ... + (corr_range - 1) ] + I32 corr = real - pred; + // we fold the corrector into the interval [ corr_min ... corr_max ] + if (corr < corr_min) corr += corr_range; + else if (corr > corr_max) corr -= corr_range; + writeCorrector(corr, mBits[context]); +} + +void IntegerCompressor::initDecompressor() +{ + U32 i; + + assert(dec); + + // maybe create the models + if (mBits == 0) + { + mBits = new ArithmeticModel*[contexts]; + for (i = 0; i < contexts; i++) + { + mBits[i] = dec->createSymbolModel(corr_bits+1); + } +#ifndef COMPRESS_ONLY_K + mCorrector = new ArithmeticModel*[corr_bits+1]; + mCorrector[0] = (ArithmeticModel*)dec->createBitModel(); + for (i = 1; i <= corr_bits; i++) + { + if (i <= bits_high) + { + mCorrector[i] = dec->createSymbolModel(1<createSymbolModel(1<initSymbolModel(mBits[i]); + } +#ifndef COMPRESS_ONLY_K + dec->initBitModel((ArithmeticBitModel*)mCorrector[0]); + for (i = 1; i <= corr_bits; i++) + { + dec->initSymbolModel(mCorrector[i]); + } +#endif +} + +I32 IntegerCompressor::decompress(I32 pred, U32 context) +{ + assert(dec); + I32 real = pred + readCorrector(mBits[context]); + if (real < 0) real += corr_range; + else if ((U32)(real) >= corr_range) real -= corr_range; + return real; +} + +/* +static const char log_table256[256] = +{ + -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 +}; + +unsigned int v; // 32-bit word to find the log of +unsigned r; // r will be lg(v) +register unsigned int t, tt; // temporaries + +if (tt = v >> 16) +{ + r = (t = tt >> 8) ? 24 + LogTable256[t] : 16 + LogTable256[tt]; +} +else +{ + r = (t = v >> 8) ? 8 + LogTable256[t] : LogTable256[v]; +} +*/ + +void IntegerCompressor::writeCorrector(I32 c, ArithmeticModel* mBits) +{ + U32 c1; + + // find the tighest interval [ - (2^k - 1) ... + (2^k) ] that contains c + + k = 0; + + // do this by checking the absolute value of c (adjusted for the case that c is 2^k) + + c1 = (c <= 0 ? -c : c-1); + + // this loop could be replaced with more efficient code + + while (c1) + { + c1 = c1 >> 1; + k = k + 1; + } + + // the number k is between 0 and corr_bits and describes the interval the corrector falls into + // we can compress the exact location of c within this interval using k bits + + enc->encodeSymbol(mBits, k); + +#ifdef COMPRESS_ONLY_K + if (k) // then c is either smaller than 0 or bigger than 1 + { + assert((c != 0) && (c != 1)); + if (k < 32) + { + // translate the corrector c into the k-bit interval [ 0 ... 2^k - 1 ] + if (c < 0) // then c is in the interval [ - (2^k - 1) ... - (2^(k-1)) ] + { + // so we translate c into the interval [ 0 ... + 2^(k-1) - 1 ] by adding (2^k - 1) + enc->writeBits(k, c + ((1<writeBits(k, c - 1); +#ifdef CREATE_HISTOGRAMS + corr_histogram[k][c - 1]++; +#endif + } + } + } + else // then c is 0 or 1 + { + assert((c == 0) || (c == 1)); + enc->writeBit(c); +#ifdef CREATE_HISTOGRAMS + corr_histogram[0][c]++; +#endif + } +#else // COMPRESS_ONLY_K + if (k) // then c is either smaller than 0 or bigger than 1 + { + assert((c != 0) && (c != 1)); + if (k < 32) + { + // translate the corrector c into the k-bit interval [ 0 ... 2^k - 1 ] + if (c < 0) // then c is in the interval [ - (2^k - 1) ... - (2^(k-1)) ] + { + // so we translate c into the interval [ 0 ... + 2^(k-1) - 1 ] by adding (2^k - 1) + c += ((1<encodeSymbol(mCorrector[k], c); + } + else // for larger k we need to code the interval in two steps + { + // figure out how many lower bits there are + int k1 = k-bits_high; + // c1 represents the lowest k-bits_high+1 bits + c1 = c & ((1<> k1; + // compress the higher bits using a context table + enc->encodeSymbol(mCorrector[k], c); + // store the lower k1 bits raw + enc->writeBits(k1, c1); + } + } + } + else // then c is 0 or 1 + { + assert((c == 0) || (c == 1)); + enc->encodeBit((ArithmeticBitModel*)mCorrector[0],c); + } +#endif // COMPRESS_ONLY_K +} + +I32 IntegerCompressor::readCorrector(ArithmeticModel* mBits) +{ + I32 c; + + // decode within which interval the corrector is falling + + k = dec->decodeSymbol(mBits); + + // decode the exact location of the corrector within the interval + +#ifdef COMPRESS_ONLY_K + if (k) // then c is either smaller than 0 or bigger than 1 + { + if (k < 32) + { + c = dec->readBits(k); + + if (c >= (1<<(k-1))) // if c is in the interval [ 2^(k-1) ... + 2^k - 1 ] + { + // so we translate c back into the interval [ 2^(k-1) + 1 ... 2^k ] by adding 1 + c += 1; + } + else // otherwise c is in the interval [ 0 ... + 2^(k-1) - 1 ] + { + // so we translate c back into the interval [ - (2^k - 1) ... - (2^(k-1)) ] by subtracting (2^k - 1) + c -= ((1<readBit(); + } +#else // COMPRESS_ONLY_K + if (k) // then c is either smaller than 0 or bigger than 1 + { + if (k < 32) + { + if (k <= bits_high) // for small k we can do this in one step + { + // decompress c with the range coder + c = dec->decodeSymbol(mCorrector[k]); + } + else + { + // for larger k we need to do this in two steps + int k1 = k-bits_high; + // decompress higher bits with table + c = dec->decodeSymbol(mCorrector[k]); + // read lower bits raw + int c1 = dec->readBits(k1); + // put the corrector back together + c = (c << k1) | c1; + } + // translate c back into its correct interval + if (c >= (1<<(k-1))) // if c is in the interval [ 2^(k-1) ... + 2^k - 1 ] + { + // so we translate c back into the interval [ 2^(k-1) + 1 ... 2^k ] by adding 1 + c += 1; + } + else // otherwise c is in the interval [ 0 ... + 2^(k-1) - 1 ] + { + // so we translate c back into the interval [ - (2^k - 1) ... - (2^(k-1)) ] by subtracting (2^k - 1) + c -= ((1<decodeBit((ArithmeticBitModel*)mCorrector[0]); + } +#endif // COMPRESS_ONLY_K + + return c; +} diff --git a/libs/laszip/src/integercompressor.hpp b/libs/laszip/src/integercompressor.hpp new file mode 100644 index 0000000..4e1dcfb --- /dev/null +++ b/libs/laszip/src/integercompressor.hpp @@ -0,0 +1,103 @@ +/* +=============================================================================== + + FILE: integercompressor.hpp + + CONTENTS: + + This compressor provides three different contexts for encoding integer + numbers whose range may lie anywhere between 1 and 31 bits, which is + specified with the SetPrecision function. + + The compressor encodes two things: + + (1) the number k of miss-predicted low-order bits and + (2) the k-bit number that corrects the missprediction + + The k-bit number is usually coded broken in two chunks. The highest + bits are compressed using an arithmetic range table. The lower bits + are stored raw without predicive coding. How many of the higher bits + are compressed can be specified with bits_high. The default is 8. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2005-2014, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 6 September 2014 -- removed inheritance of EntropyEncoder and EntropyDecoder + 10 January 2011 -- licensing change for LGPL release and liblas integration + 10 December 2010 -- unified for all entropy coders at Baeckerei Schaefer + 31 October 2009 -- switched from the Rangecoder to the Entropycoder + 30 September 2005 -- now splitting the corrector into raw and compressed bits + 13 July 2005 -- created after returning with many mosquito bites from OBX + +=============================================================================== +*/ +#ifndef INTEGER_COMPRESSOR_HPP +#define INTEGER_COMPRESSOR_HPP + +#include "arithmeticencoder.hpp" +#include "arithmeticdecoder.hpp" + +class IntegerCompressor +{ +public: + + // Constructor & Deconstructor + IntegerCompressor(ArithmeticEncoder* enc, U32 bits=16, U32 contexts=1, U32 bits_high=8, U32 range=0); + IntegerCompressor(ArithmeticDecoder* dec, U32 bits=16, U32 contexts=1, U32 bits_high=8, U32 range=0); + ~IntegerCompressor(); + + // Manage Compressor + void initCompressor(); + void compress(I32 iPred, I32 iReal, U32 context=0); + + // Manage Decompressor + void initDecompressor(); + I32 decompress(I32 iPred, U32 context=0); + + // Get the k corrector bits from the last compress/decompress call + U32 getK() const {return k;}; + +private: + void writeCorrector(I32 c, ArithmeticModel* model); + I32 readCorrector(ArithmeticModel* model); + + U32 k; + + U32 contexts; + U32 bits_high; + + U32 bits; + U32 range; + + U32 corr_bits; + U32 corr_range; + I32 corr_min; + I32 corr_max; + + ArithmeticEncoder* enc; + ArithmeticDecoder* dec; + + ArithmeticModel** mBits; + + ArithmeticModel** mCorrector; + +#ifdef CREATE_HISTOGRAMS + int** corr_histogram; +#endif +}; + +#endif diff --git a/libs/laszip/src/lasattributer.hpp b/libs/laszip/src/lasattributer.hpp new file mode 100644 index 0000000..cbcc066 --- /dev/null +++ b/libs/laszip/src/lasattributer.hpp @@ -0,0 +1,562 @@ +/* +=============================================================================== + + FILE: lasattributer.hpp + + CONTENTS: + + This class assists with handling the "extra bytes" that allow storing + additional per point attributes. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2015, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the LICENSE.txt file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 13 September 2018 -- removed tuples and triple support from attributes + 19 July 2015 -- created after FOSS4GE in the train back from Lake Como + +=============================================================================== +*/ +#ifndef LAS_ATTRIBUTER_HPP +#define LAS_ATTRIBUTER_HPP + +#include "mydefs.hpp" + +#define LAS_ATTRIBUTE_U8 0 +#define LAS_ATTRIBUTE_I8 1 +#define LAS_ATTRIBUTE_U16 2 +#define LAS_ATTRIBUTE_I16 3 +#define LAS_ATTRIBUTE_U32 4 +#define LAS_ATTRIBUTE_I32 5 +#define LAS_ATTRIBUTE_U64 6 +#define LAS_ATTRIBUTE_I64 7 +#define LAS_ATTRIBUTE_F32 8 +#define LAS_ATTRIBUTE_F64 9 + +class LASattribute +{ +public: + U8 reserved[2]; // 2 bytes + U8 data_type; // 1 byte + U8 options; // 1 byte + CHAR name[32]; // 32 bytes + U8 unused[4]; // 4 bytes + U64I64F64 no_data[3]; // 24 = 3*8 bytes + U64I64F64 min[3]; // 24 = 3*8 bytes + U64I64F64 max[3]; // 24 = 3*8 bytes + F64 scale[3]; // 24 = 3*8 bytes + F64 offset[3]; // 24 = 3*8 bytes + CHAR description[32]; // 32 bytes + + LASattribute(U8 size) + { + if (size == 0) throw; + memset(this, 0, sizeof(LASattribute)); + scale[0] = scale[1] = scale[2] = 1.0; + this->options = size; + }; + + LASattribute(U32 type, const CHAR* name, const CHAR* description=0) + { + if (type > LAS_ATTRIBUTE_F64) throw; + if (name == 0) throw; + memset(this, 0, sizeof(LASattribute)); + scale[0] = scale[1] = scale[2] = 1.0; + this->data_type = type+1; + strncpy(this->name, name, 32); + if (description) strncpy(this->description, description, 32); + }; + + inline BOOL set_no_data(U8 no_data) { if (0 == get_type()) { this->no_data[0].u64 = no_data; options |= 0x01; return TRUE; } return FALSE; }; + inline BOOL set_no_data(I8 no_data) { if (1 == get_type()) { this->no_data[0].i64 = no_data; options |= 0x01; return TRUE; } return FALSE; }; + inline BOOL set_no_data(U16 no_data) { if (2 == get_type()) { this->no_data[0].u64 = no_data; options |= 0x01; return TRUE; } return FALSE; }; + inline BOOL set_no_data(I16 no_data) { if (3 == get_type()) { this->no_data[0].i64 = no_data; options |= 0x01; return TRUE; } return FALSE; }; + inline BOOL set_no_data(U32 no_data) { if (4 == get_type()) { this->no_data[0].u64 = no_data; options |= 0x01; return TRUE; } return FALSE; }; + inline BOOL set_no_data(I32 no_data) { if (5 == get_type()) { this->no_data[0].i64 = no_data; options |= 0x01; return TRUE; } return FALSE; }; + inline BOOL set_no_data(U64 no_data) { if (6 == get_type()) { this->no_data[0].u64 = no_data; options |= 0x01; return TRUE; } return FALSE; }; + inline BOOL set_no_data(I64 no_data) { if (7 == get_type()) { this->no_data[0].i64 = no_data; options |= 0x01; return TRUE; } return FALSE; }; + inline BOOL set_no_data(F32 no_data) { if (8 == get_type()) { this->no_data[0].f64 = no_data; options |= 0x01; return TRUE; } return FALSE; }; + inline BOOL set_no_data(F64 no_data) + { + switch (get_type()) + { + case 0: + case 2: + case 4: + case 6: + this->no_data[0].u64 = (U64)no_data; + options |= 0x01; + return TRUE; + case 1: + case 3: + case 5: + case 7: + this->no_data[0].i64 = (I64)no_data; + options |= 0x01; + return TRUE; + case 8: + case 9: + this->no_data[0].f64 = no_data; + options |= 0x01; + return TRUE; + } + return FALSE; + }; + + inline void set_min(U8* min) { this->min[0] = cast(min); options |= 0x02; }; + inline void update_min(U8* min) { this->min[0] = smallest(cast(min), this->min[0]); }; + inline BOOL set_min(U8 min) { if (0 == get_type()) { this->min[0].u64 = min; options |= 0x02; return TRUE; } return FALSE; }; + inline BOOL set_min(I8 min) { if (1 == get_type()) { this->min[0].i64 = min; options |= 0x02; return TRUE; } return FALSE; }; + inline BOOL set_min(U16 min) { if (2 == get_type()) { this->min[0].u64 = min; options |= 0x02; return TRUE; } return FALSE; }; + inline BOOL set_min(I16 min) { if (3 == get_type()) { this->min[0].i64 = min; options |= 0x02; return TRUE; } return FALSE; }; + inline BOOL set_min(U32 min) { if (4 == get_type()) { this->min[0].u64 = min; options |= 0x02; return TRUE; } return FALSE; }; + inline BOOL set_min(I32 min) { if (5 == get_type()) { this->min[0].i64 = min; options |= 0x02; return TRUE; } return FALSE; }; + inline BOOL set_min(U64 min) { if (6 == get_type()) { this->min[0].u64 = min; options |= 0x02; return TRUE; } return FALSE; }; + inline BOOL set_min(I64 min) { if (7 == get_type()) { this->min[0].i64 = min; options |= 0x02; return TRUE; } return FALSE; }; + inline BOOL set_min(F32 min) { if (8 == get_type()) { this->min[0].f64 = min; options |= 0x02; return TRUE; } return FALSE; }; + inline BOOL set_min(F64 min) { if (9 == get_type()) { this->min[0].f64 = min; options |= 0x02; return TRUE; } return FALSE; }; + + inline void set_max(U8* max) { this->max[0] = cast(max); options |= 0x04; }; + inline void update_max(U8* max) { this->max[0] = biggest(cast(max), this->max[0]); }; + inline BOOL set_max(U8 max) { if (0 == get_type()) { this->max[0].u64 = max; options |= 0x04; return TRUE; } return FALSE; }; + inline BOOL set_max(I8 max) { if (1 == get_type()) { this->max[0].i64 = max; options |= 0x04; return TRUE; } return FALSE; }; + inline BOOL set_max(U16 max) { if (2 == get_type()) { this->max[0].u64 = max; options |= 0x04; return TRUE; } return FALSE; }; + inline BOOL set_max(I16 max) { if (3 == get_type()) { this->max[0].i64 = max; options |= 0x04; return TRUE; } return FALSE; }; + inline BOOL set_max(U32 max) { if (4 == get_type()) { this->max[0].u64 = max; options |= 0x04; return TRUE; } return FALSE; }; + inline BOOL set_max(I32 max) { if (5 == get_type()) { this->max[0].i64 = max; options |= 0x04; return TRUE; } return FALSE; }; + inline BOOL set_max(U64 max) { if (6 == get_type()) { this->max[0].u64 = max; options |= 0x04; return TRUE; } return FALSE; }; + inline BOOL set_max(I64 max) { if (7 == get_type()) { this->max[0].i64 = max; options |= 0x04; return TRUE; } return FALSE; }; + inline BOOL set_max(F32 max) { if (8 == get_type()) { this->max[0].f64 = max; options |= 0x04; return TRUE; } return FALSE; }; + inline BOOL set_max(F64 max) { if (9 == get_type()) { this->max[0].f64 = max; options |= 0x04; return TRUE; } return FALSE; }; + + inline BOOL set_scale(F64 scale) { if (data_type) { this->scale[0] = scale; options |= 0x08; return TRUE; } return FALSE; }; + inline BOOL set_offset(F64 offset) { if (data_type) { this->offset[0] = offset; options |= 0x10; return TRUE; } return FALSE; }; + + inline BOOL unset_scale() { if (data_type) { options &= (~0x08); return TRUE; } return FALSE; }; + inline BOOL unset_offset() { if (data_type) { options &= (~0x10); return TRUE; } return FALSE; }; + + inline BOOL has_no_data() const { return options & 0x01; }; + inline BOOL has_min() const { return options & 0x02; }; + inline BOOL has_max() const { return options & 0x04; }; + inline BOOL has_scale() const { return options & 0x08; }; + inline BOOL has_offset() const { return options & 0x10; }; + + inline U32 get_size() const + { + if (data_type) + { + const U32 size_table[10] = { 1, 1, 2, 2, 4, 4, 8, 8, 4, 8 }; + I32 type = get_type(); + I32 dim = get_dim(); + return size_table[type] * dim; + } + else + { + return options; + } + }; + + inline F64 get_value_as_float(U8* pointer) const + { + F64 cast_value; + I32 type = get_type(); + if (type == 0) + cast_value = (F64)*((U8*)pointer); + else if (type == 1) + cast_value = (F64)*((I8*)pointer); + else if (type == 2) + cast_value = (F64)*((U16*)pointer); + else if (type == 3) + cast_value = (F64)*((I16*)pointer); + else if (type == 4) + cast_value = (F64)*((U32*)pointer); + else if (type == 5) + cast_value = (F64)*((I32*)pointer); + else if (type == 6) + cast_value = (F64)(I64)*((U64*)pointer); + else if (type == 7) + cast_value = (F64)*((I64*)pointer); + else if (type == 8) + cast_value = (F64)*((F32*)pointer); + else + cast_value = *((F64*)pointer); + if (options & 0x08) + { + if (options & 0x10) + { + return offset[0]+scale[0]*cast_value; + } + else + { + return scale[0]*cast_value; + } + } + else + { + if (options & 0x10) + { + return offset[0]+cast_value; + } + else + { + return cast_value; + } + } + }; + + inline void set_value_as_float(U8* pointer, F64 value) const + { + F64 unoffset_and_unscaled_value; + if (options & 0x08) + { + if (options & 0x10) + { + unoffset_and_unscaled_value = (value - offset[0])/scale[0]; + } + else + { + unoffset_and_unscaled_value = value/scale[0]; + } + } + else + { + if (options & 0x10) + { + unoffset_and_unscaled_value = value - offset[0]; + } + else + { + unoffset_and_unscaled_value = value; + } + } + I32 type = get_type(); + if (type == 0) + *((U8*)pointer) = U8_QUANTIZE(unoffset_and_unscaled_value); + else if (type == 1) + *((I8*)pointer) = I8_QUANTIZE(unoffset_and_unscaled_value); + else if (type == 2) + *((U16*)pointer) = U16_QUANTIZE(unoffset_and_unscaled_value); + else if (type == 3) + *((I16*)pointer) = I16_QUANTIZE(unoffset_and_unscaled_value); + else if (type == 4) + *((U32*)pointer) = U32_QUANTIZE(unoffset_and_unscaled_value); + else if (type == 5) + *((I32*)pointer) = U32_QUANTIZE(unoffset_and_unscaled_value); + else if (type == 6) + *((U64*)pointer) = U64_QUANTIZE(unoffset_and_unscaled_value); + else if (type == 7) + *((I64*)pointer) = I64_QUANTIZE(unoffset_and_unscaled_value); + else if (type == 8) + *((F32*)pointer) = (F32)unoffset_and_unscaled_value; + else + *((F64*)pointer) = unoffset_and_unscaled_value; + }; + +private: + inline I32 get_type() const + { + return ((I32)data_type - 1)%10; + }; + inline I32 get_dim() const // compute dimension of deprecated tuple and triple attributes + { + return ((I32)data_type - 1)/10 + 1; + }; + inline U64I64F64 cast(U8* pointer) const + { + I32 type = get_type(); + U64I64F64 cast_value; + if (type == 0) + cast_value.u64 = *((U8*)pointer); + else if (type == 1) + cast_value.i64 = *((I8*)pointer); + else if (type == 2) + cast_value.u64 = *((U16*)pointer); + else if (type == 3) + cast_value.i64 = *((I16*)pointer); + else if (type == 4) + cast_value.u64 = *((U32*)pointer); + else if (type == 5) + cast_value.i64 = *((I32*)pointer); + else if (type == 6) + cast_value.u64 = *((U64*)pointer); + else if (type == 7) + cast_value.i64 = *((I64*)pointer); + else if (type == 8) + cast_value.f64 = *((F32*)pointer); + else + cast_value.f64 = *((F64*)pointer); + return cast_value; + }; + inline U64I64F64 smallest(U64I64F64 a, U64I64F64 b) const + { + I32 type = get_type(); + if (type >= 8) // float compare + { + if (a.f64 < b.f64) return a; + else return b; + } + if (type & 1) // int compare + { + if (a.i64 < b.i64) return a; + else return b; + } + if (a.u64 < b.u64) return a; + else return b; + }; + inline U64I64F64 biggest(U64I64F64 a, U64I64F64 b) const + { + I32 type = get_type(); + if (type >= 8) // float compare + { + if (a.f64 > b.f64) return a; + else return b; + } + if (type & 1) // int compare + { + if (a.i64 > b.i64) return a; + else return b; + } + if (a.u64 > b.u64) return a; + else return b; + }; +}; + +class LASattributer +{ +public: + BOOL attributes_linked; + I32 number_attributes; + LASattribute* attributes; + I32* attribute_starts; + I32* attribute_sizes; + + LASattributer() + { + attributes_linked = TRUE; + number_attributes = 0; + attributes = 0; + attribute_starts = 0; + attribute_sizes = 0; + }; + + ~LASattributer() + { + clean_attributes(); + }; + + void clean_attributes() + { + if (attributes_linked) + { + if (attributes) + { + number_attributes = 0; + free(attributes); attributes = 0; + free(attribute_starts); attribute_starts = 0; + free(attribute_sizes); attribute_sizes = 0; + } + } + }; + + BOOL init_attributes(U32 number_attributes, LASattribute* attributes) + { + U32 i; + clean_attributes(); + this->number_attributes = number_attributes; + this->attributes = (LASattribute*)malloc(sizeof(LASattribute)*number_attributes); + if (this->attributes == 0) + { + return FALSE; + } + memcpy(this->attributes, attributes, sizeof(LASattribute)*number_attributes); + attribute_starts = (I32*)malloc(sizeof(I32)*number_attributes); + if (attribute_starts == 0) + { + return FALSE; + } + attribute_sizes = (I32*)malloc(sizeof(I32)*number_attributes); + if (attribute_sizes == 0) + { + return FALSE; + } + attribute_starts[0] = 0; + attribute_sizes[0] = attributes[0].get_size(); + for (i = 1; i < number_attributes; i++) + { + attribute_starts[i] = attribute_starts[i-1] + attribute_sizes[i-1]; + attribute_sizes[i] = attributes[i].get_size(); + } + return TRUE; + }; + + I32 add_attribute(const LASattribute attribute) + { + if (attribute.get_size()) + { + if (attributes) + { + number_attributes++; + attributes = (LASattribute*)realloc(attributes, sizeof(LASattribute)*number_attributes); + if (attributes == 0) + { + return -1; + } + attribute_starts = (I32*)realloc(attribute_starts, sizeof(I32)*number_attributes); + if (attribute_starts == 0) + { + return -1; + } + attribute_sizes = (I32*)realloc(attribute_sizes, sizeof(I32)*number_attributes); + if (attribute_sizes == 0) + { + return -1; + } + attributes[number_attributes-1] = attribute; + attribute_starts[number_attributes-1] = attribute_starts[number_attributes-2] + attribute_sizes[number_attributes-2]; + attribute_sizes[number_attributes-1] = attributes[number_attributes-1].get_size(); + } + else + { + number_attributes = 1; + attributes = (LASattribute*)malloc(sizeof(LASattribute)); + if (attributes == 0) + { + return -1; + } + attribute_starts = (I32*)malloc(sizeof(I32)); + if (attribute_starts == 0) + { + return -1; + } + attribute_sizes = (I32*)malloc(sizeof(I32)); + if (attribute_sizes == 0) + { + return -1; + } + attributes[0] = attribute; + attribute_starts[0] = 0; + attribute_sizes[0] = attributes[0].get_size(); + } + return number_attributes-1; + } + return -1; + }; + + inline I16 get_attributes_size() const + { + return (attributes ? attribute_starts[number_attributes-1] + attribute_sizes[number_attributes-1] : 0); + } + + I32 get_attribute_index(const CHAR* name) const + { + I32 i; + for (i = 0; i < number_attributes; i++) + { + if (strcmp(attributes[i].name, name) == 0) + { + return i; + } + } + return -1; + } + + I32 get_attribute_start(const CHAR* name) const + { + I32 i; + for (i = 0; i < number_attributes; i++) + { + if (strcmp(attributes[i].name, name) == 0) + { + return attribute_starts[i]; + } + } + return -1; + } + + I32 get_attribute_start(I32 index) const + { + if (index < number_attributes) + { + return attribute_starts[index]; + } + return -1; + } + + I32 get_attribute_size(I32 index) const + { + if (index < number_attributes) + { + return attribute_sizes[index]; + } + return -1; + } + + const CHAR* get_attribute_name(I32 index) const + { + if (index < number_attributes) + { + return attributes[index].name; + } + return 0; + } + + BOOL remove_attribute(I32 index) + { + if (index < 0 || index >= number_attributes) + { + return FALSE; + } + for (index = index + 1; index < number_attributes; index++) + { + attributes[index-1] = attributes[index]; + if (index > 1) + { + attribute_starts[index-1] = attribute_starts[index-2] + attribute_sizes[index-2]; + } + else + { + attribute_starts[index-1] = 0; + } + attribute_sizes[index-1] = attribute_sizes[index]; + } + number_attributes--; + if (number_attributes) + { + attributes = (LASattribute*)realloc(attributes, sizeof(LASattribute)*number_attributes); + attribute_starts = (I32*)realloc(attribute_starts, sizeof(I32)*number_attributes); + attribute_sizes = (I32*)realloc(attribute_sizes, sizeof(I32)*number_attributes); + } + else + { + free(attributes); attributes = 0; + free(attribute_starts); attribute_starts = 0; + free(attribute_sizes); attribute_sizes = 0; + } + return TRUE; + } + + BOOL remove_attribute(const CHAR* name) + { + I32 index = get_attribute_index(name); + if (index != -1) + { + return remove_attribute(index); + } + return FALSE; + } +}; + +#endif diff --git a/libs/laszip/src/lasindex.cpp b/libs/laszip/src/lasindex.cpp new file mode 100644 index 0000000..c16bdb4 --- /dev/null +++ b/libs/laszip/src/lasindex.cpp @@ -0,0 +1,701 @@ +/* +=============================================================================== + + FILE: lasindex.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2011-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the LICENSE.txt file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ +#include "lasindex.hpp" + +#include +#include +#include + +#include "lasquadtree.hpp" +#include "lasinterval.hpp" +#ifdef LASZIPDLL_EXPORTS +#include "lasreadpoint.hpp" +#else +#include "lasreader.hpp" +#endif +#include "bytestreamin_file.hpp" +#include "bytestreamout_file.hpp" + +#ifdef UNORDERED + // Figure out whether is in tr1 +# ifdef __has_include +# if __has_include() +# include + using namespace std; +# define UNORDERED_FOUND +# endif +# endif +# ifdef HAVE_UNORDERED_MAP +# include + using namespace std; +# elif defined(UNORDERED_FOUND) +# include + using namespace std; + using namespace tr1; +# endif +typedef unordered_map my_cell_hash; +#elif defined(LZ_WIN32_VC6) +#include +using namespace std; +typedef hash_map my_cell_hash; +#else +#include +using namespace std; +typedef unordered_map my_cell_hash; +#endif + +LASindex::LASindex() +{ + spatial = 0; + interval = 0; + have_interval = FALSE; + start = 0; + end = 0; + full = 0; + total = 0; + cells = 0; +} + +LASindex::~LASindex() +{ + if (spatial) delete spatial; + if (interval) delete interval; +} + +void LASindex::prepare(LASquadtree* spatial, I32 threshold) +{ + if (this->spatial) delete this->spatial; + this->spatial = spatial; + if (this->interval) delete this->interval; + this->interval = new LASinterval(threshold); +} + +BOOL LASindex::add(const F64 x, const F64 y, const U32 p_index) +{ + I32 cell = spatial->get_cell_index(x, y); + return interval->add(p_index, cell); +} + +void LASindex::complete(U32 minimum_points, I32 maximum_intervals, const BOOL verbose) +{ + if (verbose) + { + fprintf(stderr,"before complete %d %d\n", minimum_points, maximum_intervals); + print(FALSE); + } + if (minimum_points) + { + I32 hash1 = 0; + my_cell_hash cell_hash[2]; + // insert all cells into hash1 + interval->get_cells(); + while (interval->has_cells()) + { + cell_hash[hash1][interval->index] = interval->full; + } + while (cell_hash[hash1].size()) + { + I32 hash2 = (hash1+1)%2; + cell_hash[hash2].clear(); + // coarsen if a coarser cell will still have fewer than minimum_points (and points in all subcells) + BOOL coarsened = FALSE; + U32 i, full; + I32 coarser_index; + U32 num_indices; + U32 num_filled; + I32* indices; + my_cell_hash::iterator hash_element_inner; + my_cell_hash::iterator hash_element_outer = cell_hash[hash1].begin(); + while (hash_element_outer != cell_hash[hash1].end()) + { + if ((*hash_element_outer).second) + { + if (spatial->coarsen((*hash_element_outer).first, &coarser_index, &num_indices, &indices)) + { + full = 0; + num_filled = 0; + for (i = 0; i < num_indices; i++) + { + if ((*hash_element_outer).first == indices[i]) + { + hash_element_inner = hash_element_outer; + } + else + { + hash_element_inner = cell_hash[hash1].find(indices[i]); + } + if (hash_element_inner != cell_hash[hash1].end()) + { + full += (*hash_element_inner).second; + (*hash_element_inner).second = 0; + num_filled++; + } + } + if ((full < minimum_points) && (num_filled == num_indices)) + { + interval->merge_cells(num_indices, indices, coarser_index); + coarsened = TRUE; + cell_hash[hash2][coarser_index] = full; + } + } + } + hash_element_outer++; + } + if (!coarsened) break; + hash1 = (hash1+1)%2; + } + // tell spatial about the existing cells + interval->get_cells(); + while (interval->has_cells()) + { + spatial->manage_cell(interval->index); + } + if (verbose) + { + fprintf(stderr,"after minimum_points %d\n", minimum_points); + print(FALSE); + } + } + if (maximum_intervals < 0) + { + maximum_intervals = -maximum_intervals*interval->get_number_cells(); + } + if (maximum_intervals) + { + interval->merge_intervals(maximum_intervals, verbose); + if (verbose) + { + fprintf(stderr,"after maximum_intervals %d\n", maximum_intervals); + print(FALSE); + } + } +} + +void LASindex::print(BOOL verbose) +{ + U32 total_cells = 0; + U32 total_full = 0; + U32 total_total = 0; + U32 total_intervals = 0; + U32 total_check; + U32 intervals; + interval->get_cells(); + while (interval->has_cells()) + { + total_check = 0; + intervals = 0; + while (interval->has_intervals()) + { + total_check += interval->end-interval->start+1; + intervals++; + } + if (total_check != interval->total) + { + fprintf(stderr,"ERROR: total_check %d != interval->total %d\n", total_check, interval->total); + } + if (verbose) fprintf(stderr,"cell %d intervals %d full %d total %d (%.2f)\n", interval->index, intervals, interval->full, interval->total, 100.0f*interval->full/interval->total); + total_cells++; + total_full += interval->full; + total_total += interval->total; + total_intervals += intervals; + } + if (verbose) fprintf(stderr,"total cells/intervals %d/%d full %d (%.2f)\n", total_cells, total_intervals, total_full, 100.0f*total_full/total_total); +} + +LASquadtree* LASindex::get_spatial() const +{ + return spatial; +} + +LASinterval* LASindex::get_interval() const +{ + return interval; +} + +BOOL LASindex::intersect_rectangle(const F64 r_min_x, const F64 r_min_y, const F64 r_max_x, const F64 r_max_y) +{ + have_interval = FALSE; + cells = spatial->intersect_rectangle(r_min_x, r_min_y, r_max_x, r_max_y); +// fprintf(stderr,"%d cells of %g/%g %g/%g intersect rect %g/%g %g/%g\n", num_cells, spatial->get_min_x(), spatial->get_min_y(), spatial->get_max_x(), spatial->get_max_y(), r_min_x, r_min_y, r_max_x, r_max_y); + if (cells) + return merge_intervals(); + return FALSE; +} + +BOOL LASindex::intersect_tile(const F32 ll_x, const F32 ll_y, const F32 size) +{ + have_interval = FALSE; + cells = spatial->intersect_tile(ll_x, ll_y, size); +// fprintf(stderr,"%d cells of %g/%g %g/%g intersect tile %g/%g/%g\n", num_cells, spatial->get_min_x(), spatial->get_min_y(), spatial->get_max_x(), spatial->get_max_y(), ll_x, ll_y, size); + if (cells) + return merge_intervals(); + return FALSE; +} + +BOOL LASindex::intersect_circle(const F64 center_x, const F64 center_y, const F64 radius) +{ + have_interval = FALSE; + cells = spatial->intersect_circle(center_x, center_y, radius); +// fprintf(stderr,"%d cells of %g/%g %g/%g intersect circle %g/%g/%g\n", num_cells, spatial->get_min_x(), spatial->get_min_y(), spatial->get_max_x(), spatial->get_max_y(), center_x, center_y, radius); + if (cells) + return merge_intervals(); + return FALSE; +} + +BOOL LASindex::get_intervals() +{ + have_interval = FALSE; + return interval->get_merged_cell(); +} + +BOOL LASindex::has_intervals() +{ + if (interval->has_intervals()) + { + start = interval->start; + end = interval->end; + full = interval->full; + have_interval = TRUE; + return TRUE; + } + have_interval = FALSE; + return FALSE; +} + +BOOL LASindex::read(FILE* file) +{ + if (file == 0) return FALSE; + ByteStreamIn* stream; + if (IS_LITTLE_ENDIAN()) + stream = new ByteStreamInFileLE(file); + else + stream = new ByteStreamInFileBE(file); + if (!read(stream)) + { + delete stream; + return FALSE; + } + delete stream; + return TRUE; +} + +BOOL LASindex::write(FILE* file) const +{ + if (file == 0) return FALSE; + ByteStreamOut* stream; + if (IS_LITTLE_ENDIAN()) + stream = new ByteStreamOutFileLE(file); + else + stream = new ByteStreamOutFileBE(file); + if (!write(stream)) + { + delete stream; + return FALSE; + } + delete stream; + return TRUE; +} + +BOOL LASindex::read(const char* file_name) +{ + if (file_name == 0) return FALSE; + char* name = LASCopyString(file_name); + if (strstr(file_name, ".las") || strstr(file_name, ".laz")) + { + name[strlen(name)-1] = 'x'; + } + else if (strstr(file_name, ".LAS") || strstr(file_name, ".LAZ")) + { + name[strlen(name)-1] = 'X'; + } + else + { + name[strlen(name)-3] = 'l'; + name[strlen(name)-2] = 'a'; + name[strlen(name)-1] = 'x'; + } +#ifdef _MSC_VER + wchar_t* utf16_name = UTF8toUTF16(name); + FILE* file = _wfopen(utf16_name, L"rb"); + delete [] utf16_name; +#else + FILE* file = fopen(name, "rb"); +#endif + if (file == 0) + { + free(name); + return FALSE; + } + if (!read(file)) + { + fprintf(stderr,"ERROR (LASindex): cannot read '%s'\n", name); + fclose(file); + free(name); + return FALSE; + } + fclose(file); + free(name); + return TRUE; +} + +BOOL LASindex::append(const char* file_name) const +{ +#ifdef LASZIPDLL_EXPORTS + return FALSE; +#else + LASreadOpener lasreadopener; + + if (file_name == 0) return FALSE; + + // open reader + + LASreader* lasreader = lasreadopener.open(file_name); + if (lasreader == 0) return FALSE; + if (lasreader->header.laszip == 0) return FALSE; + + // close reader + + lasreader->close(); + +#ifdef _MSC_VER + wchar_t* utf16_file_name = UTF8toUTF16(file_name); + FILE* file = _wfopen(utf16_file_name, L"rb"); + delete [] utf16_file_name; +#else + FILE* file = fopen(file_name, "rb"); +#endif + ByteStreamIn* bytestreamin = 0; + if (IS_LITTLE_ENDIAN()) + bytestreamin = new ByteStreamInFileLE(file); + else + bytestreamin = new ByteStreamInFileBE(file); + + // maybe write LASindex EVLR start position into LASzip VLR + + I64 offset_laz_vlr = -1; + + // where to write LASindex EVLR that will contain the LAX file + + I64 number_of_special_evlrs = lasreader->header.laszip->number_of_special_evlrs; + I64 offset_to_special_evlrs = lasreader->header.laszip->offset_to_special_evlrs; + + if ((number_of_special_evlrs == -1) && (offset_to_special_evlrs == -1)) + { + bytestreamin->seekEnd(); + number_of_special_evlrs = 1; + offset_to_special_evlrs = bytestreamin->tell(); + + // find LASzip VLR + + I64 total = lasreader->header.header_size + 2; + U32 number_of_variable_length_records = lasreader->header.number_of_variable_length_records + 1 + (lasreader->header.vlr_lastiling != 0) + (lasreader->header.vlr_lasoriginal != 0); + + for (U32 u = 0; u < number_of_variable_length_records; u++) + { + bytestreamin->seek(total); + + CHAR user_id[16]; + try { bytestreamin->getBytes((U8*)user_id, 16); } catch(...) + { + fprintf(stderr,"ERROR: reading header.vlrs[%d].user_id\n", u); + return FALSE; + } + if (strcmp(user_id, "laszip encoded") == 0) + { + offset_laz_vlr = bytestreamin->tell() - 18; + break; + } + U16 record_id; + try { bytestreamin->get16bitsLE((U8*)&record_id); } catch(...) + { + fprintf(stderr,"ERROR: reading header.vlrs[%d].record_id\n", u); + return FALSE; + } + U16 record_length_after_header; + try { bytestreamin->get16bitsLE((U8*)&record_length_after_header); } catch(...) + { + fprintf(stderr,"ERROR: reading header.vlrs[%d].record_length_after_header\n", u); + return FALSE; + } + total += (54 + record_length_after_header); + } + + if (number_of_special_evlrs == -1) return FALSE; + } + + delete bytestreamin; + fclose(file); + + ByteStreamOut* bytestreamout; +#ifdef _MSC_VER + utf16_file_name = UTF8toUTF16(file_name); + file = _wfopen(utf16_file_name, L"rb+"); + delete [] utf16_file_name; +#else + file = fopen(file_name, "rb+"); +#endif + if (IS_LITTLE_ENDIAN()) + bytestreamout = new ByteStreamOutFileLE(file); + else + bytestreamout = new ByteStreamOutFileBE(file); + bytestreamout->seek(offset_to_special_evlrs); + + LASevlr lax_evlr; + sprintf(lax_evlr.user_id, "LAStools"); + lax_evlr.record_id = 30; + sprintf(lax_evlr.description, "LAX spatial indexing (LASindex)"); + + bytestreamout->put16bitsLE((const U8*)&(lax_evlr.reserved)); + bytestreamout->putBytes((const U8*)lax_evlr.user_id, 16); + bytestreamout->put16bitsLE((const U8*)&(lax_evlr.record_id)); + bytestreamout->put64bitsLE((const U8*)&(lax_evlr.record_length_after_header)); + bytestreamout->putBytes((const U8*)lax_evlr.description, 32); + + if (!write(bytestreamout)) + { + fprintf(stderr,"ERROR (LASindex): cannot append LAX to '%s'\n", file_name); + delete bytestreamout; + fclose(file); + delete lasreader; + return FALSE; + } + + // update LASindex EVLR + + lax_evlr.record_length_after_header = bytestreamout->tell() - offset_to_special_evlrs - 60; + bytestreamout->seek(offset_to_special_evlrs + 20); + bytestreamout->put64bitsLE((const U8*)&(lax_evlr.record_length_after_header)); + + // maybe update LASzip VLR + + if (number_of_special_evlrs != -1) + { + bytestreamout->seek(offset_laz_vlr + 54 + 16); + bytestreamout->put64bitsLE((const U8*)&number_of_special_evlrs); + bytestreamout->put64bitsLE((const U8*)&offset_to_special_evlrs); + } + + // close writer + + bytestreamout->seekEnd(); + delete bytestreamout; + fclose(file); + + // delete reader + + delete lasreader; + + return TRUE; +#endif +} + +BOOL LASindex::write(const char* file_name) const +{ + if (file_name == 0) return FALSE; + char* name = LASCopyString(file_name); + if (strstr(file_name, ".las") || strstr(file_name, ".laz")) + { + name[strlen(name)-1] = 'x'; + } + else if (strstr(file_name, ".LAS") || strstr(file_name, ".LAZ")) + { + name[strlen(name)-1] = 'X'; + } + else + { + name[strlen(name)-3] = 'l'; + name[strlen(name)-2] = 'a'; + name[strlen(name)-1] = 'x'; + } +#ifdef _MSC_VER + wchar_t* utf16_name = UTF8toUTF16(name); + FILE* file = _wfopen(utf16_name, L"wb"); + delete [] utf16_name; +#else + FILE* file = fopen(name, "wb"); +#endif + if (file == 0) + { + fprintf(stderr,"ERROR (LASindex): cannot open '%s' for write\n", name); + free(name); + return FALSE; + } + if (!write(file)) + { + fprintf(stderr,"ERROR (LASindex): cannot write '%s'\n", name); + fclose(file); + free(name); + return FALSE; + } + fclose(file); + free(name); + return TRUE; +} + +BOOL LASindex::read(ByteStreamIn* stream) +{ + if (spatial) + { + delete spatial; + spatial = 0; + } + if (interval) + { + delete interval; + interval = 0; + } + char signature[4]; + try { stream->getBytes((U8*)signature, 4); } catch (...) + { + fprintf(stderr,"ERROR (LASindex): reading signature\n"); + return FALSE; + } + if (strncmp(signature, "LASX", 4) != 0) + { + fprintf(stderr,"ERROR (LASindex): wrong signature %4s instead of 'LASX'\n", signature); + return FALSE; + } + U32 version; + try { stream->get32bitsLE((U8*)&version); } catch (...) + { + fprintf(stderr,"ERROR (LASindex): reading version\n"); + return FALSE; + } + // read spatial quadtree + spatial = new LASquadtree(); + if (!spatial->read(stream)) + { + fprintf(stderr,"ERROR (LASindex): cannot read LASspatial (LASquadtree)\n"); + return FALSE; + } + // read interval + interval = new LASinterval(); + if (!interval->read(stream)) + { + fprintf(stderr,"ERROR (LASindex): reading LASinterval\n"); + return FALSE; + } + // tell spatial about the existing cells + interval->get_cells(); + while (interval->has_cells()) + { + spatial->manage_cell(interval->index); + } + return TRUE; +} + +BOOL LASindex::write(ByteStreamOut* stream) const +{ + if (!stream->putBytes((const U8*)"LASX", 4)) + { + fprintf(stderr,"ERROR (LASindex): writing signature\n"); + return FALSE; + } + U32 version = 0; + if (!stream->put32bitsLE((const U8*)&version)) + { + fprintf(stderr,"ERROR (LASindex): writing version\n"); + return FALSE; + } + // write spatial quadtree + if (!spatial->write(stream)) + { + fprintf(stderr,"ERROR (LASindex): cannot write LASspatial (LASquadtree)\n"); + return FALSE; + } + // write interval + if (!interval->write(stream)) + { + fprintf(stderr,"ERROR (LASindex): writing LASinterval\n"); + return FALSE; + } + return TRUE; +} + +// seek to next interval point + +#ifdef LASZIPDLL_EXPORTS +BOOL LASindex::seek_next(LASreadPoint* reader, I64 &p_count) +{ + if (!have_interval) + { + if (!has_intervals()) return FALSE; + reader->seek((U32)p_count, start); + p_count = start; + } + if (p_count == end) + { + have_interval = FALSE; + } + return TRUE; +} +#else +BOOL LASindex::seek_next(LASreader* lasreader) +{ + if (!have_interval) + { + if (!has_intervals()) return FALSE; + lasreader->seek(start); + } + if (lasreader->p_count == end) + { + have_interval = FALSE; + } + return TRUE; +} +#endif + +// merge the intervals of non-empty cells +BOOL LASindex::merge_intervals() +{ + if (spatial->get_intersected_cells()) + { + U32 used_cells = 0; + while (spatial->has_more_cells()) + { + if (interval->get_cell(spatial->current_cell)) + { + interval->add_current_cell_to_merge_cell_set(); + used_cells++; + } + } +// fprintf(stderr,"LASindex: used %d cells of total %d\n", used_cells, interval->get_number_cells()); + if (used_cells) + { + BOOL r = interval->merge(); + full = interval->full; + total = interval->total; + interval->clear_merge_cell_set(); + return r; + } + } + return FALSE; +} diff --git a/libs/laszip/src/lasindex.hpp b/libs/laszip/src/lasindex.hpp new file mode 100644 index 0000000..4890f17 --- /dev/null +++ b/libs/laszip/src/lasindex.hpp @@ -0,0 +1,113 @@ +/* +=============================================================================== + + FILE: lasindex.hpp + + CONTENTS: + + This class can create a spatial indexing, store a spatial indexing, write + a spatial indexing to file, read a spatial indexing from file, and - most + importantly - it can be used together with a lasreader for efficient access + to a particular spatial region of a LAS file or a LAZ file. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the LICENSE.txt file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 7 September 2018 -- replaced calls to _strdup with calls to the LASCopyString macro + 7 January 2017 -- add read(FILE* file) for Trimble LASzip DLL improvement + 2 April 2015 -- add seek_next(LASreadPoint* reader, I64 &p_count) for DLL + 2 April 2015 -- delete read_next(LASreader* lasreader) that was not used + 31 March 2015 -- remove unused LASquadtree inheritance of abstract LASspatial + 29 April 2011 -- created after cable outage during the royal wedding (-: + +=============================================================================== +*/ +#ifndef LAS_INDEX_HPP +#define LAS_INDEX_HPP + +#include + +#include "mydefs.hpp" + +class LASquadtree; +class LASinterval; +#ifdef LASZIPDLL_EXPORTS +class LASreadPoint; +#else +class LASreader; +#endif +class ByteStreamIn; +class ByteStreamOut; + +class LASLIB_DLL LASindex +{ +public: + LASindex(); + ~LASindex(); + + // create spatial index + void prepare(LASquadtree* spatial, I32 threshold=1000); + BOOL add(const F64 x, const F64 y, const U32 index); + void complete(U32 minimum_points=100000, I32 maximum_intervals=-1, const BOOL verbose=TRUE); + + // read from file or write to file + BOOL read(FILE* file); + BOOL write(FILE* file) const; + BOOL read(const char* file_name); + BOOL append(const char* file_name) const; + BOOL write(const char* file_name) const; + BOOL read(ByteStreamIn* stream); + BOOL write(ByteStreamOut* stream) const; + + // intersect + BOOL intersect_rectangle(const F64 r_min_x, const F64 r_min_y, const F64 r_max_x, const F64 r_max_y); + BOOL intersect_tile(const F32 ll_x, const F32 ll_y, const F32 size); + BOOL intersect_circle(const F64 center_x, const F64 center_y, const F64 radius); + + // access the intersected intervals + BOOL get_intervals(); + BOOL has_intervals(); + + U32 start; + U32 end; + U32 full; + U32 total; + U32 cells; + + // seek to next interval +#ifdef LASZIPDLL_EXPORTS + BOOL seek_next(LASreadPoint* reader, I64 &p_count); +#else + BOOL seek_next(LASreader* lasreader); +#endif + + // for debugging + void print(BOOL verbose); + + // for visualization + LASquadtree* get_spatial() const; + LASinterval* get_interval() const; + +private: + BOOL merge_intervals(); + + LASquadtree* spatial; + LASinterval* interval; + BOOL have_interval; +}; + +#endif diff --git a/libs/laszip/src/lasinterval.cpp b/libs/laszip/src/lasinterval.cpp new file mode 100644 index 0000000..b80f41d --- /dev/null +++ b/libs/laszip/src/lasinterval.cpp @@ -0,0 +1,733 @@ +/* +=============================================================================== + + FILE: lasinterval.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2011-2015, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the LICENSE.txt file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ +#include "lasinterval.hpp" +#include "laszip.hpp" + +#include "bytestreamin.hpp" +#include "bytestreamout.hpp" + +#include +#include +#include +#include + +#include +#include +using namespace std; + +#ifdef UNORDERED +// Figure out whether is in tr1 +# ifdef __has_include +# if __has_include() +# include + using namespace std; +# define UNORDERED_FOUND +# endif +# endif +# ifdef HAVE_UNORDERED_MAP +# include + using namespace std; +# elif defined(UNORDERED_FOUND) +# include + using namespace std; + using namespace tr1; +# endif +typedef unordered_map my_cell_hash; +#elif defined(LZ_WIN32_VC6) +#include +using namespace std; +typedef hash_map my_cell_hash; +#else +#include +using namespace std; +typedef unordered_map my_cell_hash; +#endif + +typedef multimap my_cell_map; +typedef set my_cell_set; + +LASintervalCell::LASintervalCell() +{ + start = 0; + end = 0; + next = 0; +} + +LASintervalCell::LASintervalCell(const U32 p_index) +{ + start = p_index; + end = p_index; + next = 0; +} + +LASintervalCell::LASintervalCell(const LASintervalCell* cell) +{ + start = cell->start; + end = cell->end; + next = 0; +} + +LASintervalStartCell::LASintervalStartCell() : LASintervalCell() +{ + full = 0; + total = 0; + last = 0; +} + +LASintervalStartCell::LASintervalStartCell(const U32 p_index) : LASintervalCell(p_index) +{ + full = 1; + total = 1; + last = 0; +} + +BOOL LASintervalStartCell::add(const U32 p_index, const U32 threshold) +{ + U32 current_end = (last ? last->end : end); + assert(p_index > current_end); + U32 diff = p_index - current_end; + full++; + if (diff > threshold) + { + if (last) + { + last->next = new LASintervalCell(p_index); + last = last->next; + } + else + { + next = new LASintervalCell(p_index); + last = next; + } + total++; + return TRUE; // created new interval + } + if (last) + { + last->end = p_index; + } + else + { + end = p_index; + } + total += diff; + return FALSE; // added to interval +} + +BOOL LASinterval::add(const U32 p_index, const I32 c_index) +{ + if (last_cell == 0 || last_index != c_index) + { + last_index = c_index; + my_cell_hash::iterator hash_element = ((my_cell_hash*)cells)->find(c_index); + if (hash_element == ((my_cell_hash*)cells)->end()) + { + last_cell = new LASintervalStartCell(p_index); + ((my_cell_hash*)cells)->insert(my_cell_hash::value_type(c_index, last_cell)); + number_intervals++; + return TRUE; + } + last_cell = (*hash_element).second; + } + if (last_cell->add(p_index, threshold)) + { + number_intervals++; + return TRUE; + } + return FALSE; +} + +// get total number of cells +U32 LASinterval::get_number_cells() const +{ + return (U32)((my_cell_hash*)cells)->size(); +} + +// get total number of intervals +U32 LASinterval::get_number_intervals() const +{ + return number_intervals; +} + +// merge cells (and their intervals) into one cell +BOOL LASinterval::merge_cells(const U32 num_indices, const I32* indices, const I32 new_index) +{ + U32 i; + if (num_indices == 1) + { + my_cell_hash::iterator hash_element = ((my_cell_hash*)cells)->find(indices[0]); + if (hash_element == ((my_cell_hash*)cells)->end()) + { + return FALSE; + } + ((my_cell_hash*)cells)->insert(my_cell_hash::value_type(new_index, (*hash_element).second)); + ((my_cell_hash*)cells)->erase(hash_element); + } + else + { + if (cells_to_merge) ((my_cell_set*)cells_to_merge)->clear(); + for (i = 0; i < num_indices; i++) + { + add_cell_to_merge_cell_set(indices[i], TRUE); + } + if (!merge(TRUE)) return FALSE; + ((my_cell_hash*)cells)->insert(my_cell_hash::value_type(new_index, merged_cells)); + merged_cells = 0; + } + return TRUE; +} + +// merge adjacent intervals with small gaps in cells to reduce total interval number to maximum +void LASinterval::merge_intervals(U32 maximum_intervals, const BOOL verbose) +{ + U32 diff; + LASintervalCell* cell; + LASintervalCell* delete_cell; + + // each cell has minimum one interval + + if (maximum_intervals < get_number_cells()) + { + maximum_intervals = 0; + } + else + { + maximum_intervals -= get_number_cells(); + } + + // order intervals by smallest gap + + my_cell_map map; + my_cell_hash::iterator hash_element = ((my_cell_hash*)cells)->begin(); + while (hash_element != ((my_cell_hash*)cells)->end()) + { + cell = (*hash_element).second; + while (cell->next) + { + diff = cell->next->start - cell->end - 1; + map.insert(my_cell_map::value_type(diff, cell)); + cell = cell->next; + } + hash_element++; + } + + // maybe nothing to do + if (map.size() <= maximum_intervals) + { + if (verbose) + { + if (map.size() == 0) + { + fprintf(stderr,"maximum_intervals: %u number of interval gaps: 0 \n", maximum_intervals); + } + else + { + diff = (*(map.begin())).first; + fprintf(stderr,"maximum_intervals: %u number of interval gaps: %u next largest interval gap %u\n", maximum_intervals, (U32)map.size(), diff); + } + } + return; + } + + my_cell_map::iterator map_element; + U32 size = (U32)map.size(); + + while (size > maximum_intervals) + { + map_element = map.begin(); + diff = (*map_element).first; + cell = (*map_element).second; + map.erase(map_element); + if ((cell->start == 1) && (cell->end == 0)) // the (start == 1 && end == 0) signals that the cell is to be deleted + { + number_intervals--; + delete cell; + } + else + { + delete_cell = cell->next; + cell->end = delete_cell->end; + cell->next = delete_cell->next; + if (cell->next) + { + map.insert(my_cell_map::value_type(cell->next->start - cell->end - 1, cell)); + delete_cell->start = 1; delete_cell->end = 0; // the (start == 1 && end == 0) signals that the cell is to be deleted + } + else + { + number_intervals--; + delete delete_cell; + } + size--; + } + } + map_element = map.begin(); + while (true) + { + if (map_element == map.end()) break; + cell = (*map_element).second; + if ((cell->start == 1) && (cell->end == 0)) // the (start == 1 && end == 0) signals that the cell is to be deleted + { + number_intervals--; + delete cell; + } + map_element++; + } + if (verbose) fprintf(stderr,"largest interval gap increased to %u\n", diff); + + // update totals + + LASintervalStartCell* start_cell; + hash_element = ((my_cell_hash*)cells)->begin(); + while (hash_element != ((my_cell_hash*)cells)->end()) + { + start_cell = (*hash_element).second; + start_cell->total = 0; + cell = start_cell; + while (cell) + { + start_cell->total += (cell->end - cell->start + 1); + cell = cell->next; + } + hash_element++; + } +} + +void LASinterval::get_cells() +{ + last_index = I32_MIN; + current_cell = 0; +} + +BOOL LASinterval::has_cells() +{ + my_cell_hash::iterator hash_element; + if (last_index == I32_MIN) + { + hash_element = ((my_cell_hash*)cells)->begin(); + } + else + { + hash_element = ((my_cell_hash*)cells)->find(last_index); + hash_element++; + } + if (hash_element == ((my_cell_hash*)cells)->end()) + { + last_index = I32_MIN; + current_cell = 0; + return FALSE; + } + last_index = (*hash_element).first; + index = (*hash_element).first; + full = (*hash_element).second->full; + total = (*hash_element).second->total; + current_cell = (*hash_element).second; + return TRUE; +} + +BOOL LASinterval::get_cell(const I32 c_index) +{ + my_cell_hash::iterator hash_element = ((my_cell_hash*)cells)->find(c_index); + if (hash_element == ((my_cell_hash*)cells)->end()) + { + current_cell = 0; + return FALSE; + } + index = (*hash_element).first; + full = (*hash_element).second->full; + total = (*hash_element).second->total; + current_cell = (*hash_element).second; + return TRUE; +} + +BOOL LASinterval::add_current_cell_to_merge_cell_set() +{ + if (current_cell == 0) + { + return FALSE; + } + if (cells_to_merge == 0) + { + cells_to_merge = (void*) new my_cell_set; + } + ((my_cell_set*)cells_to_merge)->insert((LASintervalStartCell*)current_cell); + return TRUE; +} + +BOOL LASinterval::add_cell_to_merge_cell_set(const I32 c_index, const BOOL erase) +{ + my_cell_hash::iterator hash_element = ((my_cell_hash*)cells)->find(c_index); + if (hash_element == ((my_cell_hash*)cells)->end()) + { + return FALSE; + } + if (cells_to_merge == 0) + { + cells_to_merge = (void*) new my_cell_set; + } + ((my_cell_set*)cells_to_merge)->insert((*hash_element).second); + if (erase) ((my_cell_hash*)cells)->erase(hash_element); + return TRUE; +} + +BOOL LASinterval::merge(const BOOL erase) +{ + // maybe delete temporary merge cells from the previous merge + if (merged_cells) + { + if (merged_cells_temporary) + { + LASintervalCell* next_next; + LASintervalCell* next = merged_cells->next; + while (next) + { + next_next = next->next; + delete next; + next = next_next; + } + delete merged_cells; + } + merged_cells = 0; + } + // are there cells to merge + if (cells_to_merge == 0) return FALSE; + if (((my_cell_set*)cells_to_merge)->size() == 0) return FALSE; + // is there just one cell + if (((my_cell_set*)cells_to_merge)->size() == 1) + { + merged_cells_temporary = FALSE; + // simply use this cell as the merge cell + my_cell_set::iterator set_element = ((my_cell_set*)cells_to_merge)->begin(); + merged_cells = (*set_element); + } + else + { + merged_cells_temporary = TRUE; + merged_cells = new LASintervalStartCell(); + // iterate over all cells and add their intervals to map + LASintervalCell* cell; + my_cell_map map; + my_cell_set::iterator set_element = ((my_cell_set*)cells_to_merge)->begin(); + while (true) + { + if (set_element == ((my_cell_set*)cells_to_merge)->end()) break; + cell = (*set_element); + merged_cells->full += ((LASintervalStartCell*)cell)->full; + while (cell) + { + map.insert(my_cell_map::value_type(cell->start, cell)); + cell = cell->next; + } + set_element++; + } + // initialize merged_cells with first interval + my_cell_map::iterator map_element = map.begin(); + cell = (*map_element).second; + map.erase(map_element); + merged_cells->start = cell->start; + merged_cells->end = cell->end; + merged_cells->total = cell->end - cell->start + 1; + if (erase) delete cell; + // merge intervals + LASintervalCell* last_cell = merged_cells; + I32 diff; + while (map.size()) + { + map_element = map.begin(); + cell = (*map_element).second; + map.erase(map_element); + diff = cell->start - last_cell->end; + if (diff > (I32)threshold) + { + last_cell->next = new LASintervalCell(cell); + last_cell = last_cell->next; + merged_cells->total += (cell->end - cell->start + 1); + } + else + { + diff = cell->end - last_cell->end; + if (diff > 0) + { + last_cell->end = cell->end; + merged_cells->total += diff; + } + number_intervals--; + } + if (erase) delete cell; + } + } + current_cell = merged_cells; + full = merged_cells->full; + total = merged_cells->total; + return TRUE; +} + +void LASinterval::clear_merge_cell_set() +{ + if (cells_to_merge) + { + ((my_cell_set*)cells_to_merge)->clear(); + } +} + +BOOL LASinterval::get_merged_cell() +{ + if (merged_cells) + { + full = merged_cells->full; + total = merged_cells->total; + current_cell = merged_cells; + return TRUE; + } + return FALSE; +} + +BOOL LASinterval::has_intervals() +{ + if (current_cell) + { + start = current_cell->start; + end = current_cell->end; + current_cell = current_cell->next; + return TRUE; + } + return FALSE; +} + +LASinterval::LASinterval(const U32 threshold) +{ + cells = new my_cell_hash; + cells_to_merge = 0; + this->threshold = threshold; + number_intervals = 0; + last_index = I32_MIN; + last_cell = 0; + current_cell = 0; + merged_cells = 0; + merged_cells_temporary = FALSE; +} + +LASinterval::~LASinterval() +{ + // loop over all cells + my_cell_hash::iterator hash_element = ((my_cell_hash*)cells)->begin(); + while (hash_element != ((my_cell_hash*)cells)->end()) + { + LASintervalCell* previous_cell = (*hash_element).second; + LASintervalCell* cell = previous_cell->next; + while (cell) + { + delete previous_cell; + previous_cell = cell; + cell = cell->next; + } + delete previous_cell; + hash_element++; + } + delete ((my_cell_hash*)cells); + // maybe delete temporary merge cells from the previous merge + if (merged_cells) + { + if (merged_cells_temporary) + { + LASintervalCell* next_next; + LASintervalCell* next = merged_cells->next; + while (next) + { + next_next = next->next; + delete next; + next = next_next; + } + delete merged_cells; + } + merged_cells = 0; + } + if (cells_to_merge) delete ((my_cell_set*)cells_to_merge); +} + +BOOL LASinterval::read(ByteStreamIn* stream) +{ + char signature[4]; + try { stream->getBytes((U8*)signature, 4); } catch (...) + { + fprintf(stderr,"ERROR (LASinterval): reading signature\n"); + return FALSE; + } + if (strncmp(signature, "LASV", 4) != 0) + { + fprintf(stderr,"ERROR (LASinterval): wrong signature %4s instead of 'LASV'\n", signature); + return FALSE; + } + U32 version; + try { stream->get32bitsLE((U8*)&version); } catch (...) + { + fprintf(stderr,"ERROR (LASinterval): reading version\n"); + return FALSE; + } + // read number of cells + U32 number_cells; + try { stream->get32bitsLE((U8*)&number_cells); } catch (...) + { + fprintf(stderr,"ERROR (LASinterval): reading number of cells\n"); + return FALSE; + } + // loop over all cells + while (number_cells) + { + // read index of cell + I32 cell_index; + try { stream->get32bitsLE((U8*)&cell_index); } catch (...) + { + fprintf(stderr,"ERROR (LASinterval): reading cell index\n"); + return FALSE; + } + // create cell and insert into hash + LASintervalStartCell* start_cell = new LASintervalStartCell(); + ((my_cell_hash*)cells)->insert(my_cell_hash::value_type(cell_index, start_cell)); + LASintervalCell* cell = start_cell; + // read number of intervals in cell + U32 number_intervals; + try { stream->get32bitsLE((U8*)&number_intervals); } catch (...) + { + fprintf(stderr,"ERROR (LASinterval): reading number of intervals in cell\n"); + return FALSE; + } + // read number of points in cell + U32 number_points; + try { stream->get32bitsLE((U8*)&number_points); } catch (...) + { + fprintf(stderr,"ERROR (LASinterval): reading number of points in cell\n"); + return FALSE; + } + start_cell->full = number_points; + start_cell->total = 0; + while (number_intervals) + { + // read start of interval + try { stream->get32bitsLE((U8*)&(cell->start)); } catch (...) + { + fprintf(stderr,"ERROR (LASinterval): reading start %d of interval\n", cell->start); + return FALSE; + } + // read end of interval + try { stream->get32bitsLE((U8*)&(cell->end)); } catch (...) + { + fprintf(stderr,"ERROR (LASinterval): reading end %d of interval\n", cell->end); + return FALSE; + } + start_cell->total += (cell->end - cell->start + 1); + number_intervals--; + if (number_intervals) + { + cell->next = new LASintervalCell(); + cell = cell->next; + } + } + number_cells--; + } + + return TRUE; +} + +BOOL LASinterval::write(ByteStreamOut* stream) const +{ + if (!stream->putBytes((const U8*)"LASV", 4)) + { + fprintf(stderr,"ERROR (LASinterval): writing signature\n"); + return FALSE; + } + U32 version = 0; + if (!stream->put32bitsLE((const U8*)&version)) + { + fprintf(stderr,"ERROR (LASinterval): writing version\n"); + return FALSE; + } + // write number of cells + U32 number_cells = (U32)((my_cell_hash*)cells)->size(); + if (!stream->put32bitsLE((const U8*)&number_cells)) + { + fprintf(stderr,"ERROR (LASinterval): writing number of cells %d\n", number_cells); + return FALSE; + } + // loop over all cells + my_cell_hash::iterator hash_element = ((my_cell_hash*)cells)->begin(); + while (hash_element != ((my_cell_hash*)cells)->end()) + { + LASintervalCell* cell = (*hash_element).second; + // count number of intervals and points in cell + U32 number_intervals = 0; + U32 number_points = ((LASintervalStartCell*)cell)->full; + while (cell) + { + number_intervals++; + cell = cell->next; + } + // write index of cell + I32 cell_index = (*hash_element).first; + if (!stream->put32bitsLE((const U8*)&cell_index)) + { + fprintf(stderr,"ERROR (LASinterval): writing cell index %d\n", cell_index); + return FALSE; + } + // write number of intervals in cell + if (!stream->put32bitsLE((const U8*)&number_intervals)) + { + fprintf(stderr,"ERROR (LASinterval): writing number of intervals %d in cell\n", number_intervals); + return FALSE; + } + // write number of points in cell + if (!stream->put32bitsLE((const U8*)&number_points)) + { + fprintf(stderr,"ERROR (LASinterval): writing number of points %d in cell\n", number_points); + return FALSE; + } + // write intervals + cell = (*hash_element).second; + while (cell) + { + // write start of interval + if (!stream->put32bitsLE((const U8*)&(cell->start))) + { + fprintf(stderr,"ERROR (LASinterval): writing start %d of interval\n", cell->start); + return FALSE; + } + // write end of interval + if (!stream->put32bitsLE((const U8*)&(cell->end))) + { + fprintf(stderr,"ERROR (LASinterval): writing end %d of interval\n", cell->end); + return FALSE; + } + cell = cell->next; + } + hash_element++; + } + return TRUE; +} diff --git a/libs/laszip/src/lasinterval.hpp b/libs/laszip/src/lasinterval.hpp new file mode 100644 index 0000000..3212d34 --- /dev/null +++ b/libs/laszip/src/lasinterval.hpp @@ -0,0 +1,123 @@ +/* +=============================================================================== + + FILE: lasinterval.hpp + + CONTENTS: + + Used by lasindex to manage intervals of consecutive LiDAR points that are + read sequentially. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2015, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the LICENSE.txt file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 20 October 2018 -- fixed rare bug in merge_intervals() when verbose is TRUE + 29 April 2011 -- created after cable outage during the royal wedding (-: + +=============================================================================== +*/ +#ifndef LAS_INTERVAL_HPP +#define LAS_INTERVAL_HPP + +#include "mydefs.hpp" + +class ByteStreamIn; +class ByteStreamOut; + +class LASintervalCell +{ +public: + U32 start; + U32 end; + LASintervalCell* next; + LASintervalCell(); + LASintervalCell(const U32 p_index); + LASintervalCell(const LASintervalCell* cell); +}; + +class LASintervalStartCell : public LASintervalCell +{ +public: + U32 full; + U32 total; + LASintervalCell* last; + LASintervalStartCell(); + LASintervalStartCell(const U32 p_index); + BOOL add(const U32 p_index, const U32 threshold=1000); +}; + +class LASinterval +{ +public: + LASinterval(const U32 threshold=1000); + ~LASinterval(); + + // add points and create cells with intervals + BOOL add(const U32 p_index, const I32 c_index); + + // get total number of cells + U32 get_number_cells() const; + + // get total number of intervals + U32 get_number_intervals() const; + + // merge cells (and their intervals) into one cell + BOOL merge_cells(const U32 num_indices, const I32* indices, const I32 new_index); + + // merge adjacent intervals with small gaps in cells to reduce total interval number to maximum + void merge_intervals(U32 maximum, const BOOL verbose=TRUE); + + // read from file or write to file + BOOL read(ByteStreamIn* stream); + BOOL write(ByteStreamOut* stream) const; + + // get one cell after the other + void get_cells(); + BOOL has_cells(); + + // get a particular cell + BOOL get_cell(const I32 c_index); + + // add cell's intervals to those that will be merged + BOOL add_current_cell_to_merge_cell_set(); + BOOL add_cell_to_merge_cell_set(const I32 c_index, const BOOL erase=FALSE); + BOOL merge(const BOOL erase=FALSE); + void clear_merge_cell_set(); + BOOL get_merged_cell(); + + // iterate intervals of current cell (or over merged intervals) + BOOL has_intervals(); + + I32 index; + U32 start; + U32 end; + U32 full; + U32 total; + +private: + void* cells; + void* cells_to_merge; + U32 threshold; + U32 number_intervals; + I32 last_index; + LASintervalStartCell* last_cell; + LASintervalCell* current_cell; + LASintervalStartCell* merged_cells; + BOOL merged_cells_temporary; +}; + +#endif diff --git a/libs/laszip/src/laspoint.hpp b/libs/laszip/src/laspoint.hpp new file mode 100644 index 0000000..1e09081 --- /dev/null +++ b/libs/laszip/src/laspoint.hpp @@ -0,0 +1,743 @@ +/* +=============================================================================== + + FILE: laspoint.hpp + + CONTENTS: + + This class describes an LAS point and offers helper functions to access, + convert, and set the default (and any additional) point attributes. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the LICENSE.txt file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 10 May 2019 -- checking for overflows in X, Y, Z of I32 of fixed-point LAS + 15 June 2018 -- fix in flag copy from legacy (0-5) to extended (6-10) type + 10 March 2017 -- fix in copy_to() and copy_from() new LAS 1.4 point types + 10 October 2016 -- small fixes for NIR and extended scanner channel + 19 July 2015 -- created after FOSS4GE in the train back from Lake Como + +=============================================================================== +*/ +#ifndef LAS_POINT_HPP +#define LAS_POINT_HPP + +#include "lasquantizer.hpp" +#include "lasattributer.hpp" + +class LASwavepacket +{ +public: + LASwavepacket() {zero();}; + void zero() {memset(data, 0, 29);}; + inline U8 getIndex() const {return data[0];}; + inline U64 getOffset() const {return ((U64*)&(data[1]))[0];}; + inline U32 getSize() const {return ((U32*)&(data[9]))[0];}; + inline F32 getLocation() const {return ((F32*)&(data[13]))[0];}; + inline F32 getXt() const {return ((F32*)&(data[17]))[0];}; + inline F32 getYt() const {return ((F32*)&(data[21]))[0];}; + inline F32 getZt() const {return ((F32*)&(data[25]))[0];}; + inline void setIndex(U8 index) {data[0] = index;}; + inline void setOffset(U64 offset) {((U64*)&(data[1]))[0] = offset;}; + inline void setSize(U32 size) {((U32*)&(data[9]))[0] = size;}; + inline void setLocation(F32 location) { ((F32*)&(data[13]))[0] = location;}; + inline void setXt(F32 xt) {((F32*)&(data[17]))[0] = xt;}; + inline void setYt(F32 yt) {((F32*)&(data[21]))[0] = yt;}; + inline void setZt(F32 zt) {((F32*)&(data[25]))[0] = zt;}; + inline void flipDirection() {((F32*)&(data[17]))[0] *= -1; ((F32*)&(data[21]))[0] *= -1; ((F32*)&(data[25]))[0] *= -1;}; +private: + U8 data[29]; +}; + +class LASpoint +{ +public: + +// these fields contain the data that describe each point + + I32 X; + I32 Y; + I32 Z; + U16 intensity; + U8 return_number : 3; + U8 number_of_returns : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 classification : 5; + U8 synthetic_flag : 1; + U8 keypoint_flag : 1; + U8 withheld_flag : 1; + I8 scan_angle_rank; + U8 user_data; + U16 point_source_ID; + + // LAS 1.4 only + I16 extended_scan_angle; + U8 extended_point_type : 2; + U8 extended_scanner_channel : 2; + U8 extended_classification_flags : 4; + U8 extended_classification; + U8 extended_return_number : 4; + U8 extended_number_of_returns : 4; + + // LASlib internal use only + U8 deleted_flag; + + // for 8 byte alignment of the GPS time + U8 dummy[2]; + + // compressed LASzip 1.4 points only + BOOL gps_time_change; + + F64 gps_time; + U16 rgb[4]; + LASwavepacket wavepacket; + + U8* extra_bytes; + +// for converting between x,y,z integers and scaled/translated coordinates + + const LASquantizer* quantizer; + F64 coordinates[3]; + +// for attributed access to the extra bytes + + const LASattributer* attributer; + +// this field provides generic access to the point data + + U8** point; + +// these fields describe the point format LAS specific + + BOOL have_gps_time; + BOOL have_rgb; + BOOL have_nir; + BOOL have_wavepacket; + I32 extra_bytes_number; + U32 total_point_size; + +// these fields describe the point format terms of generic items + + U16 num_items; + LASitem* items; + +// copy functions + + LASpoint(const LASpoint & other) + { + *this = other; + } + + LASpoint & operator=(const LASpoint & other) + { + X = other.X; + Y = other.Y; + Z = other.Z; + intensity = other.intensity; + return_number = other.return_number; + number_of_returns = other.number_of_returns; + scan_direction_flag = other.scan_direction_flag; + edge_of_flight_line = other.edge_of_flight_line; + classification = other.classification; + synthetic_flag = other.synthetic_flag; + keypoint_flag = other.keypoint_flag; + withheld_flag = other.withheld_flag; + scan_angle_rank = other.scan_angle_rank; + user_data = other.user_data; + point_source_ID = other.point_source_ID; + deleted_flag = other.deleted_flag; + + if (other.have_gps_time) + { + gps_time = other.gps_time; + } + if (other.have_rgb) + { + rgb[0] = other.rgb[0]; + rgb[1] = other.rgb[1]; + rgb[2] = other.rgb[2]; + if (other.have_nir) + { + rgb[3] = other.rgb[3]; + } + } + if (other.have_wavepacket) + { + wavepacket = other.wavepacket; + } + if (other.extra_bytes && extra_bytes) + { + memcpy(extra_bytes, other.extra_bytes, extra_bytes_number); + } + if (other.extended_point_type) + { + extended_classification = other.extended_classification; + extended_classification_flags = other.extended_classification_flags; + extended_number_of_returns = other.extended_number_of_returns; + extended_return_number = other.extended_return_number; + extended_scan_angle = other.extended_scan_angle; + extended_scanner_channel = other.extended_scanner_channel; + } + else if (extended_point_type) + { + extended_classification = other.classification; + extended_classification_flags = ((other.withheld_flag) << 2) | ((other.keypoint_flag) << 1) | (other.synthetic_flag); + extended_number_of_returns = other.number_of_returns; + extended_return_number = other.return_number; + extended_scan_angle = I16_QUANTIZE(((F32)other.scan_angle_rank)/0.006); + extended_scanner_channel = other.extended_scanner_channel; + } + + return *this; + }; + + void copy_to(U8* buffer) const + { + if (extended_point_type) + { + memcpy(buffer, &X, 14); + buffer[14] = ((U8*)&X)[24]; // extended return number and number of returns + buffer[15] = (((U8*)&X)[14] & 0xC0) | (extended_scanner_channel << 4) | (extended_classification_flags & 0x08) | ((((U8*)&X)[15]) >> 5); + buffer[16] = ((U8*)&X)[23]; // extended classification + buffer[17] = ((U8*)&X)[17]; // user data + ((I16*)buffer)[9] = ((I16*)&X)[10]; // extended scan angle + ((U16*)buffer)[10] = ((U16*)&X)[9]; // point source ID + memcpy(buffer+22, &gps_time, 8); + } + else + { + memcpy(buffer, &X, 20); + } + U32 i; + U32 b = items[0].size; + for (i = 1; i < num_items; i++) + { + memcpy(&buffer[b], point[i], items[i].size); + b += items[i].size; + } + }; + + void copy_from(const U8* buffer) + { + if (extended_point_type) + { + memcpy(&X, buffer, 14); + ((U8*)&X)[24] = buffer[14]; // extended return number and number of returns + extended_classification_flags = buffer[15] & 0x0F; + ((U8*)&X)[15] = (buffer[15] & 0x07) << 5; // legacy classification flags + extended_scanner_channel = (buffer[15] >> 4) & 0x03; + scan_direction_flag = (buffer[15] >> 6) & 0x01; + edge_of_flight_line = (buffer[15] >> 7) & 0x01; + ((U8*)&X)[23] = buffer[16]; // extended classification + if (extended_classification < 32) classification = extended_classification; + ((U8*)&X)[17] = buffer[17]; // user data + ((I16*)&X)[10] = ((I16*)buffer)[9]; // extended scan angle + ((U16*)&X)[9] = ((U16*)buffer)[10]; // point source ID + memcpy(&gps_time, buffer+22, 8); + } + else + { + memcpy(&X, buffer, 20); + } + U32 i; + U32 b = items[0].size; + for (i = 1; i < num_items; i++) + { + memcpy(point[i], &buffer[b], items[i].size); + b += items[i].size; + } + }; + +// these functions set the desired point format (and maybe add on attributes in extra bytes) + + BOOL init(const LASquantizer* quantizer, const U8 point_type, const U16 point_size, const LASattributer* attributer=0) + { + // clean the point + + clean(); + + // switch over the point types we know + + if (!LASzip().setup(&num_items, &items, point_type, point_size, LASZIP_COMPRESSOR_NONE)) + { + fprintf(stderr,"ERROR: unknown point type %d with point size %d\n", (I32)point_type, (I32)point_size); + return FALSE; + } + + // create point's item pointers + + point = new U8*[num_items]; + + U16 i; + for (i = 0; i < num_items; i++) + { + total_point_size += items[i].size; + switch (items[i].type) + { + case LASitem::POINT14: + have_gps_time = TRUE; + extended_point_type = 1; + case LASitem::POINT10: + this->point[i] = (U8*)&(this->X); + break; + case LASitem::GPSTIME11: + have_gps_time = TRUE; + this->point[i] = (U8*)&(this->gps_time); + break; + case LASitem::RGBNIR14: + have_nir = TRUE; + case LASitem::RGB12: + case LASitem::RGB14: + have_rgb = TRUE; + this->point[i] = (U8*)(this->rgb); + break; + case LASitem::WAVEPACKET13: + case LASitem::WAVEPACKET14: + have_wavepacket = TRUE; + this->point[i] = (U8*)&(this->wavepacket); + break; + case LASitem::BYTE: + case LASitem::BYTE14: + extra_bytes_number = items[i].size; + extra_bytes = new U8[extra_bytes_number]; + this->point[i] = extra_bytes; + break; + default: + return FALSE; + } + } + this->quantizer = quantizer; + this->attributer = attributer; + return TRUE; + }; + + BOOL init(const LASquantizer* quantizer, const U32 num_items, const LASitem* items, const LASattributer* attributer=0) + { + U32 i; + + // clean the point + + clean(); + + // create item description + + this->num_items = num_items; + if (this->items) delete [] this->items; + this->items = new LASitem[num_items]; + if (this->point) delete [] this->point; + this->point = new U8*[num_items]; + + for (i = 0; i < num_items; i++) + { + this->items[i] = items[i]; + total_point_size += items[i].size; + switch (items[i].type) + { + case LASitem::POINT14: + have_gps_time = TRUE; + extended_point_type = 1; + case LASitem::POINT10: + this->point[i] = (U8*)&(this->X); + break; + case LASitem::GPSTIME11: + have_gps_time = TRUE; + this->point[i] = (U8*)&(this->gps_time); + break; + case LASitem::RGBNIR14: + have_nir = TRUE; + case LASitem::RGB12: + case LASitem::RGB14: + have_rgb = TRUE; + this->point[i] = (U8*)(this->rgb); + break; + case LASitem::WAVEPACKET13: + case LASitem::WAVEPACKET14: + have_wavepacket = TRUE; + this->point[i] = (U8*)&(this->wavepacket); + break; + case LASitem::BYTE: + case LASitem::BYTE14: + extra_bytes_number = items[i].size; + extra_bytes = new U8[extra_bytes_number]; + this->point[i] = extra_bytes; + break; + default: + return FALSE; + } + } + this->quantizer = quantizer; + this->attributer = attributer; + return TRUE; + }; + + BOOL inside_rectangle(const F64 r_min_x, const F64 r_min_y, const F64 r_max_x, const F64 r_max_y) const + { + F64 xy; + xy = get_x(); + if (xy < r_min_x || xy >= r_max_x) return FALSE; + xy = get_y(); + if (xy < r_min_y || xy >= r_max_y) return FALSE; + return TRUE; + } + + BOOL inside_tile(const F32 ll_x, const F32 ll_y, const F32 ur_x, const F32 ur_y) const + { + F64 xy; + xy = get_x(); + if (xy < ll_x || xy >= ur_x) return FALSE; + xy = get_y(); + if (xy < ll_y || xy >= ur_y) return FALSE; + return TRUE; + } + + BOOL inside_circle(const F64 center_x, const F64 center_y, F64 squared_radius) const + { + F64 dx = center_x - get_x(); + F64 dy = center_y - get_y(); + return ((dx*dx+dy*dy) < squared_radius); + } + + BOOL inside_box(const F64 min_x, const F64 min_y, const F64 min_z, const F64 max_x, const F64 max_y, const F64 max_z) const + { + F64 xyz; + xyz = get_x(); + if (xyz < min_x || xyz >= max_x) return FALSE; + xyz = get_y(); + if (xyz < min_y || xyz >= max_y) return FALSE; + xyz = get_z(); + if (xyz < min_z || xyz >= max_z) return FALSE; + return TRUE; + } + + BOOL inside_bounding_box(const F64 min_x, const F64 min_y, const F64 min_z, const F64 max_x, const F64 max_y, const F64 max_z) const + { + F64 xyz; + xyz = get_x(); + if (xyz < min_x || xyz > max_x) return FALSE; + xyz = get_y(); + if (xyz < min_y || xyz > max_y) return FALSE; + xyz = get_z(); + if (xyz < min_z || xyz > max_z) return FALSE; + return TRUE; + } + + BOOL is_zero() const + { + if (((U32*)&(this->X))[0] || ((U32*)&(this->X))[1] || ((U32*)&(this->X))[2] || ((U32*)&(this->X))[3] || ((U32*)&(this->X))[4]) + { + return FALSE; + } + if (have_gps_time) + { + if (this->gps_time) + { + return FALSE; + } + } + if (have_rgb) + { + if (this->rgb[0] || this->rgb[1] || this->rgb[2]) + { + return FALSE; + } + if (have_nir) + { + if (this->rgb[3]) + { + return FALSE; + } + } + } + return TRUE; + } + + void zero() + { + X = 0; + Y = 0; + Z = 0; + intensity = 0; + return_number = 1; + number_of_returns = 1; + scan_direction_flag = 0; + edge_of_flight_line = 0; + classification = 0; + synthetic_flag = 0; + keypoint_flag = 0; + withheld_flag = 0; + scan_angle_rank = 0; + user_data = 0; + point_source_ID = 0; + + // LAS 1.4 only + extended_scan_angle = 0; + extended_scanner_channel = 0; + extended_classification_flags = 0; + extended_classification = 0; + extended_return_number = 1; + extended_number_of_returns = 1; + + // LASlib only + deleted_flag = 0; + + gps_time = 0.0; + rgb[0] = rgb[1] = rgb[2] = rgb[3] = 0; + wavepacket.zero(); + }; + + void clean() + { + zero(); + + if (extra_bytes) + { + delete [] extra_bytes; + extra_bytes = 0; + }; + + if (point) delete [] point; + point = 0; + + have_gps_time = FALSE; + have_rgb = FALSE; + have_wavepacket = FALSE; + have_nir = FALSE; + extra_bytes_number = 0; + total_point_size = 0; + + num_items = 0; + if (items) delete [] items; + items = 0; + + // LAS 1.4 only + extended_point_type = 0; + }; + + LASpoint() + { + extra_bytes = 0; + point = 0; + items = 0; + clean(); + }; + + inline BOOL is_first() const { return get_return_number() <= 1; }; + inline BOOL is_intermediate() const { return (!is_first() && !is_last()); }; + inline BOOL is_last() const { return get_return_number() >= get_number_of_returns(); }; + inline BOOL is_single() const { return get_number_of_returns() <= 1; }; + + inline BOOL is_first_of_many() const { return !is_single() && is_first(); }; + inline BOOL is_last_of_many() const { return !is_single() && is_last(); }; + + inline I32 get_X() const { return X; }; + inline I32 get_Y() const { return Y; }; + inline I32 get_Z() const { return Z; }; + inline U16 get_intensity() const { return intensity; }; + inline U8 get_return_number() const { return return_number; }; + inline U8 get_number_of_returns() const { return number_of_returns; }; + inline U8 get_scan_direction_flag() const { return scan_direction_flag; }; + inline U8 get_edge_of_flight_line() const { return edge_of_flight_line; }; + inline U8 get_classification() const { return classification; }; + inline U8 get_synthetic_flag() const { return synthetic_flag; }; + inline U8 get_keypoint_flag() const { return keypoint_flag; }; + inline U8 get_withheld_flag() const { return withheld_flag; }; + inline I8 get_scan_angle_rank() const { return scan_angle_rank; }; + inline U8 get_user_data() const { return user_data; }; + inline U16 get_point_source_ID() const { return point_source_ID; }; + inline U8 get_deleted_flag() const { return deleted_flag; }; + inline F64 get_gps_time() const { return gps_time; }; + inline const U16* get_RGB() const { return rgb; }; + inline const U16* get_RGBI() const { return rgb; }; + inline U16 get_RGBI(const U32 band) const { return rgb[band]; }; + inline U16 get_R() const { return rgb[0]; }; + inline U16 get_G() const { return rgb[1]; }; + inline U16 get_B() const { return rgb[2]; }; + inline U16 get_I() const { return rgb[3]; }; + inline U16 get_NIR() const { return rgb[3]; }; + + inline void set_X(const I32 X) { this->X = X; }; + inline void set_Y(const I32 Y) { this->Y = Y; }; + inline void set_Z(const I32 Z) { this->Z = Z; }; + inline void set_intensity(const U16 intensity) { this->intensity = intensity; }; + inline void set_return_number(const U8 return_number) { this->return_number = (return_number > 7 ? 7 : return_number); }; + inline void set_number_of_returns(const U8 number_of_returns) { this->number_of_returns = (number_of_returns > 7 ? 7 : number_of_returns); }; + inline void set_scan_direction_flag(const U8 scan_direction_flag) { this->scan_direction_flag = scan_direction_flag; }; + inline void set_edge_of_flight_line(const U8 edge_of_flight_line) { this->edge_of_flight_line = edge_of_flight_line; }; + inline void set_classification(U8 classification) { if (classification < 32) { this->classification = classification; this->extended_classification = classification; } }; + inline void set_synthetic_flag(U8 synthetic_flag) { if (synthetic_flag) { this->synthetic_flag = 1; this->extended_classification_flags |= 0x01; } else { this->synthetic_flag = 0; this->extended_classification_flags &= 0x0E; } }; + inline void set_keypoint_flag(U8 keypoint_flag) { if (keypoint_flag) { this->keypoint_flag = 1; this->extended_classification_flags |= 0x02; } else { this->keypoint_flag = 0; this->extended_classification_flags &= 0x0D; } }; + inline void set_withheld_flag(U8 withheld_flag) { if (withheld_flag) { this->withheld_flag = 1; this->extended_classification_flags |= 0x04; } else { this->withheld_flag = 0; this->extended_classification_flags &= 0x0B; } }; + inline void set_scan_angle_rank(I8 scan_angle_rank) { this->scan_angle_rank = scan_angle_rank; }; + inline void set_user_data(U8 user_data) { this->user_data = user_data; }; + inline void set_point_source_ID(U16 point_source_ID) { this->point_source_ID = point_source_ID; }; + inline void set_deleted_flag(U8 deleted_flag) { this->deleted_flag = deleted_flag; }; + inline void set_gps_time(const F64 gps_time) { this->gps_time = gps_time; }; + inline void set_RGB(const U16* rgb) { memcpy(this->rgb, rgb, sizeof(U16) * 3); }; + inline void set_RGBI(const U16* rgb) { memcpy(this->rgb, rgb, sizeof(U16) * 4); }; + inline void set_RGBI(const U32 band, const U16 value) { rgb[band] = value; }; + inline void set_R(const U16 R) { this->rgb[0] = R; }; + inline void set_G(const U16 G) { this->rgb[1] = G; }; + inline void set_B(const U16 B) { this->rgb[2] = B; }; + inline void set_I(const U16 I) { this->rgb[3] = I; }; + inline void set_NIR(const U16 NIR) { this->rgb[3] = NIR; }; + + inline F64 get_x() const { return quantizer->get_x(X); }; + inline F64 get_y() const { return quantizer->get_y(Y); }; + inline F64 get_z() const { return quantizer->get_z(Z); }; + + inline BOOL set_x(const F64 x) { I64 X = quantizer->get_X(x); this->X = (I32)(X); return I32_FITS_IN_RANGE(X); }; + inline BOOL set_y(const F64 y) { I64 Y = quantizer->get_Y(y); this->Y = (I32)(Y); return I32_FITS_IN_RANGE(Y); }; + inline BOOL set_z(const F64 z) { I64 Z = quantizer->get_Z(z); this->Z = (I32)(Z); return I32_FITS_IN_RANGE(Z); }; + + inline BOOL is_extended_point_type() const { return extended_point_type; }; + + inline U8 get_extended_classification() const { return extended_classification; }; + inline U8 get_extended_return_number() const { return extended_return_number; }; + inline U8 get_extended_number_of_returns() const { return extended_number_of_returns; }; + inline I16 get_extended_scan_angle() const { return extended_scan_angle; }; + inline U8 get_extended_overlap_flag() const { return (extended_classification_flags >> 3); }; + inline U8 get_extended_scanner_channel() const { return extended_scanner_channel; }; + + inline void set_extended_classification(U8 extended_classification) { this->extended_classification = extended_classification; if (extended_classification > 31) this->classification = 0; else this->classification = extended_classification; }; + inline void set_extended_return_number(U8 extended_return_number) { this->extended_return_number = extended_return_number; }; + inline void set_extended_number_of_returns(U8 extended_number_of_returns) { this->extended_number_of_returns = extended_number_of_returns; }; + inline void set_extended_scan_angle(I16 extended_scan_angle) { this->extended_scan_angle = extended_scan_angle; }; + inline void set_extended_overlap_flag(U8 extended_overlap_flag) { this->extended_classification_flags = (extended_overlap_flag << 3) | (this->extended_classification_flags & 7); }; + inline void set_extended_scanner_channel(U8 extended_scanner_channel) { this->extended_scanner_channel = extended_scanner_channel; }; + + inline F32 get_scan_angle() const { if (extended_point_type) return 0.006f*extended_scan_angle; else return (F32)scan_angle_rank; }; + inline F32 get_abs_scan_angle() const { if (extended_point_type) return (extended_scan_angle < 0 ? -0.006f*extended_scan_angle : 0.006f*extended_scan_angle) ; else return (scan_angle_rank < 0 ? (F32)-scan_angle_rank : (F32)scan_angle_rank); }; + + inline void set_scan_angle(F32 scan_angle) { if (extended_point_type) set_extended_scan_angle(I16_QUANTIZE(scan_angle/0.006f)); else set_scan_angle_rank(I8_QUANTIZE(scan_angle)); }; + + inline void compute_coordinates() + { + coordinates[0] = get_x(); + coordinates[1] = get_y(); + coordinates[2] = get_z(); + }; + + inline BOOL compute_XYZ() + { + BOOL retX = set_x(coordinates[0]); + BOOL retY = set_y(coordinates[1]); + BOOL retZ = set_z(coordinates[2]); + return (retX && retY && retZ); + }; + + inline BOOL compute_XYZ(const LASquantizer* quantizer) + { + I64 X = quantizer->get_X(coordinates[0]); + I64 Y = quantizer->get_Y(coordinates[1]); + I64 Z = quantizer->get_Z(coordinates[2]); + this->X = (I32)(X); + this->Y = (I32)(Y); + this->Z = (I32)(Z); + return (I32_FITS_IN_RANGE(X) && I32_FITS_IN_RANGE(Y) && I32_FITS_IN_RANGE(Z)); + }; + + // generic functions for attributes in extra bytes + + inline BOOL has_attribute(U32 index) const + { + if (attributer) + { + if (((I32)index) < attributer->number_attributes) + { + return TRUE; + } + } + return FALSE; + }; + + inline BOOL get_attribute(U32 index, U8* data) const + { + if (has_attribute(index)) + { + memcpy(data, extra_bytes + attributer->attribute_starts[index], attributer->attribute_sizes[index]); + return TRUE; + } + return FALSE; + }; + + inline BOOL set_attribute(U32 index, const U8* data) + { + if (has_attribute(index)) + { + memcpy(extra_bytes + attributer->attribute_starts[index], data, attributer->attribute_sizes[index]); + return TRUE; + } + return FALSE; + }; + + inline const CHAR* get_attribute_name(U32 index) const + { + if (has_attribute(index)) + { + return attributer->attributes[index].name; + } + return 0; + }; + + inline F64 get_attribute_as_float(U32 index) const + { + if (has_attribute(index)) + { + return attributer->attributes[index].get_value_as_float(extra_bytes + attributer->attribute_starts[index]); + } + return 0.0; + }; + + inline void set_attribute_as_float(U32 index, F64 value) const + { + if (has_attribute(index)) + { + attributer->attributes[index].set_value_as_float(extra_bytes + attributer->attribute_starts[index], value); + } + }; + + // typed and offset functions for attributes in extra bytes (more efficient) + + inline void get_attribute(I32 start, U8 &data) const { data = extra_bytes[start]; }; + inline void set_attribute(I32 start, U8 data) { extra_bytes[start] = data; }; + inline void get_attribute(I32 start, I8 &data) const { data = (I8)(extra_bytes[start]); }; + inline void set_attribute(I32 start, I8 data) { extra_bytes[start] = data; }; + inline void get_attribute(I32 start, U16 &data) const { data = *((U16*)(extra_bytes + start)); }; + inline void set_attribute(I32 start, U16 data) { *((U16*)(extra_bytes + start)) = data; }; + inline void get_attribute(I32 start, I16 &data) const { data = *((I16*)(extra_bytes + start)); }; + inline void set_attribute(I32 start, I16 data) { *((I16*)(extra_bytes + start)) = data; }; + inline void get_attribute(I32 start, U32 &data) const { data = *((U32*)(extra_bytes + start)); }; + inline void set_attribute(I32 start, U32 data) { *((U32*)(extra_bytes + start)) = data; }; + inline void get_attribute(I32 start, I32 &data) const { data = *((I32*)(extra_bytes + start)); }; + inline void set_attribute(I32 start, I32 data) { *((I32*)(extra_bytes + start)) = data; }; + inline void get_attribute(I32 start, U64 &data) const { data = *((U64*)(extra_bytes + start)); }; + inline void set_attribute(I32 start, U64 data) { *((U64*)(extra_bytes + start)) = data; }; + inline void get_attribute(I32 start, I64 &data) const { data = *((I64*)(extra_bytes + start)); }; + inline void set_attribute(I32 start, I64 data) { *((I64*)(extra_bytes + start)) = data; }; + inline void get_attribute(I32 start, F32 &data) const { data = *((F32*)(extra_bytes + start)); }; + inline void set_attribute(I32 start, F32 data) { *((F32*)(extra_bytes + start)) = data; }; + inline void get_attribute(I32 start, F64 &data) const { data = *((F64*)(extra_bytes + start)); }; + inline void set_attribute(I32 start, F64 data) { *((F64*)(extra_bytes + start)) = data; }; + + ~LASpoint() + { + clean(); + }; +}; + +#endif diff --git a/libs/laszip/src/lasquadtree.cpp b/libs/laszip/src/lasquadtree.cpp new file mode 100644 index 0000000..67a8e98 --- /dev/null +++ b/libs/laszip/src/lasquadtree.cpp @@ -0,0 +1,1673 @@ +/* +=============================================================================== + + FILE: lasquadtree.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2015, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the LICENSE.txt file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ +#include "lasquadtree.hpp" + +#include "bytestreamin.hpp" +#include "bytestreamout.hpp" + +#include +#include +#include + +#include +using namespace std; + +typedef vector my_cell_vector; + +/* + +class LAScell +{ +public: + LAScell* parent; + LAScell* child[4]; + U32 num_children : 3; + U32 level : 5; + U32 idx : 24; + F32 mid_x; + F32 mid_y; + F32 half_size; + F32 get_min_x() const { return mid_x - half_size; }; + F32 get_min_y() const { return mid_y - half_size; }; + F32 get_max_x() const { return mid_x + half_size; }; + F32 get_max_y() const { return mid_y + half_size; }; + LAScell(); +}; + +LAScell::LAScell() +{ + memset(this, 0, sizeof(LAScell)); +} + + LAScell* get_cell(const F64 x, const F64 y); +LAScell* LASquadtree::get_cell(const F64 x, const F64 y) +{ + LAScell* cell = &root; + while (cell && cell->num_children) + { + int child = 0; + if (x < cell->mid_x) // only if strictly less + { + child |= 1; + } + if (!(y < cell->mid_y)) // only if not strictly less + { + child |= 2; + } + cell = cell->child[child]; + } + return cell; +} + +static bool intersect_point(LAScell* cell, const float* p_pos) +{ + if (cell->num_children) // check if cell has children + { + int idx = 0; + if (p_pos[0] < cell->r_mid[0]) idx |= 1; + if (!(p_pos[1] < cell->r_mid[1])) idx |= 2; + if (cell->child[idx] && intersect_point(cell->child[idx], p_pos)) return true; + return false; + } + return true; +} +*/ + +// returns the bounding box of the cell that x & y fall into at the specified level +void LASquadtree::get_cell_bounding_box(const F64 x, const F64 y, U32 level, F32* min, F32* max) const +{ + volatile float cell_mid_x; + volatile float cell_mid_y; + float cell_min_x, cell_max_x; + float cell_min_y, cell_max_y; + + cell_min_x = min_x; + cell_max_x = max_x; + cell_min_y = min_y; + cell_max_y = max_y; + + while (level) + { + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + if (x < cell_mid_x) + { + cell_max_x = cell_mid_x; + } + else + { + cell_min_x = cell_mid_x; + } + if (y < cell_mid_y) + { + cell_max_y = cell_mid_y; + } + else + { + cell_min_y = cell_mid_y; + } + level--; + } + if (min) + { + min[0] = cell_min_x; + min[1] = cell_min_y; + } + if (max) + { + max[0] = cell_max_x; + max[1] = cell_max_y; + } +} + +// returns the bounding box of the cell that x & y fall into +void LASquadtree::get_cell_bounding_box(const F64 x, const F64 y, F32* min, F32* max) const +{ + get_cell_bounding_box(x, y, levels, min, max); +} + +// returns the bounding box of the cell with the specified level_index at the specified level +void LASquadtree::get_cell_bounding_box(U32 level_index, U32 level, F32* min, F32* max) const +{ + volatile F32 cell_mid_x; + volatile F32 cell_mid_y; + F32 cell_min_x, cell_max_x; + F32 cell_min_y, cell_max_y; + + cell_min_x = min_x; + cell_max_x = max_x; + cell_min_y = min_y; + cell_max_y = max_y; + + U32 index; + while (level) + { + index = (level_index >>(2*(level-1)))&3; + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + if (index & 1) + { + cell_min_x = cell_mid_x; + } + else + { + cell_max_x = cell_mid_x; + } + if (index & 2) + { + cell_min_y = cell_mid_y; + } + else + { + cell_max_y = cell_mid_y; + } + level--; + } + if (min) + { + min[0] = cell_min_x; + min[1] = cell_min_y; + } + if (max) + { + max[0] = cell_max_x; + max[1] = cell_max_y; + } +} + +// returns the bounding box of the cell with the specified level_index at the specified level +void LASquadtree::get_cell_bounding_box(U32 level_index, U32 level, F64* min, F64* max) const +{ + volatile F64 cell_mid_x; + volatile F64 cell_mid_y; + F64 cell_min_x, cell_max_x; + F64 cell_min_y, cell_max_y; + + cell_min_x = min_x; + cell_max_x = max_x; + cell_min_y = min_y; + cell_max_y = max_y; + + U32 index; + while (level) + { + index = (level_index >>(2*(level-1)))&3; + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + if (index & 1) + { + cell_min_x = cell_mid_x; + } + else + { + cell_max_x = cell_mid_x; + } + if (index & 2) + { + cell_min_y = cell_mid_y; + } + else + { + cell_max_y = cell_mid_y; + } + level--; + } + if (min) + { + min[0] = cell_min_x; + min[1] = cell_min_y; + } + if (max) + { + max[0] = cell_max_x; + max[1] = cell_max_y; + } +} + +// returns the bounding box of the cell with the specified level_index +void LASquadtree::get_cell_bounding_box(U32 level_index, F32* min, F32* max) const +{ + get_cell_bounding_box(level_index, levels, min, max); +} + +// returns the bounding box of the cell with the specified level_index +void LASquadtree::get_cell_bounding_box(U32 level_index, F64* min, F64* max) const +{ + get_cell_bounding_box(level_index, levels, min, max); +} + +// returns the bounding box of the cell with the specified cell_index +void LASquadtree::get_cell_bounding_box(const I32 cell_index, F32* min, F32* max) const +{ + U32 level = get_level((U32)cell_index); + U32 level_index = get_level_index((U32)cell_index, level); + get_cell_bounding_box(level_index, level, min, max); +} + +// returns the (sub-)level index of the cell that x & y fall into at the specified level +U32 LASquadtree::get_level_index(const F64 x, const F64 y, U32 level) const +{ + volatile float cell_mid_x; + volatile float cell_mid_y; + float cell_min_x, cell_max_x; + float cell_min_y, cell_max_y; + + cell_min_x = min_x; + cell_max_x = max_x; + cell_min_y = min_y; + cell_max_y = max_y; + + U32 level_index = 0; + + while (level) + { + level_index <<= 2; + + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + + if (x < cell_mid_x) + { + cell_max_x = cell_mid_x; + } + else + { + cell_min_x = cell_mid_x; + level_index |= 1; + } + if (y < cell_mid_y) + { + cell_max_y = cell_mid_y; + } + else + { + cell_min_y = cell_mid_y; + level_index |= 2; + } + level--; + } + + return level_index; +} + +// returns the (sub-)level index of the cell that x & y fall into +U32 LASquadtree::get_level_index(const F64 x, const F64 y) const +{ + return get_level_index(x, y, levels); +} + +// returns the (sub-)level index and the bounding box of the cell that x & y fall into at the specified level +U32 LASquadtree::get_level_index(const F64 x, const F64 y, U32 level, F32* min, F32* max) const +{ + volatile float cell_mid_x; + volatile float cell_mid_y; + float cell_min_x, cell_max_x; + float cell_min_y, cell_max_y; + + cell_min_x = min_x; + cell_max_x = max_x; + cell_min_y = min_y; + cell_max_y = max_y; + + U32 level_index = 0; + + while (level) + { + level_index <<= 2; + + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + + if (x < cell_mid_x) + { + cell_max_x = cell_mid_x; + } + else + { + cell_min_x = cell_mid_x; + level_index |= 1; + } + if (y < cell_mid_y) + { + cell_max_y = cell_mid_y; + } + else + { + cell_min_y = cell_mid_y; + level_index |= 2; + } + level--; + } + if (min) + { + min[0] = cell_min_x; + min[1] = cell_min_y; + } + if (max) + { + max[0] = cell_max_x; + max[1] = cell_max_y; + } + return level_index; +} + +// returns the (sub-)level index and the bounding box of the cell that x & y fall into +U32 LASquadtree::get_level_index(const F64 x, const F64 y, F32* min, F32* max) const +{ + return get_level_index(x, y, levels, min, max); +} + +// returns the index of the cell that x & y fall into at the specified level +U32 LASquadtree::get_cell_index(const F64 x, const F64 y, U32 level) const +{ + if (sub_level) + { + return level_offset[sub_level+level] + (sub_level_index << (level*2)) + get_level_index(x, y, level); + } + else + { + return level_offset[level]+get_level_index(x, y, level); + } +} + +// returns the index of the cell that x & y fall into +U32 LASquadtree::get_cell_index(const F64 x, const F64 y) const +{ + return get_cell_index(x, y, levels); +} + +// returns the indices of parent and siblings for the specified cell index +BOOL LASquadtree::coarsen(const I32 cell_index, I32* coarser_cell_index, U32* num_cell_indices, I32** cell_indices) +{ + if (cell_index < 0) return FALSE; + U32 level = get_level((U32)cell_index); + if (level == 0) return FALSE; + U32 level_index = get_level_index((U32)cell_index, level); + level_index = level_index >> 2; + if (coarser_cell_index) (*coarser_cell_index) = get_cell_index(level_index, level-1); + if (num_cell_indices && cell_indices) + { + (*num_cell_indices) = 4; + (*cell_indices) = (I32*)coarser_indices; + level_index = level_index << 2; + (*cell_indices)[0] = get_cell_index(level_index + 0, level); + (*cell_indices)[1] = get_cell_index(level_index + 1, level); + (*cell_indices)[2] = get_cell_index(level_index + 2, level); + (*cell_indices)[3] = get_cell_index(level_index + 3, level); + } + return TRUE; +} + +// returns the level index of the cell index at the specified level +U32 LASquadtree::get_level_index(U32 cell_index, U32 level) const +{ + if (sub_level) + { + return cell_index - (sub_level_index << (level*2)) - level_offset[sub_level+level]; + } + else + { + return cell_index - level_offset[level]; + } +} + +// returns the level index of the cell index +U32 LASquadtree::get_level_index(U32 cell_index) const +{ + return get_level_index(cell_index, levels); +} + +// returns the level the cell index +U32 LASquadtree::get_level(U32 cell_index) const +{ + int level = 0; + while (cell_index >= level_offset[level+1]) level++; + return level; +} + +// returns the cell index of the level index at the specified level +U32 LASquadtree::get_cell_index(U32 level_index, U32 level) const +{ + if (sub_level) + { + return level_index + (sub_level_index << (level*2)) + level_offset[sub_level+level]; + } + else + { + return level_index + level_offset[level]; + } +} + +// returns the cell index of the level index +U32 LASquadtree::get_cell_index(U32 level_index) const +{ + return get_cell_index(level_index, levels); +} + +// returns the maximal level index at the specified level +U32 LASquadtree::get_max_level_index(U32 level) const +{ + return (1<getBytes((U8*)signature, 4); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading LASspatial signature\n"); + return FALSE; + } + if (strncmp(signature, "LASS", 4) != 0) + { + fprintf(stderr,"ERROR (LASquadtree): wrong LASspatial signature %4s instead of 'LASS'\n", signature); + return FALSE; + } + U32 type; + try { stream->getBytes((U8*)&type, 4); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading LASspatial type\n"); + return 0; + } + if (type != LAS_SPATIAL_QUAD_TREE) + { + fprintf(stderr,"ERROR (LASquadtree): unknown LASspatial type %u\n", type); + return 0; + } + try { stream->getBytes((U8*)signature, 4); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading signature\n"); + return FALSE; + } + if (strncmp(signature, "LASQ", 4) != 0) + { +// fprintf(stderr,"ERROR (LASquadtree): wrong signature %4s instead of 'LASV'\n", signature); +// return FALSE; + levels = ((U32*)signature)[0]; + } + else + { + U32 version; + try { stream->get32bitsLE((U8*)&version); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading version\n"); + return FALSE; + } + try { stream->get32bitsLE((U8*)&levels); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading levels\n"); + return FALSE; + } + } + U32 level_index; + try { stream->get32bitsLE((U8*)&level_index); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading level_index\n"); + return FALSE; + } + U32 implicit_levels; + try { stream->get32bitsLE((U8*)&implicit_levels); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading implicit_levels\n"); + return FALSE; + } + try { stream->get32bitsLE((U8*)&min_x); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading min_x\n"); + return FALSE; + } + try { stream->get32bitsLE((U8*)&max_x); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading max_x\n"); + return FALSE; + } + try { stream->get32bitsLE((U8*)&min_y); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading min_y\n"); + return FALSE; + } + try { stream->get32bitsLE((U8*)&max_y); } catch(...) + { + fprintf(stderr,"ERROR (LASquadtree): reading max_y\n"); + return FALSE; + } + return TRUE; +} + +BOOL LASquadtree::write(ByteStreamOut* stream) const +{ + // which totals 28 bytes + // U32 levels 4 bytes + // U32 level_index 4 bytes (default 0) + // U32 implicit_levels 4 bytes (only used when level_index != 0)) + // F32 min_x 4 bytes + // F32 max_x 4 bytes + // F32 min_y 4 bytes + // F32 max_y 4 bytes + // which totals 28 bytes + + if (!stream->putBytes((const U8*)"LASS", 4)) + { + fprintf(stderr,"ERROR (LASquadtree): writing LASspatial signature\n"); + return FALSE; + } + + U32 type = LAS_SPATIAL_QUAD_TREE; + if (!stream->put32bitsLE((U8*)&type)) + { + fprintf(stderr,"ERROR (LASquadtree): writing LASspatial type %u\n", type); + return FALSE; + } + + if (!stream->putBytes((const U8*)"LASQ", 4)) + { + fprintf(stderr,"ERROR (LASquadtree): writing signature\n"); + return FALSE; + } + + U32 version = 0; + if (!stream->put32bitsLE((const U8*)&version)) + { + fprintf(stderr,"ERROR (LASquadtree): writing version\n"); + return FALSE; + } + + if (!stream->put32bitsLE((const U8*)&levels)) + { + fprintf(stderr,"ERROR (LASquadtree): writing levels %u\n", levels); + return FALSE; + } + U32 level_index = 0; + if (!stream->put32bitsLE((const U8*)&level_index)) + { + fprintf(stderr,"ERROR (LASquadtree): writing level_index %u\n", level_index); + return FALSE; + } + U32 implicit_levels = 0; + if (!stream->put32bitsLE((const U8*)&implicit_levels)) + { + fprintf(stderr,"ERROR (LASquadtree): writing implicit_levels %u\n", implicit_levels); + return FALSE; + } + if (!stream->put32bitsLE((const U8*)&min_x)) + { + fprintf(stderr,"ERROR (LASquadtree): writing min_x %g\n", min_x); + return FALSE; + } + if (!stream->put32bitsLE((const U8*)&max_x)) + { + fprintf(stderr,"ERROR (LASquadtree): writing max_x %g\n", max_x); + return FALSE; + } + if (!stream->put32bitsLE((const U8*)&min_y)) + { + fprintf(stderr,"ERROR (LASquadtree): writing min_y %g\n", min_y); + return FALSE; + } + if (!stream->put32bitsLE((const U8*)&max_y)) + { + fprintf(stderr,"ERROR (LASquadtree): writing max_y %g\n", max_y); + return FALSE; + } + return TRUE; +} + +// create or finalize the cell (in the spatial hierarchy) +BOOL LASquadtree::manage_cell(const U32 cell_index, const BOOL finalize) +{ + U32 adaptive_pos = cell_index/32; + U32 adaptive_bit = ((U32)1) << (cell_index%32); + if (adaptive_pos >= adaptive_alloc) + { + if (adaptive) + { + adaptive = (U32*)realloc(adaptive, adaptive_pos*2*sizeof(U32)); + for (U32 i = adaptive_alloc; i < adaptive_pos*2; i++) adaptive[i] = 0; + adaptive_alloc = adaptive_pos*2; + } + else + { + adaptive = (U32*)malloc((adaptive_pos+1)*sizeof(U32)); + for (U32 i = adaptive_alloc; i <= adaptive_pos; i++) adaptive[i] = 0; + adaptive_alloc = adaptive_pos+1; + } + } + adaptive[adaptive_pos] &= ~adaptive_bit; + U32 index; + U32 level = get_level(cell_index); + U32 level_index = get_level_index(cell_index, level); + while (level) + { + level--; + level_index = level_index >> 2; + index = get_cell_index(level_index, level); + adaptive_pos = index/32; + adaptive_bit = ((U32)1) << (index%32); + if (adaptive[adaptive_pos] & adaptive_bit) break; + adaptive[adaptive_pos] |= adaptive_bit; + } + return TRUE; +} + +// check whether the x & y coordinates fall into the tiling +BOOL LASquadtree::inside(const F64 x, const F64 y) const +{ + return ((min_x <= x) && (x < max_x) && (min_y <= y) && (y < max_y)); +} + +U32 LASquadtree::intersect_rectangle(const F64 r_min_x, const F64 r_min_y, const F64 r_max_x, const F64 r_max_y, U32 level) +{ + if (current_cells == 0) + { + current_cells = (void*) new my_cell_vector; + } + else + { + ((my_cell_vector*)current_cells)->clear(); + } + + if (r_max_x <= min_x || !(r_min_x <= max_x) || r_max_y <= min_y || !(r_min_y <= max_y)) + { + return 0; + } + + if (adaptive) + { + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, min_x, max_x, min_y, max_y, 0, 0); + } + else + { + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, min_x, max_x, min_y, max_y, level, 0); + } + + return (U32)(((my_cell_vector*)current_cells)->size()); +} + +U32 LASquadtree::intersect_rectangle(const F64 r_min_x, const F64 r_min_y, const F64 r_max_x, const F64 r_max_y) +{ + return intersect_rectangle(r_min_x, r_min_y, r_max_x, r_max_y, levels); +} + +U32 LASquadtree::intersect_tile(const F32 ll_x, const F32 ll_y, const F32 size, U32 level) +{ + if (current_cells == 0) + { + current_cells = (void*) new my_cell_vector; + } + else + { + ((my_cell_vector*)current_cells)->clear(); + } + + volatile F32 ur_x = ll_x + size; + volatile F32 ur_y = ll_y + size; + + if (ur_x <= min_x || !(ll_x <= max_x) || ur_y <= min_y || !(ll_y <= max_y)) + { + return 0; + } + + if (adaptive) + { + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, min_x, max_x, min_y, max_y, 0, 0); + } + else + { + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, min_x, max_x, min_y, max_y, level, 0); + } + + return (U32)(((my_cell_vector*)current_cells)->size()); +} + +U32 LASquadtree::intersect_tile(const F32 ll_x, const F32 ll_y, const F32 size) +{ + return intersect_tile(ll_x, ll_y, size, levels); +} + +U32 LASquadtree::intersect_circle(const F64 center_x, const F64 center_y, const F64 radius, U32 level) +{ + if (current_cells == 0) + { + current_cells = (void*) new my_cell_vector; + } + else + { + ((my_cell_vector*)current_cells)->clear(); + } + + F64 r_min_x = center_x - radius; + F64 r_min_y = center_y - radius; + F64 r_max_x = center_x + radius; + F64 r_max_y = center_y + radius; + + if (r_max_x <= min_x || !(r_min_x <= max_x) || r_max_y <= min_y || !(r_min_y <= max_y)) + { + return 0; + } + + if (adaptive) + { + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, min_x, max_x, min_y, max_y, 0, 0); + } + else + { + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, min_x, max_x, min_y, max_y, level, 0); + } + + return (U32)(((my_cell_vector*)current_cells)->size()); +} + +U32 LASquadtree::intersect_circle(const F64 center_x, const F64 center_y, const F64 radius) +{ + return intersect_circle(center_x, center_y, radius, levels); +} + +void LASquadtree::intersect_rectangle_with_cells(const F64 r_min_x, const F64 r_min_y, const F64 r_max_x, const F64 r_max_y, const F32 cell_min_x, const F32 cell_max_x, const F32 cell_min_y, const F32 cell_max_y, U32 level, U32 level_index) +{ + volatile float cell_mid_x; + volatile float cell_mid_y; + if (level) + { + level--; + level_index <<= 2; + + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + + if (r_max_x <= cell_mid_x) + { + // cell_max_x = cell_mid_x; + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + else + { + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + } + else if (!(r_min_x < cell_mid_x)) + { + // cell_min_x = cell_mid_x; + // level_index |= 1; + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + else + { + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_rectangle_with_cells(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + } + else + { + ((my_cell_vector*)current_cells)->push_back(level_index); + } +} + +void LASquadtree::intersect_rectangle_with_cells_adaptive(const F64 r_min_x, const F64 r_min_y, const F64 r_max_x, const F64 r_max_y, const F32 cell_min_x, const F32 cell_max_x, const F32 cell_min_y, const F32 cell_max_y, U32 level, U32 level_index) +{ + volatile float cell_mid_x; + volatile float cell_mid_y; + U32 cell_index = get_cell_index(level_index, level); + U32 adaptive_pos = cell_index/32; + U32 adaptive_bit = ((U32)1) << (cell_index%32); + if ((level < levels) && (adaptive[adaptive_pos] & adaptive_bit)) + { + level++; + level_index <<= 2; + + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + + if (r_max_x <= cell_mid_x) + { + // cell_max_x = cell_mid_x; + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + else + { + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + } + else if (!(r_min_x < cell_mid_x)) + { + // cell_min_x = cell_mid_x; + // level_index |= 1; + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + else + { + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_rectangle_with_cells_adaptive(r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + } + else + { + ((my_cell_vector*)current_cells)->push_back(cell_index); + } +} + +void LASquadtree::intersect_tile_with_cells(const F32 ll_x, const F32 ll_y, const F32 ur_x, const F32 ur_y, const F32 cell_min_x, const F32 cell_max_x, const F32 cell_min_y, const F32 cell_max_y, U32 level, U32 level_index) +{ + volatile float cell_mid_x; + volatile float cell_mid_y; + if (level) + { + level--; + level_index <<= 2; + + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + + if (ur_x <= cell_mid_x) + { + // cell_max_x = cell_mid_x; + if (ur_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + } + else if (!(ll_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + else + { + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + } + else if (!(ll_x < cell_mid_x)) + { + // cell_min_x = cell_mid_x; + // level_index |= 1; + if (ur_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(ll_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + else + { + if (ur_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(ll_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_tile_with_cells(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + } + else + { + ((my_cell_vector*)current_cells)->push_back(level_index); + } +} + +void LASquadtree::intersect_tile_with_cells_adaptive(const F32 ll_x, const F32 ll_y, const F32 ur_x, const F32 ur_y, const F32 cell_min_x, const F32 cell_max_x, const F32 cell_min_y, const F32 cell_max_y, U32 level, U32 level_index) +{ + volatile float cell_mid_x; + volatile float cell_mid_y; + U32 cell_index = get_cell_index(level_index, level); + U32 adaptive_pos = cell_index/32; + U32 adaptive_bit = ((U32)1) << (cell_index%32); + if ((level < levels) && (adaptive[adaptive_pos] & adaptive_bit)) + { + level++; + level_index <<= 2; + + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + + if (ur_x <= cell_mid_x) + { + // cell_max_x = cell_mid_x; + if (ur_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + } + else if (!(ll_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + else + { + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + } + else if (!(ll_x < cell_mid_x)) + { + // cell_min_x = cell_mid_x; + // level_index |= 1; + if (ur_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(ll_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + else + { + if (ur_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(ll_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_tile_with_cells_adaptive(ll_x, ll_y, ur_x, ur_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + } + else + { + ((my_cell_vector*)current_cells)->push_back(cell_index); + } +} + +void LASquadtree::intersect_circle_with_cells(const F64 center_x, const F64 center_y, const F64 radius, const F64 r_min_x, const F64 r_min_y, const F64 r_max_x, const F64 r_max_y, const F32 cell_min_x, const F32 cell_max_x, const F32 cell_min_y, const F32 cell_max_y, U32 level, U32 level_index) +{ + volatile float cell_mid_x; + volatile float cell_mid_y; + if (level) + { + level--; + level_index <<= 2; + + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + + if (r_max_x <= cell_mid_x) + { + // cell_max_x = cell_mid_x; + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + else + { + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + } + else if (!(r_min_x < cell_mid_x)) + { + // cell_min_x = cell_mid_x; + // level_index |= 1; + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + else + { + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_circle_with_cells(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + } + else + { + if (intersect_circle_with_rectangle(center_x, center_y, radius, cell_min_x, cell_max_x, cell_min_y, cell_max_y)) + { + ((my_cell_vector*)current_cells)->push_back(level_index); + } + } +} + +void LASquadtree::intersect_circle_with_cells_adaptive(const F64 center_x, const F64 center_y, const F64 radius, const F64 r_min_x, const F64 r_min_y, const F64 r_max_x, const F64 r_max_y, const F32 cell_min_x, const F32 cell_max_x, const F32 cell_min_y, const F32 cell_max_y, U32 level, U32 level_index) +{ + volatile float cell_mid_x; + volatile float cell_mid_y; + U32 cell_index = get_cell_index(level_index, level); + U32 adaptive_pos = cell_index/32; + U32 adaptive_bit = ((U32)1) << (cell_index%32); + if ((level < levels) && (adaptive[adaptive_pos] & adaptive_bit)) + { + level++; + level_index <<= 2; + + cell_mid_x = (cell_min_x + cell_max_x)/2; + cell_mid_y = (cell_min_y + cell_max_y)/2; + + if (r_max_x <= cell_mid_x) + { + // cell_max_x = cell_mid_x; + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + else + { + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + } + } + else if (!(r_min_x < cell_mid_x)) + { + // cell_min_x = cell_mid_x; + // level_index |= 1; + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + else + { + if (r_max_y <= cell_mid_y) + { + // cell_max_y = cell_mid_y; + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + } + else if (!(r_min_y < cell_mid_y)) + { + // cell_min_y = cell_mid_y; + // level_index |= 1; + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + else + { + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_min_y, cell_mid_y, level, level_index); + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_min_y, cell_mid_y, level, level_index | 1); + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_min_x, cell_mid_x, cell_mid_y, cell_max_y, level, level_index | 2); + intersect_circle_with_cells_adaptive(center_x, center_y, radius, r_min_x, r_min_y, r_max_x, r_max_y, cell_mid_x, cell_max_x, cell_mid_y, cell_max_y, level, level_index | 3); + } + } + } + else + { + if (intersect_circle_with_rectangle(center_x, center_y, radius, cell_min_x, cell_max_x, cell_min_y, cell_max_y)) + { + ((my_cell_vector*)current_cells)->push_back(cell_index); + } + } +} + +BOOL LASquadtree::intersect_circle_with_rectangle(const F64 center_x, const F64 center_y, const F64 radius, const F32 r_min_x, const F32 r_max_x, const F32 r_min_y, const F32 r_max_y) +{ + F64 r_diff_x, r_diff_y; + F64 radius_squared = radius * radius; + if (r_max_x < center_x) // R to left of circle center + { + r_diff_x = center_x - r_max_x; + if (r_max_y < center_y) // R in lower left corner + { + r_diff_y = center_y - r_max_y; + return ((r_diff_x * r_diff_x + r_diff_y * r_diff_y) < radius_squared); + } + else if (r_min_y > center_y) // R in upper left corner + { + r_diff_y = -center_y + r_min_y; + return ((r_diff_x * r_diff_x + r_diff_y * r_diff_y) < radius_squared); + } + else // R due West of circle + { + return (r_diff_x < radius); + } + } + else if (r_min_x > center_x) // R to right of circle center + { + r_diff_x = -center_x + r_min_x; + if (r_max_y < center_y) // R in lower right corner + { + r_diff_y = center_y - r_max_y; + return ((r_diff_x * r_diff_x + r_diff_y * r_diff_y) < radius_squared); + } + else if (r_min_y > center_y) // R in upper right corner + { + r_diff_y = -center_y + r_min_y; + return ((r_diff_x * r_diff_x + r_diff_y * r_diff_y) < radius_squared); + } + else // R due East of circle + { + return (r_diff_x < radius); + } + } + else // R on circle vertical centerline + { + if (r_max_y < center_y) // R due South of circle + { + r_diff_y = center_y - r_max_y; + return (r_diff_y < radius); + } + else if (r_min_y > center_y) // R due North of circle + { + r_diff_y = -center_y + r_min_y; + return (r_diff_y < radius); + } + else // R contains circle centerpoint + { + return TRUE; + } + } +} + +BOOL LASquadtree::get_all_cells() +{ + intersect_rectangle(min_x, min_y, max_x, max_y); + return get_intersected_cells(); +} + +BOOL LASquadtree::get_intersected_cells() +{ + next_cell_index = 0; + if (current_cells == 0) + { + return FALSE; + } + if (((my_cell_vector*)current_cells)->size() == 0) + { + return FALSE; + } + return TRUE; +} + +BOOL LASquadtree::has_more_cells() +{ + if (current_cells == 0) + { + return FALSE; + } + if (next_cell_index >= ((my_cell_vector*)current_cells)->size()) + { + return FALSE; + } + if (adaptive) + { + current_cell = ((my_cell_vector*)current_cells)->at(next_cell_index); + } + else + { + current_cell = level_offset[levels] + ((my_cell_vector*)current_cells)->at(next_cell_index); + } + next_cell_index++; + return TRUE; +} + +BOOL LASquadtree::setup(F64 bb_min_x, F64 bb_max_x, F64 bb_min_y, F64 bb_max_y, F32 cell_size) +{ + this->cell_size = cell_size; + this->sub_level = 0; + this->sub_level_index = 0; + + // enlarge bounding box to units of cells + if (bb_min_x >= 0) min_x = cell_size*((I32)(bb_min_x/cell_size)); + else min_x = cell_size*((I32)(bb_min_x/cell_size)-1); + if (bb_max_x >= 0) max_x = cell_size*((I32)(bb_max_x/cell_size)+1); + else max_x = cell_size*((I32)(bb_max_x/cell_size)); + if (bb_min_y >= 0) min_y = cell_size*((I32)(bb_min_y/cell_size)); + else min_y = cell_size*((I32)(bb_min_y/cell_size)-1); + if (bb_max_y >= 0) max_y = cell_size*((I32)(bb_max_y/cell_size)+1); + else max_y = cell_size*((I32)(bb_max_y/cell_size)); + + // how many cells minimally in each direction + cells_x = U32_QUANTIZE((max_x - min_x)/cell_size); + cells_y = U32_QUANTIZE((max_y - min_y)/cell_size); + + if (cells_x == 0 || cells_y == 0) + { + fprintf(stderr, "ERROR: cells_x %d cells_y %d\n", cells_x, cells_y); + return FALSE; + } + + // how many quad tree levels to get to that many cells + U32 c = ((cells_x > cells_y) ? cells_x - 1 : cells_y - 1); + levels = 0; + while (c) + { + c = c >> 1; + levels++; + } + + // enlarge bounding box to quad tree size + U32 c1, c2; + c = (1 << levels) - cells_x; + c1 = c/2; + c2 = c - c1; + min_x -= (c2 * cell_size); + max_x += (c1 * cell_size); + c = (1 << levels) - cells_y; + c1 = c/2; + c2 = c - c1; + min_y -= (c2 * cell_size); + max_y += (c1 * cell_size); + + return TRUE; +} + +BOOL LASquadtree::setup(F64 bb_min_x, F64 bb_max_x, F64 bb_min_y, F64 bb_max_y, F32 cell_size, F32 offset_x, F32 offset_y) +{ + this->cell_size = cell_size; + this->sub_level = 0; + this->sub_level_index = 0; + + // enlarge bounding box to units of cells + if ((bb_min_x-offset_x) >= 0) min_x = cell_size*((I32)((bb_min_x-offset_x)/cell_size)) + offset_x; + else min_x = cell_size*((I32)((bb_min_x-offset_x)/cell_size)-1) + offset_x; + if ((bb_max_x-offset_x) >= 0) max_x = cell_size*((I32)((bb_max_x-offset_x)/cell_size)+1) + offset_x; + else max_x = cell_size*((I32)((bb_max_x-offset_x)/cell_size)) + offset_x; + if ((bb_min_y-offset_y) >= 0) min_y = cell_size*((I32)((bb_min_y-offset_y)/cell_size)) + offset_y; + else min_y = cell_size*((I32)((bb_min_y-offset_y)/cell_size)-1) + offset_y; + if ((bb_max_y-offset_y) >= 0) max_y = cell_size*((I32)((bb_max_y-offset_y)/cell_size)+1) + offset_y; + else max_y = cell_size*((I32)((bb_max_y-offset_y)/cell_size)) + offset_y; + + // how many cells minimally in each direction + cells_x = U32_QUANTIZE((max_x - min_x)/cell_size); + cells_y = U32_QUANTIZE((max_y - min_y)/cell_size); + + if (cells_x == 0 || cells_y == 0) + { + fprintf(stderr, "ERROR: cells_x %d cells_y %d\n", cells_x, cells_y); + return FALSE; + } + + // how many quad tree levels to get to that many cells + U32 c = ((cells_x > cells_y) ? cells_x - 1 : cells_y - 1); + levels = 0; + while (c) + { + c = c >> 1; + levels++; + } + + // enlarge bounding box to quad tree size + U32 c1, c2; + c = (1 << levels) - cells_x; + c1 = c/2; + c2 = c - c1; + min_x -= (c2 * cell_size); + max_x += (c1 * cell_size); + c = (1 << levels) - cells_y; + c1 = c/2; + c2 = c - c1; + min_y -= (c2 * cell_size); + max_y += (c1 * cell_size); + + return TRUE; +} + +BOOL LASquadtree::tiling_setup(F32 min_x, F32 max_x, F32 min_y, F32 max_y, U32 levels) +{ + this->min_x = min_x; + this->max_x = max_x; + this->min_y = min_y; + this->max_y = max_y; + this->levels = levels; + this->sub_level = 0; + this->sub_level_index = 0; + return TRUE; +} + +BOOL LASquadtree::subtiling_setup(F32 min_x, F32 max_x, F32 min_y, F32 max_y, U32 sub_level, U32 sub_level_index, U32 levels) +{ + this->min_x = min_x; + this->max_x = max_x; + this->min_y = min_y; + this->max_y = max_y; + F32 min[2]; + F32 max[2]; + get_cell_bounding_box(sub_level_index, sub_level, min, max); + this->min_x = min[0]; + this->max_x = max[0]; + this->min_y = min[1]; + this->max_y = max[1]; + this->sub_level = sub_level; + this->sub_level_index = sub_level_index; + this->levels = levels; + return TRUE; +} + +LASquadtree::LASquadtree() +{ + U32 l; + levels = 0; + cell_size = 0; + min_x = 0; + max_x = 0; + min_y = 0; + max_y = 0; + cells_x = 0; + cells_y = 0; + sub_level = 0; + sub_level_index = 0; + level_offset[0] = 0; + for (l = 0; l < 23; l++) + { + level_offset[l+1] = level_offset[l] + ((1<= x_offset) return (I64)(((x-x_offset)/x_scale_factor)+0.5); else return (I64)(((x-x_offset)/x_scale_factor)-0.5); }; + inline I64 get_Y(const F64 y) const { if (y >= y_offset) return (I64)(((y-y_offset)/y_scale_factor)+0.5); else return (I64)(((y-y_offset)/y_scale_factor)-0.5); }; + inline I64 get_Z(const F64 z) const { if (z >= z_offset) return (I64)(((z-z_offset)/z_scale_factor)+0.5); else return (I64)(((z-z_offset)/z_scale_factor)-0.5); }; + + LASquantizer() + { + x_scale_factor = 0.01; + y_scale_factor = 0.01; + z_scale_factor = 0.01; + x_offset = 0.0; + y_offset = 0.0; + z_offset = 0.0; + }; + + LASquantizer & operator=(const LASquantizer & quantizer) + { + this->x_scale_factor = quantizer.x_scale_factor; + this->y_scale_factor = quantizer.y_scale_factor; + this->z_scale_factor = quantizer.z_scale_factor; + this->x_offset = quantizer.x_offset; + this->y_offset = quantizer.y_offset; + this->z_offset = quantizer.z_offset; + return *this; + }; +}; + +#endif diff --git a/libs/laszip/src/lasreaditem.hpp b/libs/laszip/src/lasreaditem.hpp new file mode 100644 index 0000000..0765839 --- /dev/null +++ b/libs/laszip/src/lasreaditem.hpp @@ -0,0 +1,76 @@ +/* +=============================================================================== + + FILE: lasreaditem.hpp + + CONTENTS: + + Common interface for all classes that read the items that compose a point. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 August 2017 -- moving 'context' from global development hack to interface + 23 August 2016 -- layering of items for selective decompression in LAS 1.4 + 10 January 2011 -- licensing change for LGPL release and liblas integration + 7 December 2010 -- refactored after getting invited to KAUST in Saudi Arabia + +=============================================================================== +*/ +#ifndef LAS_READ_ITEM_HPP +#define LAS_READ_ITEM_HPP + +#include "mydefs.hpp" + +class ByteStreamIn; + +class LASreadItem +{ +public: + virtual void read(U8* item, U32& context)=0; + + virtual ~LASreadItem(){}; +}; + +class LASreadItemRaw : public LASreadItem +{ +public: + LASreadItemRaw() + { + instream = 0; + }; + BOOL init(ByteStreamIn* instream) + { + if (!instream) return FALSE; + this->instream = instream; + return TRUE; + }; + virtual ~LASreadItemRaw(){}; +protected: + ByteStreamIn* instream; +}; + +class LASreadItemCompressed : public LASreadItem +{ +public: + virtual BOOL chunk_sizes() { return FALSE; }; + virtual BOOL init(const U8* item, U32& context)=0; + + virtual ~LASreadItemCompressed(){}; +}; + +#endif diff --git a/libs/laszip/src/lasreaditemcompressed_v1.cpp b/libs/laszip/src/lasreaditemcompressed_v1.cpp new file mode 100644 index 0000000..adff943 --- /dev/null +++ b/libs/laszip/src/lasreaditemcompressed_v1.cpp @@ -0,0 +1,575 @@ +/* +=============================================================================== + + FILE: lasreaditemcompressed_v1.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ + +#include "lasreaditemcompressed_v1.hpp" +#include "laszip_common_v1.hpp" + +#include +#include + +/* +=============================================================================== + LASreadItemCompressed_POINT10_v1 +=============================================================================== +*/ + +struct LASpoint10 +{ + I32 x; + I32 y; + I32 z; + U16 intensity; + U8 return_number : 3; + U8 number_of_returns_of_given_pulse : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 classification; + I8 scan_angle_rank; + U8 user_data; + U16 point_source_ID; +}; + +LASreadItemCompressed_POINT10_v1::LASreadItemCompressed_POINT10_v1(ArithmeticDecoder* dec) +{ + U32 i; + + /* set decoder */ + assert(dec); + this->dec = dec; + + /* create models and integer compressors */ + ic_dx = new IntegerCompressor(dec, 32); // 32 bits, 1 context + ic_dy = new IntegerCompressor(dec, 32, 20); // 32 bits, 20 contexts + ic_z = new IntegerCompressor(dec, 32, 20); // 32 bits, 20 contexts + ic_intensity = new IntegerCompressor(dec, 16); + ic_scan_angle_rank = new IntegerCompressor(dec, 8, 2); + ic_point_source_ID = new IntegerCompressor(dec, 16); + m_changed_values = dec->createSymbolModel(64); + for (i = 0; i < 256; i++) + { + m_bit_byte[i] = 0; + m_classification[i] = 0; + m_user_data[i] = 0; + } +} + +LASreadItemCompressed_POINT10_v1::~LASreadItemCompressed_POINT10_v1() +{ + U32 i; + + delete ic_dx; + delete ic_dy; + delete ic_z; + delete ic_intensity; + delete ic_scan_angle_rank; + delete ic_point_source_ID; + dec->destroySymbolModel(m_changed_values); + for (i = 0; i < 256; i++) + { + if (m_bit_byte[i]) dec->destroySymbolModel(m_bit_byte[i]); + if (m_classification[i]) dec->destroySymbolModel(m_classification[i]); + if (m_user_data[i]) dec->destroySymbolModel(m_user_data[i]); + } +} + +BOOL LASreadItemCompressed_POINT10_v1::init(const U8* item, U32& context) +{ + U32 i; + + /* init state */ + last_x_diff[0] = last_x_diff[1] = last_x_diff[2] = 0; + last_y_diff[0] = last_y_diff[1] = last_y_diff[2] = 0; + last_incr = 0; + + /* init models and integer compressors */ + ic_dx->initDecompressor(); + ic_dy->initDecompressor(); + ic_z->initDecompressor(); + ic_intensity->initDecompressor(); + ic_scan_angle_rank->initDecompressor(); + ic_point_source_ID->initDecompressor(); + dec->initSymbolModel(m_changed_values); + for (i = 0; i < 256; i++) + { + if (m_bit_byte[i]) dec->initSymbolModel(m_bit_byte[i]); + if (m_classification[i]) dec->initSymbolModel(m_classification[i]); + if (m_user_data[i]) dec->initSymbolModel(m_user_data[i]); + } + + /* init last item */ + memcpy(last_item, item, 20); + + return TRUE; +} + +inline void LASreadItemCompressed_POINT10_v1::read(U8* item, U32& context) +{ + // find median difference for x and y from 3 preceding differences + I32 median_x; + if (last_x_diff[0] < last_x_diff[1]) + { + if (last_x_diff[1] < last_x_diff[2]) + median_x = last_x_diff[1]; + else if (last_x_diff[0] < last_x_diff[2]) + median_x = last_x_diff[2]; + else + median_x = last_x_diff[0]; + } + else + { + if (last_x_diff[0] < last_x_diff[2]) + median_x = last_x_diff[0]; + else if (last_x_diff[1] < last_x_diff[2]) + median_x = last_x_diff[2]; + else + median_x = last_x_diff[1]; + } + + I32 median_y; + if (last_y_diff[0] < last_y_diff[1]) + { + if (last_y_diff[1] < last_y_diff[2]) + median_y = last_y_diff[1]; + else if (last_y_diff[0] < last_y_diff[2]) + median_y = last_y_diff[2]; + else + median_y = last_y_diff[0]; + } + else + { + if (last_y_diff[0] < last_y_diff[2]) + median_y = last_y_diff[0]; + else if (last_y_diff[1] < last_y_diff[2]) + median_y = last_y_diff[2]; + else + median_y = last_y_diff[1]; + } + + // decompress x y z coordinates + I32 x_diff = ic_dx->decompress(median_x); + ((LASpoint10*)last_item)->x += x_diff; + // we use the number k of bits corrector bits to switch contexts + U32 k_bits = ic_dx->getK(); + I32 y_diff = ic_dy->decompress(median_y, (k_bits < 19 ? k_bits : 19)); + ((LASpoint10*)last_item)->y += y_diff; + k_bits = (k_bits + ic_dy->getK())/2; + ((LASpoint10*)last_item)->z = ic_z->decompress(((LASpoint10*)last_item)->z, (k_bits < 19 ? k_bits : 19)); + + // decompress which other values have changed + I32 changed_values = dec->decodeSymbol(m_changed_values); + + if (changed_values) + { + // decompress the intensity if it has changed + if (changed_values & 32) + { + ((LASpoint10*)last_item)->intensity = (U16)ic_intensity->decompress(((LASpoint10*)last_item)->intensity); + } + + // decompress the edge_of_flight_line, scan_direction_flag, ... if it has changed + if (changed_values & 16) + { + if (m_bit_byte[last_item[14]] == 0) + { + m_bit_byte[last_item[14]] = dec->createSymbolModel(256); + dec->initSymbolModel(m_bit_byte[last_item[14]]); + } + last_item[14] = (U8)dec->decodeSymbol(m_bit_byte[last_item[14]]); + } + + // decompress the classification ... if it has changed + if (changed_values & 8) + { + if (m_classification[last_item[15]] == 0) + { + m_classification[last_item[15]] = dec->createSymbolModel(256); + dec->initSymbolModel(m_classification[last_item[15]]); + } + last_item[15] = (U8)dec->decodeSymbol(m_classification[last_item[15]]); + } + + // decompress the scan_angle_rank ... if it has changed + if (changed_values & 4) + { + last_item[16] = (U8)ic_scan_angle_rank->decompress(last_item[16], k_bits < 3); + } + + // decompress the user_data ... if it has changed + if (changed_values & 2) + { + if (m_user_data[last_item[17]] == 0) + { + m_user_data[last_item[17]] = dec->createSymbolModel(256); + dec->initSymbolModel(m_user_data[last_item[17]]); + } + last_item[17] = (U8)dec->decodeSymbol(m_user_data[last_item[17]]); + } + + // decompress the point_source_ID ... if it has changed + if (changed_values & 1) + { + ((LASpoint10*)last_item)->point_source_ID = (U16)ic_point_source_ID->decompress(((LASpoint10*)last_item)->point_source_ID); + } + } + + // record the difference + last_x_diff[last_incr] = x_diff; + last_y_diff[last_incr] = y_diff; + last_incr++; + if (last_incr > 2) last_incr = 0; + + // copy the last point + memcpy(item, last_item, 20); +} + +/* +=============================================================================== + LASreadItemCompressed_GPSTIME11_v1 +=============================================================================== +*/ + +#define LASZIP_GPSTIME_MULTIMAX 512 + +LASreadItemCompressed_GPSTIME11_v1::LASreadItemCompressed_GPSTIME11_v1(ArithmeticDecoder* dec) +{ + /* set decoder */ + assert(dec); + this->dec = dec; + /* create entropy models and integer compressors */ + m_gpstime_multi = dec->createSymbolModel(LASZIP_GPSTIME_MULTIMAX); + m_gpstime_0diff = dec->createSymbolModel(3); + ic_gpstime = new IntegerCompressor(dec, 32, 6); // 32 bits, 6 contexts +} + +LASreadItemCompressed_GPSTIME11_v1::~LASreadItemCompressed_GPSTIME11_v1() +{ + dec->destroySymbolModel(m_gpstime_multi); + dec->destroySymbolModel(m_gpstime_0diff); + delete ic_gpstime; +} + +BOOL LASreadItemCompressed_GPSTIME11_v1::init(const U8* item, U32& context) +{ + /* init state */ + last_gpstime_diff = 0; + multi_extreme_counter = 0; + + /* init models and integer compressors */ + dec->initSymbolModel(m_gpstime_multi); + dec->initSymbolModel(m_gpstime_0diff); + ic_gpstime->initDecompressor(); + + /* init last item */ + last_gpstime.u64 = *((U64*)item); + return TRUE; +} + +inline void LASreadItemCompressed_GPSTIME11_v1::read(U8* item, U32& context) +{ + I32 multi; + if (last_gpstime_diff == 0) // if the last integer difference was zero + { + multi = dec->decodeSymbol(m_gpstime_0diff); + if (multi == 1) // the difference can be represented with 32 bits + { + last_gpstime_diff = ic_gpstime->decompress(0, 0); + last_gpstime.i64 += last_gpstime_diff; + } + else if (multi == 2) // the difference is huge + { + last_gpstime.u64 = dec->readInt64(); + } + } + else + { + multi = dec->decodeSymbol(m_gpstime_multi); + + if (multi < LASZIP_GPSTIME_MULTIMAX-2) + { + I32 gpstime_diff; + if (multi == 1) + { + gpstime_diff = ic_gpstime->decompress(last_gpstime_diff, 1); + last_gpstime_diff = gpstime_diff; + multi_extreme_counter = 0; + } + else if (multi == 0) + { + gpstime_diff = ic_gpstime->decompress(last_gpstime_diff/4, 2); + multi_extreme_counter++; + if (multi_extreme_counter > 3) + { + last_gpstime_diff = gpstime_diff; + multi_extreme_counter = 0; + } + } + else if (multi < 10) + { + gpstime_diff = ic_gpstime->decompress(multi*last_gpstime_diff, 3); + } + else if (multi < 50) + { + gpstime_diff = ic_gpstime->decompress(multi*last_gpstime_diff, 4); + } + else + { + gpstime_diff = ic_gpstime->decompress(multi*last_gpstime_diff, 5); + if (multi == LASZIP_GPSTIME_MULTIMAX-3) + { + multi_extreme_counter++; + if (multi_extreme_counter > 3) + { + last_gpstime_diff = gpstime_diff; + multi_extreme_counter = 0; + } + } + } + last_gpstime.i64 += gpstime_diff; + } + else if (multi < LASZIP_GPSTIME_MULTIMAX-1) + { + last_gpstime.u64 = dec->readInt64(); + } + } + *((I64*)item) = last_gpstime.i64; +} + +/* +=============================================================================== + LASreadItemCompressed_RGB12_v1 +=============================================================================== +*/ + +LASreadItemCompressed_RGB12_v1::LASreadItemCompressed_RGB12_v1(ArithmeticDecoder* dec) +{ + /* set decoder */ + assert(dec); + this->dec = dec; + + /* create models and integer compressors */ + m_byte_used = dec->createSymbolModel(64); + ic_rgb = new IntegerCompressor(dec, 8, 6); + + /* create last item */ + last_item = new U8[6]; +} + +LASreadItemCompressed_RGB12_v1::~LASreadItemCompressed_RGB12_v1() +{ + dec->destroySymbolModel(m_byte_used); + delete ic_rgb; + delete [] last_item; +} + +BOOL LASreadItemCompressed_RGB12_v1::init(const U8* item, U32& context) +{ + /* init state */ + + /* init models and integer compressors */ + dec->initSymbolModel(m_byte_used); + ic_rgb->initDecompressor(); + + /* init last item */ + memcpy(last_item, item, 6); + return TRUE; +} + +inline void LASreadItemCompressed_RGB12_v1::read(U8* item, U32& context) +{ + U32 sym = dec->decodeSymbol(m_byte_used); + if (sym & (1 << 0)) ((U16*)item)[0] = (U16)ic_rgb->decompress(((U16*)last_item)[0]&255, 0); + else ((U16*)item)[0] = (U16)(((U16*)last_item)[0]&0xFF); + if (sym & (1 << 1)) ((U16*)item)[0] |= (((U16)ic_rgb->decompress(((U16*)last_item)[0]>>8, 1)) << 8); + else ((U16*)item)[0] |= (((U16*)last_item)[0]&0xFF00); + if (sym & (1 << 2)) ((U16*)item)[1] = (U16)ic_rgb->decompress(((U16*)last_item)[1]&255, 2); + else ((U16*)item)[1] = (U16)(((U16*)last_item)[1]&0xFF); + if (sym & (1 << 3)) ((U16*)item)[1] |= (((U16)ic_rgb->decompress(((U16*)last_item)[1]>>8, 3)) << 8); + else ((U16*)item)[1] |= (((U16*)last_item)[1]&0xFF00); + if (sym & (1 << 4)) ((U16*)item)[2] = (U16)ic_rgb->decompress(((U16*)last_item)[2]&255, 4); + else ((U16*)item)[2] = (U16)(((U16*)last_item)[2]&0xFF); + if (sym & (1 << 5)) ((U16*)item)[2] |= (((U16)ic_rgb->decompress(((U16*)last_item)[2]>>8, 5)) << 8); + else ((U16*)item)[2] |= (((U16*)last_item)[2]&0xFF00); + memcpy(last_item, item, 6); +} + +/* +=============================================================================== + LASreadItemCompressed_WAVEPACKET13_v1 +=============================================================================== +*/ + +LASreadItemCompressed_WAVEPACKET13_v1::LASreadItemCompressed_WAVEPACKET13_v1(ArithmeticDecoder* dec) +{ + /* set decoder */ + assert(dec); + this->dec = dec; + + /* create models and integer compressors */ + m_packet_index = dec->createSymbolModel(256); + m_offset_diff[0] = dec->createSymbolModel(4); + m_offset_diff[1] = dec->createSymbolModel(4); + m_offset_diff[2] = dec->createSymbolModel(4); + m_offset_diff[3] = dec->createSymbolModel(4); + ic_offset_diff = new IntegerCompressor(dec, 32); + ic_packet_size = new IntegerCompressor(dec, 32); + ic_return_point = new IntegerCompressor(dec, 32); + ic_xyz = new IntegerCompressor(dec, 32, 3); + + /* create last item */ + last_item = new U8[28]; +} + +LASreadItemCompressed_WAVEPACKET13_v1::~LASreadItemCompressed_WAVEPACKET13_v1() +{ + dec->destroySymbolModel(m_packet_index); + dec->destroySymbolModel(m_offset_diff[0]); + dec->destroySymbolModel(m_offset_diff[1]); + dec->destroySymbolModel(m_offset_diff[2]); + dec->destroySymbolModel(m_offset_diff[3]); + delete ic_offset_diff; + delete ic_packet_size; + delete ic_return_point; + delete ic_xyz; + delete [] last_item; +} + +BOOL LASreadItemCompressed_WAVEPACKET13_v1::init(const U8* item, U32& context) +{ + /* init state */ + last_diff_32 = 0; + sym_last_offset_diff = 0; + + /* init models and integer compressors */ + dec->initSymbolModel(m_packet_index); + dec->initSymbolModel(m_offset_diff[0]); + dec->initSymbolModel(m_offset_diff[1]); + dec->initSymbolModel(m_offset_diff[2]); + dec->initSymbolModel(m_offset_diff[3]); + ic_offset_diff->initDecompressor(); + ic_packet_size->initDecompressor(); + ic_return_point->initDecompressor(); + ic_xyz->initDecompressor(); + + /* init last item */ + item++; + memcpy(last_item, item, 28); + return TRUE; +} + +inline void LASreadItemCompressed_WAVEPACKET13_v1::read(U8* item, U32& context) +{ + item[0] = (U8)(dec->decodeSymbol(m_packet_index)); + item++; + + LASwavepacket13 this_item_m; + LASwavepacket13 last_item_m = LASwavepacket13::unpack(last_item); + + sym_last_offset_diff = dec->decodeSymbol(m_offset_diff[sym_last_offset_diff]); + + if (sym_last_offset_diff == 0) + { + this_item_m.offset = last_item_m.offset; + } + else if (sym_last_offset_diff == 1) + { + this_item_m.offset = last_item_m.offset + last_item_m.packet_size; + } + else if (sym_last_offset_diff == 2) + { + last_diff_32 = ic_offset_diff->decompress(last_diff_32); + this_item_m.offset = last_item_m.offset + last_diff_32; + } + else + { + this_item_m.offset = dec->readInt64(); + } + + this_item_m.packet_size = ic_packet_size->decompress(last_item_m.packet_size); + this_item_m.return_point.i32 = ic_return_point->decompress(last_item_m.return_point.i32); + this_item_m.x.i32 = ic_xyz->decompress(last_item_m.x.i32, 0); + this_item_m.y.i32 = ic_xyz->decompress(last_item_m.y.i32, 1); + this_item_m.z.i32 = ic_xyz->decompress(last_item_m.z.i32, 2); + + this_item_m.pack(item); + + memcpy(last_item, item, 28); +} + +/* +=============================================================================== + LASreadItemCompressed_BYTE_v1 +=============================================================================== +*/ + +LASreadItemCompressed_BYTE_v1::LASreadItemCompressed_BYTE_v1(ArithmeticDecoder* dec, U32 number) +{ + /* set decoder */ + assert(dec); + this->dec = dec; + assert(number); + this->number = number; + + /* create models and integer compressors */ + ic_byte = new IntegerCompressor(dec, 8, number); + + /* create last item */ + last_item = new U8[number]; +} + +LASreadItemCompressed_BYTE_v1::~LASreadItemCompressed_BYTE_v1() +{ + delete ic_byte; + delete [] last_item; +} + +BOOL LASreadItemCompressed_BYTE_v1::init(const U8* item, U32& context) +{ + /* init state */ + + /* init models and integer compressors */ + ic_byte->initDecompressor(); + + /* init last item */ + memcpy(last_item, item, number); + return TRUE; +} + +inline void LASreadItemCompressed_BYTE_v1::read(U8* item, U32& context) +{ + U32 i; + for (i = 0; i < number; i++) + { + item[i] = (U8)(ic_byte->decompress(last_item[i], i)); + } + memcpy(last_item, item, number); +} + +// vim: set ts=2 sw=2 expandtabs diff --git a/libs/laszip/src/lasreaditemcompressed_v1.hpp b/libs/laszip/src/lasreaditemcompressed_v1.hpp new file mode 100644 index 0000000..859f9f2 --- /dev/null +++ b/libs/laszip/src/lasreaditemcompressed_v1.hpp @@ -0,0 +1,156 @@ +/* +=============================================================================== + + FILE: lasreaditemcompressed_v1.hpp + + CONTENTS: + + Implementation of LASitemReadCompressed for *all* items (version 1). + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 August 2017 -- moving 'context' from global development hack to interface + 6 September 2014 -- removed inheritance of EntropyEncoder and EntropyDecoder + 10 January 2011 -- licensing change for LGPL release and liblas integration + 7 December 2010 -- refactored after getting invited to KAUST in Saudi Arabia + +=============================================================================== +*/ +#ifndef LAS_READ_ITEM_COMPRESSED_V1_HPP +#define LAS_READ_ITEM_COMPRESSED_V1_HPP + +#include "lasreaditem.hpp" +#include "arithmeticdecoder.hpp" +#include "integercompressor.hpp" + +class LASreadItemCompressed_POINT10_v1 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_POINT10_v1(ArithmeticDecoder* dec); + + BOOL init(const U8* item, U32& context); // context is unused + void read(U8* item, U32& context); // context is unused + + ~LASreadItemCompressed_POINT10_v1(); + +private: + ArithmeticDecoder* dec; + U8 last_item[20]; + + I32 last_x_diff[3]; + I32 last_y_diff[3]; + I32 last_incr; + IntegerCompressor* ic_dx; + IntegerCompressor* ic_dy; + IntegerCompressor* ic_z; + IntegerCompressor* ic_intensity; + IntegerCompressor* ic_scan_angle_rank; + IntegerCompressor* ic_point_source_ID; + ArithmeticModel* m_changed_values; + ArithmeticModel* m_bit_byte[256]; + ArithmeticModel* m_classification[256]; + ArithmeticModel* m_user_data[256]; +}; + +class LASreadItemCompressed_GPSTIME11_v1 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_GPSTIME11_v1(ArithmeticDecoder* dec); + + BOOL init(const U8* item, U32& context); // context is unused + void read(U8* item, U32& context); // context is unused + + ~LASreadItemCompressed_GPSTIME11_v1(); + +private: + ArithmeticDecoder* dec; + U64I64F64 last_gpstime; + + ArithmeticModel* m_gpstime_multi; + ArithmeticModel* m_gpstime_0diff; + IntegerCompressor* ic_gpstime; + I32 multi_extreme_counter; + I32 last_gpstime_diff; +}; + +class LASreadItemCompressed_RGB12_v1 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_RGB12_v1(ArithmeticDecoder* dec); + + BOOL init(const U8* item, U32& context); // context is unused + void read(U8* item, U32& context); // context is unused + + ~LASreadItemCompressed_RGB12_v1(); + +private: + ArithmeticDecoder* dec; + U8* last_item; + + ArithmeticModel* m_byte_used; + IntegerCompressor* ic_rgb; +}; + +class LASreadItemCompressed_WAVEPACKET13_v1 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_WAVEPACKET13_v1(ArithmeticDecoder* dec); + + BOOL init(const U8* item, U32& context); // context is unused + void read(U8* item, U32& context); // context is unused + + ~LASreadItemCompressed_WAVEPACKET13_v1(); + +private: + ArithmeticDecoder* dec; + U8* last_item; + + I32 last_diff_32; + U32 sym_last_offset_diff; + ArithmeticModel* m_packet_index; + ArithmeticModel* m_offset_diff[4]; + IntegerCompressor* ic_offset_diff; + IntegerCompressor* ic_packet_size; + IntegerCompressor* ic_return_point; + IntegerCompressor* ic_xyz; +}; + +class LASreadItemCompressed_BYTE_v1 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_BYTE_v1(ArithmeticDecoder* dec, U32 number); + + BOOL init(const U8* item, U32& context); // context is unused + void read(U8* item, U32& context); // context is unused + + ~LASreadItemCompressed_BYTE_v1(); + +private: + ArithmeticDecoder* dec; + U32 number; + U8* last_item; + + IntegerCompressor* ic_byte; +}; + +#endif diff --git a/libs/laszip/src/lasreaditemcompressed_v2.cpp b/libs/laszip/src/lasreaditemcompressed_v2.cpp new file mode 100644 index 0000000..720a70c --- /dev/null +++ b/libs/laszip/src/lasreaditemcompressed_v2.cpp @@ -0,0 +1,591 @@ +/* +=============================================================================== + + FILE: lasreaditemcompressed_v2.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ + +#include "lasreaditemcompressed_v2.hpp" + +#include +#include + +struct LASpoint10 +{ + I32 x; + I32 y; + I32 z; + U16 intensity; + U8 return_number : 3; + U8 number_of_returns_of_given_pulse : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 classification; + I8 scan_angle_rank; + U8 user_data; + U16 point_source_ID; +}; + +LASreadItemCompressed_POINT10_v2::LASreadItemCompressed_POINT10_v2(ArithmeticDecoder* dec) +{ + U32 i; + + /* set decoder */ + assert(dec); + this->dec = dec; + + /* create models and integer compressors */ + m_changed_values = dec->createSymbolModel(64); + ic_intensity = new IntegerCompressor(dec, 16, 4); + m_scan_angle_rank[0] = dec->createSymbolModel(256); + m_scan_angle_rank[1] = dec->createSymbolModel(256); + ic_point_source_ID = new IntegerCompressor(dec, 16); + for (i = 0; i < 256; i++) + { + m_bit_byte[i] = 0; + m_classification[i] = 0; + m_user_data[i] = 0; + } + ic_dx = new IntegerCompressor(dec, 32, 2); // 32 bits, 2 context + ic_dy = new IntegerCompressor(dec, 32, 22); // 32 bits, 22 contexts + ic_z = new IntegerCompressor(dec, 32, 20); // 32 bits, 20 contexts +} + +LASreadItemCompressed_POINT10_v2::~LASreadItemCompressed_POINT10_v2() +{ + U32 i; + + dec->destroySymbolModel(m_changed_values); + delete ic_intensity; + dec->destroySymbolModel(m_scan_angle_rank[0]); + dec->destroySymbolModel(m_scan_angle_rank[1]); + delete ic_point_source_ID; + for (i = 0; i < 256; i++) + { + if (m_bit_byte[i]) dec->destroySymbolModel(m_bit_byte[i]); + if (m_classification[i]) dec->destroySymbolModel(m_classification[i]); + if (m_user_data[i]) dec->destroySymbolModel(m_user_data[i]); + } + delete ic_dx; + delete ic_dy; + delete ic_z; +} + +BOOL LASreadItemCompressed_POINT10_v2::init(const U8* item, U32& context) +{ + U32 i; + + /* init state */ + for (i=0; i < 16; i++) + { + last_x_diff_median5[i].init(); + last_y_diff_median5[i].init(); + last_intensity[i] = 0; + last_height[i/2] = 0; + } + + /* init models and integer compressors */ + dec->initSymbolModel(m_changed_values); + ic_intensity->initDecompressor(); + dec->initSymbolModel(m_scan_angle_rank[0]); + dec->initSymbolModel(m_scan_angle_rank[1]); + ic_point_source_ID->initDecompressor(); + for (i = 0; i < 256; i++) + { + if (m_bit_byte[i]) dec->initSymbolModel(m_bit_byte[i]); + if (m_classification[i]) dec->initSymbolModel(m_classification[i]); + if (m_user_data[i]) dec->initSymbolModel(m_user_data[i]); + } + ic_dx->initDecompressor(); + ic_dy->initDecompressor(); + ic_z->initDecompressor(); + + /* init last item */ + memcpy(last_item, item, 20); + + /* but set intensity to zero */ + last_item[12] = 0; + last_item[13] = 0; + + return TRUE; +} + +inline void LASreadItemCompressed_POINT10_v2::read(U8* item, U32& context) +{ + U32 r, n, m, l; + U32 k_bits; + I32 median, diff; + + // decompress which other values have changed + I32 changed_values = dec->decodeSymbol(m_changed_values); + + if (changed_values) + { + // decompress the edge_of_flight_line, scan_direction_flag, ... if it has changed + if (changed_values & 32) + { + if (m_bit_byte[last_item[14]] == 0) + { + m_bit_byte[last_item[14]] = dec->createSymbolModel(256); + dec->initSymbolModel(m_bit_byte[last_item[14]]); + } + last_item[14] = (U8)dec->decodeSymbol(m_bit_byte[last_item[14]]); + } + + r = ((LASpoint10*)last_item)->return_number; + n = ((LASpoint10*)last_item)->number_of_returns_of_given_pulse; + m = number_return_map[n][r]; + l = number_return_level[n][r]; + + // decompress the intensity if it has changed + if (changed_values & 16) + { + ((LASpoint10*)last_item)->intensity = (U16)ic_intensity->decompress(last_intensity[m], (m < 3 ? m : 3)); + last_intensity[m] = ((LASpoint10*)last_item)->intensity; + } + else + { + ((LASpoint10*)last_item)->intensity = last_intensity[m]; + } + + // decompress the classification ... if it has changed + if (changed_values & 8) + { + if (m_classification[last_item[15]] == 0) + { + m_classification[last_item[15]] = dec->createSymbolModel(256); + dec->initSymbolModel(m_classification[last_item[15]]); + } + last_item[15] = (U8)dec->decodeSymbol(m_classification[last_item[15]]); + } + + // decompress the scan_angle_rank ... if it has changed + if (changed_values & 4) + { + I32 val = dec->decodeSymbol(m_scan_angle_rank[((LASpoint10*)last_item)->scan_direction_flag]); + last_item[16] = U8_FOLD(val + last_item[16]); + } + + // decompress the user_data ... if it has changed + if (changed_values & 2) + { + if (m_user_data[last_item[17]] == 0) + { + m_user_data[last_item[17]] = dec->createSymbolModel(256); + dec->initSymbolModel(m_user_data[last_item[17]]); + } + last_item[17] = (U8)dec->decodeSymbol(m_user_data[last_item[17]]); + } + + // decompress the point_source_ID ... if it has changed + if (changed_values & 1) + { + ((LASpoint10*)last_item)->point_source_ID = (U16)ic_point_source_ID->decompress(((LASpoint10*)last_item)->point_source_ID); + } + } + else + { + r = ((LASpoint10*)last_item)->return_number; + n = ((LASpoint10*)last_item)->number_of_returns_of_given_pulse; + m = number_return_map[n][r]; + l = number_return_level[n][r]; + } + + // decompress x coordinate + median = last_x_diff_median5[m].get(); + diff = ic_dx->decompress(median, n==1); + ((LASpoint10*)last_item)->x += diff; + last_x_diff_median5[m].add(diff); + + // decompress y coordinate + median = last_y_diff_median5[m].get(); + k_bits = ic_dx->getK(); + diff = ic_dy->decompress(median, (n==1) + ( k_bits < 20 ? U32_ZERO_BIT_0(k_bits) : 20 )); + ((LASpoint10*)last_item)->y += diff; + last_y_diff_median5[m].add(diff); + + // decompress z coordinate + k_bits = (ic_dx->getK() + ic_dy->getK()) / 2; + ((LASpoint10*)last_item)->z = ic_z->decompress(last_height[l], (n==1) + (k_bits < 18 ? U32_ZERO_BIT_0(k_bits) : 18)); + last_height[l] = ((LASpoint10*)last_item)->z; + + // copy the last point + memcpy(item, last_item, 20); +} + +/* +=============================================================================== + LASreadItemCompressed_GPSTIME11_v2 +=============================================================================== +*/ + +#define LASZIP_GPSTIME_MULTI 500 +#define LASZIP_GPSTIME_MULTI_MINUS -10 +#define LASZIP_GPSTIME_MULTI_UNCHANGED (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 1) +#define LASZIP_GPSTIME_MULTI_CODE_FULL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 2) + +#define LASZIP_GPSTIME_MULTI_TOTAL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 6) + +LASreadItemCompressed_GPSTIME11_v2::LASreadItemCompressed_GPSTIME11_v2(ArithmeticDecoder* dec) +{ + /* set decoder */ + assert(dec); + this->dec = dec; + /* create entropy models and integer compressors */ + m_gpstime_multi = dec->createSymbolModel(LASZIP_GPSTIME_MULTI_TOTAL); + m_gpstime_0diff = dec->createSymbolModel(6); + ic_gpstime = new IntegerCompressor(dec, 32, 9); // 32 bits, 9 contexts +} + +LASreadItemCompressed_GPSTIME11_v2::~LASreadItemCompressed_GPSTIME11_v2() +{ + dec->destroySymbolModel(m_gpstime_multi); + dec->destroySymbolModel(m_gpstime_0diff); + delete ic_gpstime; +} + +BOOL LASreadItemCompressed_GPSTIME11_v2::init(const U8* item, U32& context) +{ + /* init state */ + last = 0, next = 0; + last_gpstime_diff[0] = 0; + last_gpstime_diff[1] = 0; + last_gpstime_diff[2] = 0; + last_gpstime_diff[3] = 0; + multi_extreme_counter[0] = 0; + multi_extreme_counter[1] = 0; + multi_extreme_counter[2] = 0; + multi_extreme_counter[3] = 0; + + /* init models and integer compressors */ + dec->initSymbolModel(m_gpstime_multi); + dec->initSymbolModel(m_gpstime_0diff); + ic_gpstime->initDecompressor(); + + /* init last item */ + last_gpstime[0].u64 = *((U64*)item); + last_gpstime[1].u64 = 0; + last_gpstime[2].u64 = 0; + last_gpstime[3].u64 = 0; + return TRUE; +} + +inline void LASreadItemCompressed_GPSTIME11_v2::read(U8* item, U32& context) +{ + I32 multi; + if (last_gpstime_diff[last] == 0) // if the last integer difference was zero + { + multi = dec->decodeSymbol(m_gpstime_0diff); + if (multi == 1) // the difference can be represented with 32 bits + { + last_gpstime_diff[last] = ic_gpstime->decompress(0, 0); + last_gpstime[last].i64 += last_gpstime_diff[last]; + multi_extreme_counter[last] = 0; + } + else if (multi == 2) // the difference is huge + { + next = (next+1)&3; + last_gpstime[next].u64 = ic_gpstime->decompress((I32)(last_gpstime[last].u64 >> 32), 8); + last_gpstime[next].u64 = last_gpstime[next].u64 << 32; + last_gpstime[next].u64 |= dec->readInt(); + last = next; + last_gpstime_diff[last] = 0; + multi_extreme_counter[last] = 0; + } + else if (multi > 2) // we switch to another sequence + { + last = (last+multi-2)&3; + read(item, context); + } + } + else + { + multi = dec->decodeSymbol(m_gpstime_multi); + if (multi == 1) + { + last_gpstime[last].i64 += ic_gpstime->decompress(last_gpstime_diff[last], 1);; + multi_extreme_counter[last] = 0; + } + else if (multi < LASZIP_GPSTIME_MULTI_UNCHANGED) + { + I32 gpstime_diff; + if (multi == 0) + { + gpstime_diff = ic_gpstime->decompress(0, 7); + multi_extreme_counter[last]++; + if (multi_extreme_counter[last] > 3) + { + last_gpstime_diff[last] = gpstime_diff; + multi_extreme_counter[last] = 0; + } + } + else if (multi < LASZIP_GPSTIME_MULTI) + { + if (multi < 10) + gpstime_diff = ic_gpstime->decompress(multi*last_gpstime_diff[last], 2); + else + gpstime_diff = ic_gpstime->decompress(multi*last_gpstime_diff[last], 3); + } + else if (multi == LASZIP_GPSTIME_MULTI) + { + gpstime_diff = ic_gpstime->decompress(LASZIP_GPSTIME_MULTI*last_gpstime_diff[last], 4); + multi_extreme_counter[last]++; + if (multi_extreme_counter[last] > 3) + { + last_gpstime_diff[last] = gpstime_diff; + multi_extreme_counter[last] = 0; + } + } + else + { + multi = LASZIP_GPSTIME_MULTI - multi; + if (multi > LASZIP_GPSTIME_MULTI_MINUS) + { + gpstime_diff = ic_gpstime->decompress(multi*last_gpstime_diff[last], 5); + } + else + { + gpstime_diff = ic_gpstime->decompress(LASZIP_GPSTIME_MULTI_MINUS*last_gpstime_diff[last], 6); + multi_extreme_counter[last]++; + if (multi_extreme_counter[last] > 3) + { + last_gpstime_diff[last] = gpstime_diff; + multi_extreme_counter[last] = 0; + } + } + } + last_gpstime[last].i64 += gpstime_diff; + } + else if (multi == LASZIP_GPSTIME_MULTI_CODE_FULL) + { + next = (next+1)&3; + last_gpstime[next].u64 = ic_gpstime->decompress((I32)(last_gpstime[last].u64 >> 32), 8); + last_gpstime[next].u64 = last_gpstime[next].u64 << 32; + last_gpstime[next].u64 |= dec->readInt(); + last = next; + last_gpstime_diff[last] = 0; + multi_extreme_counter[last] = 0; + } + else if (multi >= LASZIP_GPSTIME_MULTI_CODE_FULL) + { + last = (last+multi-LASZIP_GPSTIME_MULTI_CODE_FULL)&3; + read(item, context); + } + } + *((I64*)item) = last_gpstime[last].i64; +} + +/* +=============================================================================== + LASreadItemCompressed_RGB12_v2 +=============================================================================== +*/ + +LASreadItemCompressed_RGB12_v2::LASreadItemCompressed_RGB12_v2(ArithmeticDecoder* dec) +{ + /* set decoder */ + assert(dec); + this->dec = dec; + + /* create models and integer compressors */ + m_byte_used = dec->createSymbolModel(128); + m_rgb_diff_0 = dec->createSymbolModel(256); + m_rgb_diff_1 = dec->createSymbolModel(256); + m_rgb_diff_2 = dec->createSymbolModel(256); + m_rgb_diff_3 = dec->createSymbolModel(256); + m_rgb_diff_4 = dec->createSymbolModel(256); + m_rgb_diff_5 = dec->createSymbolModel(256); +} + +LASreadItemCompressed_RGB12_v2::~LASreadItemCompressed_RGB12_v2() +{ + dec->destroySymbolModel(m_byte_used); + dec->destroySymbolModel(m_rgb_diff_0); + dec->destroySymbolModel(m_rgb_diff_1); + dec->destroySymbolModel(m_rgb_diff_2); + dec->destroySymbolModel(m_rgb_diff_3); + dec->destroySymbolModel(m_rgb_diff_4); + dec->destroySymbolModel(m_rgb_diff_5); +} + +BOOL LASreadItemCompressed_RGB12_v2::init(const U8* item, U32& context) +{ + /* init state */ + + /* init models and integer compressors */ + dec->initSymbolModel(m_byte_used); + dec->initSymbolModel(m_rgb_diff_0); + dec->initSymbolModel(m_rgb_diff_1); + dec->initSymbolModel(m_rgb_diff_2); + dec->initSymbolModel(m_rgb_diff_3); + dec->initSymbolModel(m_rgb_diff_4); + dec->initSymbolModel(m_rgb_diff_5); + + /* init last item */ + memcpy(last_item, item, 6); + return TRUE; +} + +inline void LASreadItemCompressed_RGB12_v2::read(U8* item, U32& context) +{ + U8 corr; + I32 diff = 0; + U32 sym = dec->decodeSymbol(m_byte_used); + if (sym & (1 << 0)) + { + corr = dec->decodeSymbol(m_rgb_diff_0); + ((U16*)item)[0] = (U16)U8_FOLD(corr + (last_item[0]&255)); + } + else + { + ((U16*)item)[0] = last_item[0]&0xFF; + } + if (sym & (1 << 1)) + { + corr = dec->decodeSymbol(m_rgb_diff_1); + ((U16*)item)[0] |= (((U16)U8_FOLD(corr + (last_item[0]>>8))) << 8); + } + else + { + ((U16*)item)[0] |= (last_item[0]&0xFF00); + } + if (sym & (1 << 6)) + { + diff = (((U16*)item)[0]&0x00FF) - (last_item[0]&0x00FF); + if (sym & (1 << 2)) + { + corr = dec->decodeSymbol(m_rgb_diff_2); + ((U16*)item)[1] = (U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[1]&255))); + } + else + { + ((U16*)item)[1] = last_item[1]&0xFF; + } + if (sym & (1 << 4)) + { + corr = dec->decodeSymbol(m_rgb_diff_4); + diff = (diff + ((((U16*)item)[1]&0x00FF) - (last_item[1]&0x00FF))) / 2; + ((U16*)item)[2] = (U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[2]&255))); + } + else + { + ((U16*)item)[2] = last_item[2]&0xFF; + } + diff = (((U16*)item)[0]>>8) - (last_item[0]>>8); + if (sym & (1 << 3)) + { + corr = dec->decodeSymbol(m_rgb_diff_3); + ((U16*)item)[1] |= (((U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[1]>>8))))<<8); + } + else + { + ((U16*)item)[1] |= (last_item[1]&0xFF00); + } + if (sym & (1 << 5)) + { + corr = dec->decodeSymbol(m_rgb_diff_5); + diff = (diff + ((((U16*)item)[1]>>8) - (last_item[1]>>8))) / 2; + ((U16*)item)[2] |= (((U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[2]>>8))))<<8); + } + else + { + ((U16*)item)[2] |= (last_item[2]&0xFF00); + } + } + else + { + ((U16*)item)[1] = ((U16*)item)[0]; + ((U16*)item)[2] = ((U16*)item)[0]; + } + memcpy(last_item, item, 6); +} + +/* +=============================================================================== + LASreadItemCompressed_BYTE_v2 +=============================================================================== +*/ + +LASreadItemCompressed_BYTE_v2::LASreadItemCompressed_BYTE_v2(ArithmeticDecoder* dec, U32 number) +{ + U32 i; + + /* set decoder */ + assert(dec); + this->dec = dec; + assert(number); + this->number = number; + + /* create models and integer compressors */ + m_byte = new ArithmeticModel*[number]; + for (i = 0; i < number; i++) + { + m_byte[i] = dec->createSymbolModel(256); + } + + /* create last item */ + last_item = new U8[number]; +} + +LASreadItemCompressed_BYTE_v2::~LASreadItemCompressed_BYTE_v2() +{ + U32 i; + for (i = 0; i < number; i++) + { + dec->destroySymbolModel(m_byte[i]); + } + delete [] m_byte; + delete [] last_item; +} + +BOOL LASreadItemCompressed_BYTE_v2::init(const U8* item, U32& context) +{ + U32 i; + /* init state */ + + /* init models and integer compressors */ + for (i = 0; i < number; i++) + { + dec->initSymbolModel(m_byte[i]); + } + + /* init last item */ + memcpy(last_item, item, number); + return TRUE; +} + +inline void LASreadItemCompressed_BYTE_v2::read(U8* item, U32& context) +{ + U32 i; + I32 value; + for (i = 0; i < number; i++) + { + value = last_item[i] + dec->decodeSymbol(m_byte[i]); + item[i] = U8_FOLD(value); + } + memcpy(last_item, item, number); +} diff --git a/libs/laszip/src/lasreaditemcompressed_v2.hpp b/libs/laszip/src/lasreaditemcompressed_v2.hpp new file mode 100644 index 0000000..82bc110 --- /dev/null +++ b/libs/laszip/src/lasreaditemcompressed_v2.hpp @@ -0,0 +1,139 @@ +/* +=============================================================================== + + FILE: lasreaditemcompressed_v2.hpp + + CONTENTS: + + Implementation of LASitemReadCompressed for *all* items (version 2). + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2014, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 August 2017 -- moving 'context' from global development hack to interface + 6 September 2014 -- removed inheritance of EntropyEncoder and EntropyDecoder + 5 March 2011 -- created first night in ibiza to improve the RGB compressor + +=============================================================================== +*/ +#ifndef LAS_READ_ITEM_COMPRESSED_V2_HPP +#define LAS_READ_ITEM_COMPRESSED_V2_HPP + +#include "lasreaditem.hpp" +#include "arithmeticdecoder.hpp" +#include "integercompressor.hpp" + +#include "laszip_common_v2.hpp" + +class LASreadItemCompressed_POINT10_v2 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_POINT10_v2(ArithmeticDecoder* dec); + + BOOL init(const U8* item, U32& context); // context is unused + void read(U8* item, U32& context); // context is unused + + ~LASreadItemCompressed_POINT10_v2(); + +private: + ArithmeticDecoder* dec; + U8 last_item[20]; + U16 last_intensity[16]; + StreamingMedian5 last_x_diff_median5[16]; + StreamingMedian5 last_y_diff_median5[16]; + I32 last_height[8]; + + ArithmeticModel* m_changed_values; + IntegerCompressor* ic_intensity; + ArithmeticModel* m_scan_angle_rank[2]; + IntegerCompressor* ic_point_source_ID; + ArithmeticModel* m_bit_byte[256]; + ArithmeticModel* m_classification[256]; + ArithmeticModel* m_user_data[256]; + IntegerCompressor* ic_dx; + IntegerCompressor* ic_dy; + IntegerCompressor* ic_z; +}; + +class LASreadItemCompressed_GPSTIME11_v2 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_GPSTIME11_v2(ArithmeticDecoder* dec); + + BOOL init(const U8* item, U32& context); // context is unused + void read(U8* item, U32& context); // context is unused + + ~LASreadItemCompressed_GPSTIME11_v2(); + +private: + ArithmeticDecoder* dec; + U32 last, next; + U64I64F64 last_gpstime[4]; + I32 last_gpstime_diff[4]; + I32 multi_extreme_counter[4]; + + ArithmeticModel* m_gpstime_multi; + ArithmeticModel* m_gpstime_0diff; + IntegerCompressor* ic_gpstime; +}; + +class LASreadItemCompressed_RGB12_v2 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_RGB12_v2(ArithmeticDecoder* dec); + + BOOL init(const U8* item, U32& context); // context is unused + void read(U8* item, U32& context); // context is unused + + ~LASreadItemCompressed_RGB12_v2(); + +private: + ArithmeticDecoder* dec; + U16 last_item[3]; + + ArithmeticModel* m_byte_used; + ArithmeticModel* m_rgb_diff_0; + ArithmeticModel* m_rgb_diff_1; + ArithmeticModel* m_rgb_diff_2; + ArithmeticModel* m_rgb_diff_3; + ArithmeticModel* m_rgb_diff_4; + ArithmeticModel* m_rgb_diff_5; +}; + +class LASreadItemCompressed_BYTE_v2 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_BYTE_v2(ArithmeticDecoder* dec, U32 number); + + BOOL init(const U8* item, U32& context); // context is unused + void read(U8* item, U32& context); // context is unused + + ~LASreadItemCompressed_BYTE_v2(); + +private: + ArithmeticDecoder* dec; + U32 number; + U8* last_item; + + ArithmeticModel** m_byte; +}; + +#endif diff --git a/libs/laszip/src/lasreaditemcompressed_v3.cpp b/libs/laszip/src/lasreaditemcompressed_v3.cpp new file mode 100644 index 0000000..48df564 --- /dev/null +++ b/libs/laszip/src/lasreaditemcompressed_v3.cpp @@ -0,0 +1,2412 @@ +/* +=============================================================================== + + FILE: lasreaditemcompressed_v3.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ + +#include "lasreaditemcompressed_v3.hpp" + +#include +#include + +typedef struct LASpoint14 +{ + I32 X; + I32 Y; + I32 Z; + U16 intensity; + U8 legacy_return_number : 3; + U8 legacy_number_of_returns : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 legacy_classification : 5; + U8 legacy_flags : 3; + I8 legacy_scan_angle_rank; + U8 user_data; + U16 point_source_ID; + + // LAS 1.4 only + I16 scan_angle; + U8 legacy_point_type : 2; + U8 scanner_channel : 2; + U8 classification_flags : 4; + U8 classification; + U8 return_number : 4; + U8 number_of_returns : 4; + + // LASlib internal use only + U8 deleted_flag; + + // for 8 byte alignment of the GPS time + U8 dummy[2]; + + // compressed LASzip 1.4 points only + BOOL gps_time_change; + + F64 gps_time; + U16 rgb[4]; +// LASwavepacket wavepacket; +} LASpoint14; + +#define LASZIP_GPSTIME_MULTI 500 +#define LASZIP_GPSTIME_MULTI_MINUS -10 +#define LASZIP_GPSTIME_MULTI_CODE_FULL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 1) + +#define LASZIP_GPSTIME_MULTI_TOTAL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 5) + +LASreadItemCompressed_POINT14_v3::LASreadItemCompressed_POINT14_v3(ArithmeticDecoder* dec, const U32 decompress_selective) +{ + /* not used as a decoder. just gives access to instream */ + + assert(dec); + this->dec = dec; + + /* zero instreams and decoders */ + + instream_channel_returns_XY = 0; + instream_Z = 0; + instream_classification = 0; + instream_flags = 0; + instream_intensity = 0; + instream_scan_angle = 0; + instream_user_data = 0; + instream_point_source = 0; + instream_gps_time = 0; + + dec_channel_returns_XY = 0; + dec_Z = 0; + dec_classification = 0; + dec_flags = 0; + dec_intensity = 0; + dec_scan_angle = 0; + dec_user_data = 0; + dec_point_source = 0; + dec_gps_time = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_changed_values[0] = 0; + } + current_context = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_channel_returns_XY = 0; + num_bytes_Z = 0; + num_bytes_classification = 0; + num_bytes_flags = 0; + num_bytes_intensity = 0; + num_bytes_scan_angle = 0; + num_bytes_user_data = 0; + num_bytes_point_source = 0; + num_bytes_gps_time = 0; + + changed_Z = FALSE; + changed_classification = FALSE; + changed_flags = FALSE; + changed_intensity = FALSE; + changed_scan_angle = FALSE; + changed_user_data = FALSE; + changed_point_source = FALSE; + changed_gps_time = FALSE; + + requested_Z = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_Z ? TRUE : FALSE); + requested_classification = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_CLASSIFICATION ? TRUE : FALSE); + requested_flags = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_FLAGS ? TRUE : FALSE); + requested_intensity = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_INTENSITY ? TRUE : FALSE); + requested_scan_angle = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_SCAN_ANGLE ? TRUE : FALSE); + requested_user_data = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_USER_DATA ? TRUE : FALSE); + requested_point_source = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_POINT_SOURCE ? TRUE : FALSE); + requested_gps_time = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_GPS_TIME ? TRUE : FALSE); + + /* init the bytes buffer to zero */ + + bytes = 0; + num_bytes_allocated = 0; +} + +LASreadItemCompressed_POINT14_v3::~LASreadItemCompressed_POINT14_v3() +{ + U32 c, i; + + /* destroy all initialized scanner channel contexts */ + + for (c = 0; c < 4; c++) + { + if (contexts[c].m_changed_values[0]) + { + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[0]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[1]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[2]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[3]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[4]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[5]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[6]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[7]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_scanner_channel); + for (i = 0; i < 16; i++) + { + if (contexts[c].m_number_of_returns[i]) dec_channel_returns_XY->destroySymbolModel(contexts[c].m_number_of_returns[i]); + if (contexts[c].m_return_number[i]) dec_channel_returns_XY->destroySymbolModel(contexts[c].m_return_number[i]); + } + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_return_number_gps_same); + delete contexts[c].ic_dX; + delete contexts[c].ic_dY; + delete contexts[c].ic_Z; + for (i = 0; i < 64; i++) + { + if (contexts[c].m_classification[i]) dec_classification->destroySymbolModel(contexts[c].m_classification[i]); + if (contexts[c].m_flags[i]) dec_flags->destroySymbolModel(contexts[c].m_flags[i]); + if (contexts[c].m_user_data[i]) dec_user_data->destroySymbolModel(contexts[c].m_user_data[i]); + } + delete contexts[c].ic_intensity; + delete contexts[c].ic_scan_angle; + delete contexts[c].ic_point_source_ID; + dec_gps_time->destroySymbolModel(contexts[c].m_gpstime_multi); + dec_gps_time->destroySymbolModel(contexts[c].m_gpstime_0diff); + delete contexts[c].ic_gpstime; + } + } + + /* destroy all decoders and instreams */ + + if (instream_channel_returns_XY) + { + delete dec_channel_returns_XY; + delete dec_Z; + delete dec_classification; + delete dec_flags; + delete dec_intensity; + delete dec_scan_angle; + delete dec_user_data; + delete dec_gps_time; + + delete instream_channel_returns_XY; + delete instream_Z; + delete instream_classification; + delete instream_flags; + delete instream_intensity; + delete instream_scan_angle; + delete instream_user_data; + delete instream_gps_time; + } + + if (bytes) delete [] bytes; +} + +inline BOOL LASreadItemCompressed_POINT14_v3::createAndInitModelsAndDecompressors(U32 context, const U8* item) +{ + I32 i; + + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models and integer decompressors (if needed) */ + + if (contexts[context].m_changed_values[0] == 0) + { + /* for the channel_returns_XY layer */ + + contexts[context].m_changed_values[0] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[1] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[2] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[3] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[4] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[5] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[6] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[7] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_scanner_channel = dec_channel_returns_XY->createSymbolModel(3); + for (i = 0; i < 16; i++) + { + contexts[context].m_number_of_returns[i] = 0; + contexts[context].m_return_number[i] = 0; + } + contexts[context].m_return_number_gps_same = dec_channel_returns_XY->createSymbolModel(13); + + contexts[context].ic_dX = new IntegerCompressor(dec_channel_returns_XY, 32, 2); // 32 bits, 2 context + contexts[context].ic_dY = new IntegerCompressor(dec_channel_returns_XY, 32, 22); // 32 bits, 22 contexts + + /* for the Z layer */ + + contexts[context].ic_Z = new IntegerCompressor(dec_Z, 32, 20); // 32 bits, 20 contexts + + /* for the classification layer */ + /* for the flags layer */ + /* for the user_data layer */ + + for (i = 0; i < 64; i++) + { + contexts[context].m_classification[i] = 0; + contexts[context].m_flags[i] = 0; + contexts[context].m_user_data[i] = 0; + } + + /* for the intensity layer */ + + contexts[context].ic_intensity = new IntegerCompressor(dec_intensity, 16, 4); + + /* for the scan_angle layer */ + + contexts[context].ic_scan_angle = new IntegerCompressor(dec_scan_angle, 16, 2); + + /* for the point_source_ID layer */ + + contexts[context].ic_point_source_ID = new IntegerCompressor(dec_point_source, 16); + + /* for the gps_time layer */ + + contexts[context].m_gpstime_multi = dec_gps_time->createSymbolModel(LASZIP_GPSTIME_MULTI_TOTAL); + contexts[context].m_gpstime_0diff = dec_gps_time->createSymbolModel(5); + contexts[context].ic_gpstime = new IntegerCompressor(dec_gps_time, 32, 9); // 32 bits, 9 contexts + } + + /* then init entropy models and integer compressors */ + + /* for the channel_returns_XY layer */ + + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[0]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[1]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[2]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[3]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[4]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[5]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[6]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[7]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_scanner_channel); + for (i = 0; i < 16; i++) + { + if (contexts[context].m_number_of_returns[i]) dec_channel_returns_XY->initSymbolModel(contexts[context].m_number_of_returns[i]); + if (contexts[context].m_return_number[i]) dec_channel_returns_XY->initSymbolModel(contexts[context].m_return_number[i]); + } + dec_channel_returns_XY->initSymbolModel(contexts[context].m_return_number_gps_same); + contexts[context].ic_dX->initDecompressor(); + contexts[context].ic_dY->initDecompressor(); + for (i = 0; i < 12; i++) + { + contexts[context].last_X_diff_median5[i].init(); + contexts[context].last_Y_diff_median5[i].init(); + } + + /* for the Z layer */ + + contexts[context].ic_Z->initDecompressor(); + for (i = 0; i < 8; i++) + { + contexts[context].last_Z[i] = ((LASpoint14*)item)->Z; + } + + /* for the classification layer */ + /* for the flags layer */ + /* for the user_data layer */ + + for (i = 0; i < 64; i++) + { + if (contexts[context].m_classification[i]) dec_classification->initSymbolModel(contexts[context].m_classification[i]); + if (contexts[context].m_flags[i]) dec_flags->initSymbolModel(contexts[context].m_flags[i]); + if (contexts[context].m_user_data[i]) dec_user_data->initSymbolModel(contexts[context].m_user_data[i]); + } + + /* for the intensity layer */ + + contexts[context].ic_intensity->initDecompressor(); + for (i = 0; i < 8; i++) + { + contexts[context].last_intensity[i] = ((LASpoint14*)item)->intensity; + } + + /* for the scan_angle layer */ + + contexts[context].ic_scan_angle->initDecompressor(); + + /* for the point_source_ID layer */ + + contexts[context].ic_point_source_ID->initDecompressor(); + + /* for the gps_time layer */ + + dec_gps_time->initSymbolModel(contexts[context].m_gpstime_multi); + dec_gps_time->initSymbolModel(contexts[context].m_gpstime_0diff); + contexts[context].ic_gpstime->initDecompressor(); + contexts[context].last = 0, contexts[context].next = 0; + contexts[context].last_gpstime_diff[0] = 0; + contexts[context].last_gpstime_diff[1] = 0; + contexts[context].last_gpstime_diff[2] = 0; + contexts[context].last_gpstime_diff[3] = 0; + contexts[context].multi_extreme_counter[0] = 0; + contexts[context].multi_extreme_counter[1] = 0; + contexts[context].multi_extreme_counter[2] = 0; + contexts[context].multi_extreme_counter[3] = 0; + contexts[context].last_gpstime[0].f64 = ((LASpoint14*)item)->gps_time; + contexts[context].last_gpstime[1].u64 = 0; + contexts[context].last_gpstime[2].u64 = 0; + contexts[context].last_gpstime[3].u64 = 0; + + /* init current context from last item */ + + memcpy(contexts[context].last_item, item, sizeof(LASpoint14)); + ((LASpoint14*)contexts[context].last_item)->gps_time_change = FALSE; + +// fprintf(stderr, "INIT: current_context %d last item %.14g %d %d %d %d %d %d\n", current_context, ((LASpoint14*)item)->gps_time, ((LASpoint14*)item)->X, ((LASpoint14*)item)->Y, ((LASpoint14*)item)->Z, ((LASpoint14*)item)->intensity, ((LASpoint14*)item)->return_number, ((LASpoint14*)item)->number_of_returns); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASreadItemCompressed_POINT14_v3::chunk_sizes() +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* read bytes per layer */ + + instream->get32bitsLE(((U8*)&num_bytes_channel_returns_XY)); + instream->get32bitsLE(((U8*)&num_bytes_Z)); + instream->get32bitsLE(((U8*)&num_bytes_classification)); + instream->get32bitsLE(((U8*)&num_bytes_flags)); + instream->get32bitsLE(((U8*)&num_bytes_intensity)); + instream->get32bitsLE(((U8*)&num_bytes_scan_angle)); + instream->get32bitsLE(((U8*)&num_bytes_user_data)); + instream->get32bitsLE(((U8*)&num_bytes_point_source)); + instream->get32bitsLE(((U8*)&num_bytes_gps_time)); + + return TRUE; +} + +BOOL LASreadItemCompressed_POINT14_v3::init(const U8* item, U32& context) +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* on the first init create instreams and decoders */ + + if (instream_channel_returns_XY == 0) + { + /* create instreams */ + + if (IS_LITTLE_ENDIAN()) + { + instream_channel_returns_XY = new ByteStreamInArrayLE(); + instream_Z = new ByteStreamInArrayLE(); + instream_classification = new ByteStreamInArrayLE(); + instream_flags = new ByteStreamInArrayLE(); + instream_intensity = new ByteStreamInArrayLE(); + instream_scan_angle = new ByteStreamInArrayLE(); + instream_user_data = new ByteStreamInArrayLE(); + instream_point_source = new ByteStreamInArrayLE(); + instream_gps_time = new ByteStreamInArrayLE(); + } + else + { + instream_channel_returns_XY = new ByteStreamInArrayBE(); + instream_Z = new ByteStreamInArrayBE(); + instream_classification = new ByteStreamInArrayBE(); + instream_flags = new ByteStreamInArrayBE(); + instream_intensity = new ByteStreamInArrayBE(); + instream_scan_angle = new ByteStreamInArrayBE(); + instream_user_data = new ByteStreamInArrayBE(); + instream_point_source = new ByteStreamInArrayBE(); + instream_gps_time = new ByteStreamInArrayBE(); + } + + /* create decoders */ + + dec_channel_returns_XY = new ArithmeticDecoder(); + dec_Z = new ArithmeticDecoder(); + dec_classification = new ArithmeticDecoder(); + dec_flags = new ArithmeticDecoder(); + dec_intensity = new ArithmeticDecoder(); + dec_scan_angle = new ArithmeticDecoder(); + dec_user_data = new ArithmeticDecoder(); + dec_point_source = new ArithmeticDecoder(); + dec_gps_time = new ArithmeticDecoder(); + } + + /* how many bytes do we need to read */ + + U32 num_bytes = num_bytes_channel_returns_XY; + if (requested_Z) num_bytes += num_bytes_Z; + if (requested_classification) num_bytes += num_bytes_classification; + if (requested_flags) num_bytes += num_bytes_flags; + if (requested_intensity) num_bytes += num_bytes_intensity; + if (requested_scan_angle) num_bytes += num_bytes_scan_angle; + if (requested_user_data) num_bytes += num_bytes_user_data; + if (requested_point_source) num_bytes += num_bytes_point_source; + if (requested_gps_time) num_bytes += num_bytes_gps_time; + + /* make sure the buffer is sufficiently large */ + + if (num_bytes > num_bytes_allocated) + { + if (bytes) delete [] bytes; + bytes = new U8[num_bytes]; + if (bytes == 0) return FALSE; + num_bytes_allocated = num_bytes; + } + + /* load the requested bytes and init the corresponding instreams and decoders */ + + num_bytes = 0; + instream->getBytes(bytes, num_bytes_channel_returns_XY); + instream_channel_returns_XY->init(bytes, num_bytes_channel_returns_XY); + dec_channel_returns_XY->init(instream_channel_returns_XY); + num_bytes += num_bytes_channel_returns_XY; + + if (requested_Z) + { + if (num_bytes_Z) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_Z); + instream_Z->init(&(bytes[num_bytes]), num_bytes_Z); + dec_Z->init(instream_Z); + num_bytes += num_bytes_Z; + changed_Z = TRUE; + } + else + { + instream_Z->init(0, 0); + changed_Z = FALSE; + } + } + else + { + if (num_bytes_Z) + { + instream->skipBytes(num_bytes_Z); + } + changed_Z = FALSE; + } + + if (requested_classification) + { + if (num_bytes_classification) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_classification); + instream_classification->init(&(bytes[num_bytes]), num_bytes_classification); + dec_classification->init(instream_classification); + num_bytes += num_bytes_classification; + changed_classification = TRUE; + } + else + { + instream_classification->init(0, 0); + changed_classification = FALSE; + } + } + else + { + if (num_bytes_classification) + { + instream->skipBytes(num_bytes_classification); + } + changed_classification = FALSE; + } + + if (requested_flags) + { + if (num_bytes_flags) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_flags); + instream_flags->init(&(bytes[num_bytes]), num_bytes_flags); + dec_flags->init(instream_flags); + num_bytes += num_bytes_flags; + changed_flags = TRUE; + } + else + { + instream_flags->init(0, 0); + changed_flags = FALSE; + } + } + else + { + if (num_bytes_flags) + { + instream->skipBytes(num_bytes_flags); + } + changed_flags = FALSE; + } + + if (requested_intensity) + { + if (num_bytes_intensity) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_intensity); + instream_intensity->init(&(bytes[num_bytes]), num_bytes_intensity); + dec_intensity->init(instream_intensity); + num_bytes += num_bytes_intensity; + changed_intensity = TRUE; + } + else + { + instream_intensity->init(0, 0); + changed_intensity = FALSE; + } + } + else + { + if (num_bytes_intensity) + { + instream->skipBytes(num_bytes_intensity); + } + changed_intensity = FALSE; + } + + if (requested_scan_angle) + { + if (num_bytes_scan_angle) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_scan_angle); + instream_scan_angle->init(&(bytes[num_bytes]), num_bytes_scan_angle); + dec_scan_angle->init(instream_scan_angle); + num_bytes += num_bytes_scan_angle; + changed_scan_angle = TRUE; + } + else + { + instream_scan_angle->init(0, 0); + changed_scan_angle = FALSE; + } + } + else + { + if (num_bytes_scan_angle) + { + instream->skipBytes(num_bytes_scan_angle); + } + changed_scan_angle = FALSE; + } + + if (requested_user_data) + { + if (num_bytes_user_data) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_user_data); + instream_user_data->init(&(bytes[num_bytes]), num_bytes_user_data); + dec_user_data->init(instream_user_data); + num_bytes += num_bytes_user_data; + changed_user_data = TRUE; + } + else + { + instream_user_data->init(0, 0); + changed_user_data = FALSE; + } + } + else + { + if (num_bytes_user_data) + { + instream->skipBytes(num_bytes_user_data); + } + changed_user_data = FALSE; + } + + if (requested_point_source) + { + if (num_bytes_point_source) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_point_source); + instream_point_source->init(&(bytes[num_bytes]), num_bytes_point_source); + dec_point_source->init(instream_point_source); + num_bytes += num_bytes_point_source; + changed_point_source = TRUE; + } + else + { + instream_point_source->init(0, 0); + changed_point_source = FALSE; + } + } + else + { + if (num_bytes_point_source) + { + instream->skipBytes(num_bytes_point_source); + } + changed_point_source = FALSE; + } + + if (requested_gps_time) + { + if (num_bytes_gps_time) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_gps_time); + instream_gps_time->init(&(bytes[num_bytes]), num_bytes_gps_time); + dec_gps_time->init(instream_gps_time); + num_bytes += num_bytes_gps_time; + changed_gps_time = TRUE; + } + else + { + instream_gps_time->init(0, 0); + changed_gps_time = FALSE; + } + } + else + { + if (num_bytes_gps_time) + { + instream->skipBytes(num_bytes_gps_time); + } + changed_gps_time = FALSE; + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = ((LASpoint14*)item)->scanner_channel; + context = current_context; // the POINT14 reader sets context for all other items + + /* create and init models and decompressors */ + + createAndInitModelsAndDecompressors(current_context, item); + + return TRUE; +} + +inline void LASreadItemCompressed_POINT14_v3::read(U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + //////////////////////////////////////// + // decompress returns_XY layer + //////////////////////////////////////// + + // create single (3) / first (1) / last (2) / intermediate (0) context from last point return + + I32 lpr = (((LASpoint14*)last_item)->return_number == 1 ? 1 : 0); // first? + lpr += (((LASpoint14*)last_item)->return_number >= ((LASpoint14*)last_item)->number_of_returns ? 2 : 0); // last? + + // add info whether the GPS time changed in the last return to the context + + lpr += (((LASpoint14*)last_item)->gps_time_change ? 4 : 0); + + // decompress which values have changed with last point return context + + I32 changed_values = dec_channel_returns_XY->decodeSymbol(contexts[current_context].m_changed_values[lpr]); + + // if scanner channel has changed + + if (changed_values & (1 << 6)) + { + U32 diff = dec_channel_returns_XY->decodeSymbol(contexts[current_context].m_scanner_channel); // curr = last + (sym + 1) + U32 scanner_channel = (current_context + diff + 1) % 4; + // maybe create and init entropy models and integer compressors + if (contexts[scanner_channel].unused) + { + // create and init entropy models and integer decompressors + createAndInitModelsAndDecompressors(scanner_channel, contexts[current_context].last_item); + } + // switch context to current scanner channel + current_context = scanner_channel; + context = current_context; // the POINT14 reader sets context for all other items + + // get last for new context + last_item = contexts[current_context].last_item; + ((LASpoint14*)last_item)->scanner_channel = scanner_channel; + } + + // determine changed attributes + + BOOL point_source_change = (changed_values & (1 << 5) ? TRUE : FALSE); + BOOL gps_time_change = (changed_values & (1 << 4) ? TRUE : FALSE); + BOOL scan_angle_change = (changed_values & (1 << 3) ? TRUE : FALSE); + + // get last return counts + + U32 last_n = ((LASpoint14*)last_item)->number_of_returns; + U32 last_r = ((LASpoint14*)last_item)->return_number; + + // if number of returns is different we decompress it + + U32 n; + if (changed_values & (1 << 2)) + { + if (contexts[current_context].m_number_of_returns[last_n] == 0) + { + contexts[current_context].m_number_of_returns[last_n] = dec_channel_returns_XY->createSymbolModel(16); + dec_channel_returns_XY->initSymbolModel(contexts[current_context].m_number_of_returns[last_n]); + } + n = dec_channel_returns_XY->decodeSymbol(contexts[current_context].m_number_of_returns[last_n]); + ((LASpoint14*)last_item)->number_of_returns = n; + } + else + { + n = last_n; + } + + // how is the return number different + + U32 r; + if ((changed_values & 3) == 0) // same return number + { + r = last_r; + } + else if ((changed_values & 3) == 1) // return number plus 1 mod 16 + { + r = ((last_r + 1) % 16); + ((LASpoint14*)last_item)->return_number = r; + } + else if ((changed_values & 3) == 2) // return number minus 1 mod 16 + { + r = ((last_r + 15) % 16); + ((LASpoint14*)last_item)->return_number = r; + } + else + { + // the return number difference is bigger than +1 / -1 so we decompress how it is different + + if (gps_time_change) // if the GPS time has changed + { + if (contexts[current_context].m_return_number[last_r] == 0) + { + contexts[current_context].m_return_number[last_r] = dec_channel_returns_XY->createSymbolModel(16); + dec_channel_returns_XY->initSymbolModel(contexts[current_context].m_return_number[last_r]); + } + r = dec_channel_returns_XY->decodeSymbol(contexts[current_context].m_return_number[last_r]); + } + else // if the GPS time has not changed + { + I32 sym = dec_channel_returns_XY->decodeSymbol(contexts[current_context].m_return_number_gps_same); + r = (last_r + (sym + 2)) % 16; + } + ((LASpoint14*)last_item)->return_number = r; + } + + // set legacy return counts and number of returns + + if (n > 7) + { + if (r > 6) + { + if (r >= n) + { + ((LASpoint14*)last_item)->legacy_return_number = 7; + } + else + { + ((LASpoint14*)last_item)->legacy_return_number = 6; + } + } + else + { + ((LASpoint14*)last_item)->legacy_return_number = r; + } + ((LASpoint14*)last_item)->legacy_number_of_returns = 7; + } + else + { + ((LASpoint14*)last_item)->legacy_return_number = r; + ((LASpoint14*)last_item)->legacy_number_of_returns = n; + } + + // get return map m and return level l context for current point + + U32 m = number_return_map_6ctx[n][r]; + U32 l = number_return_level_8ctx[n][r]; + + // create single (3) / first (1) / last (2) / intermediate (0) return context for current point + + I32 cpr = (r == 1 ? 2 : 0); // first ? + cpr += (r >= n ? 1 : 0); // last ? + + U32 k_bits; + I32 median, diff; + + // decompress X coordinate + median = contexts[current_context].last_X_diff_median5[(m<<1) | gps_time_change].get(); + diff = contexts[current_context].ic_dX->decompress(median, n==1); + ((LASpoint14*)last_item)->X += diff; + contexts[current_context].last_X_diff_median5[(m<<1) | gps_time_change].add(diff); + + // decompress Y coordinate + median = contexts[current_context].last_Y_diff_median5[(m<<1) | gps_time_change].get(); + k_bits = contexts[current_context].ic_dX->getK(); + diff = contexts[current_context].ic_dY->decompress(median, (n==1) + ( k_bits < 20 ? U32_ZERO_BIT_0(k_bits) : 20 )); + ((LASpoint14*)last_item)->Y += diff; + contexts[current_context].last_Y_diff_median5[(m<<1) | gps_time_change].add(diff); + + //////////////////////////////////////// + // decompress Z layer (if changed and requested) + //////////////////////////////////////// + + if (changed_Z) // if the Z coordinate should be decompressed and changes within this chunk + { + k_bits = (contexts[current_context].ic_dX->getK() + contexts[current_context].ic_dY->getK()) / 2; + ((LASpoint14*)last_item)->Z = contexts[current_context].ic_Z->decompress(contexts[current_context].last_Z[l], (n==1) + (k_bits < 18 ? U32_ZERO_BIT_0(k_bits) : 18)); + contexts[current_context].last_Z[l] = ((LASpoint14*)last_item)->Z; + } + + //////////////////////////////////////// + // decompress classifications layer (if changed and requested) + //////////////////////////////////////// + + if (changed_classification) // if the classification should be decompressed and changes within this chunk + { + U32 last_classification = ((LASpoint14*)last_item)->classification; + I32 ccc = ((last_classification & 0x1F) << 1) + (cpr == 3 ? 1 : 0); + if (contexts[current_context].m_classification[ccc] == 0) + { + contexts[current_context].m_classification[ccc] = dec_classification->createSymbolModel(256); + dec_classification->initSymbolModel(contexts[current_context].m_classification[ccc]); + } + ((LASpoint14*)last_item)->classification = dec_classification->decodeSymbol(contexts[current_context].m_classification[ccc]); + + // update the legacy copy + if (((LASpoint14*)last_item)->classification < 32) + { + ((LASpoint14*)last_item)->legacy_classification = ((LASpoint14*)last_item)->classification; + } + else + { + ((LASpoint14*)last_item)->legacy_classification = 0; + } + } + + //////////////////////////////////////// + // decompress flags layer (if changed and requested) + //////////////////////////////////////// + + if (changed_flags) // if the flags should be decompressed and change within this chunk + { + U32 last_flags = (((LASpoint14*)last_item)->edge_of_flight_line << 5) | (((LASpoint14*)last_item)->scan_direction_flag << 4) | ((LASpoint14*)last_item)->classification_flags; + if (contexts[current_context].m_flags[last_flags] == 0) + { + contexts[current_context].m_flags[last_flags] = dec_flags->createSymbolModel(64); + dec_flags->initSymbolModel(contexts[current_context].m_flags[last_flags]); + } + U32 flags = dec_flags->decodeSymbol(contexts[current_context].m_flags[last_flags]); + ((LASpoint14*)last_item)->edge_of_flight_line = !!(flags & (1 << 5)); + ((LASpoint14*)last_item)->scan_direction_flag = !!(flags & (1 << 4)); + ((LASpoint14*)last_item)->classification_flags = (flags & 0x0F); + + // legacy copies + ((LASpoint14*)last_item)->legacy_flags = (flags & 0x07); + } + + //////////////////////////////////////// + // decompress intensity layer (if changed and requested) + //////////////////////////////////////// + + if (changed_intensity) // if the intensity should be decompressed and changes within this chunk + { + U16 intensity = contexts[current_context].ic_intensity->decompress(contexts[current_context].last_intensity[(cpr<<1) | gps_time_change], cpr); + contexts[current_context].last_intensity[(cpr<<1) | gps_time_change] = intensity; + ((LASpoint14*)last_item)->intensity = intensity; + } + + //////////////////////////////////////// + // decompress scan_angle layer (if changed and requested) + //////////////////////////////////////// + + if (changed_scan_angle) // if the scan angle should be decompressed and changes within this chunk + { + if (scan_angle_change) // if the scan angle has actually changed + { + ((LASpoint14*)last_item)->scan_angle = contexts[current_context].ic_scan_angle->decompress(((LASpoint14*)last_item)->scan_angle, gps_time_change); // if the GPS time has changed + ((LASpoint14*)last_item)->legacy_scan_angle_rank = I8_CLAMP(I16_QUANTIZE(0.006f*((LASpoint14*)last_item)->scan_angle)); + } + } + + //////////////////////////////////////// + // decompress user_data layer (if changed and requested) + //////////////////////////////////////// + + if (changed_user_data) // if the user data should be decompressed and changes within this chunk + { + if (contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4] == 0) + { + contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4] = dec_user_data->createSymbolModel(256); + dec_user_data->initSymbolModel(contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4]); + } + ((LASpoint14*)last_item)->user_data = dec_user_data->decodeSymbol(contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4]); + } + + //////////////////////////////////////// + // decompress point_source layer (if changed and requested) + //////////////////////////////////////// + + if (changed_point_source) // if the point source ID should be decompressed and changes within this chunk + { + if (point_source_change) // if the point source ID has actually changed + { + ((LASpoint14*)last_item)->point_source_ID = contexts[current_context].ic_point_source_ID->decompress(((LASpoint14*)last_item)->point_source_ID); + } + } + + //////////////////////////////////////// + // decompress gps_time layer (if changed and requested) + //////////////////////////////////////// + + if (changed_gps_time) // if the GPS time should be decompressed and changes within this chunk + { + if (gps_time_change) // if the GPS time has actually changed + { + read_gps_time(); + ((LASpoint14*)last_item)->gps_time = contexts[current_context].last_gpstime[contexts[current_context].last].f64; + } + } + + // copy the last item + memcpy(item, last_item, sizeof(LASpoint14)); + // remember if the last point had a gps_time_change + ((LASpoint14*)last_item)->gps_time_change = gps_time_change; +} + +void LASreadItemCompressed_POINT14_v3::read_gps_time() +{ + I32 multi; + if (contexts[current_context].last_gpstime_diff[contexts[current_context].last] == 0) // if the last integer difference was zero + { + multi = dec_gps_time->decodeSymbol(contexts[current_context].m_gpstime_0diff); + if (multi == 0) // the difference can be represented with 32 bits + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = contexts[current_context].ic_gpstime->decompress(0, 0); + contexts[current_context].last_gpstime[contexts[current_context].last].i64 += contexts[current_context].last_gpstime_diff[contexts[current_context].last]; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else if (multi == 1) // the difference is huge + { + contexts[current_context].next = (contexts[current_context].next+1)&3; + contexts[current_context].last_gpstime[contexts[current_context].next].u64 = contexts[current_context].ic_gpstime->decompress((I32)(contexts[current_context].last_gpstime[contexts[current_context].last].u64 >> 32), 8); + contexts[current_context].last_gpstime[contexts[current_context].next].u64 = contexts[current_context].last_gpstime[contexts[current_context].next].u64 << 32; + contexts[current_context].last_gpstime[contexts[current_context].next].u64 |= dec_gps_time->readInt(); + contexts[current_context].last = contexts[current_context].next; + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = 0; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else // we switch to another sequence + { + contexts[current_context].last = (contexts[current_context].last+multi-1)&3; + read_gps_time(); + } + } + else + { + multi = dec_gps_time->decodeSymbol(contexts[current_context].m_gpstime_multi); + if (multi == 1) + { + contexts[current_context].last_gpstime[contexts[current_context].last].i64 += contexts[current_context].ic_gpstime->decompress(contexts[current_context].last_gpstime_diff[contexts[current_context].last], 1);; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else if (multi < LASZIP_GPSTIME_MULTI_CODE_FULL) + { + I32 gpstime_diff; + if (multi == 0) + { + gpstime_diff = contexts[current_context].ic_gpstime->decompress(0, 7); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + else if (multi < LASZIP_GPSTIME_MULTI) + { + if (multi < 10) + gpstime_diff = contexts[current_context].ic_gpstime->decompress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], 2); + else + gpstime_diff = contexts[current_context].ic_gpstime->decompress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], 3); + } + else if (multi == LASZIP_GPSTIME_MULTI) + { + gpstime_diff = contexts[current_context].ic_gpstime->decompress(LASZIP_GPSTIME_MULTI*contexts[current_context].last_gpstime_diff[contexts[current_context].last], 4); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + else + { + multi = LASZIP_GPSTIME_MULTI - multi; + if (multi > LASZIP_GPSTIME_MULTI_MINUS) + { + gpstime_diff = contexts[current_context].ic_gpstime->decompress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], 5); + } + else + { + gpstime_diff = contexts[current_context].ic_gpstime->decompress(LASZIP_GPSTIME_MULTI_MINUS*contexts[current_context].last_gpstime_diff[contexts[current_context].last], 6); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + } + contexts[current_context].last_gpstime[contexts[current_context].last].i64 += gpstime_diff; + } + else if (multi == LASZIP_GPSTIME_MULTI_CODE_FULL) + { + contexts[current_context].next = (contexts[current_context].next+1)&3; + contexts[current_context].last_gpstime[contexts[current_context].next].u64 = contexts[current_context].ic_gpstime->decompress((I32)(contexts[current_context].last_gpstime[contexts[current_context].last].u64 >> 32), 8); + contexts[current_context].last_gpstime[contexts[current_context].next].u64 = contexts[current_context].last_gpstime[contexts[current_context].next].u64 << 32; + contexts[current_context].last_gpstime[contexts[current_context].next].u64 |= dec_gps_time->readInt(); + contexts[current_context].last = contexts[current_context].next; + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = 0; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else if (multi >= LASZIP_GPSTIME_MULTI_CODE_FULL) + { + contexts[current_context].last = (contexts[current_context].last+multi-LASZIP_GPSTIME_MULTI_CODE_FULL)&3; + read_gps_time(); + } + } +} + +/* +=============================================================================== + LASreadItemCompressed_RGB14_v3 +=============================================================================== +*/ + +LASreadItemCompressed_RGB14_v3::LASreadItemCompressed_RGB14_v3(ArithmeticDecoder* dec, const U32 decompress_selective) +{ + /* not used as a decoder. just gives access to instream */ + + assert(dec); + this->dec = dec; + + /* zero instreams and decoders */ + + instream_RGB = 0; + + dec_RGB = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_RGB = 0; + + changed_RGB = FALSE; + + requested_RGB = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_RGB ? TRUE : FALSE); + + /* init the bytes buffer to zero */ + + bytes = 0; + num_bytes_allocated = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_byte_used = 0; + } + current_context = 0; +} + +LASreadItemCompressed_RGB14_v3::~LASreadItemCompressed_RGB14_v3() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_byte_used) + { + dec_RGB->destroySymbolModel(contexts[c].m_byte_used); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_0); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_1); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_2); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_3); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_4); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_5); + } + } + + /* destroy all instreams and decoders */ + + if (instream_RGB) + { + delete instream_RGB; + + delete dec_RGB; + } + + if (bytes) delete [] bytes; +} + +inline BOOL LASreadItemCompressed_RGB14_v3::createAndInitModelsAndDecompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (contexts[context].m_byte_used == 0) + { + contexts[context].m_byte_used = dec_RGB->createSymbolModel(128); + contexts[context].m_rgb_diff_0 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_1 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_2 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_3 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_4 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_5 = dec_RGB->createSymbolModel(256); + } + + /* then init entropy models */ + + dec_RGB->initSymbolModel(contexts[context].m_byte_used); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_0); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_1); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_2); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_3); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_4); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_5); + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, 6); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASreadItemCompressed_RGB14_v3::chunk_sizes() +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* read bytes per layer */ + + instream->get32bitsLE(((U8*)&num_bytes_RGB)); + + return TRUE; +} + +BOOL LASreadItemCompressed_RGB14_v3::init(const U8* item, U32& context) +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* on the first init create instreams and decoders */ + + if (instream_RGB == 0) + { + /* create instreams */ + + if (IS_LITTLE_ENDIAN()) + { + instream_RGB = new ByteStreamInArrayLE(); + } + else + { + instream_RGB = new ByteStreamInArrayBE(); + } + + /* create decoders */ + + dec_RGB = new ArithmeticDecoder(); + } + + /* make sure the buffer is sufficiently large */ + + if (num_bytes_RGB > num_bytes_allocated) + { + if (bytes) delete [] bytes; + bytes = new U8[num_bytes_RGB]; + if (bytes == 0) return FALSE; + num_bytes_allocated = num_bytes_RGB; + } + + /* load the requested bytes and init the corresponding instreams an decoders */ + + if (requested_RGB) + { + if (num_bytes_RGB) + { + instream->getBytes(bytes, num_bytes_RGB); + instream_RGB->init(bytes, num_bytes_RGB); + dec_RGB->init(instream_RGB); + changed_RGB = TRUE; + } + else + { + instream_RGB->init(0, 0); + changed_RGB = FALSE; + } + } + else + { + if (num_bytes_RGB) + { + instream->skipBytes(num_bytes_RGB); + } + changed_RGB = FALSE; + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 reader + + /* create and init models and decompressors */ + + createAndInitModelsAndDecompressors(current_context, item); + + return TRUE; +} + +inline void LASreadItemCompressed_RGB14_v3::read(U8* item, U32& context) +{ + // get last + + U16* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 reader + if (contexts[current_context].unused) + { + createAndInitModelsAndDecompressors(current_context, (U8*)last_item); + last_item = contexts[current_context].last_item; + } + } + + // decompress + + if (changed_RGB) + { + U8 corr; + I32 diff = 0; + U32 sym = dec_RGB->decodeSymbol(contexts[current_context].m_byte_used); + if (sym & (1 << 0)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_0); + ((U16*)item)[0] = (U16)U8_FOLD(corr + (last_item[0]&255)); + } + else + { + ((U16*)item)[0] = last_item[0]&0xFF; + } + if (sym & (1 << 1)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_1); + ((U16*)item)[0] |= (((U16)U8_FOLD(corr + (last_item[0]>>8))) << 8); + } + else + { + ((U16*)item)[0] |= (last_item[0]&0xFF00); + } + if (sym & (1 << 6)) + { + diff = (((U16*)item)[0]&0x00FF) - (last_item[0]&0x00FF); + if (sym & (1 << 2)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_2); + ((U16*)item)[1] = (U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[1]&255))); + } + else + { + ((U16*)item)[1] = last_item[1]&0xFF; + } + if (sym & (1 << 4)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_4); + diff = (diff + ((((U16*)item)[1]&0x00FF) - (last_item[1]&0x00FF))) / 2; + ((U16*)item)[2] = (U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[2]&255))); + } + else + { + ((U16*)item)[2] = last_item[2]&0xFF; + } + diff = (((U16*)item)[0]>>8) - (last_item[0]>>8); + if (sym & (1 << 3)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_3); + ((U16*)item)[1] |= (((U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[1]>>8))))<<8); + } + else + { + ((U16*)item)[1] |= (last_item[1]&0xFF00); + } + if (sym & (1 << 5)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_5); + diff = (diff + ((((U16*)item)[1]>>8) - (last_item[1]>>8))) / 2; + ((U16*)item)[2] |= (((U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[2]>>8))))<<8); + } + else + { + ((U16*)item)[2] |= (last_item[2]&0xFF00); + } + } + else + { + ((U16*)item)[1] = ((U16*)item)[0]; + ((U16*)item)[2] = ((U16*)item)[0]; + } + memcpy(last_item, item, 6); + } + else + { + memcpy(item, last_item, 6); + } +} + +/* +=============================================================================== + LASreadItemCompressed_RGBNIR14_v3 +=============================================================================== +*/ + +LASreadItemCompressed_RGBNIR14_v3::LASreadItemCompressed_RGBNIR14_v3(ArithmeticDecoder* dec, const U32 decompress_selective) +{ + /* not used as a decoder. just gives access to instream */ + + assert(dec); + this->dec = dec; + + /* zero instreams and decoders */ + + instream_RGB = 0; + instream_NIR = 0; + + dec_RGB = 0; + dec_NIR = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_RGB = 0; + num_bytes_NIR = 0; + + changed_RGB = FALSE; + changed_NIR = FALSE; + + requested_RGB = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_RGB ? TRUE : FALSE); + requested_NIR = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_NIR ? TRUE : FALSE); + + /* init the bytes buffer to zero */ + + bytes = 0; + num_bytes_allocated = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_rgb_bytes_used = 0; + contexts[c].m_nir_bytes_used = 0; + } + current_context = 0; +} + +LASreadItemCompressed_RGBNIR14_v3::~LASreadItemCompressed_RGBNIR14_v3() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_rgb_bytes_used) + { + dec_RGB->destroySymbolModel(contexts[c].m_rgb_bytes_used); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_0); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_1); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_2); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_3); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_4); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_5); + } + + if (contexts[c].m_nir_bytes_used) + { + dec_NIR->destroySymbolModel(contexts[c].m_nir_bytes_used); + dec_NIR->destroySymbolModel(contexts[c].m_nir_diff_0); + dec_NIR->destroySymbolModel(contexts[c].m_nir_diff_1); + } + } + + /* destroy all instreams and decoders */ + + if (instream_RGB) + { + delete instream_RGB; + + delete dec_RGB; + } + + if (instream_NIR) + { + delete instream_NIR; + + delete dec_NIR; + } + + if (bytes) delete [] bytes; +} + +inline BOOL LASreadItemCompressed_RGBNIR14_v3::createAndInitModelsAndDecompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (requested_RGB) + { + if (contexts[context].m_rgb_bytes_used == 0) + { + contexts[context].m_rgb_bytes_used = dec_RGB->createSymbolModel(128); + contexts[context].m_rgb_diff_0 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_1 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_2 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_3 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_4 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_5 = dec_RGB->createSymbolModel(256); + } + + /* then init entropy models */ + + dec_RGB->initSymbolModel(contexts[context].m_rgb_bytes_used); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_0); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_1); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_2); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_3); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_4); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_5); + } + + if (requested_NIR) + { + if (contexts[context].m_nir_bytes_used == 0) + { + contexts[context].m_nir_bytes_used = dec_NIR->createSymbolModel(4); + contexts[context].m_nir_diff_0 = dec_NIR->createSymbolModel(256); + contexts[context].m_nir_diff_1 = dec_NIR->createSymbolModel(256); + } + + /* then init entropy models */ + + dec_NIR->initSymbolModel(contexts[context].m_nir_bytes_used); + dec_NIR->initSymbolModel(contexts[context].m_nir_diff_0); + dec_NIR->initSymbolModel(contexts[context].m_nir_diff_1); + } + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, 8); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASreadItemCompressed_RGBNIR14_v3::chunk_sizes() +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* read bytes per layer */ + + instream->get32bitsLE(((U8*)&num_bytes_RGB)); + instream->get32bitsLE(((U8*)&num_bytes_NIR)); + + return TRUE; +} + +BOOL LASreadItemCompressed_RGBNIR14_v3::init(const U8* item, U32& context) +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* on the first init create instreams and decoders */ + + if (instream_RGB == 0) + { + /* create instreams */ + + if (IS_LITTLE_ENDIAN()) + { + instream_RGB = new ByteStreamInArrayLE(); + instream_NIR = new ByteStreamInArrayLE(); + } + else + { + instream_RGB = new ByteStreamInArrayBE(); + instream_NIR = new ByteStreamInArrayBE(); + } + + /* create decoders */ + + dec_RGB = new ArithmeticDecoder(); + dec_NIR = new ArithmeticDecoder(); + } + + /* how many bytes do we need to read */ + + U32 num_bytes = 0; + if (requested_RGB) num_bytes += num_bytes_RGB; + if (requested_NIR) num_bytes += num_bytes_NIR; + + /* make sure the buffer is sufficiently large */ + + if (num_bytes > num_bytes_allocated) + { + if (bytes) delete [] bytes; + bytes = new U8[num_bytes]; + if (bytes == 0) return FALSE; + num_bytes_allocated = num_bytes; + } + + /* load the requested bytes and init the corresponding instreams an decoders */ + + num_bytes = 0; + + if (requested_RGB) + { + if (num_bytes_RGB) + { + instream->getBytes(bytes, num_bytes_RGB); + num_bytes += num_bytes_RGB; + instream_RGB->init(bytes, num_bytes_RGB); + dec_RGB->init(instream_RGB); + changed_RGB = TRUE; + } + else + { + instream_RGB->init(0, 0); + changed_RGB = FALSE; + } + } + else + { + if (num_bytes_RGB) + { + instream->skipBytes(num_bytes_RGB); + } + changed_RGB = FALSE; + } + + if (requested_NIR) + { + if (num_bytes_NIR) + { + instream->getBytes(&bytes[num_bytes], num_bytes_NIR); + instream_NIR->init(&bytes[num_bytes], num_bytes_NIR); + dec_NIR->init(instream_NIR); + changed_NIR = TRUE; + } + else + { + instream_NIR->init(0, 0); + changed_NIR = FALSE; + } + } + else + { + if (num_bytes_NIR) + { + instream->skipBytes(num_bytes_NIR); + } + changed_NIR = FALSE; + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 reader + + /* create and init models and decompressors */ + + createAndInitModelsAndDecompressors(current_context, item); + + return TRUE; +} + +inline void LASreadItemCompressed_RGBNIR14_v3::read(U8* item, U32& context) +{ + // get last + + U16* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 reader + if (contexts[current_context].unused) + { + createAndInitModelsAndDecompressors(current_context, (U8*)last_item); + last_item = contexts[current_context].last_item; + } + } + + // decompress + + //////////////////////////////////////// + // decompress RGB layer + //////////////////////////////////////// + + if (changed_RGB) + { + U8 corr; + I32 diff = 0; + U32 sym = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_bytes_used); + if (sym & (1 << 0)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_0); + ((U16*)item)[0] = (U16)U8_FOLD(corr + (last_item[0]&255)); + } + else + { + ((U16*)item)[0] = last_item[0]&0xFF; + } + if (sym & (1 << 1)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_1); + ((U16*)item)[0] |= (((U16)U8_FOLD(corr + (last_item[0]>>8))) << 8); + } + else + { + ((U16*)item)[0] |= (last_item[0]&0xFF00); + } + if (sym & (1 << 6)) + { + diff = (((U16*)item)[0]&0x00FF) - (last_item[0]&0x00FF); + if (sym & (1 << 2)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_2); + ((U16*)item)[1] = (U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[1]&255))); + } + else + { + ((U16*)item)[1] = last_item[1]&0xFF; + } + if (sym & (1 << 4)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_4); + diff = (diff + ((((U16*)item)[1]&0x00FF) - (last_item[1]&0x00FF))) / 2; + ((U16*)item)[2] = (U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[2]&255))); + } + else + { + ((U16*)item)[2] = last_item[2]&0xFF; + } + diff = (((U16*)item)[0]>>8) - (last_item[0]>>8); + if (sym & (1 << 3)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_3); + ((U16*)item)[1] |= (((U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[1]>>8))))<<8); + } + else + { + ((U16*)item)[1] |= (last_item[1]&0xFF00); + } + if (sym & (1 << 5)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_5); + diff = (diff + ((((U16*)item)[1]>>8) - (last_item[1]>>8))) / 2; + ((U16*)item)[2] |= (((U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[2]>>8))))<<8); + } + else + { + ((U16*)item)[2] |= (last_item[2]&0xFF00); + } + } + else + { + ((U16*)item)[1] = ((U16*)item)[0]; + ((U16*)item)[2] = ((U16*)item)[0]; + } + memcpy(last_item, item, 6); + } + else + { + memcpy(item, last_item, 6); + } + + //////////////////////////////////////// + // decompress NIR layer + //////////////////////////////////////// + + if (changed_NIR) + { + U8 corr; + U32 sym = dec_NIR->decodeSymbol(contexts[current_context].m_nir_bytes_used); + if (sym & (1 << 0)) + { + corr = dec_NIR->decodeSymbol(contexts[current_context].m_nir_diff_0); + ((U16*)item)[3] = (U16)U8_FOLD(corr + (last_item[3]&255)); + } + else + { + ((U16*)item)[3] = last_item[3]&0xFF; + } + if (sym & (1 << 1)) + { + corr = dec_NIR->decodeSymbol(contexts[current_context].m_nir_diff_1); + ((U16*)item)[3] |= (((U16)U8_FOLD(corr + (last_item[3]>>8))) << 8); + } + else + { + ((U16*)item)[3] |= (last_item[3]&0xFF00); + } + last_item[3] = ((U16*)item)[3]; + } + else + { + ((U16*)item)[3] = last_item[3]; + } +} + +/* +=============================================================================== + LASreadItemCompressed_WAVEPACKET14_v3 +=============================================================================== +*/ + +LASreadItemCompressed_WAVEPACKET14_v3::LASreadItemCompressed_WAVEPACKET14_v3(ArithmeticDecoder* dec, const U32 decompress_selective) +{ + /* not used as a decoder. just gives access to instream */ + + assert(dec); + this->dec = dec; + + /* zero instreams and decoders */ + + instream_wavepacket = 0; + + dec_wavepacket = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_wavepacket = 0; + + changed_wavepacket = FALSE; + + requested_wavepacket = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_WAVEPACKET ? TRUE : FALSE); + + /* init the bytes buffer to zero */ + + bytes = 0; + num_bytes_allocated = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_packet_index = 0; + } + current_context = 0; +} + +LASreadItemCompressed_WAVEPACKET14_v3::~LASreadItemCompressed_WAVEPACKET14_v3() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_packet_index) + { + dec_wavepacket->destroySymbolModel(contexts[c].m_packet_index); + dec_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[0]); + dec_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[1]); + dec_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[2]); + dec_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[3]); + delete contexts[c].ic_offset_diff; + delete contexts[c].ic_packet_size; + delete contexts[c].ic_return_point; + delete contexts[c].ic_xyz; + } + } + + /* destroy all instreams and decoders */ + + if (instream_wavepacket) + { + delete instream_wavepacket; + + delete dec_wavepacket; + } + + if (bytes) delete [] bytes; +} + +inline BOOL LASreadItemCompressed_WAVEPACKET14_v3::createAndInitModelsAndDecompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (requested_wavepacket) + { + if (contexts[context].m_packet_index == 0) + { + contexts[context].m_packet_index = dec_wavepacket->createSymbolModel(256); + contexts[context].m_offset_diff[0] = dec_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[1] = dec_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[2] = dec_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[3] = dec_wavepacket->createSymbolModel(4); + contexts[context].ic_offset_diff = new IntegerCompressor(dec_wavepacket, 32); + contexts[context].ic_packet_size = new IntegerCompressor(dec_wavepacket, 32); + contexts[context].ic_return_point = new IntegerCompressor(dec_wavepacket, 32); + contexts[context].ic_xyz = new IntegerCompressor(dec_wavepacket, 32, 3); + } + + /* then init entropy models */ + + dec_wavepacket->initSymbolModel(contexts[context].m_packet_index); + dec_wavepacket->initSymbolModel(contexts[context].m_offset_diff[0]); + dec_wavepacket->initSymbolModel(contexts[context].m_offset_diff[1]); + dec_wavepacket->initSymbolModel(contexts[context].m_offset_diff[2]); + dec_wavepacket->initSymbolModel(contexts[context].m_offset_diff[3]); + contexts[context].ic_offset_diff->initDecompressor(); + contexts[context].ic_packet_size->initDecompressor(); + contexts[context].ic_return_point->initDecompressor(); + contexts[context].ic_xyz->initDecompressor(); + } + + /* init current context from item */ + + contexts[context].last_diff_32 = 0; + contexts[context].sym_last_offset_diff = 0; + memcpy(contexts[context].last_item, item, 29); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASreadItemCompressed_WAVEPACKET14_v3::chunk_sizes() +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* read bytes per layer */ + + instream->get32bitsLE(((U8*)&num_bytes_wavepacket)); + + return TRUE; +} + +BOOL LASreadItemCompressed_WAVEPACKET14_v3::init(const U8* item, U32& context) +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* on the first init create instreams and decoders */ + + if (instream_wavepacket == 0) + { + /* create instreams */ + + if (IS_LITTLE_ENDIAN()) + { + instream_wavepacket = new ByteStreamInArrayLE(); + } + else + { + instream_wavepacket = new ByteStreamInArrayBE(); + } + + /* create decoders */ + + dec_wavepacket = new ArithmeticDecoder(); + } + + /* make sure the buffer is sufficiently large */ + + if (num_bytes_wavepacket > num_bytes_allocated) + { + if (bytes) delete [] bytes; + bytes = new U8[num_bytes_wavepacket]; + if (bytes == 0) return FALSE; + num_bytes_allocated = num_bytes_wavepacket; + } + + /* load the requested bytes and init the corresponding instreams an decoders */ + + if (requested_wavepacket) + { + if (num_bytes_wavepacket) + { + instream->getBytes(bytes, num_bytes_wavepacket); + instream_wavepacket->init(bytes, num_bytes_wavepacket); + dec_wavepacket->init(instream_wavepacket); + changed_wavepacket = TRUE; + } + else + { + instream_wavepacket->init(0, 0); + changed_wavepacket = FALSE; + } + } + else + { + if (num_bytes_wavepacket) + { + instream->skipBytes(num_bytes_wavepacket); + } + changed_wavepacket = FALSE; + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 reader + + /* create and init models and decompressors */ + + createAndInitModelsAndDecompressors(current_context, item); + + return TRUE; +} + +inline void LASreadItemCompressed_WAVEPACKET14_v3::read(U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 reader + if (contexts[current_context].unused) + { + createAndInitModelsAndDecompressors(current_context, last_item); + last_item = contexts[current_context].last_item; + } + } + + // decompress + + if (changed_wavepacket) + { + item[0] = (U8)(dec_wavepacket->decodeSymbol(contexts[current_context].m_packet_index)); + + LASwavepacket13 this_item_m; + LASwavepacket13 last_item_m = LASwavepacket13::unpack(last_item+1); + + contexts[current_context].sym_last_offset_diff = dec_wavepacket->decodeSymbol(contexts[current_context].m_offset_diff[contexts[current_context].sym_last_offset_diff]); + + if (contexts[current_context].sym_last_offset_diff == 0) + { + this_item_m.offset = last_item_m.offset; + } + else if (contexts[current_context].sym_last_offset_diff == 1) + { + this_item_m.offset = last_item_m.offset + last_item_m.packet_size; + } + else if (contexts[current_context].sym_last_offset_diff == 2) + { + contexts[current_context].last_diff_32 = contexts[current_context].ic_offset_diff->decompress(contexts[current_context].last_diff_32); + this_item_m.offset = last_item_m.offset + contexts[current_context].last_diff_32; + } + else + { + this_item_m.offset = dec_wavepacket->readInt64(); + } + + this_item_m.packet_size = contexts[current_context].ic_packet_size->decompress(last_item_m.packet_size); + this_item_m.return_point.i32 = contexts[current_context].ic_return_point->decompress(last_item_m.return_point.i32); + this_item_m.x.i32 = contexts[current_context].ic_xyz->decompress(last_item_m.x.i32, 0); + this_item_m.y.i32 = contexts[current_context].ic_xyz->decompress(last_item_m.y.i32, 1); + this_item_m.z.i32 = contexts[current_context].ic_xyz->decompress(last_item_m.z.i32, 2); + + this_item_m.pack(item+1); + + memcpy(last_item, item, 29); + } +} + +/* +=============================================================================== + LASreadItemCompressed_BYTE14_v3 +=============================================================================== +*/ + +LASreadItemCompressed_BYTE14_v3::LASreadItemCompressed_BYTE14_v3(ArithmeticDecoder* dec, U32 number, const U32 decompress_selective) +{ + /* not used as a decoder. just gives access to instream */ + + assert(dec); + this->dec = dec; + + /* must be more than one byte */ + + assert(number); + this->number = number; + + /* zero instream and decoder pointer arrays */ + + instream_Bytes = 0; + + dec_Bytes = 0; + + /* create and init num_bytes and booleans arrays */ + + num_bytes_Bytes = new U32[number]; + + changed_Bytes = new BOOL[number]; + + requested_Bytes = new BOOL[number]; + + U32 i; + for (i = 0; i < number; i++) + { + num_bytes_Bytes[i] = 0; + + changed_Bytes[i] = FALSE; + + if (i > 15) // currently only the first 16 extra bytes can be selectively decompressed + { + requested_Bytes[i] = TRUE; + } + else + { + requested_Bytes[i] = (decompress_selective & (LASZIP_DECOMPRESS_SELECTIVE_BYTE0 << i) ? TRUE : FALSE); + } + } + + /* init the bytes buffer to zero */ + + bytes = 0; + num_bytes_allocated = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_bytes = 0; + } + current_context = 0; +} + +LASreadItemCompressed_BYTE14_v3::~LASreadItemCompressed_BYTE14_v3() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c, i; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_bytes) + { + for (i = 0; i < number; i++) + { + dec_Bytes[i]->destroySymbolModel(contexts[c].m_bytes[i]); + } + delete [] contexts[c].m_bytes; + delete [] contexts[c].last_item; + } + } + + /* destroy all instream and decoder arrays */ + + if (instream_Bytes) + { + for (i = 0; i < number; i++) + { + if (instream_Bytes[i]) + { + delete instream_Bytes[i]; + delete dec_Bytes[i]; + } + } + + delete [] instream_Bytes; + delete [] dec_Bytes; + } + + /* destroy all other arrays */ + + if (num_bytes_Bytes) delete [] num_bytes_Bytes; + + if (changed_Bytes) delete [] changed_Bytes; + + if (requested_Bytes) delete [] requested_Bytes; + + if (bytes) delete [] bytes; +} + +inline BOOL LASreadItemCompressed_BYTE14_v3::createAndInitModelsAndDecompressors(U32 context, const U8* item) +{ + U32 i; + + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models and last items (if needed) */ + + if (contexts[context].m_bytes == 0) + { + contexts[context].m_bytes = new ArithmeticModel*[number]; + for (i = 0; i < number; i++) + { + contexts[context].m_bytes[i] = dec_Bytes[i]->createSymbolModel(256); + dec_Bytes[i]->initSymbolModel(contexts[context].m_bytes[i]); + } + + /* create last item */ + contexts[context].last_item = new U8[number]; + } + + /* then init entropy models */ + + for (i = 0; i < number; i++) + { + dec_Bytes[i]->initSymbolModel(contexts[context].m_bytes[i]); + } + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, number); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASreadItemCompressed_BYTE14_v3::chunk_sizes() +{ + U32 i; + + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + for (i = 0; i < number; i++) + { + /* read bytes per layer */ + + instream->get32bitsLE(((U8*)&(num_bytes_Bytes[i]))); + } + + return TRUE; +} + +BOOL LASreadItemCompressed_BYTE14_v3::init(const U8* item, U32& context) +{ + U32 i; + + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* on the first init create instreams and decoders */ + + if (instream_Bytes == 0) + { + /* create instream pointer array */ + + instream_Bytes = new ByteStreamInArray*[number]; + + /* create instreams */ + + if (IS_LITTLE_ENDIAN()) + { + for (i = 0; i < number; i++) + { + instream_Bytes[i] = new ByteStreamInArrayLE(); + } + } + else + { + for (i = 0; i < number; i++) + { + instream_Bytes[i] = new ByteStreamInArrayBE(); + } + } + + /* create decoder pointer array */ + + dec_Bytes = new ArithmeticDecoder*[number]; + + /* create layer decoders */ + + for (i = 0; i < number; i++) + { + dec_Bytes[i] = new ArithmeticDecoder(); + } + } + + /* how many bytes do we need to read */ + + U32 num_bytes = 0; + + for (i = 0; i < number; i++) + { + if (requested_Bytes[i]) num_bytes += num_bytes_Bytes[i]; + } + + /* make sure the buffer is sufficiently large */ + + if (num_bytes > num_bytes_allocated) + { + if (bytes) delete [] bytes; + bytes = new U8[num_bytes]; + if (bytes == 0) return FALSE; + num_bytes_allocated = num_bytes; + } + + /* load the requested bytes and init the corresponding instreams an decoders */ + + num_bytes = 0; + for (i = 0; i < number; i++) + { + if (requested_Bytes[i]) + { + if (num_bytes_Bytes[i]) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_Bytes[i]); + instream_Bytes[i]->init(&(bytes[num_bytes]), num_bytes_Bytes[i]); + dec_Bytes[i]->init(instream_Bytes[i]); + num_bytes += num_bytes_Bytes[i]; + changed_Bytes[i] = TRUE; + } + else + { + dec_Bytes[i]->init(0, 0); + changed_Bytes[i] = FALSE; + } + } + else + { + if (num_bytes_Bytes[i]) + { + instream->skipBytes(num_bytes_Bytes[i]); + } + changed_Bytes[i] = FALSE; + } + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 reader + + /* create and init models and decompressors */ + + createAndInitModelsAndDecompressors(current_context, item); + + return TRUE; +} + +inline void LASreadItemCompressed_BYTE14_v3::read(U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 reader + if (contexts[current_context].unused) + { + createAndInitModelsAndDecompressors(current_context, (U8*)last_item); + last_item = contexts[current_context].last_item; + } + } + + // decompress + + U32 i; + for (i = 0; i < number; i++) + { + if (changed_Bytes[i]) + { + I32 value = last_item[i] + dec_Bytes[i]->decodeSymbol(contexts[current_context].m_bytes[i]); + item[i] = U8_FOLD(value); + last_item[i] = item[i]; + } + else + { + item[i] = last_item[i]; + } + } +} diff --git a/libs/laszip/src/lasreaditemcompressed_v3.hpp b/libs/laszip/src/lasreaditemcompressed_v3.hpp new file mode 100644 index 0000000..1b50261 --- /dev/null +++ b/libs/laszip/src/lasreaditemcompressed_v3.hpp @@ -0,0 +1,275 @@ +/* +=============================================================================== + + FILE: lasreaditemcompressed_v3.hpp + + CONTENTS: + + Native extension for decompressing the *new* point types 6 to 10 of LAS 1.4 + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2019, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 19 March 2019 -- set "legacy classification" to zero if "classification > 31" + 28 August 2017 -- moving 'context' from global development hack to interface + 19 April 2017 -- support for selective decompression for new LAS 1.4 points + 22 June 2016 -- created after Island beat Austria 2:1 in the EM2016 + +=============================================================================== +*/ +#ifndef LAS_READ_ITEM_COMPRESSED_V3_HPP +#define LAS_READ_ITEM_COMPRESSED_V3_HPP + +#include "lasreaditem.hpp" +#include "arithmeticdecoder.hpp" +#include "integercompressor.hpp" +#include "bytestreamin_array.hpp" + +#include "laszip_common_v3.hpp" +#include "laszip_decompress_selective_v3.hpp" + +class LASreadItemCompressed_POINT14_v3 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_POINT14_v3(ArithmeticDecoder* dec, const U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + + BOOL chunk_sizes(); + BOOL init(const U8* item, U32& context); // context is set + void read(U8* item, U32& context); // context is set + + ~LASreadItemCompressed_POINT14_v3(); + +private: + + /* not used as a decoder. just gives access to instream */ + + ArithmeticDecoder* dec; + + ByteStreamInArray* instream_channel_returns_XY; + ByteStreamInArray* instream_Z; + ByteStreamInArray* instream_classification; + ByteStreamInArray* instream_flags; + ByteStreamInArray* instream_intensity; + ByteStreamInArray* instream_scan_angle; + ByteStreamInArray* instream_user_data; + ByteStreamInArray* instream_point_source; + ByteStreamInArray* instream_gps_time; + + ArithmeticDecoder* dec_channel_returns_XY; + ArithmeticDecoder* dec_Z; + ArithmeticDecoder* dec_classification; + ArithmeticDecoder* dec_flags; + ArithmeticDecoder* dec_intensity; + ArithmeticDecoder* dec_scan_angle; + ArithmeticDecoder* dec_user_data; + ArithmeticDecoder* dec_point_source; + ArithmeticDecoder* dec_gps_time; + + BOOL changed_Z; + BOOL changed_classification; + BOOL changed_flags; + BOOL changed_intensity; + BOOL changed_scan_angle; + BOOL changed_user_data; + BOOL changed_point_source; + BOOL changed_gps_time; + + U32 num_bytes_channel_returns_XY; + U32 num_bytes_Z; + U32 num_bytes_classification; + U32 num_bytes_flags; + U32 num_bytes_intensity; + U32 num_bytes_scan_angle; + U32 num_bytes_user_data; + U32 num_bytes_point_source; + U32 num_bytes_gps_time; + + BOOL requested_Z; + BOOL requested_classification; + BOOL requested_flags; + BOOL requested_intensity; + BOOL requested_scan_angle; + BOOL requested_user_data; + BOOL requested_point_source; + BOOL requested_gps_time; + + U8* bytes; + U32 num_bytes_allocated; + + U32 current_context; + LAScontextPOINT14 contexts[4]; + + BOOL createAndInitModelsAndDecompressors(U32 context, const U8* item); + void read_gps_time(); +}; + +class LASreadItemCompressed_RGB14_v3 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_RGB14_v3(ArithmeticDecoder* dec, const U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + + BOOL chunk_sizes(); + BOOL init(const U8* item, U32& context); // context is only read + void read(U8* item, U32& context); // context is only read + + ~LASreadItemCompressed_RGB14_v3(); + +private: + + /* not used as a decoder. just gives access to instream */ + + ArithmeticDecoder* dec; + + ByteStreamInArray* instream_RGB; + + ArithmeticDecoder* dec_RGB; + + BOOL changed_RGB; + + U32 num_bytes_RGB; + + BOOL requested_RGB; + + U8* bytes; + U32 num_bytes_allocated; + + U32 current_context; + LAScontextRGB14 contexts[4]; + + BOOL createAndInitModelsAndDecompressors(U32 context, const U8* item); +}; + +class LASreadItemCompressed_RGBNIR14_v3 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_RGBNIR14_v3(ArithmeticDecoder* dec, const U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + + BOOL chunk_sizes(); + BOOL init(const U8* item, U32& context); // context is only read + void read(U8* item, U32& context); // context is only read + + ~LASreadItemCompressed_RGBNIR14_v3(); + +private: + + /* not used as a decoder. just gives access to instream */ + + ArithmeticDecoder* dec; + + ByteStreamInArray* instream_RGB; + ByteStreamInArray* instream_NIR; + + ArithmeticDecoder* dec_RGB; + ArithmeticDecoder* dec_NIR; + + BOOL changed_RGB; + BOOL changed_NIR; + + U32 num_bytes_RGB; + U32 num_bytes_NIR; + + BOOL requested_RGB; + BOOL requested_NIR; + + U8* bytes; + U32 num_bytes_allocated; + + U32 current_context; + LAScontextRGBNIR14 contexts[4]; + + BOOL createAndInitModelsAndDecompressors(U32 context, const U8* item); +}; + +class LASreadItemCompressed_WAVEPACKET14_v3 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_WAVEPACKET14_v3(ArithmeticDecoder* dec, const U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + + BOOL chunk_sizes(); + BOOL init(const U8* item, U32& context); // context is only read + void read(U8* item, U32& context); // context is only read + + ~LASreadItemCompressed_WAVEPACKET14_v3(); + +private: + + /* not used as a decoder. just gives access to instream */ + + ArithmeticDecoder* dec; + + ByteStreamInArray* instream_wavepacket; + + ArithmeticDecoder* dec_wavepacket; + + BOOL changed_wavepacket; + + U32 num_bytes_wavepacket; + + BOOL requested_wavepacket; + + U8* bytes; + U32 num_bytes_allocated; + + U32 current_context; + LAScontextWAVEPACKET14 contexts[4]; + + BOOL createAndInitModelsAndDecompressors(U32 context, const U8* item); +}; + +class LASreadItemCompressed_BYTE14_v3 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_BYTE14_v3(ArithmeticDecoder* dec, U32 number, const U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + + BOOL chunk_sizes(); + BOOL init(const U8* item, U32& context); // context is only read + void read(U8* item, U32& context); // context is only read + + ~LASreadItemCompressed_BYTE14_v3(); + +private: + + /* not used as a decoder. just gives access to instream */ + + ArithmeticDecoder* dec; + + ByteStreamInArray** instream_Bytes; + + ArithmeticDecoder** dec_Bytes; + + U32* num_bytes_Bytes; + + BOOL* changed_Bytes; + + BOOL* requested_Bytes; + + U8* bytes; + U32 num_bytes_allocated; + + U32 current_context; + LAScontextBYTE14 contexts[4]; + + U32 number; + BOOL createAndInitModelsAndDecompressors(U32 context, const U8* item); +}; + +#endif diff --git a/libs/laszip/src/lasreaditemcompressed_v4.cpp b/libs/laszip/src/lasreaditemcompressed_v4.cpp new file mode 100644 index 0000000..f729362 --- /dev/null +++ b/libs/laszip/src/lasreaditemcompressed_v4.cpp @@ -0,0 +1,2412 @@ +/* +=============================================================================== + + FILE: lasreaditemcompressed_v4.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ + +#include "lasreaditemcompressed_v4.hpp" + +#include +#include + +typedef struct LASpoint14 +{ + I32 X; + I32 Y; + I32 Z; + U16 intensity; + U8 legacy_return_number : 3; + U8 legacy_number_of_returns : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 legacy_classification : 5; + U8 legacy_flags : 3; + I8 legacy_scan_angle_rank; + U8 user_data; + U16 point_source_ID; + + // LAS 1.4 only + I16 scan_angle; + U8 legacy_point_type : 2; + U8 scanner_channel : 2; + U8 classification_flags : 4; + U8 classification; + U8 return_number : 4; + U8 number_of_returns : 4; + + // LASlib internal use only + U8 deleted_flag; + + // for 8 byte alignment of the GPS time + U8 dummy[2]; + + // compressed LASzip 1.4 points only + BOOL gps_time_change; + + F64 gps_time; + U16 rgb[4]; +// LASwavepacket wavepacket; +} LASpoint14; + +#define LASZIP_GPSTIME_MULTI 500 +#define LASZIP_GPSTIME_MULTI_MINUS -10 +#define LASZIP_GPSTIME_MULTI_CODE_FULL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 1) + +#define LASZIP_GPSTIME_MULTI_TOTAL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 5) + +LASreadItemCompressed_POINT14_v4::LASreadItemCompressed_POINT14_v4(ArithmeticDecoder* dec, const U32 decompress_selective) +{ + /* not used as a decoder. just gives access to instream */ + + assert(dec); + this->dec = dec; + + /* zero instreams and decoders */ + + instream_channel_returns_XY = 0; + instream_Z = 0; + instream_classification = 0; + instream_flags = 0; + instream_intensity = 0; + instream_scan_angle = 0; + instream_user_data = 0; + instream_point_source = 0; + instream_gps_time = 0; + + dec_channel_returns_XY = 0; + dec_Z = 0; + dec_classification = 0; + dec_flags = 0; + dec_intensity = 0; + dec_scan_angle = 0; + dec_user_data = 0; + dec_point_source = 0; + dec_gps_time = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_changed_values[0] = 0; + } + current_context = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_channel_returns_XY = 0; + num_bytes_Z = 0; + num_bytes_classification = 0; + num_bytes_flags = 0; + num_bytes_intensity = 0; + num_bytes_scan_angle = 0; + num_bytes_user_data = 0; + num_bytes_point_source = 0; + num_bytes_gps_time = 0; + + changed_Z = FALSE; + changed_classification = FALSE; + changed_flags = FALSE; + changed_intensity = FALSE; + changed_scan_angle = FALSE; + changed_user_data = FALSE; + changed_point_source = FALSE; + changed_gps_time = FALSE; + + requested_Z = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_Z ? TRUE : FALSE); + requested_classification = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_CLASSIFICATION ? TRUE : FALSE); + requested_flags = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_FLAGS ? TRUE : FALSE); + requested_intensity = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_INTENSITY ? TRUE : FALSE); + requested_scan_angle = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_SCAN_ANGLE ? TRUE : FALSE); + requested_user_data = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_USER_DATA ? TRUE : FALSE); + requested_point_source = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_POINT_SOURCE ? TRUE : FALSE); + requested_gps_time = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_GPS_TIME ? TRUE : FALSE); + + /* init the bytes buffer to zero */ + + bytes = 0; + num_bytes_allocated = 0; +} + +LASreadItemCompressed_POINT14_v4::~LASreadItemCompressed_POINT14_v4() +{ + U32 c, i; + + /* destroy all initialized scanner channel contexts */ + + for (c = 0; c < 4; c++) + { + if (contexts[c].m_changed_values[0]) + { + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[0]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[1]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[2]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[3]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[4]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[5]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[6]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[7]); + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_scanner_channel); + for (i = 0; i < 16; i++) + { + if (contexts[c].m_number_of_returns[i]) dec_channel_returns_XY->destroySymbolModel(contexts[c].m_number_of_returns[i]); + if (contexts[c].m_return_number[i]) dec_channel_returns_XY->destroySymbolModel(contexts[c].m_return_number[i]); + } + dec_channel_returns_XY->destroySymbolModel(contexts[c].m_return_number_gps_same); + delete contexts[c].ic_dX; + delete contexts[c].ic_dY; + delete contexts[c].ic_Z; + for (i = 0; i < 64; i++) + { + if (contexts[c].m_classification[i]) dec_classification->destroySymbolModel(contexts[c].m_classification[i]); + if (contexts[c].m_flags[i]) dec_flags->destroySymbolModel(contexts[c].m_flags[i]); + if (contexts[c].m_user_data[i]) dec_user_data->destroySymbolModel(contexts[c].m_user_data[i]); + } + delete contexts[c].ic_intensity; + delete contexts[c].ic_scan_angle; + delete contexts[c].ic_point_source_ID; + dec_gps_time->destroySymbolModel(contexts[c].m_gpstime_multi); + dec_gps_time->destroySymbolModel(contexts[c].m_gpstime_0diff); + delete contexts[c].ic_gpstime; + } + } + + /* destroy all decoders and instreams */ + + if (instream_channel_returns_XY) + { + delete dec_channel_returns_XY; + delete dec_Z; + delete dec_classification; + delete dec_flags; + delete dec_intensity; + delete dec_scan_angle; + delete dec_user_data; + delete dec_gps_time; + + delete instream_channel_returns_XY; + delete instream_Z; + delete instream_classification; + delete instream_flags; + delete instream_intensity; + delete instream_scan_angle; + delete instream_user_data; + delete instream_gps_time; + } + + if (bytes) delete [] bytes; +} + +inline BOOL LASreadItemCompressed_POINT14_v4::createAndInitModelsAndDecompressors(U32 context, const U8* item) +{ + I32 i; + + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models and integer decompressors (if needed) */ + + if (contexts[context].m_changed_values[0] == 0) + { + /* for the channel_returns_XY layer */ + + contexts[context].m_changed_values[0] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[1] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[2] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[3] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[4] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[5] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[6] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[7] = dec_channel_returns_XY->createSymbolModel(128); + contexts[context].m_scanner_channel = dec_channel_returns_XY->createSymbolModel(3); + for (i = 0; i < 16; i++) + { + contexts[context].m_number_of_returns[i] = 0; + contexts[context].m_return_number[i] = 0; + } + contexts[context].m_return_number_gps_same = dec_channel_returns_XY->createSymbolModel(13); + + contexts[context].ic_dX = new IntegerCompressor(dec_channel_returns_XY, 32, 2); // 32 bits, 2 context + contexts[context].ic_dY = new IntegerCompressor(dec_channel_returns_XY, 32, 22); // 32 bits, 22 contexts + + /* for the Z layer */ + + contexts[context].ic_Z = new IntegerCompressor(dec_Z, 32, 20); // 32 bits, 20 contexts + + /* for the classification layer */ + /* for the flags layer */ + /* for the user_data layer */ + + for (i = 0; i < 64; i++) + { + contexts[context].m_classification[i] = 0; + contexts[context].m_flags[i] = 0; + contexts[context].m_user_data[i] = 0; + } + + /* for the intensity layer */ + + contexts[context].ic_intensity = new IntegerCompressor(dec_intensity, 16, 4); + + /* for the scan_angle layer */ + + contexts[context].ic_scan_angle = new IntegerCompressor(dec_scan_angle, 16, 2); + + /* for the point_source_ID layer */ + + contexts[context].ic_point_source_ID = new IntegerCompressor(dec_point_source, 16); + + /* for the gps_time layer */ + + contexts[context].m_gpstime_multi = dec_gps_time->createSymbolModel(LASZIP_GPSTIME_MULTI_TOTAL); + contexts[context].m_gpstime_0diff = dec_gps_time->createSymbolModel(5); + contexts[context].ic_gpstime = new IntegerCompressor(dec_gps_time, 32, 9); // 32 bits, 9 contexts + } + + /* then init entropy models and integer compressors */ + + /* for the channel_returns_XY layer */ + + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[0]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[1]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[2]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[3]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[4]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[5]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[6]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[7]); + dec_channel_returns_XY->initSymbolModel(contexts[context].m_scanner_channel); + for (i = 0; i < 16; i++) + { + if (contexts[context].m_number_of_returns[i]) dec_channel_returns_XY->initSymbolModel(contexts[context].m_number_of_returns[i]); + if (contexts[context].m_return_number[i]) dec_channel_returns_XY->initSymbolModel(contexts[context].m_return_number[i]); + } + dec_channel_returns_XY->initSymbolModel(contexts[context].m_return_number_gps_same); + contexts[context].ic_dX->initDecompressor(); + contexts[context].ic_dY->initDecompressor(); + for (i = 0; i < 12; i++) + { + contexts[context].last_X_diff_median5[i].init(); + contexts[context].last_Y_diff_median5[i].init(); + } + + /* for the Z layer */ + + contexts[context].ic_Z->initDecompressor(); + for (i = 0; i < 8; i++) + { + contexts[context].last_Z[i] = ((LASpoint14*)item)->Z; + } + + /* for the classification layer */ + /* for the flags layer */ + /* for the user_data layer */ + + for (i = 0; i < 64; i++) + { + if (contexts[context].m_classification[i]) dec_classification->initSymbolModel(contexts[context].m_classification[i]); + if (contexts[context].m_flags[i]) dec_flags->initSymbolModel(contexts[context].m_flags[i]); + if (contexts[context].m_user_data[i]) dec_user_data->initSymbolModel(contexts[context].m_user_data[i]); + } + + /* for the intensity layer */ + + contexts[context].ic_intensity->initDecompressor(); + for (i = 0; i < 8; i++) + { + contexts[context].last_intensity[i] = ((LASpoint14*)item)->intensity; + } + + /* for the scan_angle layer */ + + contexts[context].ic_scan_angle->initDecompressor(); + + /* for the point_source_ID layer */ + + contexts[context].ic_point_source_ID->initDecompressor(); + + /* for the gps_time layer */ + + dec_gps_time->initSymbolModel(contexts[context].m_gpstime_multi); + dec_gps_time->initSymbolModel(contexts[context].m_gpstime_0diff); + contexts[context].ic_gpstime->initDecompressor(); + contexts[context].last = 0, contexts[context].next = 0; + contexts[context].last_gpstime_diff[0] = 0; + contexts[context].last_gpstime_diff[1] = 0; + contexts[context].last_gpstime_diff[2] = 0; + contexts[context].last_gpstime_diff[3] = 0; + contexts[context].multi_extreme_counter[0] = 0; + contexts[context].multi_extreme_counter[1] = 0; + contexts[context].multi_extreme_counter[2] = 0; + contexts[context].multi_extreme_counter[3] = 0; + contexts[context].last_gpstime[0].f64 = ((LASpoint14*)item)->gps_time; + contexts[context].last_gpstime[1].u64 = 0; + contexts[context].last_gpstime[2].u64 = 0; + contexts[context].last_gpstime[3].u64 = 0; + + /* init current context from last item */ + + memcpy(contexts[context].last_item, item, sizeof(LASpoint14)); + ((LASpoint14*)contexts[context].last_item)->gps_time_change = FALSE; + +// fprintf(stderr, "INIT: current_context %d last item %.14g %d %d %d %d %d %d\n", current_context, ((LASpoint14*)item)->gps_time, ((LASpoint14*)item)->X, ((LASpoint14*)item)->Y, ((LASpoint14*)item)->Z, ((LASpoint14*)item)->intensity, ((LASpoint14*)item)->return_number, ((LASpoint14*)item)->number_of_returns); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASreadItemCompressed_POINT14_v4::chunk_sizes() +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* read bytes per layer */ + + instream->get32bitsLE(((U8*)&num_bytes_channel_returns_XY)); + instream->get32bitsLE(((U8*)&num_bytes_Z)); + instream->get32bitsLE(((U8*)&num_bytes_classification)); + instream->get32bitsLE(((U8*)&num_bytes_flags)); + instream->get32bitsLE(((U8*)&num_bytes_intensity)); + instream->get32bitsLE(((U8*)&num_bytes_scan_angle)); + instream->get32bitsLE(((U8*)&num_bytes_user_data)); + instream->get32bitsLE(((U8*)&num_bytes_point_source)); + instream->get32bitsLE(((U8*)&num_bytes_gps_time)); + + return TRUE; +} + +BOOL LASreadItemCompressed_POINT14_v4::init(const U8* item, U32& context) +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* on the first init create instreams and decoders */ + + if (instream_channel_returns_XY == 0) + { + /* create instreams */ + + if (IS_LITTLE_ENDIAN()) + { + instream_channel_returns_XY = new ByteStreamInArrayLE(); + instream_Z = new ByteStreamInArrayLE(); + instream_classification = new ByteStreamInArrayLE(); + instream_flags = new ByteStreamInArrayLE(); + instream_intensity = new ByteStreamInArrayLE(); + instream_scan_angle = new ByteStreamInArrayLE(); + instream_user_data = new ByteStreamInArrayLE(); + instream_point_source = new ByteStreamInArrayLE(); + instream_gps_time = new ByteStreamInArrayLE(); + } + else + { + instream_channel_returns_XY = new ByteStreamInArrayBE(); + instream_Z = new ByteStreamInArrayBE(); + instream_classification = new ByteStreamInArrayBE(); + instream_flags = new ByteStreamInArrayBE(); + instream_intensity = new ByteStreamInArrayBE(); + instream_scan_angle = new ByteStreamInArrayBE(); + instream_user_data = new ByteStreamInArrayBE(); + instream_point_source = new ByteStreamInArrayBE(); + instream_gps_time = new ByteStreamInArrayBE(); + } + + /* create decoders */ + + dec_channel_returns_XY = new ArithmeticDecoder(); + dec_Z = new ArithmeticDecoder(); + dec_classification = new ArithmeticDecoder(); + dec_flags = new ArithmeticDecoder(); + dec_intensity = new ArithmeticDecoder(); + dec_scan_angle = new ArithmeticDecoder(); + dec_user_data = new ArithmeticDecoder(); + dec_point_source = new ArithmeticDecoder(); + dec_gps_time = new ArithmeticDecoder(); + } + + /* how many bytes do we need to read */ + + U32 num_bytes = num_bytes_channel_returns_XY; + if (requested_Z) num_bytes += num_bytes_Z; + if (requested_classification) num_bytes += num_bytes_classification; + if (requested_flags) num_bytes += num_bytes_flags; + if (requested_intensity) num_bytes += num_bytes_intensity; + if (requested_scan_angle) num_bytes += num_bytes_scan_angle; + if (requested_user_data) num_bytes += num_bytes_user_data; + if (requested_point_source) num_bytes += num_bytes_point_source; + if (requested_gps_time) num_bytes += num_bytes_gps_time; + + /* make sure the buffer is sufficiently large */ + + if (num_bytes > num_bytes_allocated) + { + if (bytes) delete [] bytes; + bytes = new U8[num_bytes]; + if (bytes == 0) return FALSE; + num_bytes_allocated = num_bytes; + } + + /* load the requested bytes and init the corresponding instreams and decoders */ + + num_bytes = 0; + instream->getBytes(bytes, num_bytes_channel_returns_XY); + instream_channel_returns_XY->init(bytes, num_bytes_channel_returns_XY); + dec_channel_returns_XY->init(instream_channel_returns_XY); + num_bytes += num_bytes_channel_returns_XY; + + if (requested_Z) + { + if (num_bytes_Z) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_Z); + instream_Z->init(&(bytes[num_bytes]), num_bytes_Z); + dec_Z->init(instream_Z); + num_bytes += num_bytes_Z; + changed_Z = TRUE; + } + else + { + instream_Z->init(0, 0); + changed_Z = FALSE; + } + } + else + { + if (num_bytes_Z) + { + instream->skipBytes(num_bytes_Z); + } + changed_Z = FALSE; + } + + if (requested_classification) + { + if (num_bytes_classification) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_classification); + instream_classification->init(&(bytes[num_bytes]), num_bytes_classification); + dec_classification->init(instream_classification); + num_bytes += num_bytes_classification; + changed_classification = TRUE; + } + else + { + instream_classification->init(0, 0); + changed_classification = FALSE; + } + } + else + { + if (num_bytes_classification) + { + instream->skipBytes(num_bytes_classification); + } + changed_classification = FALSE; + } + + if (requested_flags) + { + if (num_bytes_flags) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_flags); + instream_flags->init(&(bytes[num_bytes]), num_bytes_flags); + dec_flags->init(instream_flags); + num_bytes += num_bytes_flags; + changed_flags = TRUE; + } + else + { + instream_flags->init(0, 0); + changed_flags = FALSE; + } + } + else + { + if (num_bytes_flags) + { + instream->skipBytes(num_bytes_flags); + } + changed_flags = FALSE; + } + + if (requested_intensity) + { + if (num_bytes_intensity) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_intensity); + instream_intensity->init(&(bytes[num_bytes]), num_bytes_intensity); + dec_intensity->init(instream_intensity); + num_bytes += num_bytes_intensity; + changed_intensity = TRUE; + } + else + { + instream_intensity->init(0, 0); + changed_intensity = FALSE; + } + } + else + { + if (num_bytes_intensity) + { + instream->skipBytes(num_bytes_intensity); + } + changed_intensity = FALSE; + } + + if (requested_scan_angle) + { + if (num_bytes_scan_angle) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_scan_angle); + instream_scan_angle->init(&(bytes[num_bytes]), num_bytes_scan_angle); + dec_scan_angle->init(instream_scan_angle); + num_bytes += num_bytes_scan_angle; + changed_scan_angle = TRUE; + } + else + { + instream_scan_angle->init(0, 0); + changed_scan_angle = FALSE; + } + } + else + { + if (num_bytes_scan_angle) + { + instream->skipBytes(num_bytes_scan_angle); + } + changed_scan_angle = FALSE; + } + + if (requested_user_data) + { + if (num_bytes_user_data) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_user_data); + instream_user_data->init(&(bytes[num_bytes]), num_bytes_user_data); + dec_user_data->init(instream_user_data); + num_bytes += num_bytes_user_data; + changed_user_data = TRUE; + } + else + { + instream_user_data->init(0, 0); + changed_user_data = FALSE; + } + } + else + { + if (num_bytes_user_data) + { + instream->skipBytes(num_bytes_user_data); + } + changed_user_data = FALSE; + } + + if (requested_point_source) + { + if (num_bytes_point_source) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_point_source); + instream_point_source->init(&(bytes[num_bytes]), num_bytes_point_source); + dec_point_source->init(instream_point_source); + num_bytes += num_bytes_point_source; + changed_point_source = TRUE; + } + else + { + instream_point_source->init(0, 0); + changed_point_source = FALSE; + } + } + else + { + if (num_bytes_point_source) + { + instream->skipBytes(num_bytes_point_source); + } + changed_point_source = FALSE; + } + + if (requested_gps_time) + { + if (num_bytes_gps_time) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_gps_time); + instream_gps_time->init(&(bytes[num_bytes]), num_bytes_gps_time); + dec_gps_time->init(instream_gps_time); + num_bytes += num_bytes_gps_time; + changed_gps_time = TRUE; + } + else + { + instream_gps_time->init(0, 0); + changed_gps_time = FALSE; + } + } + else + { + if (num_bytes_gps_time) + { + instream->skipBytes(num_bytes_gps_time); + } + changed_gps_time = FALSE; + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = ((LASpoint14*)item)->scanner_channel; + context = current_context; // the POINT14 reader sets context for all other items + + /* create and init models and decompressors */ + + createAndInitModelsAndDecompressors(current_context, item); + + return TRUE; +} + +inline void LASreadItemCompressed_POINT14_v4::read(U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + //////////////////////////////////////// + // decompress returns_XY layer + //////////////////////////////////////// + + // create single (3) / first (1) / last (2) / intermediate (0) context from last point return + + I32 lpr = (((LASpoint14*)last_item)->return_number == 1 ? 1 : 0); // first? + lpr += (((LASpoint14*)last_item)->return_number >= ((LASpoint14*)last_item)->number_of_returns ? 2 : 0); // last? + + // add info whether the GPS time changed in the last return to the context + + lpr += (((LASpoint14*)last_item)->gps_time_change ? 4 : 0); + + // decompress which values have changed with last point return context + + I32 changed_values = dec_channel_returns_XY->decodeSymbol(contexts[current_context].m_changed_values[lpr]); + + // if scanner channel has changed + + if (changed_values & (1 << 6)) + { + U32 diff = dec_channel_returns_XY->decodeSymbol(contexts[current_context].m_scanner_channel); // curr = last + (sym + 1) + U32 scanner_channel = (current_context + diff + 1) % 4; + // maybe create and init entropy models and integer compressors + if (contexts[scanner_channel].unused) + { + // create and init entropy models and integer decompressors + createAndInitModelsAndDecompressors(scanner_channel, contexts[current_context].last_item); + } + // switch context to current scanner channel + current_context = scanner_channel; + + // get last for new context + last_item = contexts[current_context].last_item; + ((LASpoint14*)last_item)->scanner_channel = scanner_channel; + } + context = current_context; // the POINT14 reader sets context for all other items + + // determine changed attributes + + BOOL point_source_change = (changed_values & (1 << 5) ? TRUE : FALSE); + BOOL gps_time_change = (changed_values & (1 << 4) ? TRUE : FALSE); + BOOL scan_angle_change = (changed_values & (1 << 3) ? TRUE : FALSE); + + // get last return counts + + U32 last_n = ((LASpoint14*)last_item)->number_of_returns; + U32 last_r = ((LASpoint14*)last_item)->return_number; + + // if number of returns is different we decompress it + + U32 n; + if (changed_values & (1 << 2)) + { + if (contexts[current_context].m_number_of_returns[last_n] == 0) + { + contexts[current_context].m_number_of_returns[last_n] = dec_channel_returns_XY->createSymbolModel(16); + dec_channel_returns_XY->initSymbolModel(contexts[current_context].m_number_of_returns[last_n]); + } + n = dec_channel_returns_XY->decodeSymbol(contexts[current_context].m_number_of_returns[last_n]); + ((LASpoint14*)last_item)->number_of_returns = n; + } + else + { + n = last_n; + } + + // how is the return number different + + U32 r; + if ((changed_values & 3) == 0) // same return number + { + r = last_r; + } + else if ((changed_values & 3) == 1) // return number plus 1 mod 16 + { + r = ((last_r + 1) % 16); + ((LASpoint14*)last_item)->return_number = r; + } + else if ((changed_values & 3) == 2) // return number minus 1 mod 16 + { + r = ((last_r + 15) % 16); + ((LASpoint14*)last_item)->return_number = r; + } + else + { + // the return number difference is bigger than +1 / -1 so we decompress how it is different + + if (gps_time_change) // if the GPS time has changed + { + if (contexts[current_context].m_return_number[last_r] == 0) + { + contexts[current_context].m_return_number[last_r] = dec_channel_returns_XY->createSymbolModel(16); + dec_channel_returns_XY->initSymbolModel(contexts[current_context].m_return_number[last_r]); + } + r = dec_channel_returns_XY->decodeSymbol(contexts[current_context].m_return_number[last_r]); + } + else // if the GPS time has not changed + { + I32 sym = dec_channel_returns_XY->decodeSymbol(contexts[current_context].m_return_number_gps_same); + r = (last_r + (sym + 2)) % 16; + } + ((LASpoint14*)last_item)->return_number = r; + } + + // set legacy return counts and number of returns + + if (n > 7) + { + if (r > 6) + { + if (r >= n) + { + ((LASpoint14*)last_item)->legacy_return_number = 7; + } + else + { + ((LASpoint14*)last_item)->legacy_return_number = 6; + } + } + else + { + ((LASpoint14*)last_item)->legacy_return_number = r; + } + ((LASpoint14*)last_item)->legacy_number_of_returns = 7; + } + else + { + ((LASpoint14*)last_item)->legacy_return_number = r; + ((LASpoint14*)last_item)->legacy_number_of_returns = n; + } + + // get return map m and return level l context for current point + + U32 m = number_return_map_6ctx[n][r]; + U32 l = number_return_level_8ctx[n][r]; + + // create single (3) / first (1) / last (2) / intermediate (0) return context for current point + + I32 cpr = (r == 1 ? 2 : 0); // first ? + cpr += (r >= n ? 1 : 0); // last ? + + U32 k_bits; + I32 median, diff; + + // decompress X coordinate + median = contexts[current_context].last_X_diff_median5[(m<<1) | gps_time_change].get(); + diff = contexts[current_context].ic_dX->decompress(median, n==1); + ((LASpoint14*)last_item)->X += diff; + contexts[current_context].last_X_diff_median5[(m<<1) | gps_time_change].add(diff); + + // decompress Y coordinate + median = contexts[current_context].last_Y_diff_median5[(m<<1) | gps_time_change].get(); + k_bits = contexts[current_context].ic_dX->getK(); + diff = contexts[current_context].ic_dY->decompress(median, (n==1) + ( k_bits < 20 ? U32_ZERO_BIT_0(k_bits) : 20 )); + ((LASpoint14*)last_item)->Y += diff; + contexts[current_context].last_Y_diff_median5[(m<<1) | gps_time_change].add(diff); + + //////////////////////////////////////// + // decompress Z layer (if changed and requested) + //////////////////////////////////////// + + if (changed_Z) // if the Z coordinate should be decompressed and changes within this chunk + { + k_bits = (contexts[current_context].ic_dX->getK() + contexts[current_context].ic_dY->getK()) / 2; + ((LASpoint14*)last_item)->Z = contexts[current_context].ic_Z->decompress(contexts[current_context].last_Z[l], (n==1) + (k_bits < 18 ? U32_ZERO_BIT_0(k_bits) : 18)); + contexts[current_context].last_Z[l] = ((LASpoint14*)last_item)->Z; + } + + //////////////////////////////////////// + // decompress classifications layer (if changed and requested) + //////////////////////////////////////// + + if (changed_classification) // if the classification should be decompressed and changes within this chunk + { + U32 last_classification = ((LASpoint14*)last_item)->classification; + I32 ccc = ((last_classification & 0x1F) << 1) + (cpr == 3 ? 1 : 0); + if (contexts[current_context].m_classification[ccc] == 0) + { + contexts[current_context].m_classification[ccc] = dec_classification->createSymbolModel(256); + dec_classification->initSymbolModel(contexts[current_context].m_classification[ccc]); + } + ((LASpoint14*)last_item)->classification = dec_classification->decodeSymbol(contexts[current_context].m_classification[ccc]); + + // update the legacy copy + if (((LASpoint14*)last_item)->classification < 32) + { + ((LASpoint14*)last_item)->legacy_classification = ((LASpoint14*)last_item)->classification; + } + else + { + ((LASpoint14*)last_item)->legacy_classification = 0; + } + } + + //////////////////////////////////////// + // decompress flags layer (if changed and requested) + //////////////////////////////////////// + + if (changed_flags) // if the flags should be decompressed and change within this chunk + { + U32 last_flags = (((LASpoint14*)last_item)->edge_of_flight_line << 5) | (((LASpoint14*)last_item)->scan_direction_flag << 4) | ((LASpoint14*)last_item)->classification_flags; + if (contexts[current_context].m_flags[last_flags] == 0) + { + contexts[current_context].m_flags[last_flags] = dec_flags->createSymbolModel(64); + dec_flags->initSymbolModel(contexts[current_context].m_flags[last_flags]); + } + U32 flags = dec_flags->decodeSymbol(contexts[current_context].m_flags[last_flags]); + ((LASpoint14*)last_item)->edge_of_flight_line = !!(flags & (1 << 5)); + ((LASpoint14*)last_item)->scan_direction_flag = !!(flags & (1 << 4)); + ((LASpoint14*)last_item)->classification_flags = (flags & 0x0F); + + // legacy copies + ((LASpoint14*)last_item)->legacy_flags = (flags & 0x07); + } + + //////////////////////////////////////// + // decompress intensity layer (if changed and requested) + //////////////////////////////////////// + + if (changed_intensity) // if the intensity should be decompressed and changes within this chunk + { + U16 intensity = contexts[current_context].ic_intensity->decompress(contexts[current_context].last_intensity[(cpr<<1) | gps_time_change], cpr); + contexts[current_context].last_intensity[(cpr<<1) | gps_time_change] = intensity; + ((LASpoint14*)last_item)->intensity = intensity; + } + + //////////////////////////////////////// + // decompress scan_angle layer (if changed and requested) + //////////////////////////////////////// + + if (changed_scan_angle) // if the scan angle should be decompressed and changes within this chunk + { + if (scan_angle_change) // if the scan angle has actually changed + { + ((LASpoint14*)last_item)->scan_angle = contexts[current_context].ic_scan_angle->decompress(((LASpoint14*)last_item)->scan_angle, gps_time_change); // if the GPS time has changed + ((LASpoint14*)last_item)->legacy_scan_angle_rank = I8_CLAMP(I16_QUANTIZE(0.006f*((LASpoint14*)last_item)->scan_angle)); + } + } + + //////////////////////////////////////// + // decompress user_data layer (if changed and requested) + //////////////////////////////////////// + + if (changed_user_data) // if the user data should be decompressed and changes within this chunk + { + if (contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4] == 0) + { + contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4] = dec_user_data->createSymbolModel(256); + dec_user_data->initSymbolModel(contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4]); + } + ((LASpoint14*)last_item)->user_data = dec_user_data->decodeSymbol(contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4]); + } + + //////////////////////////////////////// + // decompress point_source layer (if changed and requested) + //////////////////////////////////////// + + if (changed_point_source) // if the point source ID should be decompressed and changes within this chunk + { + if (point_source_change) // if the point source ID has actually changed + { + ((LASpoint14*)last_item)->point_source_ID = contexts[current_context].ic_point_source_ID->decompress(((LASpoint14*)last_item)->point_source_ID); + } + } + + //////////////////////////////////////// + // decompress gps_time layer (if changed and requested) + //////////////////////////////////////// + + if (changed_gps_time) // if the GPS time should be decompressed and changes within this chunk + { + if (gps_time_change) // if the GPS time has actually changed + { + read_gps_time(); + ((LASpoint14*)last_item)->gps_time = contexts[current_context].last_gpstime[contexts[current_context].last].f64; + } + } + + // copy the last item + memcpy(item, last_item, sizeof(LASpoint14)); + // remember if the last point had a gps_time_change + ((LASpoint14*)last_item)->gps_time_change = gps_time_change; +} + +void LASreadItemCompressed_POINT14_v4::read_gps_time() +{ + I32 multi; + if (contexts[current_context].last_gpstime_diff[contexts[current_context].last] == 0) // if the last integer difference was zero + { + multi = dec_gps_time->decodeSymbol(contexts[current_context].m_gpstime_0diff); + if (multi == 0) // the difference can be represented with 32 bits + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = contexts[current_context].ic_gpstime->decompress(0, 0); + contexts[current_context].last_gpstime[contexts[current_context].last].i64 += contexts[current_context].last_gpstime_diff[contexts[current_context].last]; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else if (multi == 1) // the difference is huge + { + contexts[current_context].next = (contexts[current_context].next+1)&3; + contexts[current_context].last_gpstime[contexts[current_context].next].u64 = contexts[current_context].ic_gpstime->decompress((I32)(contexts[current_context].last_gpstime[contexts[current_context].last].u64 >> 32), 8); + contexts[current_context].last_gpstime[contexts[current_context].next].u64 = contexts[current_context].last_gpstime[contexts[current_context].next].u64 << 32; + contexts[current_context].last_gpstime[contexts[current_context].next].u64 |= dec_gps_time->readInt(); + contexts[current_context].last = contexts[current_context].next; + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = 0; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else // we switch to another sequence + { + contexts[current_context].last = (contexts[current_context].last+multi-1)&3; + read_gps_time(); + } + } + else + { + multi = dec_gps_time->decodeSymbol(contexts[current_context].m_gpstime_multi); + if (multi == 1) + { + contexts[current_context].last_gpstime[contexts[current_context].last].i64 += contexts[current_context].ic_gpstime->decompress(contexts[current_context].last_gpstime_diff[contexts[current_context].last], 1);; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else if (multi < LASZIP_GPSTIME_MULTI_CODE_FULL) + { + I32 gpstime_diff; + if (multi == 0) + { + gpstime_diff = contexts[current_context].ic_gpstime->decompress(0, 7); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + else if (multi < LASZIP_GPSTIME_MULTI) + { + if (multi < 10) + gpstime_diff = contexts[current_context].ic_gpstime->decompress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], 2); + else + gpstime_diff = contexts[current_context].ic_gpstime->decompress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], 3); + } + else if (multi == LASZIP_GPSTIME_MULTI) + { + gpstime_diff = contexts[current_context].ic_gpstime->decompress(LASZIP_GPSTIME_MULTI*contexts[current_context].last_gpstime_diff[contexts[current_context].last], 4); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + else + { + multi = LASZIP_GPSTIME_MULTI - multi; + if (multi > LASZIP_GPSTIME_MULTI_MINUS) + { + gpstime_diff = contexts[current_context].ic_gpstime->decompress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], 5); + } + else + { + gpstime_diff = contexts[current_context].ic_gpstime->decompress(LASZIP_GPSTIME_MULTI_MINUS*contexts[current_context].last_gpstime_diff[contexts[current_context].last], 6); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + } + contexts[current_context].last_gpstime[contexts[current_context].last].i64 += gpstime_diff; + } + else if (multi == LASZIP_GPSTIME_MULTI_CODE_FULL) + { + contexts[current_context].next = (contexts[current_context].next+1)&3; + contexts[current_context].last_gpstime[contexts[current_context].next].u64 = contexts[current_context].ic_gpstime->decompress((I32)(contexts[current_context].last_gpstime[contexts[current_context].last].u64 >> 32), 8); + contexts[current_context].last_gpstime[contexts[current_context].next].u64 = contexts[current_context].last_gpstime[contexts[current_context].next].u64 << 32; + contexts[current_context].last_gpstime[contexts[current_context].next].u64 |= dec_gps_time->readInt(); + contexts[current_context].last = contexts[current_context].next; + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = 0; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else if (multi >= LASZIP_GPSTIME_MULTI_CODE_FULL) + { + contexts[current_context].last = (contexts[current_context].last+multi-LASZIP_GPSTIME_MULTI_CODE_FULL)&3; + read_gps_time(); + } + } +} + +/* +=============================================================================== + LASreadItemCompressed_RGB14_v4 +=============================================================================== +*/ + +LASreadItemCompressed_RGB14_v4::LASreadItemCompressed_RGB14_v4(ArithmeticDecoder* dec, const U32 decompress_selective) +{ + /* not used as a decoder. just gives access to instream */ + + assert(dec); + this->dec = dec; + + /* zero instreams and decoders */ + + instream_RGB = 0; + + dec_RGB = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_RGB = 0; + + changed_RGB = FALSE; + + requested_RGB = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_RGB ? TRUE : FALSE); + + /* init the bytes buffer to zero */ + + bytes = 0; + num_bytes_allocated = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_byte_used = 0; + } + current_context = 0; +} + +LASreadItemCompressed_RGB14_v4::~LASreadItemCompressed_RGB14_v4() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_byte_used) + { + dec_RGB->destroySymbolModel(contexts[c].m_byte_used); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_0); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_1); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_2); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_3); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_4); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_5); + } + } + + /* destroy all instreams and decoders */ + + if (instream_RGB) + { + delete instream_RGB; + + delete dec_RGB; + } + + if (bytes) delete [] bytes; +} + +inline BOOL LASreadItemCompressed_RGB14_v4::createAndInitModelsAndDecompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (contexts[context].m_byte_used == 0) + { + contexts[context].m_byte_used = dec_RGB->createSymbolModel(128); + contexts[context].m_rgb_diff_0 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_1 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_2 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_3 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_4 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_5 = dec_RGB->createSymbolModel(256); + } + + /* then init entropy models */ + + dec_RGB->initSymbolModel(contexts[context].m_byte_used); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_0); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_1); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_2); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_3); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_4); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_5); + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, 6); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASreadItemCompressed_RGB14_v4::chunk_sizes() +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* read bytes per layer */ + + instream->get32bitsLE(((U8*)&num_bytes_RGB)); + + return TRUE; +} + +BOOL LASreadItemCompressed_RGB14_v4::init(const U8* item, U32& context) +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* on the first init create instreams and decoders */ + + if (instream_RGB == 0) + { + /* create instreams */ + + if (IS_LITTLE_ENDIAN()) + { + instream_RGB = new ByteStreamInArrayLE(); + } + else + { + instream_RGB = new ByteStreamInArrayBE(); + } + + /* create decoders */ + + dec_RGB = new ArithmeticDecoder(); + } + + /* make sure the buffer is sufficiently large */ + + if (num_bytes_RGB > num_bytes_allocated) + { + if (bytes) delete [] bytes; + bytes = new U8[num_bytes_RGB]; + if (bytes == 0) return FALSE; + num_bytes_allocated = num_bytes_RGB; + } + + /* load the requested bytes and init the corresponding instreams an decoders */ + + if (requested_RGB) + { + if (num_bytes_RGB) + { + instream->getBytes(bytes, num_bytes_RGB); + instream_RGB->init(bytes, num_bytes_RGB); + dec_RGB->init(instream_RGB); + changed_RGB = TRUE; + } + else + { + instream_RGB->init(0, 0); + changed_RGB = FALSE; + } + } + else + { + if (num_bytes_RGB) + { + instream->skipBytes(num_bytes_RGB); + } + changed_RGB = FALSE; + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 reader + + /* create and init models and decompressors */ + + createAndInitModelsAndDecompressors(current_context, item); + + return TRUE; +} + +inline void LASreadItemCompressed_RGB14_v4::read(U8* item, U32& context) +{ + // get last + + U16* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 reader + if (contexts[current_context].unused) + { + createAndInitModelsAndDecompressors(current_context, (U8*)last_item); + } + last_item = contexts[current_context].last_item; + } + + // decompress + + if (changed_RGB) + { + U8 corr; + I32 diff = 0; + U32 sym = dec_RGB->decodeSymbol(contexts[current_context].m_byte_used); + if (sym & (1 << 0)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_0); + ((U16*)item)[0] = (U16)U8_FOLD(corr + (last_item[0]&255)); + } + else + { + ((U16*)item)[0] = last_item[0]&0xFF; + } + if (sym & (1 << 1)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_1); + ((U16*)item)[0] |= (((U16)U8_FOLD(corr + (last_item[0]>>8))) << 8); + } + else + { + ((U16*)item)[0] |= (last_item[0]&0xFF00); + } + if (sym & (1 << 6)) + { + diff = (((U16*)item)[0]&0x00FF) - (last_item[0]&0x00FF); + if (sym & (1 << 2)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_2); + ((U16*)item)[1] = (U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[1]&255))); + } + else + { + ((U16*)item)[1] = last_item[1]&0xFF; + } + if (sym & (1 << 4)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_4); + diff = (diff + ((((U16*)item)[1]&0x00FF) - (last_item[1]&0x00FF))) / 2; + ((U16*)item)[2] = (U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[2]&255))); + } + else + { + ((U16*)item)[2] = last_item[2]&0xFF; + } + diff = (((U16*)item)[0]>>8) - (last_item[0]>>8); + if (sym & (1 << 3)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_3); + ((U16*)item)[1] |= (((U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[1]>>8))))<<8); + } + else + { + ((U16*)item)[1] |= (last_item[1]&0xFF00); + } + if (sym & (1 << 5)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_5); + diff = (diff + ((((U16*)item)[1]>>8) - (last_item[1]>>8))) / 2; + ((U16*)item)[2] |= (((U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[2]>>8))))<<8); + } + else + { + ((U16*)item)[2] |= (last_item[2]&0xFF00); + } + } + else + { + ((U16*)item)[1] = ((U16*)item)[0]; + ((U16*)item)[2] = ((U16*)item)[0]; + } + memcpy(last_item, item, 6); + } + else + { + memcpy(item, last_item, 6); + } +} + +/* +=============================================================================== + LASreadItemCompressed_RGBNIR14_v4 +=============================================================================== +*/ + +LASreadItemCompressed_RGBNIR14_v4::LASreadItemCompressed_RGBNIR14_v4(ArithmeticDecoder* dec, const U32 decompress_selective) +{ + /* not used as a decoder. just gives access to instream */ + + assert(dec); + this->dec = dec; + + /* zero instreams and decoders */ + + instream_RGB = 0; + instream_NIR = 0; + + dec_RGB = 0; + dec_NIR = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_RGB = 0; + num_bytes_NIR = 0; + + changed_RGB = FALSE; + changed_NIR = FALSE; + + requested_RGB = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_RGB ? TRUE : FALSE); + requested_NIR = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_NIR ? TRUE : FALSE); + + /* init the bytes buffer to zero */ + + bytes = 0; + num_bytes_allocated = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_rgb_bytes_used = 0; + contexts[c].m_nir_bytes_used = 0; + } + current_context = 0; +} + +LASreadItemCompressed_RGBNIR14_v4::~LASreadItemCompressed_RGBNIR14_v4() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_rgb_bytes_used) + { + dec_RGB->destroySymbolModel(contexts[c].m_rgb_bytes_used); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_0); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_1); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_2); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_3); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_4); + dec_RGB->destroySymbolModel(contexts[c].m_rgb_diff_5); + } + + if (contexts[c].m_nir_bytes_used) + { + dec_NIR->destroySymbolModel(contexts[c].m_nir_bytes_used); + dec_NIR->destroySymbolModel(contexts[c].m_nir_diff_0); + dec_NIR->destroySymbolModel(contexts[c].m_nir_diff_1); + } + } + + /* destroy all instreams and decoders */ + + if (instream_RGB) + { + delete instream_RGB; + + delete dec_RGB; + } + + if (instream_NIR) + { + delete instream_NIR; + + delete dec_NIR; + } + + if (bytes) delete [] bytes; +} + +inline BOOL LASreadItemCompressed_RGBNIR14_v4::createAndInitModelsAndDecompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (requested_RGB) + { + if (contexts[context].m_rgb_bytes_used == 0) + { + contexts[context].m_rgb_bytes_used = dec_RGB->createSymbolModel(128); + contexts[context].m_rgb_diff_0 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_1 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_2 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_3 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_4 = dec_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_5 = dec_RGB->createSymbolModel(256); + } + + /* then init entropy models */ + + dec_RGB->initSymbolModel(contexts[context].m_rgb_bytes_used); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_0); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_1); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_2); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_3); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_4); + dec_RGB->initSymbolModel(contexts[context].m_rgb_diff_5); + } + + if (requested_NIR) + { + if (contexts[context].m_nir_bytes_used == 0) + { + contexts[context].m_nir_bytes_used = dec_NIR->createSymbolModel(4); + contexts[context].m_nir_diff_0 = dec_NIR->createSymbolModel(256); + contexts[context].m_nir_diff_1 = dec_NIR->createSymbolModel(256); + } + + /* then init entropy models */ + + dec_NIR->initSymbolModel(contexts[context].m_nir_bytes_used); + dec_NIR->initSymbolModel(contexts[context].m_nir_diff_0); + dec_NIR->initSymbolModel(contexts[context].m_nir_diff_1); + } + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, 8); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASreadItemCompressed_RGBNIR14_v4::chunk_sizes() +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* read bytes per layer */ + + instream->get32bitsLE(((U8*)&num_bytes_RGB)); + instream->get32bitsLE(((U8*)&num_bytes_NIR)); + + return TRUE; +} + +BOOL LASreadItemCompressed_RGBNIR14_v4::init(const U8* item, U32& context) +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* on the first init create instreams and decoders */ + + if (instream_RGB == 0) + { + /* create instreams */ + + if (IS_LITTLE_ENDIAN()) + { + instream_RGB = new ByteStreamInArrayLE(); + instream_NIR = new ByteStreamInArrayLE(); + } + else + { + instream_RGB = new ByteStreamInArrayBE(); + instream_NIR = new ByteStreamInArrayBE(); + } + + /* create decoders */ + + dec_RGB = new ArithmeticDecoder(); + dec_NIR = new ArithmeticDecoder(); + } + + /* how many bytes do we need to read */ + + U32 num_bytes = 0; + if (requested_RGB) num_bytes += num_bytes_RGB; + if (requested_NIR) num_bytes += num_bytes_NIR; + + /* make sure the buffer is sufficiently large */ + + if (num_bytes > num_bytes_allocated) + { + if (bytes) delete [] bytes; + bytes = new U8[num_bytes]; + if (bytes == 0) return FALSE; + num_bytes_allocated = num_bytes; + } + + /* load the requested bytes and init the corresponding instreams an decoders */ + + num_bytes = 0; + + if (requested_RGB) + { + if (num_bytes_RGB) + { + instream->getBytes(bytes, num_bytes_RGB); + num_bytes += num_bytes_RGB; + instream_RGB->init(bytes, num_bytes_RGB); + dec_RGB->init(instream_RGB); + changed_RGB = TRUE; + } + else + { + instream_RGB->init(0, 0); + changed_RGB = FALSE; + } + } + else + { + if (num_bytes_RGB) + { + instream->skipBytes(num_bytes_RGB); + } + changed_RGB = FALSE; + } + + if (requested_NIR) + { + if (num_bytes_NIR) + { + instream->getBytes(&bytes[num_bytes], num_bytes_NIR); + instream_NIR->init(&bytes[num_bytes], num_bytes_NIR); + dec_NIR->init(instream_NIR); + changed_NIR = TRUE; + } + else + { + instream_NIR->init(0, 0); + changed_NIR = FALSE; + } + } + else + { + if (num_bytes_NIR) + { + instream->skipBytes(num_bytes_NIR); + } + changed_NIR = FALSE; + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 reader + + /* create and init models and decompressors */ + + createAndInitModelsAndDecompressors(current_context, item); + + return TRUE; +} + +inline void LASreadItemCompressed_RGBNIR14_v4::read(U8* item, U32& context) +{ + // get last + + U16* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 reader + if (contexts[current_context].unused) + { + createAndInitModelsAndDecompressors(current_context, (U8*)last_item); + } + last_item = contexts[current_context].last_item; + } + + // decompress + + //////////////////////////////////////// + // decompress RGB layer + //////////////////////////////////////// + + if (changed_RGB) + { + U8 corr; + I32 diff = 0; + U32 sym = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_bytes_used); + if (sym & (1 << 0)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_0); + ((U16*)item)[0] = (U16)U8_FOLD(corr + (last_item[0]&255)); + } + else + { + ((U16*)item)[0] = last_item[0]&0xFF; + } + if (sym & (1 << 1)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_1); + ((U16*)item)[0] |= (((U16)U8_FOLD(corr + (last_item[0]>>8))) << 8); + } + else + { + ((U16*)item)[0] |= (last_item[0]&0xFF00); + } + if (sym & (1 << 6)) + { + diff = (((U16*)item)[0]&0x00FF) - (last_item[0]&0x00FF); + if (sym & (1 << 2)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_2); + ((U16*)item)[1] = (U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[1]&255))); + } + else + { + ((U16*)item)[1] = last_item[1]&0xFF; + } + if (sym & (1 << 4)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_4); + diff = (diff + ((((U16*)item)[1]&0x00FF) - (last_item[1]&0x00FF))) / 2; + ((U16*)item)[2] = (U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[2]&255))); + } + else + { + ((U16*)item)[2] = last_item[2]&0xFF; + } + diff = (((U16*)item)[0]>>8) - (last_item[0]>>8); + if (sym & (1 << 3)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_3); + ((U16*)item)[1] |= (((U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[1]>>8))))<<8); + } + else + { + ((U16*)item)[1] |= (last_item[1]&0xFF00); + } + if (sym & (1 << 5)) + { + corr = dec_RGB->decodeSymbol(contexts[current_context].m_rgb_diff_5); + diff = (diff + ((((U16*)item)[1]>>8) - (last_item[1]>>8))) / 2; + ((U16*)item)[2] |= (((U16)U8_FOLD(corr + U8_CLAMP(diff+(last_item[2]>>8))))<<8); + } + else + { + ((U16*)item)[2] |= (last_item[2]&0xFF00); + } + } + else + { + ((U16*)item)[1] = ((U16*)item)[0]; + ((U16*)item)[2] = ((U16*)item)[0]; + } + memcpy(last_item, item, 6); + } + else + { + memcpy(item, last_item, 6); + } + + //////////////////////////////////////// + // decompress NIR layer + //////////////////////////////////////// + + if (changed_NIR) + { + U8 corr; + U32 sym = dec_NIR->decodeSymbol(contexts[current_context].m_nir_bytes_used); + if (sym & (1 << 0)) + { + corr = dec_NIR->decodeSymbol(contexts[current_context].m_nir_diff_0); + ((U16*)item)[3] = (U16)U8_FOLD(corr + (last_item[3]&255)); + } + else + { + ((U16*)item)[3] = last_item[3]&0xFF; + } + if (sym & (1 << 1)) + { + corr = dec_NIR->decodeSymbol(contexts[current_context].m_nir_diff_1); + ((U16*)item)[3] |= (((U16)U8_FOLD(corr + (last_item[3]>>8))) << 8); + } + else + { + ((U16*)item)[3] |= (last_item[3]&0xFF00); + } + contexts[current_context].last_item[3] = ((U16*)item)[3]; + } + else + { + ((U16*)item)[3] = contexts[current_context].last_item[3]; + } +} + +/* +=============================================================================== + LASreadItemCompressed_WAVEPACKET14_v4 +=============================================================================== +*/ + +LASreadItemCompressed_WAVEPACKET14_v4::LASreadItemCompressed_WAVEPACKET14_v4(ArithmeticDecoder* dec, const U32 decompress_selective) +{ + /* not used as a decoder. just gives access to instream */ + + assert(dec); + this->dec = dec; + + /* zero instreams and decoders */ + + instream_wavepacket = 0; + + dec_wavepacket = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_wavepacket = 0; + + changed_wavepacket = FALSE; + + requested_wavepacket = (decompress_selective & LASZIP_DECOMPRESS_SELECTIVE_WAVEPACKET ? TRUE : FALSE); + + /* init the bytes buffer to zero */ + + bytes = 0; + num_bytes_allocated = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_packet_index = 0; + } + current_context = 0; +} + +LASreadItemCompressed_WAVEPACKET14_v4::~LASreadItemCompressed_WAVEPACKET14_v4() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_packet_index) + { + dec_wavepacket->destroySymbolModel(contexts[c].m_packet_index); + dec_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[0]); + dec_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[1]); + dec_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[2]); + dec_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[3]); + delete contexts[c].ic_offset_diff; + delete contexts[c].ic_packet_size; + delete contexts[c].ic_return_point; + delete contexts[c].ic_xyz; + } + } + + /* destroy all instreams and decoders */ + + if (instream_wavepacket) + { + delete instream_wavepacket; + + delete dec_wavepacket; + } + + if (bytes) delete [] bytes; +} + +inline BOOL LASreadItemCompressed_WAVEPACKET14_v4::createAndInitModelsAndDecompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (requested_wavepacket) + { + if (contexts[context].m_packet_index == 0) + { + contexts[context].m_packet_index = dec_wavepacket->createSymbolModel(256); + contexts[context].m_offset_diff[0] = dec_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[1] = dec_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[2] = dec_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[3] = dec_wavepacket->createSymbolModel(4); + contexts[context].ic_offset_diff = new IntegerCompressor(dec_wavepacket, 32); + contexts[context].ic_packet_size = new IntegerCompressor(dec_wavepacket, 32); + contexts[context].ic_return_point = new IntegerCompressor(dec_wavepacket, 32); + contexts[context].ic_xyz = new IntegerCompressor(dec_wavepacket, 32, 3); + } + + /* then init entropy models */ + + dec_wavepacket->initSymbolModel(contexts[context].m_packet_index); + dec_wavepacket->initSymbolModel(contexts[context].m_offset_diff[0]); + dec_wavepacket->initSymbolModel(contexts[context].m_offset_diff[1]); + dec_wavepacket->initSymbolModel(contexts[context].m_offset_diff[2]); + dec_wavepacket->initSymbolModel(contexts[context].m_offset_diff[3]); + contexts[context].ic_offset_diff->initDecompressor(); + contexts[context].ic_packet_size->initDecompressor(); + contexts[context].ic_return_point->initDecompressor(); + contexts[context].ic_xyz->initDecompressor(); + } + + /* init current context from item */ + + contexts[context].last_diff_32 = 0; + contexts[context].sym_last_offset_diff = 0; + memcpy(contexts[context].last_item, item, 29); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASreadItemCompressed_WAVEPACKET14_v4::chunk_sizes() +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* read bytes per layer */ + + instream->get32bitsLE(((U8*)&num_bytes_wavepacket)); + + return TRUE; +} + +BOOL LASreadItemCompressed_WAVEPACKET14_v4::init(const U8* item, U32& context) +{ + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* on the first init create instreams and decoders */ + + if (instream_wavepacket == 0) + { + /* create instreams */ + + if (IS_LITTLE_ENDIAN()) + { + instream_wavepacket = new ByteStreamInArrayLE(); + } + else + { + instream_wavepacket = new ByteStreamInArrayBE(); + } + + /* create decoders */ + + dec_wavepacket = new ArithmeticDecoder(); + } + + /* make sure the buffer is sufficiently large */ + + if (num_bytes_wavepacket > num_bytes_allocated) + { + if (bytes) delete [] bytes; + bytes = new U8[num_bytes_wavepacket]; + if (bytes == 0) return FALSE; + num_bytes_allocated = num_bytes_wavepacket; + } + + /* load the requested bytes and init the corresponding instreams an decoders */ + + if (requested_wavepacket) + { + if (num_bytes_wavepacket) + { + instream->getBytes(bytes, num_bytes_wavepacket); + instream_wavepacket->init(bytes, num_bytes_wavepacket); + dec_wavepacket->init(instream_wavepacket); + changed_wavepacket = TRUE; + } + else + { + instream_wavepacket->init(0, 0); + changed_wavepacket = FALSE; + } + } + else + { + if (num_bytes_wavepacket) + { + instream->skipBytes(num_bytes_wavepacket); + } + changed_wavepacket = FALSE; + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 reader + + /* create and init models and decompressors */ + + createAndInitModelsAndDecompressors(current_context, item); + + return TRUE; +} + +inline void LASreadItemCompressed_WAVEPACKET14_v4::read(U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 reader + if (contexts[current_context].unused) + { + createAndInitModelsAndDecompressors(current_context, last_item); + } + last_item = contexts[current_context].last_item; + } + + // decompress + + if (changed_wavepacket) + { + item[0] = (U8)(dec_wavepacket->decodeSymbol(contexts[current_context].m_packet_index)); + + LASwavepacket13 this_item_m; + LASwavepacket13 last_item_m = LASwavepacket13::unpack(last_item+1); + + contexts[current_context].sym_last_offset_diff = dec_wavepacket->decodeSymbol(contexts[current_context].m_offset_diff[contexts[current_context].sym_last_offset_diff]); + + if (contexts[current_context].sym_last_offset_diff == 0) + { + this_item_m.offset = last_item_m.offset; + } + else if (contexts[current_context].sym_last_offset_diff == 1) + { + this_item_m.offset = last_item_m.offset + last_item_m.packet_size; + } + else if (contexts[current_context].sym_last_offset_diff == 2) + { + contexts[current_context].last_diff_32 = contexts[current_context].ic_offset_diff->decompress(contexts[current_context].last_diff_32); + this_item_m.offset = last_item_m.offset + contexts[current_context].last_diff_32; + } + else + { + this_item_m.offset = dec_wavepacket->readInt64(); + } + + this_item_m.packet_size = contexts[current_context].ic_packet_size->decompress(last_item_m.packet_size); + this_item_m.return_point.i32 = contexts[current_context].ic_return_point->decompress(last_item_m.return_point.i32); + this_item_m.x.i32 = contexts[current_context].ic_xyz->decompress(last_item_m.x.i32, 0); + this_item_m.y.i32 = contexts[current_context].ic_xyz->decompress(last_item_m.y.i32, 1); + this_item_m.z.i32 = contexts[current_context].ic_xyz->decompress(last_item_m.z.i32, 2); + + this_item_m.pack(item+1); + + memcpy(last_item, item, 29); + } +} + +/* +=============================================================================== + LASreadItemCompressed_BYTE14_v4 +=============================================================================== +*/ + +LASreadItemCompressed_BYTE14_v4::LASreadItemCompressed_BYTE14_v4(ArithmeticDecoder* dec, U32 number, const U32 decompress_selective) +{ + /* not used as a decoder. just gives access to instream */ + + assert(dec); + this->dec = dec; + + /* must be more than one byte */ + + assert(number); + this->number = number; + + /* zero instream and decoder pointer arrays */ + + instream_Bytes = 0; + + dec_Bytes = 0; + + /* create and init num_bytes and booleans arrays */ + + num_bytes_Bytes = new U32[number]; + + changed_Bytes = new BOOL[number]; + + requested_Bytes = new BOOL[number]; + + U32 i; + for (i = 0; i < number; i++) + { + num_bytes_Bytes[i] = 0; + + changed_Bytes[i] = FALSE; + + if (i > 15) // currently only the first 16 extra bytes can be selectively decompressed + { + requested_Bytes[i] = TRUE; + } + else + { + requested_Bytes[i] = (decompress_selective & (LASZIP_DECOMPRESS_SELECTIVE_BYTE0 << i) ? TRUE : FALSE); + } + } + + /* init the bytes buffer to zero */ + + bytes = 0; + num_bytes_allocated = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_bytes = 0; + } + current_context = 0; +} + +LASreadItemCompressed_BYTE14_v4::~LASreadItemCompressed_BYTE14_v4() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c, i; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_bytes) + { + for (i = 0; i < number; i++) + { + dec_Bytes[i]->destroySymbolModel(contexts[c].m_bytes[i]); + } + delete [] contexts[c].m_bytes; + delete [] contexts[c].last_item; + } + } + + /* destroy all instream and decoder arrays */ + + if (instream_Bytes) + { + for (i = 0; i < number; i++) + { + if (instream_Bytes[i]) + { + delete instream_Bytes[i]; + delete dec_Bytes[i]; + } + } + + delete [] instream_Bytes; + delete [] dec_Bytes; + } + + /* destroy all other arrays */ + + if (num_bytes_Bytes) delete [] num_bytes_Bytes; + + if (changed_Bytes) delete [] changed_Bytes; + + if (requested_Bytes) delete [] requested_Bytes; + + if (bytes) delete [] bytes; +} + +inline BOOL LASreadItemCompressed_BYTE14_v4::createAndInitModelsAndDecompressors(U32 context, const U8* item) +{ + U32 i; + + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models and last items (if needed) */ + + if (contexts[context].m_bytes == 0) + { + contexts[context].m_bytes = new ArithmeticModel*[number]; + for (i = 0; i < number; i++) + { + contexts[context].m_bytes[i] = dec_Bytes[i]->createSymbolModel(256); + dec_Bytes[i]->initSymbolModel(contexts[context].m_bytes[i]); + } + + /* create last item */ + contexts[context].last_item = new U8[number]; + } + + /* then init entropy models */ + + for (i = 0; i < number; i++) + { + dec_Bytes[i]->initSymbolModel(contexts[context].m_bytes[i]); + } + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, number); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASreadItemCompressed_BYTE14_v4::chunk_sizes() +{ + U32 i; + + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + for (i = 0; i < number; i++) + { + /* read bytes per layer */ + + instream->get32bitsLE(((U8*)&(num_bytes_Bytes[i]))); + } + + return TRUE; +} + +BOOL LASreadItemCompressed_BYTE14_v4::init(const U8* item, U32& context) +{ + U32 i; + + /* for layered compression 'dec' only hands over the stream */ + + ByteStreamIn* instream = dec->getByteStreamIn(); + + /* on the first init create instreams and decoders */ + + if (instream_Bytes == 0) + { + /* create instream pointer array */ + + instream_Bytes = new ByteStreamInArray*[number]; + + /* create instreams */ + + if (IS_LITTLE_ENDIAN()) + { + for (i = 0; i < number; i++) + { + instream_Bytes[i] = new ByteStreamInArrayLE(); + } + } + else + { + for (i = 0; i < number; i++) + { + instream_Bytes[i] = new ByteStreamInArrayBE(); + } + } + + /* create decoder pointer array */ + + dec_Bytes = new ArithmeticDecoder*[number]; + + /* create layer decoders */ + + for (i = 0; i < number; i++) + { + dec_Bytes[i] = new ArithmeticDecoder(); + } + } + + /* how many bytes do we need to read */ + + U32 num_bytes = 0; + + for (i = 0; i < number; i++) + { + if (requested_Bytes[i]) num_bytes += num_bytes_Bytes[i]; + } + + /* make sure the buffer is sufficiently large */ + + if (num_bytes > num_bytes_allocated) + { + if (bytes) delete [] bytes; + bytes = new U8[num_bytes]; + if (bytes == 0) return FALSE; + num_bytes_allocated = num_bytes; + } + + /* load the requested bytes and init the corresponding instreams an decoders */ + + num_bytes = 0; + for (i = 0; i < number; i++) + { + if (requested_Bytes[i]) + { + if (num_bytes_Bytes[i]) + { + instream->getBytes(&(bytes[num_bytes]), num_bytes_Bytes[i]); + instream_Bytes[i]->init(&(bytes[num_bytes]), num_bytes_Bytes[i]); + dec_Bytes[i]->init(instream_Bytes[i]); + num_bytes += num_bytes_Bytes[i]; + changed_Bytes[i] = TRUE; + } + else + { + dec_Bytes[i]->init(0, 0); + changed_Bytes[i] = FALSE; + } + } + else + { + if (num_bytes_Bytes[i]) + { + instream->skipBytes(num_bytes_Bytes[i]); + } + changed_Bytes[i] = FALSE; + } + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 reader + + /* create and init models and decompressors */ + + createAndInitModelsAndDecompressors(current_context, item); + + return TRUE; +} + +inline void LASreadItemCompressed_BYTE14_v4::read(U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 reader + if (contexts[current_context].unused) + { + createAndInitModelsAndDecompressors(current_context, (U8*)last_item); + } + last_item = contexts[current_context].last_item; + } + + // decompress + + U32 i; + for (i = 0; i < number; i++) + { + if (changed_Bytes[i]) + { + I32 value = last_item[i] + dec_Bytes[i]->decodeSymbol(contexts[current_context].m_bytes[i]); + item[i] = U8_FOLD(value); + last_item[i] = item[i]; + } + else + { + item[i] = last_item[i]; + } + } +} diff --git a/libs/laszip/src/lasreaditemcompressed_v4.hpp b/libs/laszip/src/lasreaditemcompressed_v4.hpp new file mode 100644 index 0000000..92bb69e --- /dev/null +++ b/libs/laszip/src/lasreaditemcompressed_v4.hpp @@ -0,0 +1,276 @@ +/* +=============================================================================== + + FILE: lasreaditemcompressed_v4.hpp + + CONTENTS: + + Native extension for decompressing the *new* point types 6 to 10 of LAS 1.4 + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2019, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 19 March 2019 -- set "legacy classification" to zero if "classification > 31" + 28 December 2017 -- fix incorrect 'context switch' reported by Wanwannodao + 28 August 2017 -- moving 'context' from global development hack to interface + 19 April 2017 -- support for selective decompression for new LAS 1.4 points + 22 June 2016 -- created after Island beat Austria 2:1 in the EM2016 + +=============================================================================== +*/ +#ifndef LAS_READ_ITEM_COMPRESSED_V4_HPP +#define LAS_READ_ITEM_COMPRESSED_V4_HPP + +#include "lasreaditem.hpp" +#include "arithmeticdecoder.hpp" +#include "integercompressor.hpp" +#include "bytestreamin_array.hpp" + +#include "laszip_common_v3.hpp" +#include "laszip_decompress_selective_v3.hpp" + +class LASreadItemCompressed_POINT14_v4 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_POINT14_v4(ArithmeticDecoder* dec, const U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + + BOOL chunk_sizes(); + BOOL init(const U8* item, U32& context); // context is set + void read(U8* item, U32& context); // context is set + + ~LASreadItemCompressed_POINT14_v4(); + +private: + + /* not used as a decoder. just gives access to instream */ + + ArithmeticDecoder* dec; + + ByteStreamInArray* instream_channel_returns_XY; + ByteStreamInArray* instream_Z; + ByteStreamInArray* instream_classification; + ByteStreamInArray* instream_flags; + ByteStreamInArray* instream_intensity; + ByteStreamInArray* instream_scan_angle; + ByteStreamInArray* instream_user_data; + ByteStreamInArray* instream_point_source; + ByteStreamInArray* instream_gps_time; + + ArithmeticDecoder* dec_channel_returns_XY; + ArithmeticDecoder* dec_Z; + ArithmeticDecoder* dec_classification; + ArithmeticDecoder* dec_flags; + ArithmeticDecoder* dec_intensity; + ArithmeticDecoder* dec_scan_angle; + ArithmeticDecoder* dec_user_data; + ArithmeticDecoder* dec_point_source; + ArithmeticDecoder* dec_gps_time; + + BOOL changed_Z; + BOOL changed_classification; + BOOL changed_flags; + BOOL changed_intensity; + BOOL changed_scan_angle; + BOOL changed_user_data; + BOOL changed_point_source; + BOOL changed_gps_time; + + U32 num_bytes_channel_returns_XY; + U32 num_bytes_Z; + U32 num_bytes_classification; + U32 num_bytes_flags; + U32 num_bytes_intensity; + U32 num_bytes_scan_angle; + U32 num_bytes_user_data; + U32 num_bytes_point_source; + U32 num_bytes_gps_time; + + BOOL requested_Z; + BOOL requested_classification; + BOOL requested_flags; + BOOL requested_intensity; + BOOL requested_scan_angle; + BOOL requested_user_data; + BOOL requested_point_source; + BOOL requested_gps_time; + + U8* bytes; + U32 num_bytes_allocated; + + U32 current_context; + LAScontextPOINT14 contexts[4]; + + BOOL createAndInitModelsAndDecompressors(U32 context, const U8* item); + void read_gps_time(); +}; + +class LASreadItemCompressed_RGB14_v4 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_RGB14_v4(ArithmeticDecoder* dec, const U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + + BOOL chunk_sizes(); + BOOL init(const U8* item, U32& context); // context is only read + void read(U8* item, U32& context); // context is only read + + ~LASreadItemCompressed_RGB14_v4(); + +private: + + /* not used as a decoder. just gives access to instream */ + + ArithmeticDecoder* dec; + + ByteStreamInArray* instream_RGB; + + ArithmeticDecoder* dec_RGB; + + BOOL changed_RGB; + + U32 num_bytes_RGB; + + BOOL requested_RGB; + + U8* bytes; + U32 num_bytes_allocated; + + U32 current_context; + LAScontextRGB14 contexts[4]; + + BOOL createAndInitModelsAndDecompressors(U32 context, const U8* item); +}; + +class LASreadItemCompressed_RGBNIR14_v4 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_RGBNIR14_v4(ArithmeticDecoder* dec, const U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + + BOOL chunk_sizes(); + BOOL init(const U8* item, U32& context); // context is only read + void read(U8* item, U32& context); // context is only read + + ~LASreadItemCompressed_RGBNIR14_v4(); + +private: + + /* not used as a decoder. just gives access to instream */ + + ArithmeticDecoder* dec; + + ByteStreamInArray* instream_RGB; + ByteStreamInArray* instream_NIR; + + ArithmeticDecoder* dec_RGB; + ArithmeticDecoder* dec_NIR; + + BOOL changed_RGB; + BOOL changed_NIR; + + U32 num_bytes_RGB; + U32 num_bytes_NIR; + + BOOL requested_RGB; + BOOL requested_NIR; + + U8* bytes; + U32 num_bytes_allocated; + + U32 current_context; + LAScontextRGBNIR14 contexts[4]; + + BOOL createAndInitModelsAndDecompressors(U32 context, const U8* item); +}; + +class LASreadItemCompressed_WAVEPACKET14_v4 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_WAVEPACKET14_v4(ArithmeticDecoder* dec, const U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + + BOOL chunk_sizes(); + BOOL init(const U8* item, U32& context); // context is only read + void read(U8* item, U32& context); // context is only read + + ~LASreadItemCompressed_WAVEPACKET14_v4(); + +private: + + /* not used as a decoder. just gives access to instream */ + + ArithmeticDecoder* dec; + + ByteStreamInArray* instream_wavepacket; + + ArithmeticDecoder* dec_wavepacket; + + BOOL changed_wavepacket; + + U32 num_bytes_wavepacket; + + BOOL requested_wavepacket; + + U8* bytes; + U32 num_bytes_allocated; + + U32 current_context; + LAScontextWAVEPACKET14 contexts[4]; + + BOOL createAndInitModelsAndDecompressors(U32 context, const U8* item); +}; + +class LASreadItemCompressed_BYTE14_v4 : public LASreadItemCompressed +{ +public: + + LASreadItemCompressed_BYTE14_v4(ArithmeticDecoder* dec, U32 number, const U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + + BOOL chunk_sizes(); + BOOL init(const U8* item, U32& context); // context is only read + void read(U8* item, U32& context); // context is only read + + ~LASreadItemCompressed_BYTE14_v4(); + +private: + + /* not used as a decoder. just gives access to instream */ + + ArithmeticDecoder* dec; + + ByteStreamInArray** instream_Bytes; + + ArithmeticDecoder** dec_Bytes; + + U32* num_bytes_Bytes; + + BOOL* changed_Bytes; + + BOOL* requested_Bytes; + + U8* bytes; + U32 num_bytes_allocated; + + U32 current_context; + LAScontextBYTE14 contexts[4]; + + U32 number; + BOOL createAndInitModelsAndDecompressors(U32 context, const U8* item); +}; + +#endif diff --git a/libs/laszip/src/lasreaditemraw.hpp b/libs/laszip/src/lasreaditemraw.hpp new file mode 100644 index 0000000..ecc45de --- /dev/null +++ b/libs/laszip/src/lasreaditemraw.hpp @@ -0,0 +1,343 @@ +/* +=============================================================================== + + FILE: lasitemreadraw.hpp + + CONTENTS: + + Implementation of LASitemReadRaw for *all* items that compose a point. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 August 2017 -- moving 'context' from global development hack to interface + 10 January 2011 -- licensing change for LGPL release and liblas integration + 7 December 2010 -- refactored after getting invited to KAUST in Saudi Arabia + +=============================================================================== +*/ +#ifndef LAS_READ_ITEM_RAW_HPP +#define LAS_READ_ITEM_RAW_HPP + +#include "lasreaditem.hpp" + +#include + +class LASreadItemRaw_POINT10_LE : public LASreadItemRaw +{ +public: + LASreadItemRaw_POINT10_LE(){}; + inline void read(U8* item, U32& context) + { + instream->getBytes(item, 20); + } +}; + +class LASreadItemRaw_POINT10_BE : public LASreadItemRaw +{ +public: + LASreadItemRaw_POINT10_BE(){}; + inline void read(U8* item, U32& context) + { + instream->getBytes(swapped, 20); + ENDIAN_SWAP_32(&swapped[ 0], &item[ 0]); // X + ENDIAN_SWAP_32(&swapped[ 4], &item[ 4]); // Y + ENDIAN_SWAP_32(&swapped[ 8], &item[ 8]); // Z + ENDIAN_SWAP_16(&swapped[12], &item[12]); // intensity + *((U32*)&item[14]) = *((U32*)&swapped[14]); // bitfield, classification, scan_angle_rank, user_data + ENDIAN_SWAP_16(&swapped[18], &item[18]); // point_source_ID + }; +private: + U8 swapped[20]; +}; + +class LASreadItemRaw_GPSTIME11_LE : public LASreadItemRaw +{ +public: + LASreadItemRaw_GPSTIME11_LE(){}; + inline void read(U8* item, U32& context) + { + instream->getBytes(item, 8); + }; +}; + +class LASreadItemRaw_GPSTIME11_BE : public LASreadItemRaw +{ +public: + LASreadItemRaw_GPSTIME11_BE(){}; + inline void read(U8* item, U32& context) + { + instream->getBytes(swapped, 8); + ENDIAN_SWAP_64(swapped, item); + }; +private: + U8 swapped[8]; +}; + +class LASreadItemRaw_RGB12_LE : public LASreadItemRaw +{ +public: + LASreadItemRaw_RGB12_LE(){}; + inline void read(U8* item, U32& context) + { + instream->getBytes(item, 6); + }; +}; + +class LASreadItemRaw_RGB12_BE : public LASreadItemRaw +{ +public: + LASreadItemRaw_RGB12_BE(){}; + inline void read(U8* item, U32& context) + { + instream->getBytes(swapped, 6); + ENDIAN_SWAP_32(&swapped[ 0], &item[ 0]); // R + ENDIAN_SWAP_32(&swapped[ 2], &item[ 2]); // G + ENDIAN_SWAP_32(&swapped[ 4], &item[ 4]); // B + }; +private: + U8 swapped[6]; +}; + +class LASreadItemRaw_WAVEPACKET13_LE : public LASreadItemRaw +{ +public: + LASreadItemRaw_WAVEPACKET13_LE(){} + inline void read(U8* item, U32& context) + { + instream->getBytes(item, 29); + }; +}; + +class LASreadItemRaw_WAVEPACKET13_BE : public LASreadItemRaw +{ +public: + LASreadItemRaw_WAVEPACKET13_BE(){} + inline void read(U8* item, U32& context) + { + instream->getBytes(swapped, 29); + item[0] = swapped[0]; // wavepacket descriptor index + ENDIAN_SWAP_64(&swapped[ 1], &item[ 1]); // byte offset to waveform data + ENDIAN_SWAP_32(&swapped[ 9], &item[ 9]); // waveform packet size in bytes + ENDIAN_SWAP_32(&swapped[13], &item[13]); // return point waveform location + ENDIAN_SWAP_32(&swapped[17], &item[17]); // X(t) + ENDIAN_SWAP_32(&swapped[21], &item[21]); // Y(t) + ENDIAN_SWAP_32(&swapped[25], &item[25]); // Z(t) + }; +private: + U8 swapped[29]; +}; + +class LASreadItemRaw_BYTE : public LASreadItemRaw +{ +public: + LASreadItemRaw_BYTE(U32 number) + { + this->number = number; + } + inline void read(U8* item, U32& context) + { + instream->getBytes(item, number); + }; +private: + U32 number; +}; + +class LAStempReadPoint10 +{ +public: + I32 X; + I32 Y; + I32 Z; + U16 intensity; + U8 return_number : 3; + U8 number_of_returns : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 classification; + I8 scan_angle_rank; + U8 user_data; + U16 point_source_ID; + // LAS 1.4 only + I16 extended_scan_angle; + U8 extended_point_type : 2; + U8 extended_scanner_channel : 2; + U8 extended_classification_flags : 4; + U8 extended_classification; + U8 extended_return_number : 4; + U8 extended_number_of_returns : 4; + // for 8 byte alignment of the GPS time + U8 dummy[3]; + // LASlib only + U32 deleted_flag; + F64 gps_time; +}; + +class LAStempReadPoint14 +{ +public: + I32 X; + I32 Y; + I32 Z; + U16 intensity; + U8 return_number : 4; + U8 number_of_returns : 4; + U8 classification_flags : 4; + U8 scanner_channel : 2; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 classification; + U8 user_data; + I16 scan_angle; + U16 point_source_ID; +}; + +class LASreadItemRaw_POINT14_LE : public LASreadItemRaw +{ +public: + LASreadItemRaw_POINT14_LE(){}; + inline void read(U8* item, U32& context) + { + instream->getBytes(buffer, 30); + ((LAStempReadPoint10*)item)->X = ((LAStempReadPoint14*)buffer)->X; + ((LAStempReadPoint10*)item)->Y = ((LAStempReadPoint14*)buffer)->Y; + ((LAStempReadPoint10*)item)->Z = ((LAStempReadPoint14*)buffer)->Z; + ((LAStempReadPoint10*)item)->intensity = ((LAStempReadPoint14*)buffer)->intensity; + if (((LAStempReadPoint14*)buffer)->number_of_returns > 7) + { + if (((LAStempReadPoint14*)buffer)->return_number > 6) + { + if (((LAStempReadPoint14*)buffer)->return_number >= ((LAStempReadPoint14*)buffer)->number_of_returns) + { + ((LAStempReadPoint10*)item)->return_number = 7; + } + else + { + ((LAStempReadPoint10*)item)->return_number = 6; + } + } + else + { + ((LAStempReadPoint10*)item)->return_number = ((LAStempReadPoint14*)buffer)->return_number; + } + ((LAStempReadPoint10*)item)->number_of_returns = 7; + } + else + { + ((LAStempReadPoint10*)item)->return_number = ((LAStempReadPoint14*)buffer)->return_number; + ((LAStempReadPoint10*)item)->number_of_returns = ((LAStempReadPoint14*)buffer)->number_of_returns; + } + ((LAStempReadPoint10*)item)->scan_direction_flag = ((LAStempReadPoint14*)buffer)->scan_direction_flag; + ((LAStempReadPoint10*)item)->edge_of_flight_line = ((LAStempReadPoint14*)buffer)->edge_of_flight_line; + ((LAStempReadPoint10*)item)->classification = (U8)(((((LAStempReadPoint14*)buffer)->classification_flags) << 5) & 0xE0); + if (((LAStempReadPoint14*)buffer)->classification < 32) ((LAStempReadPoint10*)item)->classification |= ((LAStempReadPoint14*)buffer)->classification; + ((LAStempReadPoint10*)item)->scan_angle_rank = I8_CLAMP(I16_QUANTIZE(0.006f*((LAStempReadPoint14*)buffer)->scan_angle)); + ((LAStempReadPoint10*)item)->user_data = ((LAStempReadPoint14*)buffer)->user_data; + ((LAStempReadPoint10*)item)->point_source_ID = ((LAStempReadPoint14*)buffer)->point_source_ID; + ((LAStempReadPoint10*)item)->extended_scanner_channel = ((LAStempReadPoint14*)buffer)->scanner_channel; + ((LAStempReadPoint10*)item)->extended_classification_flags = ((LAStempReadPoint14*)buffer)->classification_flags; + ((LAStempReadPoint10*)item)->extended_classification = ((LAStempReadPoint14*)buffer)->classification; + ((LAStempReadPoint10*)item)->extended_return_number = ((LAStempReadPoint14*)buffer)->return_number; + ((LAStempReadPoint10*)item)->extended_number_of_returns = ((LAStempReadPoint14*)buffer)->number_of_returns; + ((LAStempReadPoint10*)item)->extended_scan_angle = ((LAStempReadPoint14*)buffer)->scan_angle; + ((LAStempReadPoint10*)item)->gps_time = *((F64*)&buffer[22]); + } +private: + U8 buffer[30]; +}; + +class LASreadItemRaw_POINT14_BE : public LASreadItemRaw +{ +public: + LASreadItemRaw_POINT14_BE(){}; + inline void read(U8* item, U32& context) + { + instream->getBytes(swapped, 30); + ENDIAN_SWAP_32(&swapped[ 0], &item[ 0]); // X + ENDIAN_SWAP_32(&swapped[ 4], &item[ 4]); // Y + ENDIAN_SWAP_32(&swapped[ 8], &item[ 8]); // Z + ENDIAN_SWAP_16(&swapped[12], &item[12]); // intensity + if (((LAStempReadPoint14*)swapped)->number_of_returns > 7) + { + if (((LAStempReadPoint14*)swapped)->return_number > 6) + { + if (((LAStempReadPoint14*)swapped)->return_number >= ((LAStempReadPoint14*)swapped)->number_of_returns) + { + ((LAStempReadPoint10*)item)->return_number = 7; + } + else + { + ((LAStempReadPoint10*)item)->return_number = 6; + } + } + else + { + ((LAStempReadPoint10*)item)->return_number = ((LAStempReadPoint14*)swapped)->return_number; + } + ((LAStempReadPoint10*)item)->number_of_returns = 7; + } + else + { + ((LAStempReadPoint10*)item)->return_number = ((LAStempReadPoint14*)swapped)->return_number; + ((LAStempReadPoint10*)item)->number_of_returns = ((LAStempReadPoint14*)swapped)->number_of_returns; + } + ((LAStempReadPoint10*)item)->scan_direction_flag = ((LAStempReadPoint14*)swapped)->scan_direction_flag; + ((LAStempReadPoint10*)item)->edge_of_flight_line = ((LAStempReadPoint14*)swapped)->edge_of_flight_line; + ((LAStempReadPoint10*)item)->classification = (((LAStempReadPoint14*)swapped)->classification_flags << 5); + if (((LAStempReadPoint14*)swapped)->classification < 32) ((LAStempReadPoint10*)item)->classification |= ((LAStempReadPoint14*)swapped)->classification; + ((LAStempReadPoint10*)item)->user_data = ((LAStempReadPoint14*)swapped)->user_data; + ENDIAN_SWAP_16(&swapped[20], &item[18]); // point_source_ID + ((LAStempReadPoint10*)item)->extended_scanner_channel = ((LAStempReadPoint14*)swapped)->scanner_channel; + ((LAStempReadPoint10*)item)->extended_classification_flags = ((LAStempReadPoint14*)swapped)->classification_flags; + ((LAStempReadPoint10*)item)->extended_classification = ((LAStempReadPoint14*)swapped)->classification; + ((LAStempReadPoint10*)item)->extended_return_number = ((LAStempReadPoint14*)swapped)->return_number; + ((LAStempReadPoint10*)item)->extended_number_of_returns = ((LAStempReadPoint14*)swapped)->number_of_returns; + ENDIAN_SWAP_16(&swapped[18], (U8*)&(((LAStempReadPoint10*)item)->extended_scan_angle)); + ((LAStempReadPoint10*)item)->scan_angle_rank = I8_CLAMP(I16_QUANTIZE(0.006f*((LAStempReadPoint10*)item)->extended_scan_angle)); + ENDIAN_SWAP_64(&swapped[22], (U8*)&(((LAStempReadPoint10*)item)->gps_time)); + } +private: + U8 swapped[30]; +}; + +class LASreadItemRaw_RGBNIR14_LE : public LASreadItemRaw +{ +public: + LASreadItemRaw_RGBNIR14_LE(){}; + inline void read(U8* item, U32& context) + { + instream->getBytes(item, 8); + }; +}; + +class LASreadItemRaw_RGBNIR14_BE : public LASreadItemRaw +{ +public: + LASreadItemRaw_RGBNIR14_BE(){}; + inline void read(U8* item, U32& context) + { + instream->getBytes(swapped, 8); + ENDIAN_SWAP_32(&swapped[ 0], &item[ 0]); // R + ENDIAN_SWAP_32(&swapped[ 2], &item[ 2]); // G + ENDIAN_SWAP_32(&swapped[ 4], &item[ 4]); // B + ENDIAN_SWAP_32(&swapped[ 6], &item[ 6]); // NIR + }; +private: + U8 swapped[8]; +}; + +#endif diff --git a/libs/laszip/src/lasreadpoint.cpp b/libs/laszip/src/lasreadpoint.cpp new file mode 100644 index 0000000..5a2b812 --- /dev/null +++ b/libs/laszip/src/lasreadpoint.cpp @@ -0,0 +1,826 @@ +/* +=============================================================================== + + FILE: lasreadpoint.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ + +#include "lasreadpoint.hpp" + +#include "arithmeticdecoder.hpp" +#include "lasreaditemraw.hpp" +#include "lasreaditemcompressed_v1.hpp" +#include "lasreaditemcompressed_v2.hpp" +#include "lasreaditemcompressed_v3.hpp" +#include "lasreaditemcompressed_v4.hpp" + +#include +#include +#include + +LASreadPoint::LASreadPoint(U32 decompress_selective) +{ + point_size = 0; + instream = 0; + num_readers = 0; + readers = 0; + readers_raw = 0; + readers_compressed = 0; + dec = 0; + layered_las14_compression = FALSE; + // used for chunking + chunk_size = U32_MAX; + chunk_count = 0; + current_chunk = 0; + number_chunks = 0; + tabled_chunks = 0; + chunk_totals = 0; + chunk_starts = 0; + // used for selective decompression (new LAS 1.4 point types only) + this->decompress_selective = decompress_selective; + // used for seeking + point_start = 0; + seek_point = 0; + // used for error and warning reporting + last_error = 0; + last_warning = 0; +} + +BOOL LASreadPoint::setup(U32 num_items, const LASitem* items, const LASzip* laszip) +{ + U32 i; + + // is laszip exists then we must use its items + if (laszip) + { + if (num_items == 0) return FALSE; + if (items == 0) return FALSE; + if (num_items != laszip->num_items) return FALSE; + if (items != laszip->items) return FALSE; + } + + // delete old entropy decoder + if (dec) + { + delete dec; + dec = 0; + layered_las14_compression = FALSE; + } + + // is the content compressed? + if (laszip && laszip->compressor) + { + // create new entropy decoder (if requested) + switch (laszip->coder) + { + case LASZIP_CODER_ARITHMETIC: + dec = new ArithmeticDecoder(); + break; + default: + // entropy decoder not supported + return FALSE; + } + // maybe layered compression for LAS 1.4 + layered_las14_compression = (laszip->compressor == LASZIP_COMPRESSOR_LAYERED_CHUNKED); + } + + // initizalize the readers + readers = 0; + num_readers = num_items; + + // disable chunking + chunk_size = U32_MAX; + + // always create the raw readers + readers_raw = new LASreadItem*[num_readers]; + for (i = 0; i < num_readers; i++) + { + switch (items[i].type) + { + case LASitem::POINT10: + if (IS_LITTLE_ENDIAN()) + readers_raw[i] = new LASreadItemRaw_POINT10_LE(); + else + readers_raw[i] = new LASreadItemRaw_POINT10_BE(); + break; + case LASitem::GPSTIME11: + if (IS_LITTLE_ENDIAN()) + readers_raw[i] = new LASreadItemRaw_GPSTIME11_LE(); + else + readers_raw[i] = new LASreadItemRaw_GPSTIME11_BE(); + break; + case LASitem::RGB12: + case LASitem::RGB14: + if (IS_LITTLE_ENDIAN()) + readers_raw[i] = new LASreadItemRaw_RGB12_LE(); + else + readers_raw[i] = new LASreadItemRaw_RGB12_BE(); + break; + case LASitem::BYTE: + case LASitem::BYTE14: + readers_raw[i] = new LASreadItemRaw_BYTE(items[i].size); + break; + case LASitem::POINT14: + if (IS_LITTLE_ENDIAN()) + readers_raw[i] = new LASreadItemRaw_POINT14_LE(); + else + readers_raw[i] = new LASreadItemRaw_POINT14_BE(); + break; + case LASitem::RGBNIR14: + if (IS_LITTLE_ENDIAN()) + readers_raw[i] = new LASreadItemRaw_RGBNIR14_LE(); + else + readers_raw[i] = new LASreadItemRaw_RGBNIR14_BE(); + break; + case LASitem::WAVEPACKET13: + case LASitem::WAVEPACKET14: + if (IS_LITTLE_ENDIAN()) + readers_raw[i] = new LASreadItemRaw_WAVEPACKET13_LE(); + else + readers_raw[i] = new LASreadItemRaw_WAVEPACKET13_BE(); + break; + default: + return FALSE; + } + point_size += items[i].size; + } + + if (dec) + { + readers_compressed = new LASreadItem*[num_readers]; + // seeks with compressed data need a seek point + if (seek_point) + { + delete [] seek_point[0]; + delete [] seek_point; + } + seek_point = new U8*[num_items]; + if (!seek_point) return FALSE; + if (layered_las14_compression) + { + // because combo LAS 1.0 - 1.4 point struct has padding + seek_point[0] = new U8[(point_size*2)]; + // because extended_point_type must be set + seek_point[0][22] = 1; + } + else + { + seek_point[0] = new U8[point_size]; + } + if (!seek_point[0]) return FALSE; + for (i = 0; i < num_readers; i++) + { + switch (items[i].type) + { + case LASitem::POINT10: + if (items[i].version == 1) + readers_compressed[i] = new LASreadItemCompressed_POINT10_v1(dec); + else if (items[i].version == 2) + readers_compressed[i] = new LASreadItemCompressed_POINT10_v2(dec); + else + return FALSE; + break; + case LASitem::GPSTIME11: + if (items[i].version == 1) + readers_compressed[i] = new LASreadItemCompressed_GPSTIME11_v1(dec); + else if (items[i].version == 2) + readers_compressed[i] = new LASreadItemCompressed_GPSTIME11_v2(dec); + else + return FALSE; + break; + case LASitem::RGB12: + if (items[i].version == 1) + readers_compressed[i] = new LASreadItemCompressed_RGB12_v1(dec); + else if (items[i].version == 2) + readers_compressed[i] = new LASreadItemCompressed_RGB12_v2(dec); + else + return FALSE; + break; + case LASitem::BYTE: + if (items[i].version == 1) + readers_compressed[i] = new LASreadItemCompressed_BYTE_v1(dec, items[i].size); + else if (items[i].version == 2) + readers_compressed[i] = new LASreadItemCompressed_BYTE_v2(dec, items[i].size); + else + return FALSE; + break; + case LASitem::POINT14: + if ((items[i].version == 3) || (items[i].version == 2)) // version == 2 from lasproto + readers_compressed[i] = new LASreadItemCompressed_POINT14_v3(dec, decompress_selective); + else if (items[i].version == 4) + readers_compressed[i] = new LASreadItemCompressed_POINT14_v4(dec, decompress_selective); + else + return FALSE; + break; + case LASitem::RGB14: + if ((items[i].version == 3) || (items[i].version == 2)) // version == 2 from lasproto + readers_compressed[i] = new LASreadItemCompressed_RGB14_v3(dec, decompress_selective); + else if (items[i].version == 4) + readers_compressed[i] = new LASreadItemCompressed_RGB14_v4(dec, decompress_selective); + else + return FALSE; + break; + case LASitem::RGBNIR14: + if ((items[i].version == 3) || (items[i].version == 2)) // version == 2 from lasproto + readers_compressed[i] = new LASreadItemCompressed_RGBNIR14_v3(dec, decompress_selective); + else if (items[i].version == 4) + readers_compressed[i] = new LASreadItemCompressed_RGBNIR14_v4(dec, decompress_selective); + else + return FALSE; + break; + case LASitem::BYTE14: + if ((items[i].version == 3) || (items[i].version == 2)) // version == 2 from lasproto + readers_compressed[i] = new LASreadItemCompressed_BYTE14_v3(dec, items[i].size, decompress_selective); + else if (items[i].version == 4) + readers_compressed[i] = new LASreadItemCompressed_BYTE14_v4(dec, items[i].size, decompress_selective); + else + return FALSE; + break; + case LASitem::WAVEPACKET13: + if (items[i].version == 1) + readers_compressed[i] = new LASreadItemCompressed_WAVEPACKET13_v1(dec); + else + return FALSE; + break; + case LASitem::WAVEPACKET14: + if (items[i].version == 3) + readers_compressed[i] = new LASreadItemCompressed_WAVEPACKET14_v3(dec, decompress_selective); + else if (items[i].version == 4) + readers_compressed[i] = new LASreadItemCompressed_WAVEPACKET14_v4(dec, decompress_selective); + else + return FALSE; + break; + default: + return FALSE; + } + if (i) + { + if (layered_las14_compression) + { + // because combo LAS 1.0 - 1.4 point struct has padding + seek_point[i] = seek_point[i-1]+(2*items[i-1].size); + } + else + { + seek_point[i] = seek_point[i-1]+items[i-1].size; + } + } + } + if (laszip->compressor != LASZIP_COMPRESSOR_POINTWISE) + { + if (laszip->chunk_size) chunk_size = laszip->chunk_size; + number_chunks = U32_MAX; + } + } + return TRUE; +} + +BOOL LASreadPoint::init(ByteStreamIn* instream) +{ + if (!instream) return FALSE; + this->instream = instream; + + U32 i; + for (i = 0; i < num_readers; i++) + { + ((LASreadItemRaw*)(readers_raw[i]))->init(instream); + } + + if (dec) + { + chunk_count = chunk_size; + point_start = 0; + readers = 0; + } + else + { + point_start = instream->tell(); + readers = readers_raw; + } + + return TRUE; +} + +BOOL LASreadPoint::seek(const U32 current, const U32 target) +{ + if (!instream->isSeekable()) return FALSE; + U32 delta = 0; + if (dec) + { + if (point_start == 0) + { + init_dec(); + chunk_count = 0; + } + if (chunk_starts) + { + U32 target_chunk; + if (chunk_totals) + { + target_chunk = search_chunk_table(target, 0, number_chunks); + chunk_size = chunk_totals[target_chunk+1]-chunk_totals[target_chunk]; + delta = target - chunk_totals[target_chunk]; + } + else + { + target_chunk = target/chunk_size; + delta = target%chunk_size; + } + if (target_chunk >= tabled_chunks) + { + if (current_chunk < (tabled_chunks-1)) + { + dec->done(); + current_chunk = (tabled_chunks-1); + instream->seek(chunk_starts[current_chunk]); + init_dec(); + chunk_count = 0; + } + delta += (chunk_size*(target_chunk-current_chunk) - chunk_count); + } + else if (current_chunk != target_chunk || current > target) + { + dec->done(); + current_chunk = target_chunk; + instream->seek(chunk_starts[current_chunk]); + init_dec(); + chunk_count = 0; + } + else + { + delta = target - current; + } + } + else if (current > target) + { + dec->done(); + instream->seek(point_start); + init_dec(); + delta = target; + } + else if (current < target) + { + delta = target - current; + } + while (delta) + { + read(seek_point); + delta--; + } + } + else + { + if (current != target) + { + instream->seek(point_start+(I64)point_size*target); + } + } + return TRUE; +} + +BOOL LASreadPoint::read(U8* const * point) +{ + U32 i; + U32 context = 0; + + try + { + if (dec) + { + if (chunk_count == chunk_size) + { + if (point_start != 0) + { + dec->done(); + current_chunk++; + // check integrity + if (current_chunk < tabled_chunks) + { + I64 here = instream->tell(); + if (chunk_starts[current_chunk] != here) + { + // previous chunk was corrupt + current_chunk--; + throw 4711; + } + } + } + init_dec(); + if (current_chunk == tabled_chunks) // no or incomplete chunk table? + { + if (current_chunk == number_chunks) + { + number_chunks += 256; + chunk_starts = (I64*)realloc(chunk_starts, sizeof(I64)*(number_chunks+1)); + } + chunk_starts[tabled_chunks] = point_start; // needs fixing + tabled_chunks++; + } + else if (chunk_totals) // variable sized chunks? + { + chunk_size = chunk_totals[current_chunk+1]-chunk_totals[current_chunk]; + } + chunk_count = 0; + } + chunk_count++; + + if (readers) + { + for (i = 0; i < num_readers; i++) + { + readers[i]->read(point[i], context); + } + } + else + { + for (i = 0; i < num_readers; i++) + { + readers_raw[i]->read(point[i], context); + } + if (layered_las14_compression) + { + // for layered compression 'dec' only hands over the stream + dec->init(instream, FALSE); + // read how many points are in the chunk + U32 count; + instream->get32bitsLE((U8*)&count); + // read the sizes of all layers + for (i = 0; i < num_readers; i++) + { + ((LASreadItemCompressed*)(readers_compressed[i]))->chunk_sizes(); + } + for (i = 0; i < num_readers; i++) + { + ((LASreadItemCompressed*)(readers_compressed[i]))->init(point[i], context); + } + if (DEBUG_OUTPUT_NUM_BYTES_DETAILS) fprintf(stderr, "\n"); + } + else + { + for (i = 0; i < num_readers; i++) + { + ((LASreadItemCompressed*)(readers_compressed[i]))->init(point[i], context); + } + dec->init(instream); + } + readers = readers_compressed; + } + } + else + { + for (i = 0; i < num_readers; i++) + { + readers[i]->read(point[i], context); + } + } + } + catch (I32 exception) + { + // create error string + if (last_error == 0) last_error = new CHAR[128]; + // report error + if (exception == EOF) + { + // end-of-file + if (dec) + { + sprintf(last_error, "end-of-file during chunk with index %u", current_chunk); + } + else + { + sprintf(last_error, "end-of-file"); + } + } + else + { + // decompression error + sprintf(last_error, "chunk with index %u of %u is corrupt", current_chunk, tabled_chunks); + // if we know where the next chunk starts ... + if ((current_chunk+1) < tabled_chunks) + { + // ... try to seek to the next chunk + instream->seek(chunk_starts[(current_chunk+1)]); + // ... ready for next LASreadPoint::read() + chunk_count = chunk_size; + } + } + return FALSE; + } + return TRUE; +} + +BOOL LASreadPoint::check_end() +{ + if (readers == readers_compressed) + { + if (dec) + { + dec->done(); + current_chunk++; + // check integrity + if (current_chunk < tabled_chunks) + { + I64 here = instream->tell(); + if (chunk_starts[current_chunk] != here) // then last chunk was corrupt + { + // create error string + if (last_error == 0) last_error = new CHAR[128]; + // report error + sprintf(last_error, "chunk with index %u of %u is corrupt", current_chunk, tabled_chunks); + return FALSE; + } + } + } + } + return TRUE; +} + +BOOL LASreadPoint::done() +{ + instream = 0; + return TRUE; +} + +BOOL LASreadPoint::init_dec() +{ + // maybe read chunk table (only if chunking enabled) + + if (number_chunks == U32_MAX) + { + if (!read_chunk_table()) + { + return FALSE; + } + current_chunk = 0; + if (chunk_totals) chunk_size = chunk_totals[1]; + } + + point_start = instream->tell(); + readers = 0; + + return TRUE; +} + +BOOL LASreadPoint::read_chunk_table() +{ + // read the 8 bytes that store the location of the chunk table + I64 chunk_table_start_position; + try { instream->get64bitsLE((U8*)&chunk_table_start_position); } catch(...) + { + return FALSE; + } + + // this is where the chunks start + I64 chunks_start = instream->tell(); + + // was compressor interrupted before getting a chance to write the chunk table? + if ((chunk_table_start_position + 8) == chunks_start) + { + // no choice but to fail if adaptive chunking was used + if (chunk_size == U32_MAX) + { + // create error string + if (last_error == 0) last_error = new CHAR[128]; + // report error + sprintf(last_error, "compressor was interrupted before writing adaptive chunk table of LAZ file"); + return FALSE; + } + // otherwise we build the chunk table as we read the file + number_chunks = 256; + chunk_starts = (I64*)malloc(sizeof(I64)*(number_chunks+1)); + if (chunk_starts == 0) + { + return FALSE; + } + chunk_starts[0] = chunks_start; + tabled_chunks = 1; + // create warning string + if (last_warning == 0) last_warning = new CHAR[128]; + // report warning + sprintf(last_warning, "compressor was interrupted before writing chunk table of LAZ file"); + return TRUE; + } + + // maybe the stream is not seekable + if (!instream->isSeekable()) + { + // no choice but to fail if adaptive chunking was used + if (chunk_size == U32_MAX) + { + return FALSE; + } + // then we cannot seek to the chunk table but won't need it anyways + number_chunks = 0; + tabled_chunks = 0; + return TRUE; + } + + if (chunk_table_start_position == -1) + { + // the compressor was writing to a non-seekable stream and wrote the chunk table start at the end + if (!instream->seekEnd(8)) + { + return FALSE; + } + try { instream->get64bitsLE((U8*)&chunk_table_start_position); } catch(...) + { + return FALSE; + } + } + + // read the chunk table + try + { + instream->seek(chunk_table_start_position); + U32 version; + instream->get32bitsLE((U8*)&version); + if (version != 0) + { + throw 1; + } + instream->get32bitsLE((U8*)&number_chunks); + if (chunk_totals) delete [] chunk_totals; + chunk_totals = 0; + if (chunk_starts) free(chunk_starts); + chunk_starts = 0; + if (chunk_size == U32_MAX) + { + chunk_totals = new U32[number_chunks+1]; + if (chunk_totals == 0) + { + throw 1; + } + chunk_totals[0] = 0; + } + chunk_starts = (I64*)malloc(sizeof(I64)*(number_chunks+1)); + if (chunk_starts == 0) + { + throw 1; + } + chunk_starts[0] = chunks_start; + tabled_chunks = 1; + if (number_chunks > 0) + { + U32 i; + dec->init(instream); + IntegerCompressor ic(dec, 32, 2); + ic.initDecompressor(); + for (i = 1; i <= number_chunks; i++) + { + if (chunk_size == U32_MAX) chunk_totals[i] = ic.decompress((i>1 ? chunk_totals[i-1] : 0), 0); + chunk_starts[i] = ic.decompress((i>1 ? (U32)(chunk_starts[i-1]) : 0), 1); + tabled_chunks++; + } + dec->done(); + for (i = 1; i <= number_chunks; i++) + { + if (chunk_size == U32_MAX) chunk_totals[i] += chunk_totals[i-1]; + chunk_starts[i] += chunk_starts[i-1]; + if (chunk_starts[i] <= chunk_starts[i-1]) + { + throw 1; + } + } + } + } + catch (...) + { + // something went wrong while reading the chunk table + if (chunk_totals) delete [] chunk_totals; + chunk_totals = 0; + // no choice but to fail if adaptive chunking was used + if (chunk_size == U32_MAX) + { + return FALSE; + } + // did we not even read the number of chunks + if (number_chunks == U32_MAX) + { + // then compressor was interrupted before getting a chance to write the chunk table + number_chunks = 256; + chunk_starts = (I64*)malloc(sizeof(I64)*(number_chunks+1)); + if (chunk_starts == 0) + { + return FALSE; + } + chunk_starts[0] = chunks_start; + tabled_chunks = 1; + } + else + { + // otherwise fix as many additional chunk_starts as possible + U32 i; + for (i = 1; i < tabled_chunks; i++) + { + chunk_starts[i] += chunk_starts[i-1]; + } + } + // create warning string + if (last_warning == 0) last_warning = new CHAR[128]; + // first seek to the end of the file + instream->seekEnd(); + // get position of last byte + I64 last_position = instream->tell(); + // warn if last byte position is before chunk table start position + if (last_position <= chunk_table_start_position) + { + // report warning + if (last_position == chunk_table_start_position) + { + sprintf(last_warning, "chunk table is missing. improper use of LAZ compressor?"); + } + else + { +#ifdef _WIN32 + sprintf(last_warning, "chunk table and %I64d bytes are missing. LAZ file truncated during copy or transfer?", chunk_table_start_position - last_position); +#else + sprintf(last_warning, "chunk table and %lld bytes are missing. LAZ file truncated during copy or transfer?", chunk_table_start_position - last_position); +#endif + } + } + else + { + // report warning + sprintf(last_warning, "corrupt chunk table"); + } + } + if (!instream->seek(chunks_start)) + { + return FALSE; + } + return TRUE; +} + +U32 LASreadPoint::search_chunk_table(const U32 index, const U32 lower, const U32 upper) +{ + if (lower + 1 == upper) return lower; + U32 mid = (lower+upper)/2; + if (index >= chunk_totals[mid]) + return search_chunk_table(index, mid, upper); + else + return search_chunk_table(index, lower, mid); +} + +LASreadPoint::~LASreadPoint() +{ + U32 i; + + if (readers_raw) + { + for (i = 0; i < num_readers; i++) + { + delete readers_raw[i]; + } + delete [] readers_raw; + } + + if (readers_compressed) + { + for (i = 0; i < num_readers; i++) + { + delete readers_compressed[i]; + } + delete [] readers_compressed; + } + + if (dec) + { + delete dec; + } + + if (chunk_totals) delete [] chunk_totals; + if (chunk_starts) free(chunk_starts); + + if (seek_point) + { + delete [] seek_point[0]; + delete [] seek_point; + } + + if (last_error) delete [] last_error; + if (last_warning) delete [] last_warning; +} diff --git a/libs/laszip/src/lasreadpoint.hpp b/libs/laszip/src/lasreadpoint.hpp new file mode 100644 index 0000000..dd62430 --- /dev/null +++ b/libs/laszip/src/lasreadpoint.hpp @@ -0,0 +1,103 @@ +/* +=============================================================================== + + FILE: lasreadpoint.hpp + + CONTENTS: + + Common interface for the classes that read points raw or compressed. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 August 2017 -- moving 'context' from global development hack to interface + 18 July 2017 -- bug fix for spatial-indexed reading of native compressed LAS 1.4 + 19 April 2017 -- support for selective decompression for new LAS 1.4 points + 23 August 2016 -- layering of items for selective decompression in LAS 1.4 + 6 September 2014 -- removed inheritance of EntropyEncoder and EntropyDecoder + 24 August 2014 -- delay read of chunk table until first read() or seek() is called + 6 October 2011 -- large file support & reading with missing chunk table + 9 May 2011 -- the chunked compressor now allows variable chunk sizes + 25 April 2011 -- added chunked laszip for random access decompression + 10 January 2011 -- licensing change for LGPL release and liblas integration + 7 December 2010 -- adapted from LASpointReader for better code modularity + 3 December 2010 -- updated to (somewhat) support LAS format 1.3 + 7 September 2008 -- updated to support LAS format 1.2 + 22 February 2007 -- created about an hour before henna's birthday + +=============================================================================== +*/ +#ifndef LAS_READ_POINT_HPP +#define LAS_READ_POINT_HPP + +#include "mydefs.hpp" +#include "laszip.hpp" +#include "laszip_decompress_selective_v3.hpp" +#include "bytestreamin.hpp" + +class LASreadItem; +class ArithmeticDecoder; + +class LASreadPoint +{ +public: + LASreadPoint(U32 decompress_selective=LASZIP_DECOMPRESS_SELECTIVE_ALL); + ~LASreadPoint(); + + // should only be called *once* + BOOL setup(const U32 num_items, const LASitem* items, const LASzip* laszip=0); + + BOOL init(ByteStreamIn* instream); + BOOL seek(const U32 current, const U32 target); + BOOL read(U8* const * point); + BOOL check_end(); + BOOL done(); + + inline const CHAR* error() const { return last_error; }; + inline const CHAR* warning() const { return last_warning; }; + +private: + ByteStreamIn* instream; + U32 num_readers; + LASreadItem** readers; + LASreadItem** readers_raw; + LASreadItem** readers_compressed; + ArithmeticDecoder* dec; + BOOL layered_las14_compression; + // used for chunking + U32 chunk_size; + U32 chunk_count; + U32 current_chunk; + U32 number_chunks; + U32 tabled_chunks; + I64* chunk_starts; + U32* chunk_totals; + BOOL init_dec(); + BOOL read_chunk_table(); + U32 search_chunk_table(const U32 index, const U32 lower, const U32 upper); + // used for selective decompression (new LAS 1.4 point types only) + U32 decompress_selective; + // used for seeking + I64 point_start; + U32 point_size; + U8** seek_point; + // used for error and warning reporting + CHAR* last_error; + CHAR* last_warning; +}; + +#endif diff --git a/libs/laszip/src/lasunzipper.cpp b/libs/laszip/src/lasunzipper.cpp new file mode 100644 index 0000000..3519a68 --- /dev/null +++ b/libs/laszip/src/lasunzipper.cpp @@ -0,0 +1,139 @@ +/* +=============================================================================== + + FILE: lasunzipper.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2013, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ +#include "lasunzipper.hpp" + +#include +#include + +#include "bytestreamin_file.hpp" +#include "bytestreamin_istream.hpp" +#include "lasreadpoint.hpp" + +bool LASunzipper::open(FILE* infile, const LASzip* laszip) +{ + if (!infile) return return_error("FILE* infile pointer is NULL"); + if (!laszip) return return_error("const LASzip* laszip pointer is NULL"); + count = 0; + if (reader) delete reader; + reader = new LASreadPoint(); + if (!reader) return return_error("alloc of LASreadPoint failed"); + if (!reader->setup(laszip->num_items, laszip->items, laszip)) return return_error("setup() of LASreadPoint failed"); + if (stream) delete stream; + if (IS_LITTLE_ENDIAN()) + stream = new ByteStreamInFileLE(infile); + else + stream = new ByteStreamInFileBE(infile); + if (!stream) return return_error("alloc of ByteStreamInFile failed"); + if (!reader->init(stream)) return return_error("init() of LASreadPoint failed"); + return true; +} + +bool LASunzipper::open(istream& instream, const LASzip* laszip) +{ + if (!laszip) return return_error("const LASzip* laszip pointer is NULL"); + count = 0; + if (reader) delete reader; + reader = new LASreadPoint(); + if (!reader) return return_error("alloc of LASreadPoint failed"); + if (!reader->setup(laszip->num_items, laszip->items, laszip)) return return_error("setup() of LASreadPoint failed"); + if (stream) delete stream; + if (IS_LITTLE_ENDIAN()) + stream = new ByteStreamInIstreamLE(instream); + else + stream = new ByteStreamInIstreamBE(instream); + if (!stream) return return_error("alloc of ByteStreamInStream failed"); + if (!reader->init(stream)) return return_error("init() of LASreadPoint failed"); + return true; +} + +bool LASunzipper::seek(const unsigned int position) +{ + if (!reader->seek(count, position)) return return_error("seek() of LASreadPoint failed"); + count = position; + return true; +} + +unsigned int LASunzipper::tell() const +{ + return count; +} + +bool LASunzipper::read(unsigned char * const * point) +{ + count++; + return (reader->read(point) == TRUE); +} + +bool LASunzipper::close() +{ + BOOL done = TRUE; + if (reader) + { + done = reader->done(); + delete reader; + reader = 0; + } + if (stream) + { + delete stream; + stream = 0; + } + if (!done) return return_error("done() of LASreadPoint failed"); + return true; +} + +const char* LASunzipper::get_error() const +{ + return error_string; +} + +bool LASunzipper::return_error(const char* error) +{ + char err[256]; + sprintf(err, "%s (LASzip v%d.%dr%d)", error, LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION); + if (error_string) free(error_string); + error_string = LASCopyString(err); + return false; +} + +LASunzipper::LASunzipper() +{ + error_string = 0; + count = 0; + stream = 0; + reader = 0; +} + +LASunzipper::~LASunzipper() +{ + if (error_string) free(error_string); + if (reader || stream) close(); +} diff --git a/libs/laszip/src/lasunzipper.hpp b/libs/laszip/src/lasunzipper.hpp new file mode 100644 index 0000000..e8cf653 --- /dev/null +++ b/libs/laszip/src/lasunzipper.hpp @@ -0,0 +1,77 @@ +/* +=============================================================================== + + FILE: lasunzipper.hpp + + CONTENTS: + + Reads (optionally compressed) LIDAR points to LAS formats 1.0 - 1.3. This + particular class is only used for adding LASzip to libLAS (not to LASlib). + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2013, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 23 April 2011 -- changed interface for easier future compressor support + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- created from LASwriter/LASreader after Howard got pushy (-; + +=============================================================================== +*/ +#ifndef LAS_UNZIPPER_HPP +#define LAS_UNZIPPER_HPP + +#include + +#include "laszip.hpp" + +#ifdef LZ_WIN32_VC6 +#include +#else +#include +#include +using namespace std; +#endif + +class ByteStreamIn; +class LASreadPoint; + +class LASunzipper +{ +public: + bool open(FILE* file, const LASzip* laszip); + bool open(istream& stream, const LASzip* laszip); + + unsigned int tell() const; + bool seek(const unsigned int position); + bool read(unsigned char * const * point); + bool close(); + + LASunzipper(); + ~LASunzipper(); + + // in case a function returns false this string describes the problem + const char* get_error() const; + +private: + unsigned int count; + ByteStreamIn* stream; + LASreadPoint* reader; + bool return_error(const char* err); + char* error_string; +}; + +#endif diff --git a/libs/laszip/src/laswriteitem.hpp b/libs/laszip/src/laswriteitem.hpp new file mode 100644 index 0000000..24fa3ff --- /dev/null +++ b/libs/laszip/src/laswriteitem.hpp @@ -0,0 +1,77 @@ +/* +=============================================================================== + + FILE: laswriteitem.hpp + + CONTENTS: + + Common interface for all classes that write the items that compose a point. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 August 2017 -- moving 'context' from global development hack to interface + 23 August 2016 -- layering of items for selective decompression in LAS 1.4 + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- refactored after watching two movies with silke + +=============================================================================== +*/ +#ifndef LAS_WRITE_ITEM_HPP +#define LAS_WRITE_ITEM_HPP + +#include "mydefs.hpp" + +class ByteStreamOut; + +class LASwriteItem +{ +public: + virtual BOOL write(const U8* item, U32& context)=0; + + virtual ~LASwriteItem(){}; +}; + +class LASwriteItemRaw : public LASwriteItem +{ +public: + LASwriteItemRaw() + { + outstream = 0; + }; + BOOL init(ByteStreamOut* outstream) + { + if (!outstream) return FALSE; + this->outstream = outstream; + return TRUE; + }; + virtual ~LASwriteItemRaw(){}; +protected: + ByteStreamOut* outstream; +}; + +class LASwriteItemCompressed : public LASwriteItem +{ +public: + virtual BOOL init(const U8* item, U32& context)=0; + virtual BOOL chunk_sizes() { return FALSE; }; + virtual BOOL chunk_bytes() { return FALSE; }; + + virtual ~LASwriteItemCompressed(){}; +}; + +#endif diff --git a/libs/laszip/src/laswriteitemcompressed_v1.cpp b/libs/laszip/src/laswriteitemcompressed_v1.cpp new file mode 100644 index 0000000..b6fe873 --- /dev/null +++ b/libs/laszip/src/laswriteitemcompressed_v1.cpp @@ -0,0 +1,638 @@ +/* +=============================================================================== + + FILE: laswriteitemcompressed_v1.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ + +#include "laswriteitemcompressed_v1.hpp" +#include "laszip_common_v1.hpp" + +#include +#include + +/* +=============================================================================== + LASwriteItemCompressed_POINT10_v1 +=============================================================================== +*/ + +struct LASpoint10 +{ + I32 x; + I32 y; + I32 z; + U16 intensity; + U8 return_number : 3; + U8 number_of_returns_of_given_pulse : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 classification; + I8 scan_angle_rank; + U8 user_data; + U16 point_source_ID; +}; + +LASwriteItemCompressed_POINT10_v1::LASwriteItemCompressed_POINT10_v1(ArithmeticEncoder* enc) +{ + U32 i; + + /* set encoder */ + assert(enc); + this->enc = enc; + + /* create models and integer compressors */ + ic_dx = new IntegerCompressor(enc, 32); // 32 bits, 1 context + ic_dy = new IntegerCompressor(enc, 32, 20); // 32 bits, 20 contexts + ic_z = new IntegerCompressor(enc, 32, 20); // 32 bits, 20 contexts + ic_intensity = new IntegerCompressor(enc, 16); + ic_scan_angle_rank = new IntegerCompressor(enc, 8, 2); + ic_point_source_ID = new IntegerCompressor(enc, 16); + m_changed_values = enc->createSymbolModel(64); + for (i = 0; i < 256; i++) + { + m_bit_byte[i] = 0; + m_classification[i] = 0; + m_user_data[i] = 0; + } +} + +LASwriteItemCompressed_POINT10_v1::~LASwriteItemCompressed_POINT10_v1() +{ + U32 i; + delete ic_dx; + delete ic_dy; + delete ic_z; + delete ic_intensity; + delete ic_scan_angle_rank; + delete ic_point_source_ID; + enc->destroySymbolModel(m_changed_values); + for (i = 0; i < 256; i++) + { + if (m_bit_byte[i]) enc->destroySymbolModel(m_bit_byte[i]); + if (m_classification[i]) enc->destroySymbolModel(m_classification[i]); + if (m_user_data[i]) enc->destroySymbolModel(m_user_data[i]); + } +} + +BOOL LASwriteItemCompressed_POINT10_v1::init(const U8* item, U32& context) +{ + U32 i; + + /* init state */ + last_x_diff[0] = last_x_diff[1] = last_x_diff[2] = 0; + last_y_diff[0] = last_y_diff[1] = last_y_diff[2] = 0; + last_incr = 0; + + /* init models and integer compressors */ + ic_dx->initCompressor(); + ic_dy->initCompressor(); + ic_z->initCompressor(); + ic_intensity->initCompressor(); + ic_scan_angle_rank->initCompressor(); + ic_point_source_ID->initCompressor(); + enc->initSymbolModel(m_changed_values); + for (i = 0; i < 256; i++) + { + if (m_bit_byte[i]) enc->initSymbolModel(m_bit_byte[i]); + if (m_classification[i]) enc->initSymbolModel(m_classification[i]); + if (m_user_data[i]) enc->initSymbolModel(m_user_data[i]); + } + + /* init last item */ + memcpy(last_item, item, 20); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_POINT10_v1::write(const U8* item, U32& context) +{ + // find median difference for x and y from 3 preceding differences + I32 median_x; + if (last_x_diff[0] < last_x_diff[1]) + { + if (last_x_diff[1] < last_x_diff[2]) + median_x = last_x_diff[1]; + else if (last_x_diff[0] < last_x_diff[2]) + median_x = last_x_diff[2]; + else + median_x = last_x_diff[0]; + } + else + { + if (last_x_diff[0] < last_x_diff[2]) + median_x = last_x_diff[0]; + else if (last_x_diff[1] < last_x_diff[2]) + median_x = last_x_diff[2]; + else + median_x = last_x_diff[1]; + } + + I32 median_y; + if (last_y_diff[0] < last_y_diff[1]) + { + if (last_y_diff[1] < last_y_diff[2]) + median_y = last_y_diff[1]; + else if (last_y_diff[0] < last_y_diff[2]) + median_y = last_y_diff[2]; + else + median_y = last_y_diff[0]; + } + else + { + if (last_y_diff[0] < last_y_diff[2]) + median_y = last_y_diff[0]; + else if (last_y_diff[1] < last_y_diff[2]) + median_y = last_y_diff[2]; + else + median_y = last_y_diff[1]; + } + + // compress x y z coordinates + I32 x_diff = ((LASpoint10*)item)->x - ((LASpoint10*)last_item)->x; + I32 y_diff = ((LASpoint10*)item)->y - ((LASpoint10*)last_item)->y; + + ic_dx->compress(median_x, x_diff); + // we use the number k of bits corrector bits to switch contexts + U32 k_bits = ic_dx->getK(); + ic_dy->compress(median_y, y_diff, (k_bits < 19 ? k_bits : 19)); + k_bits = (k_bits + ic_dy->getK()) / 2; + ic_z->compress(((LASpoint10*)last_item)->z, ((LASpoint10*)item)->z, (k_bits < 19 ? k_bits : 19)); + + // compress which other values have changed + I32 changed_values = ((((LASpoint10*)last_item)->intensity != ((LASpoint10*)item)->intensity) << 5) | + ((last_item[14] != item[14]) << 4) | // bit_byte + ((last_item[15] != item[15]) << 3) | // classification + ((last_item[16] != item[16]) << 2) | // scan_angle_rank + ((last_item[17] != item[17]) << 1) | // user_data + ((((LASpoint10*)last_item)->point_source_ID != ((LASpoint10*)item)->point_source_ID)); + + enc->encodeSymbol(m_changed_values, changed_values); + + // compress the intensity if it has changed + if (changed_values & 32) + { + ic_intensity->compress(((LASpoint10*)last_item)->intensity, ((LASpoint10*)item)->intensity); + } + + // compress the edge_of_flight_line, scan_direction_flag, ... if it has changed + if (changed_values & 16) + { + if (m_bit_byte[last_item[14]] == 0) + { + m_bit_byte[last_item[14]] = enc->createSymbolModel(256); + enc->initSymbolModel(m_bit_byte[last_item[14]]); + } + enc->encodeSymbol(m_bit_byte[last_item[14]], item[14]); + } + + // compress the classification ... if it has changed + if (changed_values & 8) + { + if (m_classification[last_item[15]] == 0) + { + m_classification[last_item[15]] = enc->createSymbolModel(256); + enc->initSymbolModel(m_classification[last_item[15]]); + } + enc->encodeSymbol(m_classification[last_item[15]], item[15]); + } + + // compress the scan_angle_rank ... if it has changed + if (changed_values & 4) + { + ic_scan_angle_rank->compress(last_item[16], item[16], k_bits < 3); + } + + // compress the user_data ... if it has changed + if (changed_values & 2) + { + if (m_user_data[last_item[17]] == 0) + { + m_user_data[last_item[17]] = enc->createSymbolModel(256); + enc->initSymbolModel(m_user_data[last_item[17]]); + } + enc->encodeSymbol(m_user_data[last_item[17]], item[17]); + } + + // compress the point_source_ID ... if it has changed + if (changed_values & 1) + { + ic_point_source_ID->compress(((LASpoint10*)last_item)->point_source_ID, ((LASpoint10*)item)->point_source_ID); + } + + // record the difference + last_x_diff[last_incr] = x_diff; + last_y_diff[last_incr] = y_diff; + last_incr++; + if (last_incr > 2) last_incr = 0; + + // copy the last item + memcpy(last_item, item, 20); + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_GPSTIME11_v1 +=============================================================================== +*/ + +#define LASZIP_GPSTIME_MULTIMAX 512 + +LASwriteItemCompressed_GPSTIME11_v1::LASwriteItemCompressed_GPSTIME11_v1(ArithmeticEncoder* enc) +{ + /* set encoder */ + assert(enc); + this->enc = enc; + /* create entropy models and integer compressors */ + m_gpstime_multi = enc->createSymbolModel(LASZIP_GPSTIME_MULTIMAX); + m_gpstime_0diff = enc->createSymbolModel(3); + ic_gpstime = new IntegerCompressor(enc, 32, 6); // 32 bits, 6 contexts +} + +LASwriteItemCompressed_GPSTIME11_v1::~LASwriteItemCompressed_GPSTIME11_v1() +{ + enc->destroySymbolModel(m_gpstime_multi); + enc->destroySymbolModel(m_gpstime_0diff); + delete ic_gpstime; +} + +BOOL LASwriteItemCompressed_GPSTIME11_v1::init(const U8* item, U32& context) +{ + /* init state */ + last_gpstime_diff = 0; + multi_extreme_counter = 0; + + /* init models and integer compressors */ + enc->initSymbolModel(m_gpstime_multi); + enc->initSymbolModel(m_gpstime_0diff); + ic_gpstime->initCompressor(); + + /* init last item */ + last_gpstime.u64 = *((U64*)item); + return TRUE; +} + +inline BOOL LASwriteItemCompressed_GPSTIME11_v1::write(const U8* item, U32& context) +{ + U64I64F64 this_gpstime; + this_gpstime.i64 = *((I64*)item); + + if (last_gpstime_diff == 0) // if the last integer difference was zero + { + if (this_gpstime.i64 == last_gpstime.i64) + { + enc->encodeSymbol(m_gpstime_0diff, 0); // the doubles have not changed + } + else + { + // calculate the difference between the two doubles as an integer + I64 curr_gpstime_diff_64 = this_gpstime.i64 - last_gpstime.i64; + I32 curr_gpstime_diff = (I32)curr_gpstime_diff_64; + if (curr_gpstime_diff_64 == (I64)(curr_gpstime_diff)) + { + enc->encodeSymbol(m_gpstime_0diff, 1); // the difference can be represented with 32 bits + ic_gpstime->compress(0, curr_gpstime_diff, 0); + last_gpstime_diff = curr_gpstime_diff; + } + else + { + enc->encodeSymbol(m_gpstime_0diff, 2); // the difference is huge + enc->writeInt64(this_gpstime.u64); + } + last_gpstime.i64 = this_gpstime.i64; + } + } + else // the last integer difference was *not* zero + { + if (this_gpstime.i64 == last_gpstime.i64) + { + // if the doubles have not changed use a special symbol + enc->encodeSymbol(m_gpstime_multi, LASZIP_GPSTIME_MULTIMAX-1); + } + else + { + // calculate the difference between the two doubles as an integer + I64 curr_gpstime_diff_64 = this_gpstime.i64 - last_gpstime.i64; + I32 curr_gpstime_diff = (I32)curr_gpstime_diff_64; + // if the current gpstime difference can be represented with 32 bits + if (curr_gpstime_diff_64 == (I64)(curr_gpstime_diff)) + { + // compute multiplier between current and last integer difference + I32 multi = (I32)(((F32)curr_gpstime_diff / (F32)last_gpstime_diff) + 0.5f); + + // limit the multiplier into some bounds + if (multi >= LASZIP_GPSTIME_MULTIMAX-3) + { + multi = LASZIP_GPSTIME_MULTIMAX-3; + } + else if (multi <= 0) + { + multi = 0; + } + // compress this multiplier + enc->encodeSymbol(m_gpstime_multi, multi); + // compress the residual curr_gpstime_diff in dependance on the multiplier + if (multi == 1) + { + // this is the case we assume we get most often + ic_gpstime->compress(last_gpstime_diff, curr_gpstime_diff, 1); + last_gpstime_diff = curr_gpstime_diff; + multi_extreme_counter = 0; + } + else + { + if (multi == 0) + { + ic_gpstime->compress(last_gpstime_diff/4, curr_gpstime_diff, 2); + multi_extreme_counter++; + if (multi_extreme_counter > 3) + { + last_gpstime_diff = curr_gpstime_diff; + multi_extreme_counter = 0; + } + } + else if (multi < 10) + { + ic_gpstime->compress(multi*last_gpstime_diff, curr_gpstime_diff, 3); + } + else if (multi < 50) + { + ic_gpstime->compress(multi*last_gpstime_diff, curr_gpstime_diff, 4); + } + else + { + ic_gpstime->compress(multi*last_gpstime_diff, curr_gpstime_diff, 5); + if (multi == LASZIP_GPSTIME_MULTIMAX-3) + { + multi_extreme_counter++; + if (multi_extreme_counter > 3) + { + last_gpstime_diff = curr_gpstime_diff; + multi_extreme_counter = 0; + } + } + } + } + } + else + { + // if difference is so huge ... we simply write the double + enc->encodeSymbol(m_gpstime_multi, LASZIP_GPSTIME_MULTIMAX-2); + enc->writeInt64(this_gpstime.u64); + } + last_gpstime.i64 = this_gpstime.i64; + } + } + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_RGB12_v1 +=============================================================================== +*/ + +LASwriteItemCompressed_RGB12_v1::LASwriteItemCompressed_RGB12_v1(ArithmeticEncoder* enc) +{ + /* set encoder */ + assert(enc); + this->enc = enc; + + /* create models and integer compressors */ + m_byte_used = enc->createSymbolModel(64); + ic_rgb = new IntegerCompressor(enc, 8, 6); + + /* create last item */ + last_item = new U8[6]; +} + +LASwriteItemCompressed_RGB12_v1::~LASwriteItemCompressed_RGB12_v1() +{ + enc->destroySymbolModel(m_byte_used); + delete ic_rgb; + delete [] last_item; +} + +BOOL LASwriteItemCompressed_RGB12_v1::init(const U8* item, U32& context) +{ + /* init state */ + + /* init models and integer compressors */ + enc->initSymbolModel(m_byte_used); + ic_rgb->initCompressor(); + + /* init last item */ + memcpy(last_item, item, 6); + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGB12_v1::write(const U8* item, U32& context) +{ + U32 sym = ((((U16*)last_item)[0]&0x00FF) != (((U16*)item)[0]&0x00FF)) << 0; + sym |= ((((U16*)last_item)[0]&0xFF00) != (((U16*)item)[0]&0xFF00)) << 1; + sym |= ((((U16*)last_item)[1]&0x00FF) != (((U16*)item)[1]&0x00FF)) << 2; + sym |= ((((U16*)last_item)[1]&0xFF00) != (((U16*)item)[1]&0xFF00)) << 3; + sym |= ((((U16*)last_item)[2]&0x00FF) != (((U16*)item)[2]&0x00FF)) << 4; + sym |= ((((U16*)last_item)[2]&0xFF00) != (((U16*)item)[2]&0xFF00)) << 5; + enc->encodeSymbol(m_byte_used, sym); + if (sym & (1 << 0)) ic_rgb->compress(((U16*)last_item)[0]&255,((U16*)item)[0]&255, 0); + if (sym & (1 << 1)) ic_rgb->compress(((U16*)last_item)[0]>>8,((U16*)item)[0]>>8, 1); + if (sym & (1 << 2)) ic_rgb->compress(((U16*)last_item)[1]&255,((U16*)item)[1]&255, 2); + if (sym & (1 << 3)) ic_rgb->compress(((U16*)last_item)[1]>>8,((U16*)item)[1]>>8, 3); + if (sym & (1 << 4)) ic_rgb->compress(((U16*)last_item)[2]&255,((U16*)item)[2]&255, 4); + if (sym & (1 << 5)) ic_rgb->compress(((U16*)last_item)[2]>>8,((U16*)item)[2]>>8, 5); + memcpy(last_item, item, 6); + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_WAVEPACKET13_v1 +=============================================================================== +*/ + +LASwriteItemCompressed_WAVEPACKET13_v1::LASwriteItemCompressed_WAVEPACKET13_v1(ArithmeticEncoder* enc) +{ + /* set encoder */ + assert(enc); + this->enc = enc; + + /* create models and integer compressors */ + m_packet_index = enc->createSymbolModel(256); + m_offset_diff[0] = enc->createSymbolModel(4); + m_offset_diff[1] = enc->createSymbolModel(4); + m_offset_diff[2] = enc->createSymbolModel(4); + m_offset_diff[3] = enc->createSymbolModel(4); + ic_offset_diff = new IntegerCompressor(enc, 32); + ic_packet_size = new IntegerCompressor(enc, 32); + ic_return_point = new IntegerCompressor(enc, 32); + ic_xyz = new IntegerCompressor(enc, 32, 3); + + /* create last item */ + last_item = new U8[28]; +} + +LASwriteItemCompressed_WAVEPACKET13_v1::~LASwriteItemCompressed_WAVEPACKET13_v1() +{ + enc->destroySymbolModel(m_packet_index); + enc->destroySymbolModel(m_offset_diff[0]); + enc->destroySymbolModel(m_offset_diff[1]); + enc->destroySymbolModel(m_offset_diff[2]); + enc->destroySymbolModel(m_offset_diff[3]); + delete ic_offset_diff; + delete ic_packet_size; + delete ic_return_point; + delete ic_xyz; + delete [] last_item; +} + +BOOL LASwriteItemCompressed_WAVEPACKET13_v1::init(const U8* item, U32& context) +{ + /* init state */ + last_diff_32 = 0; + sym_last_offset_diff = 0; + + /* init models and integer compressors */ + enc->initSymbolModel(m_packet_index); + enc->initSymbolModel(m_offset_diff[0]); + enc->initSymbolModel(m_offset_diff[1]); + enc->initSymbolModel(m_offset_diff[2]); + enc->initSymbolModel(m_offset_diff[3]); + ic_offset_diff->initCompressor(); + ic_packet_size->initCompressor(); + ic_return_point->initCompressor(); + ic_xyz->initCompressor(); + + /* init last item */ + item++; + memcpy(last_item, item, 28); + return TRUE; +} + +inline BOOL LASwriteItemCompressed_WAVEPACKET13_v1::write(const U8* item, U32& context) +{ + enc->encodeSymbol(m_packet_index, (U32)(item[0])); + item++; + + LASwavepacket13 this_item_m = LASwavepacket13::unpack(item); + LASwavepacket13 last_item_m = LASwavepacket13::unpack(last_item); + + // calculate the difference between the two offsets + I64 curr_diff_64 = this_item_m.offset - last_item_m.offset; + I32 curr_diff_32 = (I32)curr_diff_64; + + // if the current difference can be represented with 32 bits + if (curr_diff_64 == (I64)(curr_diff_32)) + { + if (curr_diff_32 == 0) // current difference is zero + { + enc->encodeSymbol(m_offset_diff[sym_last_offset_diff], 0); + sym_last_offset_diff = 0; + } + else if (curr_diff_32 == (I32)last_item_m.packet_size) + { + enc->encodeSymbol(m_offset_diff[sym_last_offset_diff], 1); + sym_last_offset_diff = 1; + } + else // + { + enc->encodeSymbol(m_offset_diff[sym_last_offset_diff], 2); + sym_last_offset_diff = 2; + ic_offset_diff->compress(last_diff_32, curr_diff_32); + last_diff_32 = curr_diff_32; + } + } + else + { + enc->encodeSymbol(m_offset_diff[sym_last_offset_diff], 3); + sym_last_offset_diff = 3; + + enc->writeInt64(this_item_m.offset); + } + + ic_packet_size->compress(last_item_m.packet_size, this_item_m.packet_size); + ic_return_point->compress(last_item_m.return_point.i32, this_item_m.return_point.i32); + ic_xyz->compress(last_item_m.x.i32, this_item_m.x.i32, 0); + ic_xyz->compress(last_item_m.y.i32, this_item_m.y.i32, 1); + ic_xyz->compress(last_item_m.z.i32, this_item_m.z.i32, 2); + + memcpy(last_item, item, 28); + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_BYTE_v1 +=============================================================================== +*/ + +LASwriteItemCompressed_BYTE_v1::LASwriteItemCompressed_BYTE_v1(ArithmeticEncoder* enc, U32 number) +{ + /* set encoder */ + assert(enc); + this->enc = enc; + assert(number); + this->number = number; + + /* create models and integer compressors */ + ic_byte = new IntegerCompressor(enc, 8, number); + + /* create last item */ + last_item = new U8[number]; +} + +LASwriteItemCompressed_BYTE_v1::~LASwriteItemCompressed_BYTE_v1() +{ + delete ic_byte; + delete [] last_item; +} + +BOOL LASwriteItemCompressed_BYTE_v1::init(const U8* item, U32& context) +{ + /* init state */ + + /* init models and integer compressors */ + ic_byte->initCompressor(); + + /* init last point */ + memcpy(last_item, item, number); + return TRUE; +} + +inline BOOL LASwriteItemCompressed_BYTE_v1::write(const U8* item, U32& context) +{ + U32 i; + for (i = 0; i < number; i++) + { + ic_byte->compress(last_item[i], item[i], i); + } + memcpy(last_item, item, number); + return TRUE; +} + +// vim: set ts=2 sw=2 expandtabs diff --git a/libs/laszip/src/laswriteitemcompressed_v1.hpp b/libs/laszip/src/laswriteitemcompressed_v1.hpp new file mode 100644 index 0000000..5db93b2 --- /dev/null +++ b/libs/laszip/src/laswriteitemcompressed_v1.hpp @@ -0,0 +1,156 @@ +/* +=============================================================================== + + FILE: laswriteitemcompressed_v1.hpp + + CONTENTS: + + Implementation of LASitemWriteCompressed for *all* items (version 1). + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 August 2017 -- moving 'context' from global development hack to interface + 6 September 2014 -- removed inheritance of EntropyEncoder and EntropyDecoder + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- refactored after watching two movies with silke + +=============================================================================== +*/ +#ifndef LAS_WRITE_ITEM_COMPRESSED_V1_HPP +#define LAS_WRITE_ITEM_COMPRESSED_V1_HPP + +#include "laswriteitem.hpp" +#include "arithmeticencoder.hpp" +#include "integercompressor.hpp" + +class LASwriteItemCompressed_POINT10_v1 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_POINT10_v1(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + + ~LASwriteItemCompressed_POINT10_v1(); + +private: + ArithmeticEncoder* enc; + U8 last_item[20]; + + I32 last_x_diff[3]; + I32 last_y_diff[3]; + I32 last_incr; + IntegerCompressor* ic_dx; + IntegerCompressor* ic_dy; + IntegerCompressor* ic_z; + IntegerCompressor* ic_intensity; + IntegerCompressor* ic_scan_angle_rank; + IntegerCompressor* ic_point_source_ID; + ArithmeticModel* m_changed_values; + ArithmeticModel* m_bit_byte[256]; + ArithmeticModel* m_classification[256]; + ArithmeticModel* m_user_data[256]; +}; + +class LASwriteItemCompressed_GPSTIME11_v1 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_GPSTIME11_v1(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + + ~LASwriteItemCompressed_GPSTIME11_v1(); + +private: + ArithmeticEncoder* enc; + U64I64F64 last_gpstime; + + ArithmeticModel* m_gpstime_multi; + ArithmeticModel* m_gpstime_0diff; + IntegerCompressor* ic_gpstime; + I32 multi_extreme_counter; + I32 last_gpstime_diff; +}; + +class LASwriteItemCompressed_RGB12_v1 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_RGB12_v1(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + + ~LASwriteItemCompressed_RGB12_v1(); + +private: + ArithmeticEncoder* enc; + U8* last_item; + + ArithmeticModel* m_byte_used; + IntegerCompressor* ic_rgb; +}; + +class LASwriteItemCompressed_WAVEPACKET13_v1 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_WAVEPACKET13_v1(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + + ~LASwriteItemCompressed_WAVEPACKET13_v1(); + +private: + ArithmeticEncoder* enc; + U8* last_item; + + I32 last_diff_32; + U32 sym_last_offset_diff; + ArithmeticModel* m_packet_index; + ArithmeticModel* m_offset_diff[4]; + IntegerCompressor* ic_offset_diff; + IntegerCompressor* ic_packet_size; + IntegerCompressor* ic_return_point; + IntegerCompressor* ic_xyz; +}; + +class LASwriteItemCompressed_BYTE_v1 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_BYTE_v1(ArithmeticEncoder* enc, U32 number); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + + ~LASwriteItemCompressed_BYTE_v1(); + +private: + ArithmeticEncoder* enc; + U32 number; + U8* last_item; + + IntegerCompressor* ic_byte; +}; + +#endif diff --git a/libs/laszip/src/laswriteitemcompressed_v2.cpp b/libs/laszip/src/laswriteitemcompressed_v2.cpp new file mode 100644 index 0000000..1aa6a83 --- /dev/null +++ b/libs/laszip/src/laswriteitemcompressed_v2.cpp @@ -0,0 +1,621 @@ +/* +=============================================================================== + + FILE: laswriteitemcompressed_v2.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ + +#include "laswriteitemcompressed_v2.hpp" + +#include +#include + +/* +=============================================================================== + LASwriteItemCompressed_POINT10_v2 +=============================================================================== +*/ + +struct LASpoint10 +{ + I32 x; + I32 y; + I32 z; + U16 intensity; + U8 return_number : 3; + U8 number_of_returns_of_given_pulse : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 classification; + I8 scan_angle_rank; + U8 user_data; + U16 point_source_ID; +}; + +LASwriteItemCompressed_POINT10_v2::LASwriteItemCompressed_POINT10_v2(ArithmeticEncoder* enc) +{ + U32 i; + + /* set encoder */ + assert(enc); + this->enc = enc; + + /* create models and integer compressors */ + m_changed_values = enc->createSymbolModel(64); + ic_intensity = new IntegerCompressor(enc, 16, 4); + m_scan_angle_rank[0] = enc->createSymbolModel(256); + m_scan_angle_rank[1] = enc->createSymbolModel(256); + ic_point_source_ID = new IntegerCompressor(enc, 16); + for (i = 0; i < 256; i++) + { + m_bit_byte[i] = 0; + m_classification[i] = 0; + m_user_data[i] = 0; + } + ic_dx = new IntegerCompressor(enc, 32, 2); // 32 bits, 2 context + ic_dy = new IntegerCompressor(enc, 32, 22); // 32 bits, 22 contexts + ic_z = new IntegerCompressor(enc, 32, 20); // 32 bits, 20 contexts +} + +LASwriteItemCompressed_POINT10_v2::~LASwriteItemCompressed_POINT10_v2() +{ + U32 i; + + enc->destroySymbolModel(m_changed_values); + delete ic_intensity; + enc->destroySymbolModel(m_scan_angle_rank[0]); + enc->destroySymbolModel(m_scan_angle_rank[1]); + delete ic_point_source_ID; + for (i = 0; i < 256; i++) + { + if (m_bit_byte[i]) enc->destroySymbolModel(m_bit_byte[i]); + if (m_classification[i]) enc->destroySymbolModel(m_classification[i]); + if (m_user_data[i]) enc->destroySymbolModel(m_user_data[i]); + } + delete ic_dx; + delete ic_dy; + delete ic_z; +} + +BOOL LASwriteItemCompressed_POINT10_v2::init(const U8* item, U32& context) +{ + U32 i; + + /* init state */ + for (i=0; i < 16; i++) + { + last_x_diff_median5[i].init(); + last_y_diff_median5[i].init(); + last_intensity[i] = 0; + last_height[i/2] = 0; + } + + /* init models and integer compressors */ + enc->initSymbolModel(m_changed_values); + ic_intensity->initCompressor(); + enc->initSymbolModel(m_scan_angle_rank[0]); + enc->initSymbolModel(m_scan_angle_rank[1]); + ic_point_source_ID->initCompressor(); + for (i = 0; i < 256; i++) + { + if (m_bit_byte[i]) enc->initSymbolModel(m_bit_byte[i]); + if (m_classification[i]) enc->initSymbolModel(m_classification[i]); + if (m_user_data[i]) enc->initSymbolModel(m_user_data[i]); + } + ic_dx->initCompressor(); + ic_dy->initCompressor(); + ic_z->initCompressor(); + + /* init last item */ + memcpy(last_item, item, 20); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_POINT10_v2::write(const U8* item, U32& context) +{ + U32 r = ((LASpoint10*)item)->return_number; + U32 n = ((LASpoint10*)item)->number_of_returns_of_given_pulse; + U32 m = number_return_map[n][r]; + U32 l = number_return_level[n][r]; + U32 k_bits; + I32 median, diff; + + // compress which other values have changed + I32 changed_values = (((last_item[14] != item[14]) << 5) | // bit_byte + ((last_intensity[m] != ((LASpoint10*)item)->intensity) << 4) | + ((last_item[15] != item[15]) << 3) | // classification + ((last_item[16] != item[16]) << 2) | // scan_angle_rank + ((last_item[17] != item[17]) << 1) | // user_data + (((LASpoint10*)last_item)->point_source_ID != ((LASpoint10*)item)->point_source_ID)); + + enc->encodeSymbol(m_changed_values, changed_values); + + // compress the bit_byte (edge_of_flight_line, scan_direction_flag, returns, ...) if it has changed + if (changed_values & 32) + { + if (m_bit_byte[last_item[14]] == 0) + { + m_bit_byte[last_item[14]] = enc->createSymbolModel(256); + enc->initSymbolModel(m_bit_byte[last_item[14]]); + } + enc->encodeSymbol(m_bit_byte[last_item[14]], item[14]); + } + + // compress the intensity if it has changed + if (changed_values & 16) + { + ic_intensity->compress(last_intensity[m], ((LASpoint10*)item)->intensity, (m < 3 ? m : 3)); + last_intensity[m] = ((LASpoint10*)item)->intensity; + } + + // compress the classification ... if it has changed + if (changed_values & 8) + { + if (m_classification[last_item[15]] == 0) + { + m_classification[last_item[15]] = enc->createSymbolModel(256); + enc->initSymbolModel(m_classification[last_item[15]]); + } + enc->encodeSymbol(m_classification[last_item[15]], item[15]); + } + + // compress the scan_angle_rank ... if it has changed + if (changed_values & 4) + { + enc->encodeSymbol(m_scan_angle_rank[((LASpoint10*)item)->scan_direction_flag], U8_FOLD(item[16]-last_item[16])); + } + + // compress the user_data ... if it has changed + if (changed_values & 2) + { + if (m_user_data[last_item[17]] == 0) + { + m_user_data[last_item[17]] = enc->createSymbolModel(256); + enc->initSymbolModel(m_user_data[last_item[17]]); + } + enc->encodeSymbol(m_user_data[last_item[17]], item[17]); + } + + // compress the point_source_ID ... if it has changed + if (changed_values & 1) + { + ic_point_source_ID->compress(((LASpoint10*)last_item)->point_source_ID, ((LASpoint10*)item)->point_source_ID); + } + + // compress x coordinate + median = last_x_diff_median5[m].get(); + diff = ((LASpoint10*)item)->x - ((LASpoint10*)last_item)->x; + ic_dx->compress(median, diff, n==1); + last_x_diff_median5[m].add(diff); + + // compress y coordinate + k_bits = ic_dx->getK(); + median = last_y_diff_median5[m].get(); + diff = ((LASpoint10*)item)->y - ((LASpoint10*)last_item)->y; + ic_dy->compress(median, diff, (n==1) + ( k_bits < 20 ? U32_ZERO_BIT_0(k_bits) : 20 )); + last_y_diff_median5[m].add(diff); + + // compress z coordinate + k_bits = (ic_dx->getK() + ic_dy->getK()) / 2; + ic_z->compress(last_height[l], ((LASpoint10*)item)->z, (n==1) + (k_bits < 18 ? U32_ZERO_BIT_0(k_bits) : 18)); + last_height[l] = ((LASpoint10*)item)->z; + + // copy the last item + memcpy(last_item, item, 20); + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_GPSTIME11_v2 +=============================================================================== +*/ + +#define LASZIP_GPSTIME_MULTI 500 +#define LASZIP_GPSTIME_MULTI_MINUS -10 +#define LASZIP_GPSTIME_MULTI_UNCHANGED (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 1) +#define LASZIP_GPSTIME_MULTI_CODE_FULL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 2) + +#define LASZIP_GPSTIME_MULTI_TOTAL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 6) + +LASwriteItemCompressed_GPSTIME11_v2::LASwriteItemCompressed_GPSTIME11_v2(ArithmeticEncoder* enc) +{ + /* set encoder */ + assert(enc); + this->enc = enc; + /* create entropy models and integer compressors */ + m_gpstime_multi = enc->createSymbolModel(LASZIP_GPSTIME_MULTI_TOTAL); + m_gpstime_0diff = enc->createSymbolModel(6); + ic_gpstime = new IntegerCompressor(enc, 32, 9); // 32 bits, 9 contexts +} + +LASwriteItemCompressed_GPSTIME11_v2::~LASwriteItemCompressed_GPSTIME11_v2() +{ + enc->destroySymbolModel(m_gpstime_multi); + enc->destroySymbolModel(m_gpstime_0diff); + delete ic_gpstime; +} + +BOOL LASwriteItemCompressed_GPSTIME11_v2::init(const U8* item, U32& context) +{ + /* init state */ + last = 0, next = 0; + last_gpstime_diff[0] = 0; + last_gpstime_diff[1] = 0; + last_gpstime_diff[2] = 0; + last_gpstime_diff[3] = 0; + multi_extreme_counter[0] = 0; + multi_extreme_counter[1] = 0; + multi_extreme_counter[2] = 0; + multi_extreme_counter[3] = 0; + + /* init models and integer compressors */ + enc->initSymbolModel(m_gpstime_multi); + enc->initSymbolModel(m_gpstime_0diff); + ic_gpstime->initCompressor(); + + /* init last item */ + last_gpstime[0].u64 = *((U64*)item); + last_gpstime[1].u64 = 0; + last_gpstime[2].u64 = 0; + last_gpstime[3].u64 = 0; + return TRUE; +} + +inline BOOL LASwriteItemCompressed_GPSTIME11_v2::write(const U8* item, U32& context) +{ + U64I64F64 this_gpstime; + this_gpstime.i64 = *((I64*)item); + + if (last_gpstime_diff[last] == 0) // if the last integer difference was zero + { + if (this_gpstime.i64 == last_gpstime[last].i64) + { + enc->encodeSymbol(m_gpstime_0diff, 0); // the doubles have not changed + } + else + { + // calculate the difference between the two doubles as an integer + I64 curr_gpstime_diff_64 = this_gpstime.i64 - last_gpstime[last].i64; + I32 curr_gpstime_diff = (I32)curr_gpstime_diff_64; + if (curr_gpstime_diff_64 == (I64)(curr_gpstime_diff)) + { + enc->encodeSymbol(m_gpstime_0diff, 1); // the difference can be represented with 32 bits + ic_gpstime->compress(0, curr_gpstime_diff, 0); + last_gpstime_diff[last] = curr_gpstime_diff; + multi_extreme_counter[last] = 0; + } + else // the difference is huge + { + U32 i; + // maybe the double belongs to another time sequence + for (i = 1; i < 4; i++) + { + I64 other_gpstime_diff_64 = this_gpstime.i64 - last_gpstime[(last+i)&3].i64; + I32 other_gpstime_diff = (I32)other_gpstime_diff_64; + if (other_gpstime_diff_64 == (I64)(other_gpstime_diff)) + { + enc->encodeSymbol(m_gpstime_0diff, i+2); // it belongs to another sequence + last = (last+i)&3; + return write(item, context); + } + } + // no other sequence found. start new sequence. + enc->encodeSymbol(m_gpstime_0diff, 2); + ic_gpstime->compress((I32)(last_gpstime[last].u64 >> 32), (I32)(this_gpstime.u64 >> 32), 8); + enc->writeInt((U32)(this_gpstime.u64)); + next = (next+1)&3; + last = next; + last_gpstime_diff[last] = 0; + multi_extreme_counter[last] = 0; + } + last_gpstime[last].i64 = this_gpstime.i64; + } + } + else // the last integer difference was *not* zero + { + if (this_gpstime.i64 == last_gpstime[last].i64) + { + // if the doubles have not changed use a special symbol + enc->encodeSymbol(m_gpstime_multi, LASZIP_GPSTIME_MULTI_UNCHANGED); + } + else + { + // calculate the difference between the two doubles as an integer + I64 curr_gpstime_diff_64 = this_gpstime.i64 - last_gpstime[last].i64; + I32 curr_gpstime_diff = (I32)curr_gpstime_diff_64; + + // if the current gpstime difference can be represented with 32 bits + if (curr_gpstime_diff_64 == (I64)(curr_gpstime_diff)) + { + // compute multiplier between current and last integer difference + F32 multi_f = (F32)curr_gpstime_diff / (F32)(last_gpstime_diff[last]); + I32 multi = I32_QUANTIZE(multi_f); + + // compress the residual curr_gpstime_diff in dependance on the multiplier + if (multi == 1) + { + // this is the case we assume we get most often for regular spaced pulses + enc->encodeSymbol(m_gpstime_multi, 1); + ic_gpstime->compress(last_gpstime_diff[last], curr_gpstime_diff, 1); + multi_extreme_counter[last] = 0; + } + else if (multi > 0) + { + if (multi < LASZIP_GPSTIME_MULTI) // positive multipliers up to LASZIP_GPSTIME_MULTI are compressed directly + { + enc->encodeSymbol(m_gpstime_multi, multi); + if (multi < 10) + ic_gpstime->compress(multi*last_gpstime_diff[last], curr_gpstime_diff, 2); + else + ic_gpstime->compress(multi*last_gpstime_diff[last], curr_gpstime_diff, 3); + } + else + { + enc->encodeSymbol(m_gpstime_multi, LASZIP_GPSTIME_MULTI); + ic_gpstime->compress(LASZIP_GPSTIME_MULTI*last_gpstime_diff[last], curr_gpstime_diff, 4); + multi_extreme_counter[last]++; + if (multi_extreme_counter[last] > 3) + { + last_gpstime_diff[last] = curr_gpstime_diff; + multi_extreme_counter[last] = 0; + } + } + } + else if (multi < 0) + { + if (multi > LASZIP_GPSTIME_MULTI_MINUS) // negative multipliers larger than LASZIP_GPSTIME_MULTI_MINUS are compressed directly + { + enc->encodeSymbol(m_gpstime_multi, LASZIP_GPSTIME_MULTI - multi); + ic_gpstime->compress(multi*last_gpstime_diff[last], curr_gpstime_diff, 5); + } + else + { + enc->encodeSymbol(m_gpstime_multi, LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS); + ic_gpstime->compress(LASZIP_GPSTIME_MULTI_MINUS*last_gpstime_diff[last], curr_gpstime_diff, 6); + multi_extreme_counter[last]++; + if (multi_extreme_counter[last] > 3) + { + last_gpstime_diff[last] = curr_gpstime_diff; + multi_extreme_counter[last] = 0; + } + } + } + else + { + enc->encodeSymbol(m_gpstime_multi, 0); + ic_gpstime->compress(0, curr_gpstime_diff, 7); + multi_extreme_counter[last]++; + if (multi_extreme_counter[last] > 3) + { + last_gpstime_diff[last] = curr_gpstime_diff; + multi_extreme_counter[last] = 0; + } + } + } + else // the difference is huge + { + U32 i; + // maybe the double belongs to another time sequence + for (i = 1; i < 4; i++) + { + I64 other_gpstime_diff_64 = this_gpstime.i64 - last_gpstime[(last+i)&3].i64; + I32 other_gpstime_diff = (I32)other_gpstime_diff_64; + if (other_gpstime_diff_64 == (I64)(other_gpstime_diff)) + { + // it belongs to this sequence + enc->encodeSymbol(m_gpstime_multi, LASZIP_GPSTIME_MULTI_CODE_FULL+i); + last = (last+i)&3; + return write(item, context); + } + } + // no other sequence found. start new sequence. + enc->encodeSymbol(m_gpstime_multi, LASZIP_GPSTIME_MULTI_CODE_FULL); + ic_gpstime->compress((I32)(last_gpstime[last].u64 >> 32), (I32)(this_gpstime.u64 >> 32), 8); + enc->writeInt((U32)(this_gpstime.u64)); + next = (next+1)&3; + last = next; + last_gpstime_diff[last] = 0; + multi_extreme_counter[last] = 0; + } + last_gpstime[last].i64 = this_gpstime.i64; + } + } + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_RGB12_v2 +=============================================================================== +*/ + +LASwriteItemCompressed_RGB12_v2::LASwriteItemCompressed_RGB12_v2(ArithmeticEncoder* enc) +{ + /* set encoder */ + assert(enc); + this->enc = enc; + + /* create models and integer compressors */ + m_byte_used = enc->createSymbolModel(128); + m_rgb_diff_0 = enc->createSymbolModel(256); + m_rgb_diff_1 = enc->createSymbolModel(256); + m_rgb_diff_2 = enc->createSymbolModel(256); + m_rgb_diff_3 = enc->createSymbolModel(256); + m_rgb_diff_4 = enc->createSymbolModel(256); + m_rgb_diff_5 = enc->createSymbolModel(256); +} + +LASwriteItemCompressed_RGB12_v2::~LASwriteItemCompressed_RGB12_v2() +{ + enc->destroySymbolModel(m_byte_used); + enc->destroySymbolModel(m_rgb_diff_0); + enc->destroySymbolModel(m_rgb_diff_1); + enc->destroySymbolModel(m_rgb_diff_2); + enc->destroySymbolModel(m_rgb_diff_3); + enc->destroySymbolModel(m_rgb_diff_4); + enc->destroySymbolModel(m_rgb_diff_5); +} + +BOOL LASwriteItemCompressed_RGB12_v2::init(const U8* item, U32& context) +{ + /* init state */ + + /* init models and integer compressors */ + enc->initSymbolModel(m_byte_used); + enc->initSymbolModel(m_rgb_diff_0); + enc->initSymbolModel(m_rgb_diff_1); + enc->initSymbolModel(m_rgb_diff_2); + enc->initSymbolModel(m_rgb_diff_3); + enc->initSymbolModel(m_rgb_diff_4); + enc->initSymbolModel(m_rgb_diff_5); + + /* init last item */ + memcpy(last_item, item, 6); + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGB12_v2::write(const U8* item, U32& context) +{ + I32 diff_l = 0; + I32 diff_h = 0; + I32 corr; + U32 sym = ((last_item[0]&0x00FF) != (((U16*)item)[0]&0x00FF)) << 0; + sym |= ((last_item[0]&0xFF00) != (((U16*)item)[0]&0xFF00)) << 1; + sym |= ((last_item[1]&0x00FF) != (((U16*)item)[1]&0x00FF)) << 2; + sym |= ((last_item[1]&0xFF00) != (((U16*)item)[1]&0xFF00)) << 3; + sym |= ((last_item[2]&0x00FF) != (((U16*)item)[2]&0x00FF)) << 4; + sym |= ((last_item[2]&0xFF00) != (((U16*)item)[2]&0xFF00)) << 5; + sym |= (((((U16*)item)[0]&0x00FF) != (((U16*)item)[1]&0x00FF)) || ((((U16*)item)[0]&0x00FF) != (((U16*)item)[2]&0x00FF)) || ((((U16*)item)[0]&0xFF00) != (((U16*)item)[1]&0xFF00)) || ((((U16*)item)[0]&0xFF00) != (((U16*)item)[2]&0xFF00))) << 6; + enc->encodeSymbol(m_byte_used, sym); + if (sym & (1 << 0)) + { + diff_l = ((int)(((U16*)item)[0]&255)) - (last_item[0]&255); + enc->encodeSymbol(m_rgb_diff_0, U8_FOLD(diff_l)); + } + if (sym & (1 << 1)) + { + diff_h = ((int)(((U16*)item)[0]>>8)) - (last_item[0]>>8); + enc->encodeSymbol(m_rgb_diff_1, U8_FOLD(diff_h)); + } + if (sym & (1 << 6)) + { + if (sym & (1 << 2)) + { + corr = ((int)(((U16*)item)[1]&255)) - U8_CLAMP(diff_l + (last_item[1]&255)); + enc->encodeSymbol(m_rgb_diff_2, U8_FOLD(corr)); + } + if (sym & (1 << 4)) + { + diff_l = (diff_l + (((U16*)item)[1]&255) - (last_item[1]&255)) / 2; + corr = ((int)(((U16*)item)[2]&255)) - U8_CLAMP(diff_l + (last_item[2]&255)); + enc->encodeSymbol(m_rgb_diff_4, U8_FOLD(corr)); + } + if (sym & (1 << 3)) + { + corr = ((int)(((U16*)item)[1]>>8)) - U8_CLAMP(diff_h + (last_item[1]>>8)); + enc->encodeSymbol(m_rgb_diff_3, U8_FOLD(corr)); + } + if (sym & (1 << 5)) + { + diff_h = (diff_h + (((U16*)item)[1]>>8) - (last_item[1]>>8)) / 2; + corr = ((int)(((U16*)item)[2]>>8)) - U8_CLAMP(diff_h + (last_item[2]>>8)); + enc->encodeSymbol(m_rgb_diff_5, U8_FOLD(corr)); + } + } + memcpy(last_item, item, 6); + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_BYTE_v2 +=============================================================================== +*/ + +LASwriteItemCompressed_BYTE_v2::LASwriteItemCompressed_BYTE_v2(ArithmeticEncoder* enc, U32 number) +{ + U32 i; + + /* set encoder */ + assert(enc); + this->enc = enc; + assert(number); + this->number = number; + + /* create models and integer compressors */ + m_byte = new ArithmeticModel*[number]; + for (i = 0; i < number; i++) + { + m_byte[i] = enc->createSymbolModel(256); + } + + /* create last item */ + last_item = new U8[number]; +} + +LASwriteItemCompressed_BYTE_v2::~LASwriteItemCompressed_BYTE_v2() +{ + U32 i; + for (i = 0; i < number; i++) + { + enc->destroySymbolModel(m_byte[i]); + } + delete [] m_byte; + delete [] last_item; +} + +BOOL LASwriteItemCompressed_BYTE_v2::init(const U8* item, U32& context) +{ + U32 i; + /* init state */ + + /* init models and integer compressors */ + for (i = 0; i < number; i++) + { + enc->initSymbolModel(m_byte[i]); + } + + /* init last point */ + memcpy(last_item, item, number); + return TRUE; +} + +inline BOOL LASwriteItemCompressed_BYTE_v2::write(const U8* item, U32& context) +{ + U32 i; + I32 diff; + for (i = 0; i < number; i++) + { + diff = item[i] - last_item[i]; + enc->encodeSymbol(m_byte[i], U8_FOLD(diff)); + } + memcpy(last_item, item, number); + return TRUE; +} + diff --git a/libs/laszip/src/laswriteitemcompressed_v2.hpp b/libs/laszip/src/laswriteitemcompressed_v2.hpp new file mode 100644 index 0000000..aebc300 --- /dev/null +++ b/libs/laszip/src/laswriteitemcompressed_v2.hpp @@ -0,0 +1,139 @@ +/* +=============================================================================== + + FILE: laswriteitemcompressed_v2.hpp + + CONTENTS: + + Implementation of LASitemWriteCompressed for *all* items (version 2). + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 August 2017 -- moving 'context' from global development hack to interface + 6 September 2014 -- removed inheritance of EntropyEncoder and EntropyDecoder + 5 March 2011 -- created first night in ibiza to improve the RGB compressor + +=============================================================================== +*/ +#ifndef LAS_WRITE_ITEM_COMPRESSED_V2_HPP +#define LAS_WRITE_ITEM_COMPRESSED_V2_HPP + +#include "laswriteitem.hpp" +#include "arithmeticencoder.hpp" +#include "integercompressor.hpp" + +#include "laszip_common_v2.hpp" + +class LASwriteItemCompressed_POINT10_v2 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_POINT10_v2(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + + ~LASwriteItemCompressed_POINT10_v2(); + +private: + ArithmeticEncoder* enc; + U8 last_item[20]; + U16 last_intensity[16]; + StreamingMedian5 last_x_diff_median5[16]; + StreamingMedian5 last_y_diff_median5[16]; + I32 last_height[8]; + + ArithmeticModel* m_changed_values; + IntegerCompressor* ic_intensity; + ArithmeticModel* m_scan_angle_rank[2]; + IntegerCompressor* ic_point_source_ID; + ArithmeticModel* m_bit_byte[256]; + ArithmeticModel* m_classification[256]; + ArithmeticModel* m_user_data[256]; + IntegerCompressor* ic_dx; + IntegerCompressor* ic_dy; + IntegerCompressor* ic_z; +}; + +class LASwriteItemCompressed_GPSTIME11_v2 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_GPSTIME11_v2(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + + ~LASwriteItemCompressed_GPSTIME11_v2(); + +private: + ArithmeticEncoder* enc; + U32 last, next; + U64I64F64 last_gpstime[4]; + I32 last_gpstime_diff[4]; + I32 multi_extreme_counter[4]; + + ArithmeticModel* m_gpstime_multi; + ArithmeticModel* m_gpstime_0diff; + IntegerCompressor* ic_gpstime; +}; + +class LASwriteItemCompressed_RGB12_v2 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_RGB12_v2(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + + ~LASwriteItemCompressed_RGB12_v2(); + +private: + ArithmeticEncoder* enc; + U16 last_item[3]; + + ArithmeticModel* m_byte_used; + ArithmeticModel* m_rgb_diff_0; + ArithmeticModel* m_rgb_diff_1; + ArithmeticModel* m_rgb_diff_2; + ArithmeticModel* m_rgb_diff_3; + ArithmeticModel* m_rgb_diff_4; + ArithmeticModel* m_rgb_diff_5; +}; + +class LASwriteItemCompressed_BYTE_v2 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_BYTE_v2(ArithmeticEncoder* enc, U32 number); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + + ~LASwriteItemCompressed_BYTE_v2(); + +private: + ArithmeticEncoder* enc; + U32 number; + U8* last_item; + + ArithmeticModel** m_byte; +}; + +#endif diff --git a/libs/laszip/src/laswriteitemcompressed_v3.cpp b/libs/laszip/src/laswriteitemcompressed_v3.cpp new file mode 100644 index 0000000..12440f2 --- /dev/null +++ b/libs/laszip/src/laswriteitemcompressed_v3.cpp @@ -0,0 +1,2301 @@ +/* +=============================================================================== + + FILE: laswriteitemcompressed_v3.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ + +#include "laswriteitemcompressed_v3.hpp" + +#include +#include +#include + +/* +=============================================================================== + LASwriteItemCompressed_POINT14_v3 +=============================================================================== +*/ + +typedef struct LASpoint14 +{ + I32 X; + I32 Y; + I32 Z; + U16 intensity; + U8 legacy_return_number : 3; + U8 legacy_number_of_returns : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 legacy_classification : 5; + U8 legacy_flags : 3; + I8 legacy_scan_angle_rank; + U8 user_data; + U16 point_source_ID; + + // LAS 1.4 only + I16 scan_angle; + U8 legacy_point_type : 2; + U8 scanner_channel : 2; + U8 classification_flags : 4; + U8 classification; + U8 return_number : 4; + U8 number_of_returns : 4; + + // LASlib internal use only + U8 deleted_flag; + + // for 8 byte alignment of the GPS time + U8 dummy[2]; + + // compressed LASzip 1.4 points only + BOOL gps_time_change; + + F64 gps_time; + U16 rgb[4]; +// LASwavepacket wavepacket; +} LASpoint14; + +#define LASZIP_GPSTIME_MULTI 500 +#define LASZIP_GPSTIME_MULTI_MINUS -10 +#define LASZIP_GPSTIME_MULTI_CODE_FULL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 1) + +#define LASZIP_GPSTIME_MULTI_TOTAL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 5) + +LASwriteItemCompressed_POINT14_v3::LASwriteItemCompressed_POINT14_v3(ArithmeticEncoder* enc) +{ + /* not used as a encoder. just gives access to outstream */ + + assert(enc); + this->enc = enc; + + /* zero outstreams and encoders */ + + outstream_channel_returns_XY = 0; + outstream_Z = 0; + outstream_classification = 0; + outstream_flags = 0; + outstream_intensity = 0; + outstream_scan_angle = 0; + outstream_user_data = 0; + outstream_point_source = 0; + outstream_gps_time = 0; + + enc_channel_returns_XY = 0; + enc_Z = 0; + enc_classification = 0; + enc_flags = 0; + enc_intensity = 0; + enc_scan_angle = 0; + enc_user_data = 0; + enc_point_source = 0; + enc_gps_time = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_changed_values[0] = 0; + } + current_context = 0; + + /* number of bytes per layer */ + + num_bytes_channel_returns_XY = 0; + num_bytes_Z = 0; + num_bytes_classification = 0; + num_bytes_flags = 0; + num_bytes_intensity = 0; + num_bytes_scan_angle = 0; + num_bytes_user_data = 0; + num_bytes_point_source = 0; + num_bytes_gps_time = 0; +} + +LASwriteItemCompressed_POINT14_v3::~LASwriteItemCompressed_POINT14_v3() +{ + U32 c, i; + + /* destroy all initialized scanner channel contexts */ + + for (c = 0; c < 4; c++) + { + if (contexts[c].m_changed_values[0]) + { + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[0]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[1]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[2]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[3]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[4]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[5]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[6]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[7]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_scanner_channel); + for (i = 0; i < 16; i++) + { + if (contexts[c].m_number_of_returns[i]) enc_channel_returns_XY->destroySymbolModel(contexts[c].m_number_of_returns[i]); + if (contexts[c].m_return_number[i]) enc_channel_returns_XY->destroySymbolModel(contexts[c].m_return_number[i]); + } + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_return_number_gps_same); + delete contexts[c].ic_dX; + delete contexts[c].ic_dY; + delete contexts[c].ic_Z; + for (i = 0; i < 64; i++) + { + if (contexts[c].m_classification[i]) enc_classification->destroySymbolModel(contexts[c].m_classification[i]); + if (contexts[c].m_flags[i]) enc_flags->destroySymbolModel(contexts[c].m_flags[i]); + if (contexts[c].m_user_data[i]) enc_user_data->destroySymbolModel(contexts[c].m_user_data[i]); + } + delete contexts[c].ic_intensity; + delete contexts[c].ic_scan_angle; + delete contexts[c].ic_point_source_ID; + enc_gps_time->destroySymbolModel(contexts[c].m_gpstime_multi); + enc_gps_time->destroySymbolModel(contexts[c].m_gpstime_0diff); + delete contexts[c].ic_gpstime; + } + } + + /* destroy all encoders and outstreams */ + + if (outstream_channel_returns_XY) + { + delete enc_channel_returns_XY; + delete enc_Z; + delete enc_classification; + delete enc_flags; + delete enc_intensity; + delete enc_scan_angle; + delete enc_user_data; + delete enc_point_source; + delete enc_gps_time; + + delete outstream_channel_returns_XY; + delete outstream_Z; + delete outstream_classification; + delete outstream_flags; + delete outstream_intensity; + delete outstream_scan_angle; + delete outstream_user_data; + delete outstream_point_source; + delete outstream_gps_time; + } + +// fprintf(stderr, "%u %u %u %u %u %u %u %u %u\n", num_bytes_channel_returns_XY, num_bytes_Z, num_bytes_classification, num_bytes_flags, num_bytes_intensity, num_bytes_scan_angle, num_bytes_user_data, num_bytes_point_source, num_bytes_gps_time); +} + +inline BOOL LASwriteItemCompressed_POINT14_v3::createAndInitModelsAndCompressors(U32 context, const U8* item) +{ + I32 i; + + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models and integer compressors (if needed) */ + + if (contexts[context].m_changed_values[0] == 0) + { + /* for the channel_returns_XY layer */ + + contexts[context].m_changed_values[0] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[1] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[2] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[3] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[4] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[5] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[6] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[7] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_scanner_channel = enc_channel_returns_XY->createSymbolModel(3); + for (i = 0; i < 16; i++) + { + contexts[context].m_number_of_returns[i] = 0; + contexts[context].m_return_number[i] = 0; + } + contexts[context].m_return_number_gps_same = enc_channel_returns_XY->createSymbolModel(13); + + contexts[context].ic_dX = new IntegerCompressor(enc_channel_returns_XY, 32, 2); // 32 bits, 2 context + contexts[context].ic_dY = new IntegerCompressor(enc_channel_returns_XY, 32, 22); // 32 bits, 22 contexts + + /* for the Z layer */ + + contexts[context].ic_Z = new IntegerCompressor(enc_Z, 32, 20); // 32 bits, 20 contexts + + /* for the classification layer */ + /* for the flags layer */ + /* for the user_data layer */ + + for (i = 0; i < 64; i++) + { + contexts[context].m_classification[i] = 0; + contexts[context].m_flags[i] = 0; + contexts[context].m_user_data[i] = 0; + } + + /* for the intensity layer */ + + contexts[context].ic_intensity = new IntegerCompressor(enc_intensity, 16, 4); + + /* for the scan_angle layer */ + + contexts[context].ic_scan_angle = new IntegerCompressor(enc_scan_angle, 16, 2); + + /* for the point_source_ID layer */ + + contexts[context].ic_point_source_ID = new IntegerCompressor(enc_point_source, 16); + + /* for the gps_time layer */ + + contexts[context].m_gpstime_multi = enc_gps_time->createSymbolModel(LASZIP_GPSTIME_MULTI_TOTAL); + contexts[context].m_gpstime_0diff = enc_gps_time->createSymbolModel(5); + contexts[context].ic_gpstime = new IntegerCompressor(enc_gps_time, 32, 9); // 32 bits, 9 contexts + } + + /* then init entropy models and integer compressors */ + + /* for the channel_returns_XY layer */ + + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[0]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[1]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[2]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[3]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[4]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[5]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[6]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[7]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_scanner_channel); + for (i = 0; i < 16; i++) + { + if (contexts[context].m_number_of_returns[i]) enc_channel_returns_XY->initSymbolModel(contexts[context].m_number_of_returns[i]); + if (contexts[context].m_return_number[i]) enc_channel_returns_XY->initSymbolModel(contexts[context].m_return_number[i]); + } + enc_channel_returns_XY->initSymbolModel(contexts[context].m_return_number_gps_same); + contexts[context].ic_dX->initCompressor(); + contexts[context].ic_dY->initCompressor(); + for (i = 0; i < 12; i++) + { + contexts[context].last_X_diff_median5[i].init(); + contexts[context].last_Y_diff_median5[i].init(); + } + + /* for the Z layer */ + + contexts[context].ic_Z->initCompressor(); + for (i = 0; i < 8; i++) + { + contexts[context].last_Z[i] = ((LASpoint14*)item)->Z; + } + + /* for the classification layer */ + /* for the flags layer */ + /* for the user_data layer */ + + for (i = 0; i < 64; i++) + { + if (contexts[context].m_classification[i]) enc_classification->initSymbolModel(contexts[context].m_classification[i]); + if (contexts[context].m_flags[i]) enc_flags->initSymbolModel(contexts[context].m_flags[i]); + if (contexts[context].m_user_data[i]) enc_user_data->initSymbolModel(contexts[context].m_user_data[i]); + } + + /* for the intensity layer */ + + contexts[context].ic_intensity->initCompressor(); + for (i = 0; i < 8; i++) + { + contexts[context].last_intensity[i] = ((LASpoint14*)item)->intensity; + } + + /* for the scan_angle layer */ + + contexts[context].ic_scan_angle->initCompressor(); + + /* for the point_source_ID layer */ + + contexts[context].ic_point_source_ID->initCompressor(); + + /* for the gps_time layer */ + + enc_gps_time->initSymbolModel(contexts[context].m_gpstime_multi); + enc_gps_time->initSymbolModel(contexts[context].m_gpstime_0diff); + contexts[context].ic_gpstime->initCompressor(); + contexts[context].last = 0, contexts[context].next = 0; + contexts[context].last_gpstime_diff[0] = 0; + contexts[context].last_gpstime_diff[1] = 0; + contexts[context].last_gpstime_diff[2] = 0; + contexts[context].last_gpstime_diff[3] = 0; + contexts[context].multi_extreme_counter[0] = 0; + contexts[context].multi_extreme_counter[1] = 0; + contexts[context].multi_extreme_counter[2] = 0; + contexts[context].multi_extreme_counter[3] = 0; + contexts[context].last_gpstime[0].f64 = ((LASpoint14*)item)->gps_time; + contexts[context].last_gpstime[1].u64 = 0; + contexts[context].last_gpstime[2].u64 = 0; + contexts[context].last_gpstime[3].u64 = 0; + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, sizeof(LASpoint14)); + ((LASpoint14*)contexts[context].last_item)->gps_time_change = FALSE; + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASwriteItemCompressed_POINT14_v3::init(const U8* item, U32& context) +{ + /* on the first init create outstreams and encoders */ + + if (outstream_channel_returns_XY == 0) + { + if (IS_LITTLE_ENDIAN()) + { + outstream_channel_returns_XY = new ByteStreamOutArrayLE(); + outstream_Z = new ByteStreamOutArrayLE(); + outstream_classification = new ByteStreamOutArrayLE(); + outstream_flags = new ByteStreamOutArrayLE(); + outstream_intensity = new ByteStreamOutArrayLE(); + outstream_scan_angle = new ByteStreamOutArrayLE(); + outstream_user_data = new ByteStreamOutArrayLE(); + outstream_point_source = new ByteStreamOutArrayLE(); + outstream_gps_time = new ByteStreamOutArrayLE(); + } + else + { + outstream_channel_returns_XY = new ByteStreamOutArrayBE(); + outstream_Z = new ByteStreamOutArrayBE(); + outstream_classification = new ByteStreamOutArrayBE(); + outstream_flags = new ByteStreamOutArrayBE(); + outstream_intensity = new ByteStreamOutArrayBE(); + outstream_scan_angle = new ByteStreamOutArrayBE(); + outstream_user_data = new ByteStreamOutArrayBE(); + outstream_point_source = new ByteStreamOutArrayBE(); + outstream_gps_time = new ByteStreamOutArrayBE(); + } + + /* create layer encoders */ + + enc_channel_returns_XY = new ArithmeticEncoder(); + enc_Z = new ArithmeticEncoder(); + enc_classification = new ArithmeticEncoder(); + enc_flags = new ArithmeticEncoder(); + enc_intensity = new ArithmeticEncoder(); + enc_scan_angle = new ArithmeticEncoder(); + enc_user_data = new ArithmeticEncoder(); + enc_point_source = new ArithmeticEncoder(); + enc_gps_time = new ArithmeticEncoder(); + } + else + { + /* otherwise just seek back */ + + outstream_channel_returns_XY->seek(0); + outstream_Z->seek(0); + outstream_classification->seek(0); + outstream_flags->seek(0); + outstream_intensity->seek(0); + outstream_scan_angle->seek(0); + outstream_user_data->seek(0); + outstream_point_source->seek(0); + outstream_gps_time->seek(0); + } + + /* init layer encoders */ + + enc_channel_returns_XY->init(outstream_channel_returns_XY); + enc_Z->init(outstream_Z); + enc_classification->init(outstream_classification); + enc_flags->init(outstream_flags); + enc_intensity->init(outstream_intensity); + enc_scan_angle->init(outstream_scan_angle); + enc_user_data->init(outstream_user_data); + enc_point_source->init(outstream_point_source); + enc_gps_time->init(outstream_gps_time); + + /* set changed booleans to FALSE */ + + changed_classification = FALSE; + changed_flags = FALSE; + changed_intensity = FALSE; + changed_scan_angle = FALSE; + changed_user_data = FALSE; + changed_point_source = FALSE; + changed_gps_time = FALSE; + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = ((LASpoint14*)item)->scanner_channel; + context = current_context; // the POINT14 writer sets context for all other items + + /* create and init entropy models and integer compressors (and init context from item) */ + + createAndInitModelsAndCompressors(current_context, item); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_POINT14_v3::write(const U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + //////////////////////////////////////// + // compress returns_XY layer + //////////////////////////////////////// + + // create single (3) / first (1) / last (2) / intermediate (0) context from last point return + + I32 lpr = (((LASpoint14*)last_item)->return_number == 1 ? 1 : 0); // first? + lpr += (((LASpoint14*)last_item)->return_number >= ((LASpoint14*)last_item)->number_of_returns ? 2 : 0); // last? + + // add info whether the GPS time changed in the last return to the context + + lpr += (((LASpoint14*)last_item)->gps_time_change ? 4 : 0); + + // get the (potentially new) context + + U32 scanner_channel = ((LASpoint14*)item)->scanner_channel; + + // if context has changed (and the new context already exists) get last for new context + + if (scanner_channel != current_context) + { + if (contexts[scanner_channel].unused == FALSE) + { + last_item = contexts[scanner_channel].last_item; + } + } + + // determine changed attributes + + BOOL point_source_change = (((LASpoint14*)item)->point_source_ID != ((LASpoint14*)last_item)->point_source_ID); + BOOL gps_time_change = (((LASpoint14*)item)->gps_time != ((LASpoint14*)last_item)->gps_time); + BOOL scan_angle_change = (((LASpoint14*)item)->scan_angle != ((LASpoint14*)last_item)->scan_angle); + + // get last and current return counts + + U32 last_n = ((LASpoint14*)last_item)->number_of_returns; + U32 last_r = ((LASpoint14*)last_item)->return_number; + + U32 n = ((LASpoint14*)item)->number_of_returns; + U32 r = ((LASpoint14*)item)->return_number; + + // create the 7 bit mask that encodes various changes (its value ranges from 0 to 127) + + I32 changed_values = ((scanner_channel != current_context) << 6) | // scanner channel compared to last point (same = 0 / different = 1) + (point_source_change << 5) | // point source ID compared to last point from *same* scanner channel (same = 0 / different = 1) + (gps_time_change << 4) | // GPS time stamp compared to last point from *same* scanner channel (same = 0 / different = 1) + (scan_angle_change << 3) | // scan angle compared to last point from *same* scanner channel (same = 0 / different = 1) + ((n != last_n) << 2); // number of returns compared to last point from *same* scanner channel (same = 0 / different = 1) + + // return number compared to last point of *same* scanner channel (same = 0 / plus one mod 16 = 1 / minus one mod 16 = 2 / other difference = 3) + + if (r != last_r) + { + if (r == ((last_r + 1) % 16)) + { + changed_values |= 1; + } + else if (r == ((last_r + 15) % 16)) + { + changed_values |= 2; + } + else + { + changed_values |= 3; + } + } + + // compress the 7 bit mask that encodes changes with last point return context + + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_changed_values[lpr], changed_values); + + // if scanner channel has changed, record change + + if (changed_values & (1 << 6)) + { + I32 diff = scanner_channel - current_context; + if (diff > 0) + { + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_scanner_channel, diff - 1); // curr = last + (sym + 1) + } + else + { + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_scanner_channel, diff + 4 - 1); // curr = (last + (sym + 1)) % 4 + } + // maybe create and init entropy models and integer compressors + if (contexts[scanner_channel].unused) + { + // create and init entropy models and integer compressors (and init context from last item) + createAndInitModelsAndCompressors(scanner_channel, contexts[current_context].last_item); + // get last for new context + last_item = contexts[scanner_channel].last_item; + } + // switch context to current scanner channel + current_context = scanner_channel; + context = current_context; // the POINT14 writer sets context for all other items + } + + // if number of returns is different we compress it + + if (changed_values & (1 << 2)) + { + if (contexts[current_context].m_number_of_returns[last_n] == 0) + { + contexts[current_context].m_number_of_returns[last_n] = enc_channel_returns_XY->createSymbolModel(16); + enc_channel_returns_XY->initSymbolModel(contexts[current_context].m_number_of_returns[last_n]); + } + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_number_of_returns[last_n], n); + } + + // if return number is different and difference is bigger than +1 / -1 we compress how it is different + + if ((changed_values & 3) == 3) + { + if (gps_time_change) // if the GPS time has changed + { + if (contexts[current_context].m_return_number[last_r] == 0) + { + contexts[current_context].m_return_number[last_r] = enc_channel_returns_XY->createSymbolModel(16); + enc_channel_returns_XY->initSymbolModel(contexts[current_context].m_return_number[last_r]); + } + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_return_number[last_r], r); + } + else // if the GPS time has not changed + { + I32 diff = r - last_r; + if (diff > 1) + { + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_return_number_gps_same, diff - 2); // r = last_r + (sym + 2) with sym = diff - 2 + } + else + { + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_return_number_gps_same, diff + 16 - 2); // r = (last_r + (sym + 2)) % 16 with sym = diff + 16 - 2 + } + } + } + + // get return map m and return level l context for current point + + U32 m = number_return_map_6ctx[n][r]; + U32 l = number_return_level_8ctx[n][r]; + + // create single (3) / first (1) / last (2) / intermediate (0) return context for current point + + I32 cpr = (r == 1 ? 2 : 0); // first ? + cpr += (r >= n ? 1 : 0); // last ? + + U32 k_bits; + I32 median, diff; + + // compress X coordinate + median = contexts[current_context].last_X_diff_median5[(m<<1) | gps_time_change].get(); + diff = ((LASpoint14*)item)->X - ((LASpoint14*)last_item)->X; + contexts[current_context].ic_dX->compress(median, diff, n==1); + contexts[current_context].last_X_diff_median5[(m<<1) | gps_time_change].add(diff); + + // compress Y coordinate + k_bits = contexts[current_context].ic_dX->getK(); + median = contexts[current_context].last_Y_diff_median5[(m<<1) | gps_time_change].get(); + diff = ((LASpoint14*)item)->Y - ((LASpoint14*)last_item)->Y; + contexts[current_context].ic_dY->compress(median, diff, (n==1) + ( k_bits < 20 ? U32_ZERO_BIT_0(k_bits) : 20 )); + contexts[current_context].last_Y_diff_median5[(m<<1) | gps_time_change].add(diff); + + //////////////////////////////////////// + // compress Z layer + //////////////////////////////////////// + + k_bits = (contexts[current_context].ic_dX->getK() + contexts[current_context].ic_dY->getK()) / 2; + contexts[current_context].ic_Z->compress(contexts[current_context].last_Z[l], ((LASpoint14*)item)->Z, (n==1) + (k_bits < 18 ? U32_ZERO_BIT_0(k_bits) : 18)); + contexts[current_context].last_Z[l] = ((LASpoint14*)item)->Z; + + //////////////////////////////////////// + // compress classifications layer + //////////////////////////////////////// + + U32 last_classification = ((LASpoint14*)last_item)->classification; + U32 classification = ((LASpoint14*)item)->classification; + + if (classification != last_classification) + { + changed_classification = TRUE; + } + + I32 ccc = ((last_classification & 0x1F) << 1) + (cpr == 3 ? 1 : 0); + if (contexts[current_context].m_classification[ccc] == 0) + { + contexts[current_context].m_classification[ccc] = enc_classification->createSymbolModel(256); + enc_classification->initSymbolModel(contexts[current_context].m_classification[ccc]); + } + enc_classification->encodeSymbol(contexts[current_context].m_classification[ccc], classification); + + //////////////////////////////////////// + // compress flags layer + //////////////////////////////////////// + + U32 last_flags = (((LASpoint14*)last_item)->edge_of_flight_line << 5) | (((LASpoint14*)last_item)->scan_direction_flag << 4) | ((LASpoint14*)last_item)->classification_flags; + U32 flags = (((LASpoint14*)item)->edge_of_flight_line << 5) | (((LASpoint14*)item)->scan_direction_flag << 4) | ((LASpoint14*)item)->classification_flags; + + if (flags != last_flags) + { + changed_flags = TRUE; + } + + if (contexts[current_context].m_flags[last_flags] == 0) + { + contexts[current_context].m_flags[last_flags] = enc_flags->createSymbolModel(64); + enc_flags->initSymbolModel(contexts[current_context].m_flags[last_flags]); + } + enc_flags->encodeSymbol(contexts[current_context].m_flags[last_flags], flags); + + //////////////////////////////////////// + // compress intensity layer + //////////////////////////////////////// + + if (((LASpoint14*)item)->intensity != ((LASpoint14*)last_item)->intensity) + { + changed_intensity = TRUE; + } + contexts[current_context].ic_intensity->compress(contexts[current_context].last_intensity[(cpr<<1) | gps_time_change], ((LASpoint14*)item)->intensity, cpr); + contexts[current_context].last_intensity[(cpr<<1) | gps_time_change] = ((LASpoint14*)item)->intensity; + + //////////////////////////////////////// + // compress scan_angle layer + //////////////////////////////////////// + + if (scan_angle_change) + { + changed_scan_angle = TRUE; + contexts[current_context].ic_scan_angle->compress(((LASpoint14*)last_item)->scan_angle, ((LASpoint14*)item)->scan_angle, gps_time_change); // if the GPS time has changed + } + + //////////////////////////////////////// + // compress user_data layer + //////////////////////////////////////// + + if (((LASpoint14*)item)->user_data != ((LASpoint14*)last_item)->user_data) + { + changed_user_data = TRUE; + } + if (contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4] == 0) + { + contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4] = enc_user_data->createSymbolModel(256); + enc_user_data->initSymbolModel(contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4]); + } + enc_user_data->encodeSymbol(contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4], ((LASpoint14*)item)->user_data); + + //////////////////////////////////////// + // compress point_source layer + //////////////////////////////////////// + + if (point_source_change) + { + changed_point_source = TRUE; + contexts[current_context].ic_point_source_ID->compress(((LASpoint14*)last_item)->point_source_ID, ((LASpoint14*)item)->point_source_ID); + } + + //////////////////////////////////////// + // compress gps_time layer + //////////////////////////////////////// + + if (gps_time_change) // if the GPS time has changed + { + changed_gps_time = TRUE; + + U64I64F64 gps_time; + gps_time.f64 = ((LASpoint14*)item)->gps_time; + + write_gps_time(gps_time); + } + + // copy the last item + memcpy(last_item, item, sizeof(LASpoint14)); + // remember if the last point had a gps_time_change + ((LASpoint14*)last_item)->gps_time_change = gps_time_change; + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_POINT14_v3::chunk_sizes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // finish the encoders + + enc_channel_returns_XY->done(); + enc_Z->done(); + if (changed_classification) + { + enc_classification->done(); + } + if (changed_flags) + { + enc_flags->done(); + } + if (changed_intensity) + { + enc_intensity->done(); + } + if (changed_scan_angle) + { + enc_scan_angle->done(); + } + if (changed_user_data) + { + enc_user_data->done(); + } + if (changed_point_source) + { + enc_point_source->done(); + } + if (changed_gps_time) + { + enc_gps_time->done(); + } + + // output the sizes of all layer (i.e.. number of bytes per layer) + + num_bytes = (U32)outstream_channel_returns_XY->getCurr(); + num_bytes_channel_returns_XY += num_bytes; + outstream->put32bitsLE(((U8*)&num_bytes)); + + num_bytes = (U32)outstream_Z->getCurr(); + num_bytes_Z += num_bytes; + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_classification) + { + num_bytes = (U32)outstream_classification->getCurr(); + num_bytes_classification += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_flags) + { + num_bytes = (U32)outstream_flags->getCurr(); + num_bytes_flags += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_intensity) + { + num_bytes = (U32)outstream_intensity->getCurr(); + num_bytes_intensity += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_scan_angle) + { + num_bytes = (U32)outstream_scan_angle->getCurr(); + num_bytes_scan_angle += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_user_data) + { + num_bytes = (U32)outstream_user_data->getCurr(); + num_bytes_user_data += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_point_source) + { + num_bytes = (U32)outstream_point_source->getCurr(); + num_bytes_point_source += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_gps_time) + { + num_bytes = (U32)outstream_gps_time->getCurr(); + num_bytes_gps_time += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_POINT14_v3::chunk_bytes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the bytes of all layers + + num_bytes = (U32)outstream_channel_returns_XY->getCurr(); + outstream->putBytes(outstream_channel_returns_XY->getData(), num_bytes); + + num_bytes = (U32)outstream_Z->getCurr(); + outstream->putBytes(outstream_Z->getData(), num_bytes); + + if (changed_classification) + { + num_bytes = (U32)outstream_classification->getCurr(); + outstream->putBytes(outstream_classification->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_flags) + { + num_bytes = (U32)outstream_flags->getCurr(); + outstream->putBytes(outstream_flags->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_intensity) + { + num_bytes = (U32)outstream_intensity->getCurr(); + outstream->putBytes(outstream_intensity->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_scan_angle) + { + num_bytes = (U32)outstream_scan_angle->getCurr(); + outstream->putBytes(outstream_scan_angle->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_user_data) + { + num_bytes = (U32)outstream_user_data->getCurr(); + outstream->putBytes(outstream_user_data->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_point_source) + { + num_bytes = (U32)outstream_point_source->getCurr(); + outstream->putBytes(outstream_point_source->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_gps_time) + { + num_bytes = (U32)outstream_gps_time->getCurr(); + outstream->putBytes(outstream_gps_time->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + return TRUE; +} + +void LASwriteItemCompressed_POINT14_v3::write_gps_time(const U64I64F64 gps_time) +{ + if (contexts[current_context].last_gpstime_diff[contexts[current_context].last] == 0) // if the last integer difference was zero + { + // calculate the difference between the two doubles as an integer + I64 curr_gpstime_diff_64 = gps_time.i64 - contexts[current_context].last_gpstime[contexts[current_context].last].i64; + I32 curr_gpstime_diff = (I32)curr_gpstime_diff_64; + if (curr_gpstime_diff_64 == (I64)(curr_gpstime_diff)) + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_0diff, 0); // the difference can be represented with 32 bits + contexts[current_context].ic_gpstime->compress(0, curr_gpstime_diff, 0); + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = curr_gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else // the difference is huge + { + U32 i; + // maybe the double belongs to another time sequence + for (i = 1; i < 4; i++) + { + I64 other_gpstime_diff_64 = gps_time.i64 - contexts[current_context].last_gpstime[(contexts[current_context].last+i)&3].i64; + I32 other_gpstime_diff = (I32)other_gpstime_diff_64; + if (other_gpstime_diff_64 == (I64)(other_gpstime_diff)) + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_0diff, i+1); // it belongs to another sequence + contexts[current_context].last = (contexts[current_context].last+i)&3; + write_gps_time(gps_time); + return; + } + } + // no other sequence found. start new sequence. + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_0diff, 1); + contexts[current_context].ic_gpstime->compress((I32)(contexts[current_context].last_gpstime[contexts[current_context].last].u64 >> 32), (I32)(gps_time.u64 >> 32), 8); + enc_gps_time->writeInt((U32)(gps_time.u64)); + contexts[current_context].next = (contexts[current_context].next+1)&3; + contexts[current_context].last = contexts[current_context].next; + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = 0; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + contexts[current_context].last_gpstime[contexts[current_context].last].i64 = gps_time.i64; + } + else // the last integer difference was *not* zero + { + // calculate the difference between the two doubles as an integer + I64 curr_gpstime_diff_64 = gps_time.i64 - contexts[current_context].last_gpstime[contexts[current_context].last].i64; + I32 curr_gpstime_diff = (I32)curr_gpstime_diff_64; + + // if the current gpstime difference can be represented with 32 bits + if (curr_gpstime_diff_64 == (I64)(curr_gpstime_diff)) + { + // compute multiplier between current and last integer difference + F32 multi_f = (F32)curr_gpstime_diff / (F32)(contexts[current_context].last_gpstime_diff[contexts[current_context].last]); + I32 multi = I32_QUANTIZE(multi_f); + + // compress the residual curr_gpstime_diff in dependance on the multiplier + if (multi == 1) + { + // this is the case we assume we get most often for regular spaced pulses + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, 1); + contexts[current_context].ic_gpstime->compress(contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 1); + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else if (multi > 0) + { + if (multi < LASZIP_GPSTIME_MULTI) // positive multipliers up to LASZIP_GPSTIME_MULTI are compressed directly + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, multi); + if (multi < 10) + contexts[current_context].ic_gpstime->compress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 2); + else + contexts[current_context].ic_gpstime->compress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 3); + } + else + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, LASZIP_GPSTIME_MULTI); + contexts[current_context].ic_gpstime->compress(LASZIP_GPSTIME_MULTI*contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 4); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = curr_gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + } + else if (multi < 0) + { + if (multi > LASZIP_GPSTIME_MULTI_MINUS) // negative multipliers larger than LASZIP_GPSTIME_MULTI_MINUS are compressed directly + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, LASZIP_GPSTIME_MULTI - multi); + contexts[current_context].ic_gpstime->compress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 5); + } + else + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS); + contexts[current_context].ic_gpstime->compress(LASZIP_GPSTIME_MULTI_MINUS*contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 6); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = curr_gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + } + else + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, 0); + contexts[current_context].ic_gpstime->compress(0, curr_gpstime_diff, 7); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = curr_gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + } + else // the difference is huge + { + U32 i; + // maybe the double belongs to another time sequence + for (i = 1; i < 4; i++) + { + I64 other_gpstime_diff_64 = gps_time.i64 - contexts[current_context].last_gpstime[(contexts[current_context].last+i)&3].i64; + I32 other_gpstime_diff = (I32)other_gpstime_diff_64; + if (other_gpstime_diff_64 == (I64)(other_gpstime_diff)) + { + // it belongs to this sequence + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, LASZIP_GPSTIME_MULTI_CODE_FULL+i); + contexts[current_context].last = (contexts[current_context].last+i)&3; + write_gps_time(gps_time); + return; + } + } + // no other sequence found. start new sequence. + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, LASZIP_GPSTIME_MULTI_CODE_FULL); + contexts[current_context].ic_gpstime->compress((I32)(contexts[current_context].last_gpstime[contexts[current_context].last].u64 >> 32), (I32)(gps_time.u64 >> 32), 8); + enc_gps_time->writeInt((U32)(gps_time.u64)); + contexts[current_context].next = (contexts[current_context].next+1)&3; + contexts[current_context].last = contexts[current_context].next; + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = 0; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + contexts[current_context].last_gpstime[contexts[current_context].last].i64 = gps_time.i64; + } +} + +/* +=============================================================================== + LASwriteItemCompressed_RGB14_v3 +=============================================================================== +*/ + +LASwriteItemCompressed_RGB14_v3::LASwriteItemCompressed_RGB14_v3(ArithmeticEncoder* enc) +{ + /* not used as a encoder. just gives access to outstream */ + + assert(enc); + this->enc = enc; + + /* zero outstreams and encoders */ + + outstream_RGB = 0; + + enc_RGB = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_RGB = 0; + + changed_RGB = FALSE; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_byte_used = 0; + } + current_context = 0; +} + +LASwriteItemCompressed_RGB14_v3::~LASwriteItemCompressed_RGB14_v3() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_byte_used) + { + enc_RGB->destroySymbolModel(contexts[c].m_byte_used); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_0); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_1); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_2); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_3); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_4); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_5); + } + } + + /* destroy all outstreams and encoders */ + + if (outstream_RGB) + { + delete outstream_RGB; + + delete enc_RGB; + } +} + +inline BOOL LASwriteItemCompressed_RGB14_v3::createAndInitModelsAndCompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (contexts[context].m_byte_used == 0) + { + contexts[context].m_byte_used = enc_RGB->createSymbolModel(128); + contexts[context].m_rgb_diff_0 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_1 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_2 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_3 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_4 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_5 = enc_RGB->createSymbolModel(256); + } + + /* then init entropy models */ + + enc_RGB->initSymbolModel(contexts[context].m_byte_used); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_0); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_1); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_2); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_3); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_4); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_5); + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, 6); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASwriteItemCompressed_RGB14_v3::init(const U8* item, U32& context) +{ + /* on the first init create outstreams and encoders */ + + if (outstream_RGB == 0) + { + /* create outstreams */ + + if (IS_LITTLE_ENDIAN()) + { + outstream_RGB = new ByteStreamOutArrayLE(); + } + else + { + outstream_RGB = new ByteStreamOutArrayBE(); + } + + /* create layer encoders */ + + enc_RGB = new ArithmeticEncoder(); + } + else + { + /* otherwise just seek back */ + + outstream_RGB->seek(0); + } + + /* init layer encoders */ + + enc_RGB->init(outstream_RGB); + + /* set changed booleans to FALSE */ + + changed_RGB = FALSE; + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 writer + + /* create and init entropy models and integer compressors (and init contect from item) */ + + createAndInitModelsAndCompressors(current_context, item); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGB14_v3::write(const U8* item, U32& context) +{ + // get last + + U16* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 writer + if (contexts[current_context].unused) + { + createAndInitModelsAndCompressors(current_context, (U8*)last_item); + last_item = contexts[current_context].last_item; + } + } + + // compress + + I32 diff_l = 0; + I32 diff_h = 0; + I32 corr; + U32 sym = ((last_item[0]&0x00FF) != (((U16*)item)[0]&0x00FF)) << 0; + sym |= ((last_item[0]&0xFF00) != (((U16*)item)[0]&0xFF00)) << 1; + sym |= ((last_item[1]&0x00FF) != (((U16*)item)[1]&0x00FF)) << 2; + sym |= ((last_item[1]&0xFF00) != (((U16*)item)[1]&0xFF00)) << 3; + sym |= ((last_item[2]&0x00FF) != (((U16*)item)[2]&0x00FF)) << 4; + sym |= ((last_item[2]&0xFF00) != (((U16*)item)[2]&0xFF00)) << 5; + sym |= (((((U16*)item)[0]&0x00FF) != (((U16*)item)[1]&0x00FF)) || ((((U16*)item)[0]&0x00FF) != (((U16*)item)[2]&0x00FF)) || ((((U16*)item)[0]&0xFF00) != (((U16*)item)[1]&0xFF00)) || ((((U16*)item)[0]&0xFF00) != (((U16*)item)[2]&0xFF00))) << 6; + enc_RGB->encodeSymbol(contexts[current_context].m_byte_used, sym); + if (sym & (1 << 0)) + { + diff_l = ((int)(((U16*)item)[0]&255)) - (last_item[0]&255); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_0, U8_FOLD(diff_l)); + } + if (sym & (1 << 1)) + { + diff_h = ((int)(((U16*)item)[0]>>8)) - (last_item[0]>>8); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_1, U8_FOLD(diff_h)); + } + if (sym & (1 << 6)) + { + if (sym & (1 << 2)) + { + corr = ((int)(((U16*)item)[1]&255)) - U8_CLAMP(diff_l + (last_item[1]&255)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_2, U8_FOLD(corr)); + } + if (sym & (1 << 4)) + { + diff_l = (diff_l + (((U16*)item)[1]&255) - (last_item[1]&255)) / 2; + corr = ((int)(((U16*)item)[2]&255)) - U8_CLAMP(diff_l + (last_item[2]&255)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_4, U8_FOLD(corr)); + } + if (sym & (1 << 3)) + { + corr = ((int)(((U16*)item)[1]>>8)) - U8_CLAMP(diff_h + (last_item[1]>>8)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_3, U8_FOLD(corr)); + } + if (sym & (1 << 5)) + { + diff_h = (diff_h + (((U16*)item)[1]>>8) - (last_item[1]>>8)) / 2; + corr = ((int)(((U16*)item)[2]>>8)) - U8_CLAMP(diff_h + (last_item[2]>>8)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_5, U8_FOLD(corr)); + } + } + if (sym) + { + changed_RGB = TRUE; + } + memcpy(last_item, item, 6); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGB14_v3::chunk_sizes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // finish the encoders + + enc_RGB->done(); + + // output the sizes of all layer (i.e.. number of bytes per layer) + + if (changed_RGB) + { + num_bytes = (U32)outstream_RGB->getCurr(); + num_bytes_RGB += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGB14_v3::chunk_bytes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the bytes of all layers + + if (changed_RGB) + { + num_bytes = (U32)outstream_RGB->getCurr(); + outstream->putBytes(outstream_RGB->getData(), num_bytes); + } + + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_RGBNIR14_v3 +=============================================================================== +*/ + +LASwriteItemCompressed_RGBNIR14_v3::LASwriteItemCompressed_RGBNIR14_v3(ArithmeticEncoder* enc) +{ + /* not used as a encoder. just gives access to outstream */ + + assert(enc); + this->enc = enc; + + /* zero outstreams and encoders */ + + outstream_RGB = 0; + outstream_NIR = 0; + + enc_RGB = 0; + enc_NIR = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_RGB = 0; + num_bytes_NIR = 0; + + changed_RGB = FALSE; + changed_NIR = FALSE; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_rgb_bytes_used = 0; + } + current_context = 0; +} + +LASwriteItemCompressed_RGBNIR14_v3::~LASwriteItemCompressed_RGBNIR14_v3() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_rgb_bytes_used) + { + enc_RGB->destroySymbolModel(contexts[c].m_rgb_bytes_used); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_0); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_1); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_2); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_3); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_4); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_5); + + enc_NIR->destroySymbolModel(contexts[c].m_nir_bytes_used); + enc_NIR->destroySymbolModel(contexts[c].m_nir_diff_0); + enc_NIR->destroySymbolModel(contexts[c].m_nir_diff_1); + } + } + + /* destroy all outstreams and encoders */ + + if (outstream_RGB) + { + delete outstream_RGB; + delete outstream_NIR; + + delete enc_RGB; + delete enc_NIR; + } +} + +inline BOOL LASwriteItemCompressed_RGBNIR14_v3::createAndInitModelsAndCompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (contexts[context].m_rgb_bytes_used == 0) + { + contexts[context].m_rgb_bytes_used = enc_RGB->createSymbolModel(128); + contexts[context].m_rgb_diff_0 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_1 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_2 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_3 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_4 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_5 = enc_RGB->createSymbolModel(256); + + contexts[context].m_nir_bytes_used = enc_RGB->createSymbolModel(4); + contexts[context].m_nir_diff_0 = enc_RGB->createSymbolModel(256); + contexts[context].m_nir_diff_1 = enc_RGB->createSymbolModel(256); + } + + /* then init entropy models */ + + enc_RGB->initSymbolModel(contexts[context].m_rgb_bytes_used); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_0); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_1); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_2); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_3); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_4); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_5); + + enc_NIR->initSymbolModel(contexts[context].m_nir_bytes_used); + enc_NIR->initSymbolModel(contexts[context].m_nir_diff_0); + enc_NIR->initSymbolModel(contexts[context].m_nir_diff_1); + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, 8); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASwriteItemCompressed_RGBNIR14_v3::init(const U8* item, U32& context) +{ + /* on the first init create outstreams and encoders */ + + if (outstream_RGB == 0) + { + /* create outstreams */ + + if (IS_LITTLE_ENDIAN()) + { + outstream_RGB = new ByteStreamOutArrayLE(); + outstream_NIR = new ByteStreamOutArrayLE(); + } + else + { + outstream_RGB = new ByteStreamOutArrayBE(); + outstream_NIR = new ByteStreamOutArrayBE(); + } + + /* create layer encoders */ + + enc_RGB = new ArithmeticEncoder(); + enc_NIR = new ArithmeticEncoder(); + } + else + { + /* otherwise just seek back */ + + outstream_RGB->seek(0); + outstream_NIR->seek(0); + } + + /* init layer encoders */ + + enc_RGB->init(outstream_RGB); + enc_NIR->init(outstream_NIR); + + /* set changed booleans to FALSE */ + + changed_RGB = FALSE; + changed_NIR = FALSE; + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // ll other items use context set by POINT14 writer + + /* create and init entropy models and integer compressors (and init context from item) */ + + createAndInitModelsAndCompressors(current_context, item); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGBNIR14_v3::write(const U8* item, U32& context) +{ + // get last + + U16* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 writer + if (contexts[current_context].unused) + { + createAndInitModelsAndCompressors(current_context, (U8*)last_item); + last_item = contexts[current_context].last_item; + } + } + + // compress + + I32 diff_l = 0; + I32 diff_h = 0; + I32 corr; + U32 sym = ((last_item[0]&0x00FF) != (((U16*)item)[0]&0x00FF)) << 0; + sym |= ((last_item[0]&0xFF00) != (((U16*)item)[0]&0xFF00)) << 1; + sym |= ((last_item[1]&0x00FF) != (((U16*)item)[1]&0x00FF)) << 2; + sym |= ((last_item[1]&0xFF00) != (((U16*)item)[1]&0xFF00)) << 3; + sym |= ((last_item[2]&0x00FF) != (((U16*)item)[2]&0x00FF)) << 4; + sym |= ((last_item[2]&0xFF00) != (((U16*)item)[2]&0xFF00)) << 5; + sym |= (((((U16*)item)[0]&0x00FF) != (((U16*)item)[1]&0x00FF)) || ((((U16*)item)[0]&0x00FF) != (((U16*)item)[2]&0x00FF)) || ((((U16*)item)[0]&0xFF00) != (((U16*)item)[1]&0xFF00)) || ((((U16*)item)[0]&0xFF00) != (((U16*)item)[2]&0xFF00))) << 6; + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_bytes_used, sym); + if (sym & (1 << 0)) + { + diff_l = ((int)(((U16*)item)[0]&255)) - (last_item[0]&255); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_0, U8_FOLD(diff_l)); + } + if (sym & (1 << 1)) + { + diff_h = ((int)(((U16*)item)[0]>>8)) - (last_item[0]>>8); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_1, U8_FOLD(diff_h)); + } + if (sym & (1 << 6)) + { + if (sym & (1 << 2)) + { + corr = ((int)(((U16*)item)[1]&255)) - U8_CLAMP(diff_l + (last_item[1]&255)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_2, U8_FOLD(corr)); + } + if (sym & (1 << 4)) + { + diff_l = (diff_l + (((U16*)item)[1]&255) - (last_item[1]&255)) / 2; + corr = ((int)(((U16*)item)[2]&255)) - U8_CLAMP(diff_l + (last_item[2]&255)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_4, U8_FOLD(corr)); + } + if (sym & (1 << 3)) + { + corr = ((int)(((U16*)item)[1]>>8)) - U8_CLAMP(diff_h + (last_item[1]>>8)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_3, U8_FOLD(corr)); + } + if (sym & (1 << 5)) + { + diff_h = (diff_h + (((U16*)item)[1]>>8) - (last_item[1]>>8)) / 2; + corr = ((int)(((U16*)item)[2]>>8)) - U8_CLAMP(diff_h + (last_item[2]>>8)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_5, U8_FOLD(corr)); + } + } + if (sym) + { + changed_RGB = TRUE; + } + + sym = ((last_item[3]&0x00FF) != (((U16*)item)[3]&0x00FF)) << 0; + sym |= ((last_item[3]&0xFF00) != (((U16*)item)[3]&0xFF00)) << 1; + enc_NIR->encodeSymbol(contexts[current_context].m_nir_bytes_used, sym); + if (sym & (1 << 0)) + { + diff_l = ((int)(((U16*)item)[3]&255)) - (last_item[3]&255); + enc_NIR->encodeSymbol(contexts[current_context].m_nir_diff_0, U8_FOLD(diff_l)); + } + if (sym & (1 << 1)) + { + diff_h = ((int)(((U16*)item)[3]>>8)) - (last_item[3]>>8); + enc_NIR->encodeSymbol(contexts[current_context].m_nir_diff_1, U8_FOLD(diff_h)); + } + if (sym) + { + changed_NIR = TRUE; + } + + memcpy(last_item, item, 8); + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGBNIR14_v3::chunk_sizes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // finish the encoders + + enc_RGB->done(); + enc_NIR->done(); + + // output the sizes of all layer (i.e.. number of bytes per layer) + + if (changed_RGB) + { + num_bytes = (U32)outstream_RGB->getCurr(); + num_bytes_RGB += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_NIR) + { + num_bytes = (U32)outstream_NIR->getCurr(); + num_bytes_NIR += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGBNIR14_v3::chunk_bytes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the bytes of all layers + + if (changed_RGB) + { + num_bytes = (U32)outstream_RGB->getCurr(); + outstream->putBytes(outstream_RGB->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_NIR) + { + num_bytes = (U32)outstream_NIR->getCurr(); + outstream->putBytes(outstream_NIR->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_WAVEPACKET14_v3 +=============================================================================== +*/ + +LASwriteItemCompressed_WAVEPACKET14_v3::LASwriteItemCompressed_WAVEPACKET14_v3(ArithmeticEncoder* enc) +{ + /* not used as a encoder. just gives access to outstream */ + + assert(enc); + this->enc = enc; + + /* zero outstreams and encoders */ + + outstream_wavepacket = 0; + + enc_wavepacket = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_wavepacket = 0; + + changed_wavepacket = FALSE; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_packet_index = 0; + } + current_context = 0; +} + +LASwriteItemCompressed_WAVEPACKET14_v3::~LASwriteItemCompressed_WAVEPACKET14_v3() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_packet_index) + { + enc_wavepacket->destroySymbolModel(contexts[c].m_packet_index); + enc_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[0]); + enc_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[1]); + enc_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[2]); + enc_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[3]); + delete contexts[c].ic_offset_diff; + delete contexts[c].ic_packet_size; + delete contexts[c].ic_return_point; + delete contexts[c].ic_xyz; + } + } + + /* destroy all outstreams and encoders */ + + if (outstream_wavepacket) + { + delete outstream_wavepacket; + + delete enc_wavepacket; + } +} + +inline BOOL LASwriteItemCompressed_WAVEPACKET14_v3::createAndInitModelsAndCompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (contexts[context].m_packet_index == 0) + { + contexts[context].m_packet_index = enc_wavepacket->createSymbolModel(256); + contexts[context].m_offset_diff[0] = enc_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[1] = enc_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[2] = enc_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[3] = enc_wavepacket->createSymbolModel(4); + contexts[context].ic_offset_diff = new IntegerCompressor(enc_wavepacket, 32); + contexts[context].ic_packet_size = new IntegerCompressor(enc_wavepacket, 32); + contexts[context].ic_return_point = new IntegerCompressor(enc_wavepacket, 32); + contexts[context].ic_xyz = new IntegerCompressor(enc_wavepacket, 32, 3); + } + + /* then init entropy models */ + + enc_wavepacket->initSymbolModel(contexts[context].m_packet_index); + enc_wavepacket->initSymbolModel(contexts[context].m_offset_diff[0]); + enc_wavepacket->initSymbolModel(contexts[context].m_offset_diff[1]); + enc_wavepacket->initSymbolModel(contexts[context].m_offset_diff[2]); + enc_wavepacket->initSymbolModel(contexts[context].m_offset_diff[3]); + contexts[context].ic_offset_diff->initCompressor(); + contexts[context].ic_packet_size->initCompressor(); + contexts[context].ic_return_point->initCompressor(); + contexts[context].ic_xyz->initCompressor(); + + /* init current context from item */ + + contexts[context].last_diff_32 = 0; + contexts[context].sym_last_offset_diff = 0; + memcpy(contexts[context].last_item, item, 29); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASwriteItemCompressed_WAVEPACKET14_v3::init(const U8* item, U32& context) +{ + /* on the first init create outstreams and encoders */ + + if (outstream_wavepacket == 0) + { + /* create outstreams */ + + if (IS_LITTLE_ENDIAN()) + { + outstream_wavepacket = new ByteStreamOutArrayLE(); + } + else + { + outstream_wavepacket = new ByteStreamOutArrayBE(); + } + + /* create layer encoders */ + + enc_wavepacket = new ArithmeticEncoder(); + } + else + { + /* otherwise just seek back */ + + outstream_wavepacket->seek(0); + } + + /* init layer encoders */ + + enc_wavepacket->init(outstream_wavepacket); + + /* set changed booleans to FALSE */ + + changed_wavepacket = FALSE; + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 writer + + /* create and init entropy models and integer compressors (and init contect from item) */ + + createAndInitModelsAndCompressors(current_context, item); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_WAVEPACKET14_v3::write(const U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 writer + if (contexts[current_context].unused) + { + createAndInitModelsAndCompressors(current_context, (U8*)last_item); + last_item = contexts[current_context].last_item; + } + } + + if (memcmp(item, last_item, 29) != 0) + { + changed_wavepacket = TRUE; + } + + // compress + + enc_wavepacket->encodeSymbol(contexts[current_context].m_packet_index, (U32)(item[0])); + + LASwavepacket13 this_item_m = LASwavepacket13::unpack(item+1); + LASwavepacket13 last_item_m = LASwavepacket13::unpack(last_item+1); + + // calculate the difference between the two offsets + I64 curr_diff_64 = this_item_m.offset - last_item_m.offset; + I32 curr_diff_32 = (I32)curr_diff_64; + + // if the current difference can be represented with 32 bits + if (curr_diff_64 == (I64)(curr_diff_32)) + { + if (curr_diff_32 == 0) // current difference is zero + { + enc_wavepacket->encodeSymbol(contexts[current_context].m_offset_diff[contexts[current_context].sym_last_offset_diff], 0); + contexts[current_context].sym_last_offset_diff = 0; + } + else if (curr_diff_32 == (I32)last_item_m.packet_size) + { + enc_wavepacket->encodeSymbol(contexts[current_context].m_offset_diff[contexts[current_context].sym_last_offset_diff], 1); + contexts[current_context].sym_last_offset_diff = 1; + } + else // + { + enc_wavepacket->encodeSymbol(contexts[current_context].m_offset_diff[contexts[current_context].sym_last_offset_diff], 2); + contexts[current_context].sym_last_offset_diff = 2; + contexts[current_context].ic_offset_diff->compress(contexts[current_context].last_diff_32, curr_diff_32); + contexts[current_context].last_diff_32 = curr_diff_32; + } + } + else + { + enc_wavepacket->encodeSymbol(contexts[current_context].m_offset_diff[contexts[current_context].sym_last_offset_diff], 3); + contexts[current_context].sym_last_offset_diff = 3; + + enc_wavepacket->writeInt64(this_item_m.offset); + } + + contexts[current_context].ic_packet_size->compress(last_item_m.packet_size, this_item_m.packet_size); + contexts[current_context].ic_return_point->compress(last_item_m.return_point.i32, this_item_m.return_point.i32); + contexts[current_context].ic_xyz->compress(last_item_m.x.i32, this_item_m.x.i32, 0); + contexts[current_context].ic_xyz->compress(last_item_m.y.i32, this_item_m.y.i32, 1); + contexts[current_context].ic_xyz->compress(last_item_m.z.i32, this_item_m.z.i32, 2); + + memcpy(last_item, item, 29); + return TRUE; +} + +inline BOOL LASwriteItemCompressed_WAVEPACKET14_v3::chunk_sizes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // finish the encoders + + enc_wavepacket->done(); + + // output the sizes of all layer (i.e.. number of bytes per layer) + + if (changed_wavepacket) + { + num_bytes = (U32)outstream_wavepacket->getCurr(); + num_bytes_wavepacket += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_WAVEPACKET14_v3::chunk_bytes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the bytes of all layers + + if (changed_wavepacket) + { + num_bytes = (U32)outstream_wavepacket->getCurr(); + outstream->putBytes(outstream_wavepacket->getData(), num_bytes); + } + + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_BYTE14_v3 +=============================================================================== +*/ + +LASwriteItemCompressed_BYTE14_v3::LASwriteItemCompressed_BYTE14_v3(ArithmeticEncoder* enc, U32 number) +{ + /* not used as a encoder. just gives access to outstream */ + + assert(enc); + this->enc = enc; + + /* must be more than one byte */ + + assert(number); + this->number = number; + + /* zero outstream and encoder pointer arrays */ + + outstream_Bytes = 0; + + enc_Bytes = 0; + + /* number of bytes per layer */ + + num_bytes_Bytes = new U32[number]; + + changed_Bytes = new BOOL[number]; + + U32 i; + for (i = 0; i < number; i++) + { + num_bytes_Bytes[i] = 0; + + changed_Bytes[i] = FALSE; + } + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_bytes = 0; + } + current_context = 0; +} + +LASwriteItemCompressed_BYTE14_v3::~LASwriteItemCompressed_BYTE14_v3() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c, i; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_bytes) + { + for (i = 0; i < number; i++) + { + enc_Bytes[i]->destroySymbolModel(contexts[c].m_bytes[i]); + } + delete [] contexts[c].m_bytes; + delete [] contexts[c].last_item; + } + } + + /* destroy all outstream and encoder arrays */ + + if (outstream_Bytes) + { + for (i = 0; i < number; i++) + { + if (outstream_Bytes[i]) + { + delete outstream_Bytes[i]; + delete enc_Bytes[i]; + } + } + + delete [] outstream_Bytes; + delete [] enc_Bytes; + } + + /* destroy all other arrays */ + + if (num_bytes_Bytes) delete [] num_bytes_Bytes; + + if (changed_Bytes) delete [] changed_Bytes; +} + +inline BOOL LASwriteItemCompressed_BYTE14_v3::createAndInitModelsAndCompressors(U32 context, const U8* item) +{ + U32 i; + + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models and last items (if needed) */ + + if (contexts[context].m_bytes == 0) + { + contexts[context].m_bytes = new ArithmeticModel*[number]; + for (i = 0; i < number; i++) + { + contexts[context].m_bytes[i] = enc_Bytes[i]->createSymbolModel(256); + enc_Bytes[i]->initSymbolModel(contexts[context].m_bytes[i]); + } + + /* create last item */ + contexts[context].last_item = new U8[number]; + } + + /* then init entropy models */ + + for (i = 0; i < number; i++) + { + enc_Bytes[i]->initSymbolModel(contexts[context].m_bytes[i]); + } + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, number); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASwriteItemCompressed_BYTE14_v3::init(const U8* item, U32& context) +{ + U32 i; + + /* on the first init create outstreams and encoders */ + + if (outstream_Bytes == 0) + { + /* create outstreams pointer array */ + + outstream_Bytes = new ByteStreamOutArray*[number]; + + /* create outstreams */ + + if (IS_LITTLE_ENDIAN()) + { + for (i = 0; i < number; i++) + { + outstream_Bytes[i] = new ByteStreamOutArrayLE(); + } + } + else + { + for (i = 0; i < number; i++) + { + outstream_Bytes[i] = new ByteStreamOutArrayBE(); + } + } + + /* create encoder pointer array */ + + enc_Bytes = new ArithmeticEncoder*[number]; + + /* create layer encoders */ + + for (i = 0; i < number; i++) + { + enc_Bytes[i] = new ArithmeticEncoder(); + } + } + else + { + /* otherwise just seek back */ + + for (i = 0; i < number; i++) + { + outstream_Bytes[i]->seek(0); + } + } + + /* init layer encoders */ + + for (i = 0; i < number; i++) + { + enc_Bytes[i]->init(outstream_Bytes[i]); + } + + /* set changed booleans to FALSE */ + + for (i = 0; i < number; i++) + { + changed_Bytes[i] = FALSE; + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 writer + + /* create and init entropy models and integer compressors (and init context from item) */ + + createAndInitModelsAndCompressors(current_context, item); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_BYTE14_v3::write(const U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 writer + if (contexts[current_context].unused) + { + createAndInitModelsAndCompressors(current_context, last_item); + last_item = contexts[current_context].last_item; + } + } + + // compress + + U32 i; + I32 diff; + for (i = 0; i < number; i++) + { + diff = item[i] - last_item[i]; + enc_Bytes[i]->encodeSymbol(contexts[current_context].m_bytes[i], U8_FOLD(diff)); + if (diff) + { + changed_Bytes[i] = TRUE; + last_item[i] = item[i]; + } + } + return TRUE; +} + +inline BOOL LASwriteItemCompressed_BYTE14_v3::chunk_sizes() +{ + U32 i; + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the sizes of all layer (i.e.. number of bytes per layer) + + for (i = 0; i < number; i++) + { + // finish the encoders + enc_Bytes[i]->done(); + + if (changed_Bytes[i]) + { + num_bytes = (U32)outstream_Bytes[i]->getCurr(); + num_bytes_Bytes[i] += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + } + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_BYTE14_v3::chunk_bytes() +{ + U32 i; + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the bytes of all layers + + for (i = 0; i < number; i++) + { + if (changed_Bytes[i]) + { + num_bytes = (U32)outstream_Bytes[i]->getCurr(); + outstream->putBytes(outstream_Bytes[i]->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + } + + return TRUE; +} diff --git a/libs/laszip/src/laswriteitemcompressed_v3.hpp b/libs/laszip/src/laswriteitemcompressed_v3.hpp new file mode 100644 index 0000000..b1a1d09 --- /dev/null +++ b/libs/laszip/src/laswriteitemcompressed_v3.hpp @@ -0,0 +1,244 @@ +/* +=============================================================================== + + FILE: laswriteitemcompressed_v3.hpp + + CONTENTS: + + Native extension for compressing the *new* point types 6 to 10 of LAS 1.4 + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 August 2017 -- moving 'context' from global development hack to interface + 22 August 2016 -- finalizing at Basecamp in Bonn during FOSS4g hackfest + 23 February 2016 -- created at OSGeo Code Sprint in Paris to prototype + +=============================================================================== +*/ +#ifndef LAS_WRITE_ITEM_COMPRESSED_V3_HPP +#define LAS_WRITE_ITEM_COMPRESSED_V3_HPP + +#include "laswriteitem.hpp" +#include "arithmeticencoder.hpp" +#include "integercompressor.hpp" +#include "bytestreamout_array.hpp" + +#include "laszip_common_v3.hpp" + +class LASwriteItemCompressed_POINT14_v3 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_POINT14_v3(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + BOOL chunk_sizes(); + BOOL chunk_bytes(); + + ~LASwriteItemCompressed_POINT14_v3(); + +private: + + /* not used as a encoder. just gives access to outstream */ + + ArithmeticEncoder* enc; + + ByteStreamOutArray* outstream_channel_returns_XY; + ByteStreamOutArray* outstream_Z; + ByteStreamOutArray* outstream_classification; + ByteStreamOutArray* outstream_flags; + ByteStreamOutArray* outstream_intensity; + ByteStreamOutArray* outstream_scan_angle; + ByteStreamOutArray* outstream_user_data; + ByteStreamOutArray* outstream_point_source; + ByteStreamOutArray* outstream_gps_time; + + ArithmeticEncoder* enc_channel_returns_XY; + ArithmeticEncoder* enc_Z; + ArithmeticEncoder* enc_classification; + ArithmeticEncoder* enc_flags; + ArithmeticEncoder* enc_intensity; + ArithmeticEncoder* enc_scan_angle; + ArithmeticEncoder* enc_user_data; + ArithmeticEncoder* enc_point_source; + ArithmeticEncoder* enc_gps_time; + + BOOL changed_classification; + BOOL changed_flags; + BOOL changed_intensity; + BOOL changed_scan_angle; + BOOL changed_user_data; + BOOL changed_point_source; + BOOL changed_gps_time; + + U32 num_bytes_channel_returns_XY; + U32 num_bytes_Z; + U32 num_bytes_classification; + U32 num_bytes_flags; + U32 num_bytes_intensity; + U32 num_bytes_scan_angle; + U32 num_bytes_user_data; + U32 num_bytes_point_source; + U32 num_bytes_gps_time; + + U32 current_context; + LAScontextPOINT14 contexts[4]; + + BOOL createAndInitModelsAndCompressors(U32 context, const U8* item); + void write_gps_time(const U64I64F64 gps_time); +}; + +class LASwriteItemCompressed_RGB14_v3 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_RGB14_v3(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + BOOL chunk_sizes(); + BOOL chunk_bytes(); + + ~LASwriteItemCompressed_RGB14_v3(); + +private: + + /* not used as a encoder. just gives access to outstream */ + + ArithmeticEncoder* enc; + + ByteStreamOutArray* outstream_RGB; + + ArithmeticEncoder* enc_RGB; + + BOOL changed_RGB; + + U32 num_bytes_RGB; + + U32 current_context; + LAScontextRGB14 contexts[4]; + + BOOL createAndInitModelsAndCompressors(U32 context, const U8* item); +}; + +class LASwriteItemCompressed_RGBNIR14_v3 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_RGBNIR14_v3(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + BOOL chunk_sizes(); + BOOL chunk_bytes(); + + ~LASwriteItemCompressed_RGBNIR14_v3(); + +private: + + /* not used as a encoder. just gives access to outstream */ + + ArithmeticEncoder* enc; + + ByteStreamOutArray* outstream_RGB; + ByteStreamOutArray* outstream_NIR; + + ArithmeticEncoder* enc_RGB; + ArithmeticEncoder* enc_NIR; + + BOOL changed_RGB; + BOOL changed_NIR; + + U32 num_bytes_RGB; + U32 num_bytes_NIR; + + U32 current_context; + LAScontextRGBNIR14 contexts[4]; + + BOOL createAndInitModelsAndCompressors(U32 context, const U8* item); +}; + +class LASwriteItemCompressed_WAVEPACKET14_v3 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_WAVEPACKET14_v3(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + BOOL chunk_sizes(); + BOOL chunk_bytes(); + + ~LASwriteItemCompressed_WAVEPACKET14_v3(); + +private: + + /* not used as a encoder. just gives access to outstream */ + + ArithmeticEncoder* enc; + + ByteStreamOutArray* outstream_wavepacket; + + ArithmeticEncoder* enc_wavepacket; + + BOOL changed_wavepacket; + + U32 num_bytes_wavepacket; + + U32 current_context; + LAScontextWAVEPACKET14 contexts[4]; + + BOOL createAndInitModelsAndCompressors(U32 context, const U8* item); +}; + +class LASwriteItemCompressed_BYTE14_v3 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_BYTE14_v3(ArithmeticEncoder* enc, U32 number); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + BOOL chunk_sizes(); + BOOL chunk_bytes(); + + ~LASwriteItemCompressed_BYTE14_v3(); + +private: + + /* not used as a encoder. just gives access to outstream */ + + ArithmeticEncoder* enc; + + ByteStreamOutArray** outstream_Bytes; + + ArithmeticEncoder** enc_Bytes; + + U32* num_bytes_Bytes; + + BOOL* changed_Bytes; + + U32 current_context; + LAScontextBYTE14 contexts[4]; + + U32 number; + BOOL createAndInitModelsAndCompressors(U32 context, const U8* item); +}; + +#endif diff --git a/libs/laszip/src/laswriteitemcompressed_v4.cpp b/libs/laszip/src/laswriteitemcompressed_v4.cpp new file mode 100644 index 0000000..c967006 --- /dev/null +++ b/libs/laszip/src/laswriteitemcompressed_v4.cpp @@ -0,0 +1,2299 @@ +/* +=============================================================================== + + FILE: laswriteitemcompressed_v4.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ + +#include "laswriteitemcompressed_v4.hpp" + +#include +#include +#include + +/* +=============================================================================== + LASwriteItemCompressed_POINT14_v4 +=============================================================================== +*/ + +typedef struct LASpoint14 +{ + I32 X; + I32 Y; + I32 Z; + U16 intensity; + U8 legacy_return_number : 3; + U8 legacy_number_of_returns : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 legacy_classification : 5; + U8 legacy_flags : 3; + I8 legacy_scan_angle_rank; + U8 user_data; + U16 point_source_ID; + + // LAS 1.4 only + I16 scan_angle; + U8 legacy_point_type : 2; + U8 scanner_channel : 2; + U8 classification_flags : 4; + U8 classification; + U8 return_number : 4; + U8 number_of_returns : 4; + + // LASlib internal use only + U8 deleted_flag; + + // for 8 byte alignment of the GPS time + U8 dummy[2]; + + // compressed LASzip 1.4 points only + BOOL gps_time_change; + + F64 gps_time; + U16 rgb[4]; +// LASwavepacket wavepacket; +} LASpoint14; + +#define LASZIP_GPSTIME_MULTI 500 +#define LASZIP_GPSTIME_MULTI_MINUS -10 +#define LASZIP_GPSTIME_MULTI_CODE_FULL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 1) + +#define LASZIP_GPSTIME_MULTI_TOTAL (LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS + 5) + +LASwriteItemCompressed_POINT14_v4::LASwriteItemCompressed_POINT14_v4(ArithmeticEncoder* enc) +{ + /* not used as a encoder. just gives access to outstream */ + + assert(enc); + this->enc = enc; + + /* zero outstreams and encoders */ + + outstream_channel_returns_XY = 0; + outstream_Z = 0; + outstream_classification = 0; + outstream_flags = 0; + outstream_intensity = 0; + outstream_scan_angle = 0; + outstream_user_data = 0; + outstream_point_source = 0; + outstream_gps_time = 0; + + enc_channel_returns_XY = 0; + enc_Z = 0; + enc_classification = 0; + enc_flags = 0; + enc_intensity = 0; + enc_scan_angle = 0; + enc_user_data = 0; + enc_point_source = 0; + enc_gps_time = 0; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_changed_values[0] = 0; + } + current_context = 0; + + /* number of bytes per layer */ + + num_bytes_channel_returns_XY = 0; + num_bytes_Z = 0; + num_bytes_classification = 0; + num_bytes_flags = 0; + num_bytes_intensity = 0; + num_bytes_scan_angle = 0; + num_bytes_user_data = 0; + num_bytes_point_source = 0; + num_bytes_gps_time = 0; +} + +LASwriteItemCompressed_POINT14_v4::~LASwriteItemCompressed_POINT14_v4() +{ + U32 c, i; + + /* destroy all initialized scanner channel contexts */ + + for (c = 0; c < 4; c++) + { + if (contexts[c].m_changed_values[0]) + { + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[0]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[1]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[2]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[3]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[4]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[5]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[6]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_changed_values[7]); + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_scanner_channel); + for (i = 0; i < 16; i++) + { + if (contexts[c].m_number_of_returns[i]) enc_channel_returns_XY->destroySymbolModel(contexts[c].m_number_of_returns[i]); + if (contexts[c].m_return_number[i]) enc_channel_returns_XY->destroySymbolModel(contexts[c].m_return_number[i]); + } + enc_channel_returns_XY->destroySymbolModel(contexts[c].m_return_number_gps_same); + delete contexts[c].ic_dX; + delete contexts[c].ic_dY; + delete contexts[c].ic_Z; + for (i = 0; i < 64; i++) + { + if (contexts[c].m_classification[i]) enc_classification->destroySymbolModel(contexts[c].m_classification[i]); + if (contexts[c].m_flags[i]) enc_flags->destroySymbolModel(contexts[c].m_flags[i]); + if (contexts[c].m_user_data[i]) enc_user_data->destroySymbolModel(contexts[c].m_user_data[i]); + } + delete contexts[c].ic_intensity; + delete contexts[c].ic_scan_angle; + delete contexts[c].ic_point_source_ID; + enc_gps_time->destroySymbolModel(contexts[c].m_gpstime_multi); + enc_gps_time->destroySymbolModel(contexts[c].m_gpstime_0diff); + delete contexts[c].ic_gpstime; + } + } + + /* destroy all encoders and outstreams */ + + if (outstream_channel_returns_XY) + { + delete enc_channel_returns_XY; + delete enc_Z; + delete enc_classification; + delete enc_flags; + delete enc_intensity; + delete enc_scan_angle; + delete enc_user_data; + delete enc_point_source; + delete enc_gps_time; + + delete outstream_channel_returns_XY; + delete outstream_Z; + delete outstream_classification; + delete outstream_flags; + delete outstream_intensity; + delete outstream_scan_angle; + delete outstream_user_data; + delete outstream_point_source; + delete outstream_gps_time; + } +} + +inline BOOL LASwriteItemCompressed_POINT14_v4::createAndInitModelsAndCompressors(U32 context, const U8* item) +{ + I32 i; + + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models and integer compressors (if needed) */ + + if (contexts[context].m_changed_values[0] == 0) + { + /* for the channel_returns_XY layer */ + + contexts[context].m_changed_values[0] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[1] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[2] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[3] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[4] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[5] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[6] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_changed_values[7] = enc_channel_returns_XY->createSymbolModel(128); + contexts[context].m_scanner_channel = enc_channel_returns_XY->createSymbolModel(3); + for (i = 0; i < 16; i++) + { + contexts[context].m_number_of_returns[i] = 0; + contexts[context].m_return_number[i] = 0; + } + contexts[context].m_return_number_gps_same = enc_channel_returns_XY->createSymbolModel(13); + + contexts[context].ic_dX = new IntegerCompressor(enc_channel_returns_XY, 32, 2); // 32 bits, 2 context + contexts[context].ic_dY = new IntegerCompressor(enc_channel_returns_XY, 32, 22); // 32 bits, 22 contexts + + /* for the Z layer */ + + contexts[context].ic_Z = new IntegerCompressor(enc_Z, 32, 20); // 32 bits, 20 contexts + + /* for the classification layer */ + /* for the flags layer */ + /* for the user_data layer */ + + for (i = 0; i < 64; i++) + { + contexts[context].m_classification[i] = 0; + contexts[context].m_flags[i] = 0; + contexts[context].m_user_data[i] = 0; + } + + /* for the intensity layer */ + + contexts[context].ic_intensity = new IntegerCompressor(enc_intensity, 16, 4); + + /* for the scan_angle layer */ + + contexts[context].ic_scan_angle = new IntegerCompressor(enc_scan_angle, 16, 2); + + /* for the point_source_ID layer */ + + contexts[context].ic_point_source_ID = new IntegerCompressor(enc_point_source, 16); + + /* for the gps_time layer */ + + contexts[context].m_gpstime_multi = enc_gps_time->createSymbolModel(LASZIP_GPSTIME_MULTI_TOTAL); + contexts[context].m_gpstime_0diff = enc_gps_time->createSymbolModel(5); + contexts[context].ic_gpstime = new IntegerCompressor(enc_gps_time, 32, 9); // 32 bits, 9 contexts + } + + /* then init entropy models and integer compressors */ + + /* for the channel_returns_XY layer */ + + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[0]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[1]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[2]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[3]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[4]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[5]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[6]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_changed_values[7]); + enc_channel_returns_XY->initSymbolModel(contexts[context].m_scanner_channel); + for (i = 0; i < 16; i++) + { + if (contexts[context].m_number_of_returns[i]) enc_channel_returns_XY->initSymbolModel(contexts[context].m_number_of_returns[i]); + if (contexts[context].m_return_number[i]) enc_channel_returns_XY->initSymbolModel(contexts[context].m_return_number[i]); + } + enc_channel_returns_XY->initSymbolModel(contexts[context].m_return_number_gps_same); + contexts[context].ic_dX->initCompressor(); + contexts[context].ic_dY->initCompressor(); + for (i = 0; i < 12; i++) + { + contexts[context].last_X_diff_median5[i].init(); + contexts[context].last_Y_diff_median5[i].init(); + } + + /* for the Z layer */ + + contexts[context].ic_Z->initCompressor(); + for (i = 0; i < 8; i++) + { + contexts[context].last_Z[i] = ((LASpoint14*)item)->Z; + } + + /* for the classification layer */ + /* for the flags layer */ + /* for the user_data layer */ + + for (i = 0; i < 64; i++) + { + if (contexts[context].m_classification[i]) enc_classification->initSymbolModel(contexts[context].m_classification[i]); + if (contexts[context].m_flags[i]) enc_flags->initSymbolModel(contexts[context].m_flags[i]); + if (contexts[context].m_user_data[i]) enc_user_data->initSymbolModel(contexts[context].m_user_data[i]); + } + + /* for the intensity layer */ + + contexts[context].ic_intensity->initCompressor(); + for (i = 0; i < 8; i++) + { + contexts[context].last_intensity[i] = ((LASpoint14*)item)->intensity; + } + + /* for the scan_angle layer */ + + contexts[context].ic_scan_angle->initCompressor(); + + /* for the point_source_ID layer */ + + contexts[context].ic_point_source_ID->initCompressor(); + + /* for the gps_time layer */ + + enc_gps_time->initSymbolModel(contexts[context].m_gpstime_multi); + enc_gps_time->initSymbolModel(contexts[context].m_gpstime_0diff); + contexts[context].ic_gpstime->initCompressor(); + contexts[context].last = 0, contexts[context].next = 0; + contexts[context].last_gpstime_diff[0] = 0; + contexts[context].last_gpstime_diff[1] = 0; + contexts[context].last_gpstime_diff[2] = 0; + contexts[context].last_gpstime_diff[3] = 0; + contexts[context].multi_extreme_counter[0] = 0; + contexts[context].multi_extreme_counter[1] = 0; + contexts[context].multi_extreme_counter[2] = 0; + contexts[context].multi_extreme_counter[3] = 0; + contexts[context].last_gpstime[0].f64 = ((LASpoint14*)item)->gps_time; + contexts[context].last_gpstime[1].u64 = 0; + contexts[context].last_gpstime[2].u64 = 0; + contexts[context].last_gpstime[3].u64 = 0; + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, sizeof(LASpoint14)); + ((LASpoint14*)contexts[context].last_item)->gps_time_change = FALSE; + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASwriteItemCompressed_POINT14_v4::init(const U8* item, U32& context) +{ + /* on the first init create outstreams and encoders */ + + if (outstream_channel_returns_XY == 0) + { + if (IS_LITTLE_ENDIAN()) + { + outstream_channel_returns_XY = new ByteStreamOutArrayLE(); + outstream_Z = new ByteStreamOutArrayLE(); + outstream_classification = new ByteStreamOutArrayLE(); + outstream_flags = new ByteStreamOutArrayLE(); + outstream_intensity = new ByteStreamOutArrayLE(); + outstream_scan_angle = new ByteStreamOutArrayLE(); + outstream_user_data = new ByteStreamOutArrayLE(); + outstream_point_source = new ByteStreamOutArrayLE(); + outstream_gps_time = new ByteStreamOutArrayLE(); + } + else + { + outstream_channel_returns_XY = new ByteStreamOutArrayBE(); + outstream_Z = new ByteStreamOutArrayBE(); + outstream_classification = new ByteStreamOutArrayBE(); + outstream_flags = new ByteStreamOutArrayBE(); + outstream_intensity = new ByteStreamOutArrayBE(); + outstream_scan_angle = new ByteStreamOutArrayBE(); + outstream_user_data = new ByteStreamOutArrayBE(); + outstream_point_source = new ByteStreamOutArrayBE(); + outstream_gps_time = new ByteStreamOutArrayBE(); + } + + /* create layer encoders */ + + enc_channel_returns_XY = new ArithmeticEncoder(); + enc_Z = new ArithmeticEncoder(); + enc_classification = new ArithmeticEncoder(); + enc_flags = new ArithmeticEncoder(); + enc_intensity = new ArithmeticEncoder(); + enc_scan_angle = new ArithmeticEncoder(); + enc_user_data = new ArithmeticEncoder(); + enc_point_source = new ArithmeticEncoder(); + enc_gps_time = new ArithmeticEncoder(); + } + else + { + /* otherwise just seek back */ + + outstream_channel_returns_XY->seek(0); + outstream_Z->seek(0); + outstream_classification->seek(0); + outstream_flags->seek(0); + outstream_intensity->seek(0); + outstream_scan_angle->seek(0); + outstream_user_data->seek(0); + outstream_point_source->seek(0); + outstream_gps_time->seek(0); + } + + /* init layer encoders */ + + enc_channel_returns_XY->init(outstream_channel_returns_XY); + enc_Z->init(outstream_Z); + enc_classification->init(outstream_classification); + enc_flags->init(outstream_flags); + enc_intensity->init(outstream_intensity); + enc_scan_angle->init(outstream_scan_angle); + enc_user_data->init(outstream_user_data); + enc_point_source->init(outstream_point_source); + enc_gps_time->init(outstream_gps_time); + + /* set changed booleans to FALSE */ + + changed_classification = FALSE; + changed_flags = FALSE; + changed_intensity = FALSE; + changed_scan_angle = FALSE; + changed_user_data = FALSE; + changed_point_source = FALSE; + changed_gps_time = FALSE; + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = ((LASpoint14*)item)->scanner_channel; + context = current_context; // the POINT14 writer sets context for all other items + + /* create and init entropy models and integer compressors (and init context from item) */ + + createAndInitModelsAndCompressors(current_context, item); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_POINT14_v4::write(const U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + //////////////////////////////////////// + // compress returns_XY layer + //////////////////////////////////////// + + // create single (3) / first (1) / last (2) / intermediate (0) context from last point return + + I32 lpr = (((LASpoint14*)last_item)->return_number == 1 ? 1 : 0); // first? + lpr += (((LASpoint14*)last_item)->return_number >= ((LASpoint14*)last_item)->number_of_returns ? 2 : 0); // last? + + // add info whether the GPS time changed in the last return to the context + + lpr += (((LASpoint14*)last_item)->gps_time_change ? 4 : 0); + + // get the (potentially new) context + + U32 scanner_channel = ((LASpoint14*)item)->scanner_channel; + + // if context has changed (and the new context already exists) get last for new context + + if (scanner_channel != current_context) + { + if (contexts[scanner_channel].unused == FALSE) + { + last_item = contexts[scanner_channel].last_item; + } + } + + // determine changed attributes + + BOOL point_source_change = (((LASpoint14*)item)->point_source_ID != ((LASpoint14*)last_item)->point_source_ID); + BOOL gps_time_change = (((LASpoint14*)item)->gps_time != ((LASpoint14*)last_item)->gps_time); + BOOL scan_angle_change = (((LASpoint14*)item)->scan_angle != ((LASpoint14*)last_item)->scan_angle); + + // get last and current return counts + + U32 last_n = ((LASpoint14*)last_item)->number_of_returns; + U32 last_r = ((LASpoint14*)last_item)->return_number; + + U32 n = ((LASpoint14*)item)->number_of_returns; + U32 r = ((LASpoint14*)item)->return_number; + + // create the 7 bit mask that encodes various changes (its value ranges from 0 to 127) + + I32 changed_values = ((scanner_channel != current_context) << 6) | // scanner channel compared to last point (same = 0 / different = 1) + (point_source_change << 5) | // point source ID compared to last point from *same* scanner channel (same = 0 / different = 1) + (gps_time_change << 4) | // GPS time stamp compared to last point from *same* scanner channel (same = 0 / different = 1) + (scan_angle_change << 3) | // scan angle compared to last point from *same* scanner channel (same = 0 / different = 1) + ((n != last_n) << 2); // number of returns compared to last point from *same* scanner channel (same = 0 / different = 1) + + // return number compared to last point of *same* scanner channel (same = 0 / plus one mod 16 = 1 / minus one mod 16 = 2 / other difference = 3) + + if (r != last_r) + { + if (r == ((last_r + 1) % 16)) + { + changed_values |= 1; + } + else if (r == ((last_r + 15) % 16)) + { + changed_values |= 2; + } + else + { + changed_values |= 3; + } + } + + // compress the 7 bit mask that encodes changes with last point return context + + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_changed_values[lpr], changed_values); + + // if scanner channel has changed, record change + + if (changed_values & (1 << 6)) + { + I32 diff = scanner_channel - current_context; + if (diff > 0) + { + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_scanner_channel, diff - 1); // curr = last + (sym + 1) + } + else + { + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_scanner_channel, diff + 4 - 1); // curr = (last + (sym + 1)) % 4 + } + // maybe create and init entropy models and integer compressors + if (contexts[scanner_channel].unused) + { + // create and init entropy models and integer compressors (and init context from last item) + createAndInitModelsAndCompressors(scanner_channel, contexts[current_context].last_item); + // get last for new context + last_item = contexts[scanner_channel].last_item; + } + // switch context to current scanner channel + current_context = scanner_channel; + } + context = current_context; // the POINT14 writer sets context for all other items + + // if number of returns is different we compress it + + if (changed_values & (1 << 2)) + { + if (contexts[current_context].m_number_of_returns[last_n] == 0) + { + contexts[current_context].m_number_of_returns[last_n] = enc_channel_returns_XY->createSymbolModel(16); + enc_channel_returns_XY->initSymbolModel(contexts[current_context].m_number_of_returns[last_n]); + } + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_number_of_returns[last_n], n); + } + + // if return number is different and difference is bigger than +1 / -1 we compress how it is different + + if ((changed_values & 3) == 3) + { + if (gps_time_change) // if the GPS time has changed + { + if (contexts[current_context].m_return_number[last_r] == 0) + { + contexts[current_context].m_return_number[last_r] = enc_channel_returns_XY->createSymbolModel(16); + enc_channel_returns_XY->initSymbolModel(contexts[current_context].m_return_number[last_r]); + } + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_return_number[last_r], r); + } + else // if the GPS time has not changed + { + I32 diff = r - last_r; + if (diff > 1) + { + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_return_number_gps_same, diff - 2); // r = last_r + (sym + 2) with sym = diff - 2 + } + else + { + enc_channel_returns_XY->encodeSymbol(contexts[current_context].m_return_number_gps_same, diff + 16 - 2); // r = (last_r + (sym + 2)) % 16 with sym = diff + 16 - 2 + } + } + } + + // get return map m and return level l context for current point + + U32 m = number_return_map_6ctx[n][r]; + U32 l = number_return_level_8ctx[n][r]; + + // create single (3) / first (1) / last (2) / intermediate (0) return context for current point + + I32 cpr = (r == 1 ? 2 : 0); // first ? + cpr += (r >= n ? 1 : 0); // last ? + + U32 k_bits; + I32 median, diff; + + // compress X coordinate + median = contexts[current_context].last_X_diff_median5[(m<<1) | gps_time_change].get(); + diff = ((LASpoint14*)item)->X - ((LASpoint14*)last_item)->X; + contexts[current_context].ic_dX->compress(median, diff, n==1); + contexts[current_context].last_X_diff_median5[(m<<1) | gps_time_change].add(diff); + + // compress Y coordinate + k_bits = contexts[current_context].ic_dX->getK(); + median = contexts[current_context].last_Y_diff_median5[(m<<1) | gps_time_change].get(); + diff = ((LASpoint14*)item)->Y - ((LASpoint14*)last_item)->Y; + contexts[current_context].ic_dY->compress(median, diff, (n==1) + ( k_bits < 20 ? U32_ZERO_BIT_0(k_bits) : 20 )); + contexts[current_context].last_Y_diff_median5[(m<<1) | gps_time_change].add(diff); + + //////////////////////////////////////// + // compress Z layer + //////////////////////////////////////// + + k_bits = (contexts[current_context].ic_dX->getK() + contexts[current_context].ic_dY->getK()) / 2; + contexts[current_context].ic_Z->compress(contexts[current_context].last_Z[l], ((LASpoint14*)item)->Z, (n==1) + (k_bits < 18 ? U32_ZERO_BIT_0(k_bits) : 18)); + contexts[current_context].last_Z[l] = ((LASpoint14*)item)->Z; + + //////////////////////////////////////// + // compress classifications layer + //////////////////////////////////////// + + U32 last_classification = ((LASpoint14*)last_item)->classification; + U32 classification = ((LASpoint14*)item)->classification; + + if (classification != last_classification) + { + changed_classification = TRUE; + } + + I32 ccc = ((last_classification & 0x1F) << 1) + (cpr == 3 ? 1 : 0); + if (contexts[current_context].m_classification[ccc] == 0) + { + contexts[current_context].m_classification[ccc] = enc_classification->createSymbolModel(256); + enc_classification->initSymbolModel(contexts[current_context].m_classification[ccc]); + } + enc_classification->encodeSymbol(contexts[current_context].m_classification[ccc], classification); + + //////////////////////////////////////// + // compress flags layer + //////////////////////////////////////// + + U32 last_flags = (((LASpoint14*)last_item)->edge_of_flight_line << 5) | (((LASpoint14*)last_item)->scan_direction_flag << 4) | ((LASpoint14*)last_item)->classification_flags; + U32 flags = (((LASpoint14*)item)->edge_of_flight_line << 5) | (((LASpoint14*)item)->scan_direction_flag << 4) | ((LASpoint14*)item)->classification_flags; + + if (flags != last_flags) + { + changed_flags = TRUE; + } + + if (contexts[current_context].m_flags[last_flags] == 0) + { + contexts[current_context].m_flags[last_flags] = enc_flags->createSymbolModel(64); + enc_flags->initSymbolModel(contexts[current_context].m_flags[last_flags]); + } + enc_flags->encodeSymbol(contexts[current_context].m_flags[last_flags], flags); + + //////////////////////////////////////// + // compress intensity layer + //////////////////////////////////////// + + if (((LASpoint14*)item)->intensity != ((LASpoint14*)last_item)->intensity) + { + changed_intensity = TRUE; + } + contexts[current_context].ic_intensity->compress(contexts[current_context].last_intensity[(cpr<<1) | gps_time_change], ((LASpoint14*)item)->intensity, cpr); + contexts[current_context].last_intensity[(cpr<<1) | gps_time_change] = ((LASpoint14*)item)->intensity; + + //////////////////////////////////////// + // compress scan_angle layer + //////////////////////////////////////// + + if (scan_angle_change) + { + changed_scan_angle = TRUE; + contexts[current_context].ic_scan_angle->compress(((LASpoint14*)last_item)->scan_angle, ((LASpoint14*)item)->scan_angle, gps_time_change); // if the GPS time has changed + } + + //////////////////////////////////////// + // compress user_data layer + //////////////////////////////////////// + + if (((LASpoint14*)item)->user_data != ((LASpoint14*)last_item)->user_data) + { + changed_user_data = TRUE; + } + if (contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4] == 0) + { + contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4] = enc_user_data->createSymbolModel(256); + enc_user_data->initSymbolModel(contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4]); + } + enc_user_data->encodeSymbol(contexts[current_context].m_user_data[((LASpoint14*)last_item)->user_data/4], ((LASpoint14*)item)->user_data); + + //////////////////////////////////////// + // compress point_source layer + //////////////////////////////////////// + + if (point_source_change) + { + changed_point_source = TRUE; + contexts[current_context].ic_point_source_ID->compress(((LASpoint14*)last_item)->point_source_ID, ((LASpoint14*)item)->point_source_ID); + } + + //////////////////////////////////////// + // compress gps_time layer + //////////////////////////////////////// + + if (gps_time_change) // if the GPS time has changed + { + changed_gps_time = TRUE; + + U64I64F64 gps_time; + gps_time.f64 = ((LASpoint14*)item)->gps_time; + + write_gps_time(gps_time); + } + + // copy the last item + memcpy(last_item, item, sizeof(LASpoint14)); + // remember if the last point had a gps_time_change + ((LASpoint14*)last_item)->gps_time_change = gps_time_change; + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_POINT14_v4::chunk_sizes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // finish the encoders + + enc_channel_returns_XY->done(); + enc_Z->done(); + if (changed_classification) + { + enc_classification->done(); + } + if (changed_flags) + { + enc_flags->done(); + } + if (changed_intensity) + { + enc_intensity->done(); + } + if (changed_scan_angle) + { + enc_scan_angle->done(); + } + if (changed_user_data) + { + enc_user_data->done(); + } + if (changed_point_source) + { + enc_point_source->done(); + } + if (changed_gps_time) + { + enc_gps_time->done(); + } + + // output the sizes of all layer (i.e.. number of bytes per layer) + + num_bytes = (U32)outstream_channel_returns_XY->getCurr(); + num_bytes_channel_returns_XY += num_bytes; + outstream->put32bitsLE(((U8*)&num_bytes)); + + num_bytes = (U32)outstream_Z->getCurr(); + num_bytes_Z += num_bytes; + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_classification) + { + num_bytes = (U32)outstream_classification->getCurr(); + num_bytes_classification += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_flags) + { + num_bytes = (U32)outstream_flags->getCurr(); + num_bytes_flags += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_intensity) + { + num_bytes = (U32)outstream_intensity->getCurr(); + num_bytes_intensity += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_scan_angle) + { + num_bytes = (U32)outstream_scan_angle->getCurr(); + num_bytes_scan_angle += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_user_data) + { + num_bytes = (U32)outstream_user_data->getCurr(); + num_bytes_user_data += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_point_source) + { + num_bytes = (U32)outstream_point_source->getCurr(); + num_bytes_point_source += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_gps_time) + { + num_bytes = (U32)outstream_gps_time->getCurr(); + num_bytes_gps_time += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_POINT14_v4::chunk_bytes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the bytes of all layers + + num_bytes = (U32)outstream_channel_returns_XY->getCurr(); + outstream->putBytes(outstream_channel_returns_XY->getData(), num_bytes); + + num_bytes = (U32)outstream_Z->getCurr(); + outstream->putBytes(outstream_Z->getData(), num_bytes); + + if (changed_classification) + { + num_bytes = (U32)outstream_classification->getCurr(); + outstream->putBytes(outstream_classification->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_flags) + { + num_bytes = (U32)outstream_flags->getCurr(); + outstream->putBytes(outstream_flags->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_intensity) + { + num_bytes = (U32)outstream_intensity->getCurr(); + outstream->putBytes(outstream_intensity->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_scan_angle) + { + num_bytes = (U32)outstream_scan_angle->getCurr(); + outstream->putBytes(outstream_scan_angle->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_user_data) + { + num_bytes = (U32)outstream_user_data->getCurr(); + outstream->putBytes(outstream_user_data->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_point_source) + { + num_bytes = (U32)outstream_point_source->getCurr(); + outstream->putBytes(outstream_point_source->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_gps_time) + { + num_bytes = (U32)outstream_gps_time->getCurr(); + outstream->putBytes(outstream_gps_time->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + return TRUE; +} + +void LASwriteItemCompressed_POINT14_v4::write_gps_time(const U64I64F64 gps_time) +{ + if (contexts[current_context].last_gpstime_diff[contexts[current_context].last] == 0) // if the last integer difference was zero + { + // calculate the difference between the two doubles as an integer + I64 curr_gpstime_diff_64 = gps_time.i64 - contexts[current_context].last_gpstime[contexts[current_context].last].i64; + I32 curr_gpstime_diff = (I32)curr_gpstime_diff_64; + if (curr_gpstime_diff_64 == (I64)(curr_gpstime_diff)) + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_0diff, 0); // the difference can be represented with 32 bits + contexts[current_context].ic_gpstime->compress(0, curr_gpstime_diff, 0); + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = curr_gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else // the difference is huge + { + U32 i; + // maybe the double belongs to another time sequence + for (i = 1; i < 4; i++) + { + I64 other_gpstime_diff_64 = gps_time.i64 - contexts[current_context].last_gpstime[(contexts[current_context].last+i)&3].i64; + I32 other_gpstime_diff = (I32)other_gpstime_diff_64; + if (other_gpstime_diff_64 == (I64)(other_gpstime_diff)) + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_0diff, i+1); // it belongs to another sequence + contexts[current_context].last = (contexts[current_context].last+i)&3; + write_gps_time(gps_time); + return; + } + } + // no other sequence found. start new sequence. + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_0diff, 1); + contexts[current_context].ic_gpstime->compress((I32)(contexts[current_context].last_gpstime[contexts[current_context].last].u64 >> 32), (I32)(gps_time.u64 >> 32), 8); + enc_gps_time->writeInt((U32)(gps_time.u64)); + contexts[current_context].next = (contexts[current_context].next+1)&3; + contexts[current_context].last = contexts[current_context].next; + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = 0; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + contexts[current_context].last_gpstime[contexts[current_context].last].i64 = gps_time.i64; + } + else // the last integer difference was *not* zero + { + // calculate the difference between the two doubles as an integer + I64 curr_gpstime_diff_64 = gps_time.i64 - contexts[current_context].last_gpstime[contexts[current_context].last].i64; + I32 curr_gpstime_diff = (I32)curr_gpstime_diff_64; + + // if the current gpstime difference can be represented with 32 bits + if (curr_gpstime_diff_64 == (I64)(curr_gpstime_diff)) + { + // compute multiplier between current and last integer difference + F32 multi_f = (F32)curr_gpstime_diff / (F32)(contexts[current_context].last_gpstime_diff[contexts[current_context].last]); + I32 multi = I32_QUANTIZE(multi_f); + + // compress the residual curr_gpstime_diff in dependance on the multiplier + if (multi == 1) + { + // this is the case we assume we get most often for regular spaced pulses + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, 1); + contexts[current_context].ic_gpstime->compress(contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 1); + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + else if (multi > 0) + { + if (multi < LASZIP_GPSTIME_MULTI) // positive multipliers up to LASZIP_GPSTIME_MULTI are compressed directly + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, multi); + if (multi < 10) + contexts[current_context].ic_gpstime->compress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 2); + else + contexts[current_context].ic_gpstime->compress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 3); + } + else + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, LASZIP_GPSTIME_MULTI); + contexts[current_context].ic_gpstime->compress(LASZIP_GPSTIME_MULTI*contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 4); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = curr_gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + } + else if (multi < 0) + { + if (multi > LASZIP_GPSTIME_MULTI_MINUS) // negative multipliers larger than LASZIP_GPSTIME_MULTI_MINUS are compressed directly + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, LASZIP_GPSTIME_MULTI - multi); + contexts[current_context].ic_gpstime->compress(multi*contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 5); + } + else + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, LASZIP_GPSTIME_MULTI - LASZIP_GPSTIME_MULTI_MINUS); + contexts[current_context].ic_gpstime->compress(LASZIP_GPSTIME_MULTI_MINUS*contexts[current_context].last_gpstime_diff[contexts[current_context].last], curr_gpstime_diff, 6); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = curr_gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + } + else + { + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, 0); + contexts[current_context].ic_gpstime->compress(0, curr_gpstime_diff, 7); + contexts[current_context].multi_extreme_counter[contexts[current_context].last]++; + if (contexts[current_context].multi_extreme_counter[contexts[current_context].last] > 3) + { + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = curr_gpstime_diff; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + } + } + else // the difference is huge + { + U32 i; + // maybe the double belongs to another time sequence + for (i = 1; i < 4; i++) + { + I64 other_gpstime_diff_64 = gps_time.i64 - contexts[current_context].last_gpstime[(contexts[current_context].last+i)&3].i64; + I32 other_gpstime_diff = (I32)other_gpstime_diff_64; + if (other_gpstime_diff_64 == (I64)(other_gpstime_diff)) + { + // it belongs to this sequence + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, LASZIP_GPSTIME_MULTI_CODE_FULL+i); + contexts[current_context].last = (contexts[current_context].last+i)&3; + write_gps_time(gps_time); + return; + } + } + // no other sequence found. start new sequence. + enc_gps_time->encodeSymbol(contexts[current_context].m_gpstime_multi, LASZIP_GPSTIME_MULTI_CODE_FULL); + contexts[current_context].ic_gpstime->compress((I32)(contexts[current_context].last_gpstime[contexts[current_context].last].u64 >> 32), (I32)(gps_time.u64 >> 32), 8); + enc_gps_time->writeInt((U32)(gps_time.u64)); + contexts[current_context].next = (contexts[current_context].next+1)&3; + contexts[current_context].last = contexts[current_context].next; + contexts[current_context].last_gpstime_diff[contexts[current_context].last] = 0; + contexts[current_context].multi_extreme_counter[contexts[current_context].last] = 0; + } + contexts[current_context].last_gpstime[contexts[current_context].last].i64 = gps_time.i64; + } +} + +/* +=============================================================================== + LASwriteItemCompressed_RGB14_v4 +=============================================================================== +*/ + +LASwriteItemCompressed_RGB14_v4::LASwriteItemCompressed_RGB14_v4(ArithmeticEncoder* enc) +{ + /* not used as a encoder. just gives access to outstream */ + + assert(enc); + this->enc = enc; + + /* zero outstreams and encoders */ + + outstream_RGB = 0; + + enc_RGB = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_RGB = 0; + + changed_RGB = FALSE; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_byte_used = 0; + } + current_context = 0; +} + +LASwriteItemCompressed_RGB14_v4::~LASwriteItemCompressed_RGB14_v4() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_byte_used) + { + enc_RGB->destroySymbolModel(contexts[c].m_byte_used); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_0); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_1); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_2); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_3); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_4); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_5); + } + } + + /* destroy all outstreams and encoders */ + + if (outstream_RGB) + { + delete outstream_RGB; + + delete enc_RGB; + } +} + +inline BOOL LASwriteItemCompressed_RGB14_v4::createAndInitModelsAndCompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (contexts[context].m_byte_used == 0) + { + contexts[context].m_byte_used = enc_RGB->createSymbolModel(128); + contexts[context].m_rgb_diff_0 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_1 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_2 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_3 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_4 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_5 = enc_RGB->createSymbolModel(256); + } + + /* then init entropy models */ + + enc_RGB->initSymbolModel(contexts[context].m_byte_used); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_0); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_1); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_2); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_3); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_4); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_5); + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, 6); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASwriteItemCompressed_RGB14_v4::init(const U8* item, U32& context) +{ + /* on the first init create outstreams and encoders */ + + if (outstream_RGB == 0) + { + /* create outstreams */ + + if (IS_LITTLE_ENDIAN()) + { + outstream_RGB = new ByteStreamOutArrayLE(); + } + else + { + outstream_RGB = new ByteStreamOutArrayBE(); + } + + /* create layer encoders */ + + enc_RGB = new ArithmeticEncoder(); + } + else + { + /* otherwise just seek back */ + + outstream_RGB->seek(0); + } + + /* init layer encoders */ + + enc_RGB->init(outstream_RGB); + + /* set changed booleans to FALSE */ + + changed_RGB = FALSE; + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 writer + + /* create and init entropy models and integer compressors (and init contect from item) */ + + createAndInitModelsAndCompressors(current_context, item); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGB14_v4::write(const U8* item, U32& context) +{ + // get last + + U16* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 writer + if (contexts[current_context].unused) + { + createAndInitModelsAndCompressors(current_context, (U8*)last_item); + } + last_item = contexts[current_context].last_item; + } + + // compress + + I32 diff_l = 0; + I32 diff_h = 0; + I32 corr; + U32 sym = ((last_item[0]&0x00FF) != (((U16*)item)[0]&0x00FF)) << 0; + sym |= ((last_item[0]&0xFF00) != (((U16*)item)[0]&0xFF00)) << 1; + sym |= ((last_item[1]&0x00FF) != (((U16*)item)[1]&0x00FF)) << 2; + sym |= ((last_item[1]&0xFF00) != (((U16*)item)[1]&0xFF00)) << 3; + sym |= ((last_item[2]&0x00FF) != (((U16*)item)[2]&0x00FF)) << 4; + sym |= ((last_item[2]&0xFF00) != (((U16*)item)[2]&0xFF00)) << 5; + sym |= (((((U16*)item)[0]&0x00FF) != (((U16*)item)[1]&0x00FF)) || ((((U16*)item)[0]&0x00FF) != (((U16*)item)[2]&0x00FF)) || ((((U16*)item)[0]&0xFF00) != (((U16*)item)[1]&0xFF00)) || ((((U16*)item)[0]&0xFF00) != (((U16*)item)[2]&0xFF00))) << 6; + enc_RGB->encodeSymbol(contexts[current_context].m_byte_used, sym); + if (sym & (1 << 0)) + { + diff_l = ((int)(((U16*)item)[0]&255)) - (last_item[0]&255); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_0, U8_FOLD(diff_l)); + } + if (sym & (1 << 1)) + { + diff_h = ((int)(((U16*)item)[0]>>8)) - (last_item[0]>>8); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_1, U8_FOLD(diff_h)); + } + if (sym & (1 << 6)) + { + if (sym & (1 << 2)) + { + corr = ((int)(((U16*)item)[1]&255)) - U8_CLAMP(diff_l + (last_item[1]&255)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_2, U8_FOLD(corr)); + } + if (sym & (1 << 4)) + { + diff_l = (diff_l + (((U16*)item)[1]&255) - (last_item[1]&255)) / 2; + corr = ((int)(((U16*)item)[2]&255)) - U8_CLAMP(diff_l + (last_item[2]&255)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_4, U8_FOLD(corr)); + } + if (sym & (1 << 3)) + { + corr = ((int)(((U16*)item)[1]>>8)) - U8_CLAMP(diff_h + (last_item[1]>>8)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_3, U8_FOLD(corr)); + } + if (sym & (1 << 5)) + { + diff_h = (diff_h + (((U16*)item)[1]>>8) - (last_item[1]>>8)) / 2; + corr = ((int)(((U16*)item)[2]>>8)) - U8_CLAMP(diff_h + (last_item[2]>>8)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_5, U8_FOLD(corr)); + } + } + if (sym) + { + changed_RGB = TRUE; + } + memcpy(last_item, item, 6); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGB14_v4::chunk_sizes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // finish the encoders + + enc_RGB->done(); + + // output the sizes of all layer (i.e.. number of bytes per layer) + + if (changed_RGB) + { + num_bytes = (U32)outstream_RGB->getCurr(); + num_bytes_RGB += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGB14_v4::chunk_bytes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the bytes of all layers + + if (changed_RGB) + { + num_bytes = (U32)outstream_RGB->getCurr(); + outstream->putBytes(outstream_RGB->getData(), num_bytes); + } + + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_RGBNIR14_v4 +=============================================================================== +*/ + +LASwriteItemCompressed_RGBNIR14_v4::LASwriteItemCompressed_RGBNIR14_v4(ArithmeticEncoder* enc) +{ + /* not used as a encoder. just gives access to outstream */ + + assert(enc); + this->enc = enc; + + /* zero outstreams and encoders */ + + outstream_RGB = 0; + outstream_NIR = 0; + + enc_RGB = 0; + enc_NIR = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_RGB = 0; + num_bytes_NIR = 0; + + changed_RGB = FALSE; + changed_NIR = FALSE; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_rgb_bytes_used = 0; + } + current_context = 0; +} + +LASwriteItemCompressed_RGBNIR14_v4::~LASwriteItemCompressed_RGBNIR14_v4() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_rgb_bytes_used) + { + enc_RGB->destroySymbolModel(contexts[c].m_rgb_bytes_used); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_0); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_1); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_2); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_3); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_4); + enc_RGB->destroySymbolModel(contexts[c].m_rgb_diff_5); + + enc_NIR->destroySymbolModel(contexts[c].m_nir_bytes_used); + enc_NIR->destroySymbolModel(contexts[c].m_nir_diff_0); + enc_NIR->destroySymbolModel(contexts[c].m_nir_diff_1); + } + } + + /* destroy all outstreams and encoders */ + + if (outstream_RGB) + { + delete outstream_RGB; + delete outstream_NIR; + + delete enc_RGB; + delete enc_NIR; + } +} + +inline BOOL LASwriteItemCompressed_RGBNIR14_v4::createAndInitModelsAndCompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (contexts[context].m_rgb_bytes_used == 0) + { + contexts[context].m_rgb_bytes_used = enc_RGB->createSymbolModel(128); + contexts[context].m_rgb_diff_0 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_1 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_2 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_3 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_4 = enc_RGB->createSymbolModel(256); + contexts[context].m_rgb_diff_5 = enc_RGB->createSymbolModel(256); + + contexts[context].m_nir_bytes_used = enc_RGB->createSymbolModel(4); + contexts[context].m_nir_diff_0 = enc_RGB->createSymbolModel(256); + contexts[context].m_nir_diff_1 = enc_RGB->createSymbolModel(256); + } + + /* then init entropy models */ + + enc_RGB->initSymbolModel(contexts[context].m_rgb_bytes_used); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_0); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_1); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_2); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_3); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_4); + enc_RGB->initSymbolModel(contexts[context].m_rgb_diff_5); + + enc_NIR->initSymbolModel(contexts[context].m_nir_bytes_used); + enc_NIR->initSymbolModel(contexts[context].m_nir_diff_0); + enc_NIR->initSymbolModel(contexts[context].m_nir_diff_1); + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, 8); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASwriteItemCompressed_RGBNIR14_v4::init(const U8* item, U32& context) +{ + /* on the first init create outstreams and encoders */ + + if (outstream_RGB == 0) + { + /* create outstreams */ + + if (IS_LITTLE_ENDIAN()) + { + outstream_RGB = new ByteStreamOutArrayLE(); + outstream_NIR = new ByteStreamOutArrayLE(); + } + else + { + outstream_RGB = new ByteStreamOutArrayBE(); + outstream_NIR = new ByteStreamOutArrayBE(); + } + + /* create layer encoders */ + + enc_RGB = new ArithmeticEncoder(); + enc_NIR = new ArithmeticEncoder(); + } + else + { + /* otherwise just seek back */ + + outstream_RGB->seek(0); + outstream_NIR->seek(0); + } + + /* init layer encoders */ + + enc_RGB->init(outstream_RGB); + enc_NIR->init(outstream_NIR); + + /* set changed booleans to FALSE */ + + changed_RGB = FALSE; + changed_NIR = FALSE; + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // ll other items use context set by POINT14 writer + + /* create and init entropy models and integer compressors (and init context from item) */ + + createAndInitModelsAndCompressors(current_context, item); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGBNIR14_v4::write(const U8* item, U32& context) +{ + // get last + + U16* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 writer + if (contexts[current_context].unused) + { + createAndInitModelsAndCompressors(current_context, (U8*)last_item); + } + last_item = contexts[current_context].last_item; + } + + // compress + + I32 diff_l = 0; + I32 diff_h = 0; + I32 corr; + U32 sym = ((last_item[0]&0x00FF) != (((U16*)item)[0]&0x00FF)) << 0; + sym |= ((last_item[0]&0xFF00) != (((U16*)item)[0]&0xFF00)) << 1; + sym |= ((last_item[1]&0x00FF) != (((U16*)item)[1]&0x00FF)) << 2; + sym |= ((last_item[1]&0xFF00) != (((U16*)item)[1]&0xFF00)) << 3; + sym |= ((last_item[2]&0x00FF) != (((U16*)item)[2]&0x00FF)) << 4; + sym |= ((last_item[2]&0xFF00) != (((U16*)item)[2]&0xFF00)) << 5; + sym |= (((((U16*)item)[0]&0x00FF) != (((U16*)item)[1]&0x00FF)) || ((((U16*)item)[0]&0x00FF) != (((U16*)item)[2]&0x00FF)) || ((((U16*)item)[0]&0xFF00) != (((U16*)item)[1]&0xFF00)) || ((((U16*)item)[0]&0xFF00) != (((U16*)item)[2]&0xFF00))) << 6; + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_bytes_used, sym); + if (sym & (1 << 0)) + { + diff_l = ((int)(((U16*)item)[0]&255)) - (last_item[0]&255); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_0, U8_FOLD(diff_l)); + } + if (sym & (1 << 1)) + { + diff_h = ((int)(((U16*)item)[0]>>8)) - (last_item[0]>>8); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_1, U8_FOLD(diff_h)); + } + if (sym & (1 << 6)) + { + if (sym & (1 << 2)) + { + corr = ((int)(((U16*)item)[1]&255)) - U8_CLAMP(diff_l + (last_item[1]&255)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_2, U8_FOLD(corr)); + } + if (sym & (1 << 4)) + { + diff_l = (diff_l + (((U16*)item)[1]&255) - (last_item[1]&255)) / 2; + corr = ((int)(((U16*)item)[2]&255)) - U8_CLAMP(diff_l + (last_item[2]&255)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_4, U8_FOLD(corr)); + } + if (sym & (1 << 3)) + { + corr = ((int)(((U16*)item)[1]>>8)) - U8_CLAMP(diff_h + (last_item[1]>>8)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_3, U8_FOLD(corr)); + } + if (sym & (1 << 5)) + { + diff_h = (diff_h + (((U16*)item)[1]>>8) - (last_item[1]>>8)) / 2; + corr = ((int)(((U16*)item)[2]>>8)) - U8_CLAMP(diff_h + (last_item[2]>>8)); + enc_RGB->encodeSymbol(contexts[current_context].m_rgb_diff_5, U8_FOLD(corr)); + } + } + if (sym) + { + changed_RGB = TRUE; + } + + sym = ((last_item[3]&0x00FF) != (((U16*)item)[3]&0x00FF)) << 0; + sym |= ((last_item[3]&0xFF00) != (((U16*)item)[3]&0xFF00)) << 1; + enc_NIR->encodeSymbol(contexts[current_context].m_nir_bytes_used, sym); + if (sym & (1 << 0)) + { + diff_l = ((int)(((U16*)item)[3]&255)) - (last_item[3]&255); + enc_NIR->encodeSymbol(contexts[current_context].m_nir_diff_0, U8_FOLD(diff_l)); + } + if (sym & (1 << 1)) + { + diff_h = ((int)(((U16*)item)[3]>>8)) - (last_item[3]>>8); + enc_NIR->encodeSymbol(contexts[current_context].m_nir_diff_1, U8_FOLD(diff_h)); + } + if (sym) + { + changed_NIR = TRUE; + } + + memcpy(last_item, item, 8); + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGBNIR14_v4::chunk_sizes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // finish the encoders + + enc_RGB->done(); + enc_NIR->done(); + + // output the sizes of all layer (i.e.. number of bytes per layer) + + if (changed_RGB) + { + num_bytes = (U32)outstream_RGB->getCurr(); + num_bytes_RGB += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + if (changed_NIR) + { + num_bytes = (U32)outstream_NIR->getCurr(); + num_bytes_NIR += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_RGBNIR14_v4::chunk_bytes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the bytes of all layers + + if (changed_RGB) + { + num_bytes = (U32)outstream_RGB->getCurr(); + outstream->putBytes(outstream_RGB->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + if (changed_NIR) + { + num_bytes = (U32)outstream_NIR->getCurr(); + outstream->putBytes(outstream_NIR->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_WAVEPACKET14_v4 +=============================================================================== +*/ + +LASwriteItemCompressed_WAVEPACKET14_v4::LASwriteItemCompressed_WAVEPACKET14_v4(ArithmeticEncoder* enc) +{ + /* not used as a encoder. just gives access to outstream */ + + assert(enc); + this->enc = enc; + + /* zero outstreams and encoders */ + + outstream_wavepacket = 0; + + enc_wavepacket = 0; + + /* zero num_bytes and init booleans */ + + num_bytes_wavepacket = 0; + + changed_wavepacket = FALSE; + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_packet_index = 0; + } + current_context = 0; +} + +LASwriteItemCompressed_WAVEPACKET14_v4::~LASwriteItemCompressed_WAVEPACKET14_v4() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_packet_index) + { + enc_wavepacket->destroySymbolModel(contexts[c].m_packet_index); + enc_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[0]); + enc_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[1]); + enc_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[2]); + enc_wavepacket->destroySymbolModel(contexts[c].m_offset_diff[3]); + delete contexts[c].ic_offset_diff; + delete contexts[c].ic_packet_size; + delete contexts[c].ic_return_point; + delete contexts[c].ic_xyz; + } + } + + /* destroy all outstreams and encoders */ + + if (outstream_wavepacket) + { + delete outstream_wavepacket; + + delete enc_wavepacket; + } +} + +inline BOOL LASwriteItemCompressed_WAVEPACKET14_v4::createAndInitModelsAndCompressors(U32 context, const U8* item) +{ + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models (if needed) */ + + if (contexts[context].m_packet_index == 0) + { + contexts[context].m_packet_index = enc_wavepacket->createSymbolModel(256); + contexts[context].m_offset_diff[0] = enc_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[1] = enc_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[2] = enc_wavepacket->createSymbolModel(4); + contexts[context].m_offset_diff[3] = enc_wavepacket->createSymbolModel(4); + contexts[context].ic_offset_diff = new IntegerCompressor(enc_wavepacket, 32); + contexts[context].ic_packet_size = new IntegerCompressor(enc_wavepacket, 32); + contexts[context].ic_return_point = new IntegerCompressor(enc_wavepacket, 32); + contexts[context].ic_xyz = new IntegerCompressor(enc_wavepacket, 32, 3); + } + + /* then init entropy models */ + + enc_wavepacket->initSymbolModel(contexts[context].m_packet_index); + enc_wavepacket->initSymbolModel(contexts[context].m_offset_diff[0]); + enc_wavepacket->initSymbolModel(contexts[context].m_offset_diff[1]); + enc_wavepacket->initSymbolModel(contexts[context].m_offset_diff[2]); + enc_wavepacket->initSymbolModel(contexts[context].m_offset_diff[3]); + contexts[context].ic_offset_diff->initCompressor(); + contexts[context].ic_packet_size->initCompressor(); + contexts[context].ic_return_point->initCompressor(); + contexts[context].ic_xyz->initCompressor(); + + /* init current context from item */ + + contexts[context].last_diff_32 = 0; + contexts[context].sym_last_offset_diff = 0; + memcpy(contexts[context].last_item, item, 29); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASwriteItemCompressed_WAVEPACKET14_v4::init(const U8* item, U32& context) +{ + /* on the first init create outstreams and encoders */ + + if (outstream_wavepacket == 0) + { + /* create outstreams */ + + if (IS_LITTLE_ENDIAN()) + { + outstream_wavepacket = new ByteStreamOutArrayLE(); + } + else + { + outstream_wavepacket = new ByteStreamOutArrayBE(); + } + + /* create layer encoders */ + + enc_wavepacket = new ArithmeticEncoder(); + } + else + { + /* otherwise just seek back */ + + outstream_wavepacket->seek(0); + } + + /* init layer encoders */ + + enc_wavepacket->init(outstream_wavepacket); + + /* set changed booleans to FALSE */ + + changed_wavepacket = FALSE; + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 writer + + /* create and init entropy models and integer compressors (and init contect from item) */ + + createAndInitModelsAndCompressors(current_context, item); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_WAVEPACKET14_v4::write(const U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 writer + if (contexts[current_context].unused) + { + createAndInitModelsAndCompressors(current_context, (U8*)last_item); + } + last_item = contexts[current_context].last_item; + } + + if (memcmp(item, last_item, 29) != 0) + { + changed_wavepacket = TRUE; + } + + // compress + + enc_wavepacket->encodeSymbol(contexts[current_context].m_packet_index, (U32)(item[0])); + + LASwavepacket13 this_item_m = LASwavepacket13::unpack(item+1); + LASwavepacket13 last_item_m = LASwavepacket13::unpack(last_item+1); + + // calculate the difference between the two offsets + I64 curr_diff_64 = this_item_m.offset - last_item_m.offset; + I32 curr_diff_32 = (I32)curr_diff_64; + + // if the current difference can be represented with 32 bits + if (curr_diff_64 == (I64)(curr_diff_32)) + { + if (curr_diff_32 == 0) // current difference is zero + { + enc_wavepacket->encodeSymbol(contexts[current_context].m_offset_diff[contexts[current_context].sym_last_offset_diff], 0); + contexts[current_context].sym_last_offset_diff = 0; + } + else if (curr_diff_32 == (I32)last_item_m.packet_size) + { + enc_wavepacket->encodeSymbol(contexts[current_context].m_offset_diff[contexts[current_context].sym_last_offset_diff], 1); + contexts[current_context].sym_last_offset_diff = 1; + } + else // + { + enc_wavepacket->encodeSymbol(contexts[current_context].m_offset_diff[contexts[current_context].sym_last_offset_diff], 2); + contexts[current_context].sym_last_offset_diff = 2; + contexts[current_context].ic_offset_diff->compress(contexts[current_context].last_diff_32, curr_diff_32); + contexts[current_context].last_diff_32 = curr_diff_32; + } + } + else + { + enc_wavepacket->encodeSymbol(contexts[current_context].m_offset_diff[contexts[current_context].sym_last_offset_diff], 3); + contexts[current_context].sym_last_offset_diff = 3; + + enc_wavepacket->writeInt64(this_item_m.offset); + } + + contexts[current_context].ic_packet_size->compress(last_item_m.packet_size, this_item_m.packet_size); + contexts[current_context].ic_return_point->compress(last_item_m.return_point.i32, this_item_m.return_point.i32); + contexts[current_context].ic_xyz->compress(last_item_m.x.i32, this_item_m.x.i32, 0); + contexts[current_context].ic_xyz->compress(last_item_m.y.i32, this_item_m.y.i32, 1); + contexts[current_context].ic_xyz->compress(last_item_m.z.i32, this_item_m.z.i32, 2); + + memcpy(last_item, item, 29); + return TRUE; +} + +inline BOOL LASwriteItemCompressed_WAVEPACKET14_v4::chunk_sizes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // finish the encoders + + enc_wavepacket->done(); + + // output the sizes of all layer (i.e.. number of bytes per layer) + + if (changed_wavepacket) + { + num_bytes = (U32)outstream_wavepacket->getCurr(); + num_bytes_wavepacket += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_WAVEPACKET14_v4::chunk_bytes() +{ + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the bytes of all layers + + if (changed_wavepacket) + { + num_bytes = (U32)outstream_wavepacket->getCurr(); + outstream->putBytes(outstream_wavepacket->getData(), num_bytes); + } + + return TRUE; +} + +/* +=============================================================================== + LASwriteItemCompressed_BYTE14_v4 +=============================================================================== +*/ + +LASwriteItemCompressed_BYTE14_v4::LASwriteItemCompressed_BYTE14_v4(ArithmeticEncoder* enc, U32 number) +{ + /* not used as a encoder. just gives access to outstream */ + + assert(enc); + this->enc = enc; + + /* must be more than one byte */ + + assert(number); + this->number = number; + + /* zero outstream and encoder pointer arrays */ + + outstream_Bytes = 0; + + enc_Bytes = 0; + + /* number of bytes per layer */ + + num_bytes_Bytes = new U32[number]; + + changed_Bytes = new BOOL[number]; + + U32 i; + for (i = 0; i < number; i++) + { + num_bytes_Bytes[i] = 0; + + changed_Bytes[i] = FALSE; + } + + /* mark the four scanner channel contexts as uninitialized */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].m_bytes = 0; + } + current_context = 0; +} + +LASwriteItemCompressed_BYTE14_v4::~LASwriteItemCompressed_BYTE14_v4() +{ + /* destroy all initialized scanner channel contexts */ + + U32 c, i; + for (c = 0; c < 4; c++) + { + if (contexts[c].m_bytes) + { + for (i = 0; i < number; i++) + { + enc_Bytes[i]->destroySymbolModel(contexts[c].m_bytes[i]); + } + delete [] contexts[c].m_bytes; + delete [] contexts[c].last_item; + } + } + + /* destroy all outstream and encoder arrays */ + + if (outstream_Bytes) + { + for (i = 0; i < number; i++) + { + if (outstream_Bytes[i]) + { + delete outstream_Bytes[i]; + delete enc_Bytes[i]; + } + } + + delete [] outstream_Bytes; + delete [] enc_Bytes; + } + + /* destroy all other arrays */ + + if (num_bytes_Bytes) delete [] num_bytes_Bytes; + + if (changed_Bytes) delete [] changed_Bytes; +} + +inline BOOL LASwriteItemCompressed_BYTE14_v4::createAndInitModelsAndCompressors(U32 context, const U8* item) +{ + U32 i; + + /* should only be called when context is unused */ + + assert(contexts[context].unused); + + /* first create all entropy models and last items (if needed) */ + + if (contexts[context].m_bytes == 0) + { + contexts[context].m_bytes = new ArithmeticModel*[number]; + for (i = 0; i < number; i++) + { + contexts[context].m_bytes[i] = enc_Bytes[i]->createSymbolModel(256); + enc_Bytes[i]->initSymbolModel(contexts[context].m_bytes[i]); + } + + /* create last item */ + contexts[context].last_item = new U8[number]; + } + + /* then init entropy models */ + + for (i = 0; i < number; i++) + { + enc_Bytes[i]->initSymbolModel(contexts[context].m_bytes[i]); + } + + /* init current context from item */ + + memcpy(contexts[context].last_item, item, number); + + contexts[context].unused = FALSE; + + return TRUE; +} + +BOOL LASwriteItemCompressed_BYTE14_v4::init(const U8* item, U32& context) +{ + U32 i; + + /* on the first init create outstreams and encoders */ + + if (outstream_Bytes == 0) + { + /* create outstreams pointer array */ + + outstream_Bytes = new ByteStreamOutArray*[number]; + + /* create outstreams */ + + if (IS_LITTLE_ENDIAN()) + { + for (i = 0; i < number; i++) + { + outstream_Bytes[i] = new ByteStreamOutArrayLE(); + } + } + else + { + for (i = 0; i < number; i++) + { + outstream_Bytes[i] = new ByteStreamOutArrayBE(); + } + } + + /* create encoder pointer array */ + + enc_Bytes = new ArithmeticEncoder*[number]; + + /* create layer encoders */ + + for (i = 0; i < number; i++) + { + enc_Bytes[i] = new ArithmeticEncoder(); + } + } + else + { + /* otherwise just seek back */ + + for (i = 0; i < number; i++) + { + outstream_Bytes[i]->seek(0); + } + } + + /* init layer encoders */ + + for (i = 0; i < number; i++) + { + enc_Bytes[i]->init(outstream_Bytes[i]); + } + + /* set changed booleans to FALSE */ + + for (i = 0; i < number; i++) + { + changed_Bytes[i] = FALSE; + } + + /* mark the four scanner channel contexts as unused */ + + U32 c; + for (c = 0; c < 4; c++) + { + contexts[c].unused = TRUE; + } + + /* set scanner channel as current context */ + + current_context = context; // all other items use context set by POINT14 writer + + /* create and init entropy models and integer compressors (and init context from item) */ + + createAndInitModelsAndCompressors(current_context, item); + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_BYTE14_v4::write(const U8* item, U32& context) +{ + // get last + + U8* last_item = contexts[current_context].last_item; + + // check for context switch + + if (current_context != context) + { + current_context = context; // all other items use context set by POINT14 writer + if (contexts[current_context].unused) + { + createAndInitModelsAndCompressors(current_context, last_item); + } + last_item = contexts[current_context].last_item; + } + + // compress + + U32 i; + I32 diff; + for (i = 0; i < number; i++) + { + diff = item[i] - last_item[i]; + enc_Bytes[i]->encodeSymbol(contexts[current_context].m_bytes[i], U8_FOLD(diff)); + if (diff) + { + changed_Bytes[i] = TRUE; + last_item[i] = item[i]; + } + } + return TRUE; +} + +inline BOOL LASwriteItemCompressed_BYTE14_v4::chunk_sizes() +{ + U32 i; + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the sizes of all layer (i.e.. number of bytes per layer) + + for (i = 0; i < number; i++) + { + // finish the encoders + enc_Bytes[i]->done(); + + if (changed_Bytes[i]) + { + num_bytes = (U32)outstream_Bytes[i]->getCurr(); + num_bytes_Bytes[i] += num_bytes; + } + else + { + num_bytes = 0; + } + outstream->put32bitsLE(((U8*)&num_bytes)); + } + + return TRUE; +} + +inline BOOL LASwriteItemCompressed_BYTE14_v4::chunk_bytes() +{ + U32 i; + U32 num_bytes = 0; + ByteStreamOut* outstream = enc->getByteStreamOut(); + + // output the bytes of all layers + + for (i = 0; i < number; i++) + { + if (changed_Bytes[i]) + { + num_bytes = (U32)outstream_Bytes[i]->getCurr(); + outstream->putBytes(outstream_Bytes[i]->getData(), num_bytes); + } + else + { + num_bytes = 0; + } + } + + return TRUE; +} diff --git a/libs/laszip/src/laswriteitemcompressed_v4.hpp b/libs/laszip/src/laswriteitemcompressed_v4.hpp new file mode 100644 index 0000000..11d1ae7 --- /dev/null +++ b/libs/laszip/src/laswriteitemcompressed_v4.hpp @@ -0,0 +1,245 @@ +/* +=============================================================================== + + FILE: laswriteitemcompressed_v4.hpp + + CONTENTS: + + Native extension for compressing the *new* point types 6 to 10 of LAS 1.4 + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 December 2017 -- fix incorrect 'context switch' reported by Wanwannodao + 28 August 2017 -- moving 'context' from global development hack to interface + 22 August 2016 -- finalizing at Basecamp in Bonn during FOSS4g hackfest + 23 February 2016 -- created at OSGeo Code Sprint in Paris to prototype + +=============================================================================== +*/ +#ifndef LAS_WRITE_ITEM_COMPRESSED_V4_HPP +#define LAS_WRITE_ITEM_COMPRESSED_V4_HPP + +#include "laswriteitem.hpp" +#include "arithmeticencoder.hpp" +#include "integercompressor.hpp" +#include "bytestreamout_array.hpp" + +#include "laszip_common_v3.hpp" + +class LASwriteItemCompressed_POINT14_v4 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_POINT14_v4(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + BOOL chunk_sizes(); + BOOL chunk_bytes(); + + ~LASwriteItemCompressed_POINT14_v4(); + +private: + + /* not used as a encoder. just gives access to outstream */ + + ArithmeticEncoder* enc; + + ByteStreamOutArray* outstream_channel_returns_XY; + ByteStreamOutArray* outstream_Z; + ByteStreamOutArray* outstream_classification; + ByteStreamOutArray* outstream_flags; + ByteStreamOutArray* outstream_intensity; + ByteStreamOutArray* outstream_scan_angle; + ByteStreamOutArray* outstream_user_data; + ByteStreamOutArray* outstream_point_source; + ByteStreamOutArray* outstream_gps_time; + + ArithmeticEncoder* enc_channel_returns_XY; + ArithmeticEncoder* enc_Z; + ArithmeticEncoder* enc_classification; + ArithmeticEncoder* enc_flags; + ArithmeticEncoder* enc_intensity; + ArithmeticEncoder* enc_scan_angle; + ArithmeticEncoder* enc_user_data; + ArithmeticEncoder* enc_point_source; + ArithmeticEncoder* enc_gps_time; + + BOOL changed_classification; + BOOL changed_flags; + BOOL changed_intensity; + BOOL changed_scan_angle; + BOOL changed_user_data; + BOOL changed_point_source; + BOOL changed_gps_time; + + U32 num_bytes_channel_returns_XY; + U32 num_bytes_Z; + U32 num_bytes_classification; + U32 num_bytes_flags; + U32 num_bytes_intensity; + U32 num_bytes_scan_angle; + U32 num_bytes_user_data; + U32 num_bytes_point_source; + U32 num_bytes_gps_time; + + U32 current_context; + LAScontextPOINT14 contexts[4]; + + BOOL createAndInitModelsAndCompressors(U32 context, const U8* item); + void write_gps_time(const U64I64F64 gps_time); +}; + +class LASwriteItemCompressed_RGB14_v4 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_RGB14_v4(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + BOOL chunk_sizes(); + BOOL chunk_bytes(); + + ~LASwriteItemCompressed_RGB14_v4(); + +private: + + /* not used as a encoder. just gives access to outstream */ + + ArithmeticEncoder* enc; + + ByteStreamOutArray* outstream_RGB; + + ArithmeticEncoder* enc_RGB; + + BOOL changed_RGB; + + U32 num_bytes_RGB; + + U32 current_context; + LAScontextRGB14 contexts[4]; + + BOOL createAndInitModelsAndCompressors(U32 context, const U8* item); +}; + +class LASwriteItemCompressed_RGBNIR14_v4 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_RGBNIR14_v4(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + BOOL chunk_sizes(); + BOOL chunk_bytes(); + + ~LASwriteItemCompressed_RGBNIR14_v4(); + +private: + + /* not used as a encoder. just gives access to outstream */ + + ArithmeticEncoder* enc; + + ByteStreamOutArray* outstream_RGB; + ByteStreamOutArray* outstream_NIR; + + ArithmeticEncoder* enc_RGB; + ArithmeticEncoder* enc_NIR; + + BOOL changed_RGB; + BOOL changed_NIR; + + U32 num_bytes_RGB; + U32 num_bytes_NIR; + + U32 current_context; + LAScontextRGBNIR14 contexts[4]; + + BOOL createAndInitModelsAndCompressors(U32 context, const U8* item); +}; + +class LASwriteItemCompressed_WAVEPACKET14_v4 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_WAVEPACKET14_v4(ArithmeticEncoder* enc); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + BOOL chunk_sizes(); + BOOL chunk_bytes(); + + ~LASwriteItemCompressed_WAVEPACKET14_v4(); + +private: + + /* not used as a encoder. just gives access to outstream */ + + ArithmeticEncoder* enc; + + ByteStreamOutArray* outstream_wavepacket; + + ArithmeticEncoder* enc_wavepacket; + + BOOL changed_wavepacket; + + U32 num_bytes_wavepacket; + + U32 current_context; + LAScontextWAVEPACKET14 contexts[4]; + + BOOL createAndInitModelsAndCompressors(U32 context, const U8* item); +}; + +class LASwriteItemCompressed_BYTE14_v4 : public LASwriteItemCompressed +{ +public: + + LASwriteItemCompressed_BYTE14_v4(ArithmeticEncoder* enc, U32 number); + + BOOL init(const U8* item, U32& context); + BOOL write(const U8* item, U32& context); + BOOL chunk_sizes(); + BOOL chunk_bytes(); + + ~LASwriteItemCompressed_BYTE14_v4(); + +private: + + /* not used as a encoder. just gives access to outstream */ + + ArithmeticEncoder* enc; + + ByteStreamOutArray** outstream_Bytes; + + ArithmeticEncoder** enc_Bytes; + + U32* num_bytes_Bytes; + + BOOL* changed_Bytes; + + U32 current_context; + LAScontextBYTE14 contexts[4]; + + U32 number; + BOOL createAndInitModelsAndCompressors(U32 context, const U8* item); +}; + +#endif diff --git a/libs/laszip/src/laswriteitemraw.hpp b/libs/laszip/src/laswriteitemraw.hpp new file mode 100644 index 0000000..01e2fc4 --- /dev/null +++ b/libs/laszip/src/laswriteitemraw.hpp @@ -0,0 +1,323 @@ +/* +=============================================================================== + + FILE: laswriteitemraw.hpp + + CONTENTS: + + Implementation of LASwriteItemRaw for *all* items that compose a point. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2018, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 29 September 2018 -- fix: extended_classification when classification not set + 28 August 2017 -- moving 'context' from global development hack to interface + 10 January 2011 -- licensing change for LGPL release and liblas integration + 7 January 2011 -- introduced swap buffers to reduce number of fwrite calls + 12 December 2010 -- refactored after watching two movies with silke + +=============================================================================== +*/ +#ifndef LAS_WRITE_ITEM_RAW_HPP +#define LAS_WRITE_ITEM_RAW_HPP + +#include "laswriteitem.hpp" + +#include + +class LASwriteItemRaw_POINT10_LE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_POINT10_LE(){}; + inline BOOL write(const U8* item, U32& context) + { + return outstream->putBytes(item, 20); + }; +}; + +class LASwriteItemRaw_POINT10_BE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_POINT10_BE(){}; + inline BOOL write(const U8* item, U32& context) + { + ENDIAN_SWAP_32(&item[ 0], &swapped[ 0]); // X + ENDIAN_SWAP_32(&item[ 4], &swapped[ 4]); // Y + ENDIAN_SWAP_32(&item[ 8], &swapped[ 8]); // Z + ENDIAN_SWAP_16(&item[12], &swapped[12]); // intensity + *((U32*)&swapped[14]) = *((U32*)&item[14]); // bitfield, classification, scan_angle_rank, user_data + ENDIAN_SWAP_16(&item[18], &swapped[18]); // point_source_ID + return outstream->putBytes(swapped, 20); + }; +private: + U8 swapped[20]; +}; + +class LASwriteItemRaw_GPSTIME11_LE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_GPSTIME11_LE() {}; + inline BOOL write(const U8* item, U32& context) + { + return outstream->putBytes(item, 8); + }; +}; + +class LASwriteItemRaw_GPSTIME11_BE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_GPSTIME11_BE() {}; + inline BOOL write(const U8* item, U32& context) + { + ENDIAN_SWAP_64(item, swapped); + return outstream->putBytes(swapped, 8); + }; +private: + U8 swapped[8]; +}; + +class LASwriteItemRaw_RGB12_LE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_RGB12_LE(){} + inline BOOL write(const U8* item, U32& context) + { + return outstream->putBytes(item, 6); + }; +}; + +class LASwriteItemRaw_RGB12_BE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_RGB12_BE(){} + inline BOOL write(const U8* item, U32& context) + { + ENDIAN_SWAP_32(&item[ 0], &swapped[ 0]); // R + ENDIAN_SWAP_32(&item[ 2], &swapped[ 2]); // G + ENDIAN_SWAP_32(&item[ 4], &swapped[ 4]); // B + return outstream->putBytes(swapped, 6); + }; +private: + U8 swapped[6]; +}; + +class LASwriteItemRaw_WAVEPACKET13_LE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_WAVEPACKET13_LE(){} + inline BOOL write(const U8* item, U32& context) + { + return outstream->putBytes(item, 29); + }; +}; + +class LASwriteItemRaw_WAVEPACKET13_BE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_WAVEPACKET13_BE(){} + inline BOOL write(const U8* item, U32& context) + { + swapped[0] = item[0]; // wavepacket descriptor index + ENDIAN_SWAP_64(&item[ 1], &swapped[ 1]); // byte offset to waveform data + ENDIAN_SWAP_32(&item[ 9], &swapped[ 9]); // waveform packet size in bytes + ENDIAN_SWAP_32(&item[13], &swapped[13]); // return point waveform location + ENDIAN_SWAP_32(&item[17], &swapped[17]); // X(t) + ENDIAN_SWAP_32(&item[21], &swapped[21]); // Y(t) + ENDIAN_SWAP_32(&item[25], &swapped[25]); // Z(t) + return outstream->putBytes(swapped, 29); + }; +private: + U8 swapped[29]; +}; + +class LASwriteItemRaw_BYTE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_BYTE(U32 number) + { + this->number = number; + } + inline BOOL write(const U8* item, U32& context) + { + return outstream->putBytes(item, number); + }; +private: + U32 number; +}; + +class LAStempWritePoint10 +{ +public: + I32 X; + I32 Y; + I32 Z; + U16 intensity; + U8 return_number : 3; + U8 number_of_returns : 3; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 classification; + I8 scan_angle_rank; + U8 user_data; + U16 point_source_ID; + + // LAS 1.4 only + I16 extended_scan_angle; + U8 extended_point_type : 2; + U8 extended_scanner_channel : 2; + U8 extended_classification_flags : 4; + U8 extended_classification; + U8 extended_return_number : 4; + U8 extended_number_of_returns : 4; + + // for 8 byte alignment of the GPS time + U8 dummy[3]; + + // LASlib only + U32 deleted_flag; + + F64 gps_time; +}; + +class LAStempWritePoint14 +{ +public: + I32 X; + I32 Y; + I32 Z; + U16 intensity; + U8 return_number : 4; + U8 number_of_returns : 4; + U8 classification_flags : 4; + U8 scanner_channel : 2; + U8 scan_direction_flag : 1; + U8 edge_of_flight_line : 1; + U8 classification; + U8 user_data; + I16 scan_angle; + U16 point_source_ID; +}; + +class LASwriteItemRaw_POINT14_LE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_POINT14_LE(){}; + inline BOOL write(const U8* item, U32& context) + { + ((LAStempWritePoint14*)buffer)->X = ((LAStempWritePoint10*)item)->X; + ((LAStempWritePoint14*)buffer)->Y = ((LAStempWritePoint10*)item)->Y; + ((LAStempWritePoint14*)buffer)->Z = ((LAStempWritePoint10*)item)->Z; + ((LAStempWritePoint14*)buffer)->intensity = ((LAStempWritePoint10*)item)->intensity; + ((LAStempWritePoint14*)buffer)->scan_direction_flag = ((LAStempWritePoint10*)item)->scan_direction_flag; + ((LAStempWritePoint14*)buffer)->edge_of_flight_line = ((LAStempWritePoint10*)item)->edge_of_flight_line; + ((LAStempWritePoint14*)buffer)->classification = (((LAStempWritePoint10*)item)->classification & 31); + ((LAStempWritePoint14*)buffer)->user_data = ((LAStempWritePoint10*)item)->user_data; + ((LAStempWritePoint14*)buffer)->point_source_ID = ((LAStempWritePoint10*)item)->point_source_ID; + + if (((LAStempWritePoint10*)item)->extended_point_type) + { + ((LAStempWritePoint14*)buffer)->classification_flags = (((LAStempWritePoint10*)item)->extended_classification_flags & 8) | (((LAStempWritePoint10*)item)->classification >> 5); + if (((LAStempWritePoint14*)buffer)->classification == 0) ((LAStempWritePoint14*)buffer)->classification = ((LAStempWritePoint10*)item)->extended_classification; + ((LAStempWritePoint14*)buffer)->scanner_channel = ((LAStempWritePoint10*)item)->extended_scanner_channel; + ((LAStempWritePoint14*)buffer)->return_number = ((LAStempWritePoint10*)item)->extended_return_number; + ((LAStempWritePoint14*)buffer)->number_of_returns = ((LAStempWritePoint10*)item)->extended_number_of_returns; + ((LAStempWritePoint14*)buffer)->scan_angle = ((LAStempWritePoint10*)item)->extended_scan_angle; + } + else + { + ((LAStempWritePoint14*)buffer)->classification_flags = (((LAStempWritePoint10*)item)->classification >> 5); + ((LAStempWritePoint14*)buffer)->scanner_channel = 0; + ((LAStempWritePoint14*)buffer)->return_number = ((LAStempWritePoint10*)item)->return_number; + ((LAStempWritePoint14*)buffer)->number_of_returns = ((LAStempWritePoint10*)item)->number_of_returns; + ((LAStempWritePoint14*)buffer)->scan_angle = I16_QUANTIZE(((LAStempWritePoint10*)item)->scan_angle_rank/0.006f); + } + + *((F64*)&buffer[22]) = ((LAStempWritePoint10*)item)->gps_time; + return outstream->putBytes(buffer, 30); + } +private: + U8 buffer[30]; +}; + +class LASwriteItemRaw_POINT14_BE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_POINT14_BE(){}; + inline BOOL write(const U8* item, U32& context) + { + ENDIAN_SWAP_32(&item[ 0], &swapped[ 0]); // X + ENDIAN_SWAP_32(&item[ 4], &swapped[ 4]); // Y + ENDIAN_SWAP_32(&item[ 8], &swapped[ 8]); // Z + ENDIAN_SWAP_16(&item[12], &swapped[12]); // intensity + ((LAStempWritePoint14*)swapped)->scan_direction_flag = ((LAStempWritePoint10*)item)->scan_direction_flag; + ((LAStempWritePoint14*)swapped)->edge_of_flight_line = ((LAStempWritePoint10*)item)->edge_of_flight_line; + ((LAStempWritePoint14*)swapped)->classification = (((LAStempWritePoint10*)item)->classification & 31); + ((LAStempWritePoint14*)swapped)->user_data = ((LAStempWritePoint10*)item)->user_data; + ENDIAN_SWAP_16(&item[18], &swapped[20]); // point_source_ID + + if (((LAStempWritePoint10*)item)->extended_point_type) + { + ((LAStempWritePoint14*)swapped)->classification_flags = (((LAStempWritePoint10*)item)->extended_classification_flags & 8) | (((LAStempWritePoint10*)item)->classification >> 5); + if (((LAStempWritePoint14*)swapped)->classification == 0) ((LAStempWritePoint14*)swapped)->classification = ((LAStempWritePoint10*)item)->extended_classification; + ((LAStempWritePoint14*)swapped)->scanner_channel = ((LAStempWritePoint10*)item)->extended_scanner_channel; + ((LAStempWritePoint14*)swapped)->return_number = ((LAStempWritePoint10*)item)->extended_return_number; + ((LAStempWritePoint14*)swapped)->number_of_returns = ((LAStempWritePoint10*)item)->extended_number_of_returns; + ENDIAN_SWAP_16(&item[20], &swapped[18]); // scan_angle + } + else + { + ((LAStempWritePoint14*)swapped)->classification_flags = (((LAStempWritePoint10*)item)->classification >> 5); + ((LAStempWritePoint14*)swapped)->scanner_channel = 0; + ((LAStempWritePoint14*)swapped)->return_number = ((LAStempWritePoint10*)item)->return_number; + ((LAStempWritePoint14*)swapped)->number_of_returns = ((LAStempWritePoint10*)item)->number_of_returns; + I16 scan_angle = I16_QUANTIZE(((LAStempWritePoint10*)item)->scan_angle_rank/0.006f); + ENDIAN_SWAP_16((U8*)(&scan_angle), &swapped[18]); // scan_angle + } + ENDIAN_SWAP_64((U8*)&(((LAStempWritePoint10*)item)->gps_time), &swapped[22]); + return outstream->putBytes(swapped, 30); + } +private: + U8 swapped[30]; +}; + +class LASwriteItemRaw_RGBNIR14_LE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_RGBNIR14_LE(){} + inline BOOL write(const U8* item, U32& context) + { + return outstream->putBytes(item, 8); + }; +}; + +class LASwriteItemRaw_RGBNIR14_BE : public LASwriteItemRaw +{ +public: + LASwriteItemRaw_RGBNIR14_BE(){} + inline BOOL write(const U8* item, U32& context) + { + ENDIAN_SWAP_32(&item[ 0], &swapped[ 0]); // R + ENDIAN_SWAP_32(&item[ 2], &swapped[ 2]); // G + ENDIAN_SWAP_32(&item[ 4], &swapped[ 4]); // B + ENDIAN_SWAP_32(&item[ 6], &swapped[ 6]); // NIR + return outstream->putBytes(swapped, 8); + }; +private: + U8 swapped[8]; +}; + +#endif diff --git a/libs/laszip/src/laswritepoint.cpp b/libs/laszip/src/laswritepoint.cpp new file mode 100644 index 0000000..395c58f --- /dev/null +++ b/libs/laszip/src/laswritepoint.cpp @@ -0,0 +1,529 @@ +/* +=============================================================================== + + FILE: laswritepoint.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2019, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ + +#include "laswritepoint.hpp" + +#include "arithmeticencoder.hpp" +#include "laswriteitemraw.hpp" +#include "laswriteitemcompressed_v1.hpp" +#include "laswriteitemcompressed_v2.hpp" +#include "laswriteitemcompressed_v3.hpp" +#include "laswriteitemcompressed_v4.hpp" + +#include +#include +#include + +LASwritePoint::LASwritePoint() +{ + outstream = 0; + num_writers = 0; + writers = 0; + writers_raw = 0; + writers_compressed = 0; + enc = 0; + layered_las14_compression = FALSE; + // used for chunking + chunk_size = U32_MAX; + chunk_count = 0; + number_chunks = 0; + alloced_chunks = 0; + chunk_sizes = 0; + chunk_bytes = 0; + chunk_table_start_position = 0; + chunk_start_position = 0; +} + +BOOL LASwritePoint::setup(const U32 num_items, const LASitem* items, const LASzip* laszip) +{ + U32 i; + + // is laszip exists then we must use its items + if (laszip) + { + if (num_items == 0) return FALSE; + if (items == 0) return FALSE; + if (num_items != laszip->num_items) return FALSE; + if (items != laszip->items) return FALSE; + } + + // create entropy encoder (if requested) + enc = 0; + if (laszip && laszip->compressor) + { + switch (laszip->coder) + { + case LASZIP_CODER_ARITHMETIC: + enc = new ArithmeticEncoder(); + break; + default: + // entropy decoder not supported + return FALSE; + } + // maybe layered compression for LAS 1.4 + layered_las14_compression = (laszip->compressor == LASZIP_COMPRESSOR_LAYERED_CHUNKED); + } + + // initizalize the writers + writers = 0; + num_writers = num_items; + + // disable chunking + chunk_size = U32_MAX; + + // always create the raw writers + writers_raw = new LASwriteItem*[num_writers]; + memset(writers_raw, 0, num_writers*sizeof(LASwriteItem*)); + for (i = 0; i < num_writers; i++) + { + switch (items[i].type) + { + case LASitem::POINT10: + if (IS_LITTLE_ENDIAN()) + writers_raw[i] = new LASwriteItemRaw_POINT10_LE(); + else + writers_raw[i] = new LASwriteItemRaw_POINT10_BE(); + break; + case LASitem::GPSTIME11: + if (IS_LITTLE_ENDIAN()) + writers_raw[i] = new LASwriteItemRaw_GPSTIME11_LE(); + else + writers_raw[i] = new LASwriteItemRaw_GPSTIME11_BE(); + break; + case LASitem::RGB12: + case LASitem::RGB14: + if (IS_LITTLE_ENDIAN()) + writers_raw[i] = new LASwriteItemRaw_RGB12_LE(); + else + writers_raw[i] = new LASwriteItemRaw_RGB12_BE(); + break; + case LASitem::BYTE: + case LASitem::BYTE14: + writers_raw[i] = new LASwriteItemRaw_BYTE(items[i].size); + break; + case LASitem::POINT14: + if (IS_LITTLE_ENDIAN()) + writers_raw[i] = new LASwriteItemRaw_POINT14_LE(); + else + writers_raw[i] = new LASwriteItemRaw_POINT14_BE(); + break; + case LASitem::RGBNIR14: + if (IS_LITTLE_ENDIAN()) + writers_raw[i] = new LASwriteItemRaw_RGBNIR14_LE(); + else + writers_raw[i] = new LASwriteItemRaw_RGBNIR14_BE(); + break; + case LASitem::WAVEPACKET13: + case LASitem::WAVEPACKET14: + if (IS_LITTLE_ENDIAN()) + writers_raw[i] = new LASwriteItemRaw_WAVEPACKET13_LE(); + else + writers_raw[i] = new LASwriteItemRaw_WAVEPACKET13_BE(); + break; + default: + return FALSE; + } + } + + // if needed create the compressed writers and set versions + if (enc) + { + writers_compressed = new LASwriteItem*[num_writers]; + memset(writers_compressed, 0, num_writers*sizeof(LASwriteItem*)); + for (i = 0; i < num_writers; i++) + { + switch (items[i].type) + { + case LASitem::POINT10: + if (items[i].version == 1) + writers_compressed[i] = new LASwriteItemCompressed_POINT10_v1(enc); + else if (items[i].version == 2) + writers_compressed[i] = new LASwriteItemCompressed_POINT10_v2(enc); + else + return FALSE; + break; + case LASitem::GPSTIME11: + if (items[i].version == 1) + writers_compressed[i] = new LASwriteItemCompressed_GPSTIME11_v1(enc); + else if (items[i].version == 2) + writers_compressed[i] = new LASwriteItemCompressed_GPSTIME11_v2(enc); + else + return FALSE; + break; + case LASitem::RGB12: + if (items[i].version == 1) + writers_compressed[i] = new LASwriteItemCompressed_RGB12_v1(enc); + else if (items[i].version == 2) + writers_compressed[i] = new LASwriteItemCompressed_RGB12_v2(enc); + else + return FALSE; + break; + case LASitem::BYTE: + if (items[i].version == 1) + writers_compressed[i] = new LASwriteItemCompressed_BYTE_v1(enc, items[i].size); + else if (items[i].version == 2) + writers_compressed[i] = new LASwriteItemCompressed_BYTE_v2(enc, items[i].size); + else + return FALSE; + break; + case LASitem::POINT14: + if (items[i].version == 3) + writers_compressed[i] = new LASwriteItemCompressed_POINT14_v3(enc); + else if (items[i].version == 4) + writers_compressed[i] = new LASwriteItemCompressed_POINT14_v4(enc); + else + return FALSE; + break; + case LASitem::RGB14: + if (items[i].version == 3) + writers_compressed[i] = new LASwriteItemCompressed_RGB14_v3(enc); + else if (items[i].version == 4) + writers_compressed[i] = new LASwriteItemCompressed_RGB14_v4(enc); + else + return FALSE; + break; + case LASitem::RGBNIR14: + if (items[i].version == 3) + writers_compressed[i] = new LASwriteItemCompressed_RGBNIR14_v3(enc); + else if (items[i].version == 4) + writers_compressed[i] = new LASwriteItemCompressed_RGBNIR14_v4(enc); + else + return FALSE; + break; + case LASitem::BYTE14: + if (items[i].version == 3) + writers_compressed[i] = new LASwriteItemCompressed_BYTE14_v3(enc, items[i].size); + else if (items[i].version == 4) + writers_compressed[i] = new LASwriteItemCompressed_BYTE14_v4(enc, items[i].size); + else + return FALSE; + break; + case LASitem::WAVEPACKET13: + if (items[i].version == 1) + writers_compressed[i] = new LASwriteItemCompressed_WAVEPACKET13_v1(enc); + else + return FALSE; + break; + case LASitem::WAVEPACKET14: + if (items[i].version == 3) + writers_compressed[i] = new LASwriteItemCompressed_WAVEPACKET14_v3(enc); + else if (items[i].version == 4) + writers_compressed[i] = new LASwriteItemCompressed_WAVEPACKET14_v4(enc); + else + return FALSE; + break; + default: + return FALSE; + } + } + if (laszip->compressor != LASZIP_COMPRESSOR_POINTWISE) + { + if (laszip->chunk_size) chunk_size = laszip->chunk_size; + chunk_count = 0; + number_chunks = U32_MAX; + } + } + return TRUE; +} + +BOOL LASwritePoint::init(ByteStreamOut* outstream) +{ + if (!outstream) return FALSE; + this->outstream = outstream; + + // if chunking is enabled + if (number_chunks == U32_MAX) + { + number_chunks = 0; + if (outstream->isSeekable()) + { + chunk_table_start_position = outstream->tell(); + } + else + { + chunk_table_start_position = -1; + } + outstream->put64bitsLE((U8*)&chunk_table_start_position); + chunk_start_position = outstream->tell(); + } + + U32 i; + for (i = 0; i < num_writers; i++) + { + ((LASwriteItemRaw*)(writers_raw[i]))->init(outstream); + } + + if (enc) + { + writers = 0; + } + else + { + writers = writers_raw; + } + + return TRUE; +} + +BOOL LASwritePoint::write(const U8 * const * point) +{ + U32 i; + U32 context = 0; + + if (chunk_count == chunk_size) + { + if (enc) + { + if (layered_las14_compression) + { + // write how many points are in the chunk + outstream->put32bitsLE((U8*)&chunk_count); + // write all layers + for (i = 0; i < num_writers; i++) + { + ((LASwriteItemCompressed*)writers[i])->chunk_sizes(); + } + for (i = 0; i < num_writers; i++) + { + ((LASwriteItemCompressed*)writers[i])->chunk_bytes(); + } + } + else + { + enc->done(); + } + add_chunk_to_table(); + init(outstream); + } + else + { + // happens *only* for uncompressed LAS with over U32_MAX points + assert(chunk_size == U32_MAX); + } + chunk_count = 0; + } + chunk_count++; + + if (writers) + { + for (i = 0; i < num_writers; i++) + { + writers[i]->write(point[i], context); + } + } + else + { + for (i = 0; i < num_writers; i++) + { + writers_raw[i]->write(point[i], context); + ((LASwriteItemCompressed*)(writers_compressed[i]))->init(point[i], context); + } + writers = writers_compressed; + enc->init(outstream); + } + return TRUE; +} + +BOOL LASwritePoint::chunk() +{ + if (chunk_start_position == 0 || chunk_size != U32_MAX) + { + return FALSE; + } + if (layered_las14_compression) + { + U32 i; + // write how many points are in the chunk + outstream->put32bitsLE((U8*)&chunk_count); + // write all layers + for (i = 0; i < num_writers; i++) + { + ((LASwriteItemCompressed*)writers[i])->chunk_sizes(); + } + for (i = 0; i < num_writers; i++) + { + ((LASwriteItemCompressed*)writers[i])->chunk_bytes(); + } + } + else + { + enc->done(); + } + add_chunk_to_table(); + init(outstream); + chunk_count = 0; + return TRUE; +} + +BOOL LASwritePoint::done() +{ + if (writers == writers_compressed) + { + if (layered_las14_compression) + { + U32 i; + // write how many points are in the chunk + outstream->put32bitsLE((U8*)&chunk_count); + // write all layers + for (i = 0; i < num_writers; i++) + { + ((LASwriteItemCompressed*)writers[i])->chunk_sizes(); + } + for (i = 0; i < num_writers; i++) + { + ((LASwriteItemCompressed*)writers[i])->chunk_bytes(); + } + } + else + { + enc->done(); + } + if (chunk_start_position) + { + if (chunk_count) add_chunk_to_table(); + return write_chunk_table(); + } + } + else if (writers == 0) + { + if (chunk_start_position) + { + return write_chunk_table(); + } + } + + return TRUE; +} + +BOOL LASwritePoint::add_chunk_to_table() +{ + if (number_chunks == alloced_chunks) + { + if (chunk_bytes == 0) + { + alloced_chunks = 1024; + if (chunk_size == U32_MAX) chunk_sizes = (U32*)malloc(sizeof(U32)*alloced_chunks); + chunk_bytes = (U32*)malloc(sizeof(U32)*alloced_chunks); + } + else + { + alloced_chunks *= 2; + if (chunk_size == U32_MAX) chunk_sizes = (U32*)realloc(chunk_sizes, sizeof(U32)*alloced_chunks); + chunk_bytes = (U32*)realloc(chunk_bytes, sizeof(U32)*alloced_chunks); + } + if (chunk_size == U32_MAX && chunk_sizes == 0) return FALSE; + if (chunk_bytes == 0) return FALSE; + } + I64 position = outstream->tell(); + if (chunk_size == U32_MAX) chunk_sizes[number_chunks] = chunk_count; + chunk_bytes[number_chunks] = (U32)(position - chunk_start_position); + chunk_start_position = position; + number_chunks++; + return TRUE; +} + +BOOL LASwritePoint::write_chunk_table() +{ + U32 i; + I64 position = outstream->tell(); + if (chunk_table_start_position != -1) // stream is seekable + { + if (!outstream->seek(chunk_table_start_position)) + { + return FALSE; + } + if (!outstream->put64bitsLE((U8*)&position)) + { + return FALSE; + } + if (!outstream->seek(position)) + { + return FALSE; + } + } + U32 version = 0; + if (!outstream->put32bitsLE((U8*)&version)) + { + return FALSE; + } + if (!outstream->put32bitsLE((U8*)&number_chunks)) + { + return FALSE; + } + if (number_chunks > 0) + { + enc->init(outstream); + IntegerCompressor ic(enc, 32, 2); + ic.initCompressor(); + for (i = 0; i < number_chunks; i++) + { + if (chunk_size == U32_MAX) ic.compress((i ? chunk_sizes[i-1] : 0), chunk_sizes[i], 0); + ic.compress((i ? chunk_bytes[i-1] : 0), chunk_bytes[i], 1); + } + enc->done(); + } + if (chunk_table_start_position == -1) // stream is not-seekable + { + if (!outstream->put64bitsLE((U8*)&position)) + { + return FALSE; + } + } + return TRUE; +} + +LASwritePoint::~LASwritePoint() +{ + U32 i; + + if (writers_raw) + { + for (i = 0; i < num_writers; i++) + { + delete writers_raw[i]; + } + delete [] writers_raw; + } + if (writers_compressed) + { + for (i = 0; i < num_writers; i++) + { + delete writers_compressed[i]; + } + delete [] writers_compressed; + } + if (enc) + { + delete enc; + } + + if (chunk_bytes) free(chunk_bytes); +} diff --git a/libs/laszip/src/laswritepoint.hpp b/libs/laszip/src/laswritepoint.hpp new file mode 100644 index 0000000..742d6d4 --- /dev/null +++ b/libs/laszip/src/laswritepoint.hpp @@ -0,0 +1,87 @@ +/* +=============================================================================== + + FILE: laswritepoint.hpp + + CONTENTS: + + Common interface for the classes that write points raw or compressed. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2019, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 21 February 2019 -- fix for writing 4294967295+ points uncompressed to LAS + 28 August 2017 -- moving 'context' from global development hack to interface + 23 August 2016 -- layering of items for selective decompression in LAS 1.4 + 6 September 2014 -- removed inheritance of EntropyEncoder and EntropyDecoder + 6 October 2011 -- large file support & reading with missing chunk table + 9 May 2011 -- the chunked compressor now allows variable chunk sizes + 25 April 2011 -- added chunked laszip for random access decompression + 10 January 2011 -- licensing change for LGPL release and liblas integration + 7 December 2010 -- adapted from LASpointWriter for better code modularity + 3 December 2010 -- updated to (somewhat) support LAS format 1.3 + 7 September 2008 -- updated to support LAS format 1.2 + 22 February 2007 -- created about an hour before henna's birthday + +=============================================================================== +*/ +#ifndef LAS_WRITE_POINT_HPP +#define LAS_WRITE_POINT_HPP + +#include "mydefs.hpp" +#include "laszip.hpp" +#include "bytestreamout.hpp" + +class LASwriteItem; +class ArithmeticEncoder; + +class LASwritePoint +{ +public: + LASwritePoint(); + ~LASwritePoint(); + + // should only be called *once* + BOOL setup(const U32 num_items, const LASitem* items, const LASzip* laszip=0); + + BOOL init(ByteStreamOut* outstream); + BOOL write(const U8 * const * point); + BOOL chunk(); + BOOL done(); + +private: + ByteStreamOut* outstream; + U32 num_writers; + LASwriteItem** writers; + LASwriteItem** writers_raw; + LASwriteItem** writers_compressed; + ArithmeticEncoder* enc; + BOOL layered_las14_compression; + // used for chunking + U32 chunk_size; + U32 chunk_count; + U32 number_chunks; + U32 alloced_chunks; + U32* chunk_sizes; + U32* chunk_bytes; + I64 chunk_start_position; + I64 chunk_table_start_position; + BOOL add_chunk_to_table(); + BOOL write_chunk_table(); +}; + +#endif diff --git a/libs/laszip/src/laszip.cpp b/libs/laszip/src/laszip.cpp new file mode 100644 index 0000000..532c3d4 --- /dev/null +++ b/libs/laszip/src/laszip.cpp @@ -0,0 +1,1006 @@ +/* +=============================================================================== + + FILE: laszip.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2019, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ +#include "laszip.hpp" + +#include "mydefs.hpp" + +#include + +#include +#include +#include + +LASzip::LASzip() +{ + compressor = LASZIP_COMPRESSOR_DEFAULT; + coder = LASZIP_CODER_ARITHMETIC; + version_major = LASZIP_VERSION_MAJOR; + version_minor = LASZIP_VERSION_MINOR; + version_revision = LASZIP_VERSION_REVISION; + options = 0; + num_items = 0; + chunk_size = LASZIP_CHUNK_SIZE_DEFAULT; + number_of_special_evlrs = -1; + offset_to_special_evlrs = -1; + error_string = 0; + items = 0; + bytes = 0; +} + +LASzip::~LASzip() +{ + if (error_string) free(error_string); + if (items) delete [] items; + if (bytes) delete [] bytes; +} + +// the data of the LASzip VLR +// U16 compressor 2 bytes +// U16 coder 2 bytes +// U8 version_major 1 byte +// U8 version_minor 1 byte +// U16 version_revision 2 bytes +// U32 options 4 bytes +// U32 chunk_size 4 bytes +// I64 num_points 8 bytes +// I64 num_bytes 8 bytes +// U16 num_items 2 bytes +// U16 type 2 bytes * num_items +// U16 size 2 bytes * num_items +// U16 version 2 bytes * num_items +// which totals 34+6*num_items + +// unpack from VLR data +bool LASzip::unpack(const U8* bytes, const I32 num) +{ + // check input + if (num < 34) return return_error("too few bytes to unpack"); + if (((num - 34) % 6) != 0) return return_error("wrong number bytes to unpack"); + if (((num - 34) / 6) == 0) return return_error("zero items to unpack"); + num_items = (num - 34) / 6; + + // create item list + if (items) delete [] items; + items = new LASitem[num_items]; + + // do the unpacking + U16 i; + const U8* b = bytes; + compressor = *((U16*)b); + b += 2; + coder = *((U16*)b); + b += 2; + version_major = *((U8*)b); + b += 1; + version_minor = *((U8*)b); + b += 1; + version_revision = *((U16*)b); + b += 2; + options = *((U32*)b); + b += 4; + chunk_size = *((U32*)b); + b += 4; + number_of_special_evlrs = *((I64*)b); + b += 8; + offset_to_special_evlrs = *((I64*)b); + b += 8; + num_items = *((U16*)b); + b += 2; + for (i = 0; i < num_items; i++) + { + items[i].type = (LASitem::Type)*((U16*)b); + b += 2; + items[i].size = *((U16*)b); + b += 2; + items[i].version = *((U16*)b); + b += 2; + } + assert((bytes + num) == b); + + // check if we support the contents + + for (i = 0; i < num_items; i++) + { + if (!check_item(&items[i])) return false; + } + return true; +} + +// pack to VLR data +bool LASzip::pack(U8*& bytes, I32& num) +{ + // check if we support the contents + if (!check()) return false; + + // prepare output + num = 34 + 6*num_items; + if (this->bytes) delete [] this->bytes; + this->bytes = bytes = new U8[num]; + + // pack + U16 i; + U8* b = bytes; + *((U16*)b) = compressor; + b += 2; + *((U16*)b) = coder; + b += 2; + *((U8*)b) = version_major; + b += 1; + *((U8*)b) = version_minor; + b += 1; + *((U16*)b) = version_revision; + b += 2; + *((U32*)b) = options; + b += 4; + *((U32*)b) = chunk_size; + b += 4; + *((I64*)b) = number_of_special_evlrs; + b += 8; + *((I64*)b) = offset_to_special_evlrs; + b += 8; + *((U16*)b) = num_items; + b += 2; + for (i = 0; i < num_items; i++) + { + *((U16*)b) = (U16)items[i].type; + b += 2; + *((U16*)b) = items[i].size; + b += 2; + *((U16*)b) = items[i].version; + b += 2; + } + assert((bytes + num) == b); + return true; +} + +const char* LASzip::get_error() const +{ + return error_string; +} + +bool LASzip::return_error(const char* error) +{ + +#if defined(_MSC_VER) && \ + (_MSC_FULL_VER >= 150000000) +#define CopyString _strdup +#else +#define CopyString strdup +#endif + char err[256]; + sprintf(err, "%s (LASzip v%d.%dr%d)", error, LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION); + if (error_string) free(error_string); + error_string = CopyString(err); + return false; +} + +bool LASzip::check_compressor(const U16 compressor) +{ + if (compressor < LASZIP_COMPRESSOR_TOTAL_NUMBER_OF) return true; + char error[64]; + sprintf(error, "compressor %d not supported", compressor); + return return_error(error); +} + +bool LASzip::check_coder(const U16 coder) +{ + if (coder < LASZIP_CODER_TOTAL_NUMBER_OF) return true; + char error[64]; + sprintf(error, "coder %d not supported", coder); + return return_error(error); +} + +bool LASzip::check_item(const LASitem* item) +{ + switch (item->type) + { + case LASitem::POINT10: + if (item->size != 20) return return_error("POINT10 has size != 20"); + if (item->version > 2) return return_error("POINT10 has version > 2"); + break; + case LASitem::GPSTIME11: + if (item->size != 8) return return_error("GPSTIME11 has size != 8"); + if (item->version > 2) return return_error("GPSTIME11 has version > 2"); + break; + case LASitem::RGB12: + if (item->size != 6) return return_error("RGB12 has size != 6"); + if (item->version > 2) return return_error("RGB12 has version > 2"); + break; + case LASitem::BYTE: + if (item->size < 1) return return_error("BYTE has size < 1"); + if (item->version > 2) return return_error("BYTE has version > 2"); + break; + case LASitem::POINT14: + if (item->size != 30) return return_error("POINT14 has size != 30"); + if ((item->version != 0) && (item->version != 2) && (item->version != 3) && (item->version != 4)) return return_error("POINT14 has version != 0 and != 2 and != 3 and != 4"); // version == 2 from lasproto, version == 4 fixes context-switch + break; + case LASitem::RGB14: + if (item->size != 6) return return_error("RGB14 has size != 6"); + if ((item->version != 0) && (item->version != 2) && (item->version != 3) && (item->version != 4)) return return_error("RGB14 has version != 0 and != 2 and != 3 and != 4"); // version == 2 from lasproto, version == 4 fixes context-switch + break; + case LASitem::RGBNIR14: + if (item->size != 8) return return_error("RGBNIR14 has size != 8"); + if ((item->version != 0) && (item->version != 2) && (item->version != 3) && (item->version != 4)) return return_error("RGBNIR14 has version != 0 and != 2 and != 3 and != 4"); // version == 2 from lasproto, version == 4 fixes context-switch + break; + case LASitem::BYTE14: + if (item->size < 1) return return_error("BYTE14 has size < 1"); + if ((item->version != 0) && (item->version != 2) && (item->version != 3) && (item->version != 4)) return return_error("BYTE14 has version != 0 and != 2 and != 3 and != 4"); // version == 2 from lasproto, version == 4 fixes context-switch + break; + case LASitem::WAVEPACKET13: + if (item->size != 29) return return_error("WAVEPACKET13 has size != 29"); + if (item->version > 1) return return_error("WAVEPACKET13 has version > 1"); + break; + case LASitem::WAVEPACKET14: + if (item->size != 29) return return_error("WAVEPACKET14 has size != 29"); + if ((item->version != 0) && (item->version != 3) && (item->version != 4)) return return_error("WAVEPACKET14 has version != 0 and != 3 and != 4"); // version == 4 fixes context-switch + break; + default: + if (1) + { + char error[64]; + sprintf(error, "item unknown (%d,%d,%d)", item->type, item->size, item->version); + return return_error(error); + } + } + return true; +} + +bool LASzip::check_items(const U16 num_items, const LASitem* items, const U16 point_size) +{ + if (num_items == 0) return return_error("number of items cannot be zero"); + if (items == 0) return return_error("items pointer cannot be NULL"); + U16 i; + U16 size = 0; + for (i = 0; i < num_items; i++) + { + if (!check_item(&items[i])) return false; + size += items[i].size; + } + if (point_size && (point_size != size)) + { + CHAR temp[66]; + sprintf(temp, "point has size of %d but items only add up to %d bytes", point_size, size); + return return_error(temp); + } + return true; +} + +bool LASzip::check(const U16 point_size) +{ + if (!check_compressor(compressor)) return false; + if (!check_coder(coder)) return false; + if (!check_items(num_items, items, point_size)) return false; + return true; +} + +bool LASzip::request_compatibility_mode(const U16 requested_compatibility_mode) +{ + if (num_items != 0) return return_error("request compatibility mode before calling setup()"); + if (requested_compatibility_mode > 1) + { + return return_error("compatibility mode larger than 1 not supported"); + } + if (requested_compatibility_mode) + { + options = options | 0x00000001; + } + else + { + options = options & 0xFFFFFFFE; + } + return true; +} + +bool LASzip::setup(const U8 point_type, const U16 point_size, const U16 compressor) +{ + if (!check_compressor(compressor)) return false; + this->num_items = 0; + if (this->items) delete [] this->items; + this->items = 0; + if (!setup(&num_items, &items, point_type, point_size, compressor)) return false; + if (compressor) + { + if (items[0].type == LASitem::POINT14) + { + if (compressor != LASZIP_COMPRESSOR_LAYERED_CHUNKED) + { + return false; + } + this->compressor = LASZIP_COMPRESSOR_LAYERED_CHUNKED; + } + else + { + if (compressor == LASZIP_COMPRESSOR_LAYERED_CHUNKED) + { + this->compressor = LASZIP_COMPRESSOR_CHUNKED; + } + else + { + this->compressor = compressor; + } + } + if (compressor != LASZIP_COMPRESSOR_POINTWISE) + { + if (chunk_size == 0) chunk_size = LASZIP_CHUNK_SIZE_DEFAULT; + } + } + else + { + this->compressor = LASZIP_COMPRESSOR_NONE; + } + return true; +} + +bool LASzip::setup(const U16 num_items, const LASitem* items, const U16 compressor) +{ + // check input + if (!check_compressor(compressor)) return false; + if (!check_items(num_items, items)) return false; + + // setup compressor + if (compressor) + { + if (items[0].type == LASitem::POINT14) + { + if (compressor != LASZIP_COMPRESSOR_LAYERED_CHUNKED) + { + return false; + } + this->compressor = LASZIP_COMPRESSOR_LAYERED_CHUNKED; + } + else + { + if (compressor == LASZIP_COMPRESSOR_LAYERED_CHUNKED) + { + this->compressor = LASZIP_COMPRESSOR_CHUNKED; + } + else + { + this->compressor = compressor; + } + } + if (compressor != LASZIP_COMPRESSOR_POINTWISE) + { + if (chunk_size == 0) chunk_size = LASZIP_CHUNK_SIZE_DEFAULT; + } + } + else + { + this->compressor = LASZIP_COMPRESSOR_NONE; + } + + // prepare items + this->num_items = 0; + if (this->items) delete [] this->items; + this->items = 0; + this->num_items = num_items; + this->items = new LASitem[num_items]; + + // setup items + U16 i; + for (i = 0; i < num_items; i++) + { + this->items[i] = items[i]; + } + + return true; +} + +bool LASzip::setup(U16* num_items, LASitem** items, const U8 point_type, const U16 point_size, const U16 compressor) +{ + BOOL compatible = FALSE; + BOOL have_point14 = FALSE; + BOOL have_gps_time = FALSE; + BOOL have_rgb = FALSE; + BOOL have_nir = FALSE; + BOOL have_wavepacket = FALSE; + I32 extra_bytes_number = 0; + + // turns on LAS 1.4 compatibility mode + + if (options & 1) compatible = TRUE; + + // switch over the point types we know + switch (point_type) + { + case 0: + extra_bytes_number = (I32)point_size - 20; + break; + case 1: + have_gps_time = TRUE; + extra_bytes_number = (I32)point_size - 28; + break; + case 2: + have_rgb = TRUE; + extra_bytes_number = (I32)point_size - 26; + break; + case 3: + have_gps_time = TRUE; + have_rgb = TRUE; + extra_bytes_number = (I32)point_size - 34; + break; + case 4: + have_gps_time = TRUE; + have_wavepacket = TRUE; + extra_bytes_number = (I32)point_size - 57; + break; + case 5: + have_gps_time = TRUE; + have_rgb = TRUE; + have_wavepacket = TRUE; + extra_bytes_number = (I32)point_size - 63; + break; + case 6: + have_point14 = TRUE; + extra_bytes_number = (I32)point_size - 30; + break; + case 7: + have_point14 = TRUE; + have_rgb = TRUE; + extra_bytes_number = (I32)point_size - 36; + break; + case 8: + have_point14 = TRUE; + have_rgb = TRUE; + have_nir = TRUE; + extra_bytes_number = (I32)point_size - 38; + break; + case 9: + have_point14 = TRUE; + have_wavepacket = TRUE; + extra_bytes_number = (I32)point_size - 59; + break; + case 10: + have_point14 = TRUE; + have_rgb = TRUE; + have_nir = TRUE; + have_wavepacket = TRUE; + extra_bytes_number = (I32)point_size - 67; + break; + default: + if (1) + { + char error[64]; + sprintf(error, "point type %d unknown", point_type); + return return_error(error); + } + } + + if (extra_bytes_number < 0) + { + fprintf(stderr, "WARNING: point size %d too small by %d bytes for point type %d. assuming point_size of %d\n", point_size, -extra_bytes_number, point_type, point_size-extra_bytes_number); + extra_bytes_number = 0; + } + + // maybe represent new LAS 1.4 as corresponding LAS 1.3 points plus extra bytes for compatibility + if (have_point14 && compatible) + { + // we need 4 extra bytes for the new point attributes + extra_bytes_number += 5; + // we store the GPS time separately + have_gps_time = TRUE; + // we do not use the point14 item + have_point14 = FALSE; + // if we have NIR ... + if (have_nir) + { + // we need another 2 extra bytes + extra_bytes_number += 2; + // we do not use the NIR item + have_nir = FALSE; + } + } + + // create item description + + (*num_items) = 1 + !!(have_gps_time) + !!(have_rgb) + !!(have_wavepacket) + !!(extra_bytes_number); + (*items) = new LASitem[*num_items]; + + U16 i = 1; + if (have_point14) + { + (*items)[0].type = LASitem::POINT14; + (*items)[0].size = 30; + (*items)[0].version = 0; + } + else + { + (*items)[0].type = LASitem::POINT10; + (*items)[0].size = 20; + (*items)[0].version = 0; + } + if (have_gps_time) + { + (*items)[i].type = LASitem::GPSTIME11; + (*items)[i].size = 8; + (*items)[i].version = 0; + i++; + } + if (have_rgb) + { + if (have_point14) + { + if (have_nir) + { + (*items)[i].type = LASitem::RGBNIR14; + (*items)[i].size = 8; + (*items)[i].version = 0; + } + else + { + (*items)[i].type = LASitem::RGB14; + (*items)[i].size = 6; + (*items)[i].version = 0; + } + } + else + { + (*items)[i].type = LASitem::RGB12; + (*items)[i].size = 6; + (*items)[i].version = 0; + } + i++; + } + if (have_wavepacket) + { + if (have_point14) + { + (*items)[i].type = LASitem::WAVEPACKET14; + (*items)[i].size = 29; + (*items)[i].version = 0; + } + else + { + (*items)[i].type = LASitem::WAVEPACKET13; + (*items)[i].size = 29; + (*items)[i].version = 0; + } + i++; + } + if (extra_bytes_number) + { + if (have_point14) + { + (*items)[i].type = LASitem::BYTE14; + (*items)[i].size = extra_bytes_number; + (*items)[i].version = 0; + } + else + { + (*items)[i].type = LASitem::BYTE; + (*items)[i].size = extra_bytes_number; + (*items)[i].version = 0; + } + i++; + } + if (compressor) request_version(2); + assert(i == *num_items); + return true; +} + +bool LASzip::set_chunk_size(const U32 chunk_size) +{ + if (num_items == 0) return return_error("call setup() before setting chunk size"); + if (this->compressor != LASZIP_COMPRESSOR_POINTWISE) + { + this->chunk_size = chunk_size; + return true; + } + return false; +} + +bool LASzip::request_version(const U16 requested_version) +{ + if (num_items == 0) return return_error("call setup() before requesting version"); + if (compressor == LASZIP_COMPRESSOR_NONE) + { + if (requested_version > 0) return return_error("without compression version is always 0"); + } + else + { + if (requested_version < 1) return return_error("with compression version is at least 1"); + if (requested_version > 2) return return_error("version larger than 2 not supported"); + } + U16 i; + for (i = 0; i < num_items; i++) + { + switch (items[i].type) + { + case LASitem::POINT10: + case LASitem::GPSTIME11: + case LASitem::RGB12: + case LASitem::BYTE: + items[i].version = requested_version; + break; + case LASitem::WAVEPACKET13: + items[i].version = 1; // no version 2 + break; + case LASitem::POINT14: + case LASitem::RGB14: + case LASitem::RGBNIR14: + case LASitem::WAVEPACKET14: + case LASitem::BYTE14: + items[i].version = 3; // no version 1 or 2 + break; + default: + return return_error("item type not supported"); + } + } + return true; +} + +bool LASzip::is_standard(U8* point_type, U16* record_length) +{ + return is_standard(num_items, items, point_type, record_length); +} + +bool LASzip::is_standard(const U16 num_items, const LASitem* items, U8* point_type, U16* record_length) +{ + if (items == 0) return return_error("LASitem array is zero"); + + // this is always true + if (point_type) *point_type = 127; + if (record_length) + { + U16 i; + *record_length = 0; + for (i = 0; i < num_items; i++) + { + *record_length += items[i].size; + } + } + + // the minimal number of items is 1 + if (num_items < 1) return return_error("less than one LASitem entries"); + // the maximal number of items is 5 + if (num_items > 5) return return_error("more than five LASitem entries"); + + if (items[0].is_type(LASitem::POINT10)) + { + // consider all the POINT10 combinations + if (num_items == 1) + { + if (point_type) *point_type = 0; + if (record_length) assert(*record_length == 20); + return true; + } + else + { + if (items[1].is_type(LASitem::GPSTIME11)) + { + if (num_items == 2) + { + if (point_type) *point_type = 1; + if (record_length) assert(*record_length == 28); + return true; + } + else + { + if (items[2].is_type(LASitem::RGB12)) + { + if (num_items == 3) + { + if (point_type) *point_type = 3; + if (record_length) assert(*record_length == 34); + return true; + } + else + { + if (items[3].is_type(LASitem::WAVEPACKET13)) + { + if (num_items == 4) + { + if (point_type) *point_type = 5; + if (record_length) assert(*record_length == 63); + return true; + } + else + { + if (items[4].is_type(LASitem::BYTE)) + { + if (num_items == 5) + { + if (point_type) *point_type = 5; + if (record_length) assert(*record_length == (63 + items[4].size)); + return true; + } + } + } + } + else if (items[3].is_type(LASitem::BYTE)) + { + if (num_items == 4) + { + if (point_type) *point_type = 3; + if (record_length) assert(*record_length == (34 + items[3].size)); + return true; + } + } + } + } + else if (items[2].is_type(LASitem::WAVEPACKET13)) + { + if (num_items == 3) + { + if (point_type) *point_type = 4; + if (record_length) assert(*record_length == 57); + return true; + } + else + { + if (items[3].is_type(LASitem::BYTE)) + { + if (num_items == 4) + { + if (point_type) *point_type = 4; + if (record_length) assert(*record_length == (57 + items[3].size)); + return true; + } + } + } + } + else if (items[2].is_type(LASitem::BYTE)) + { + if (num_items == 3) + { + if (point_type) *point_type = 1; + if (record_length) assert(*record_length == (28 + items[2].size)); + return true; + } + } + } + } + else if (items[1].is_type(LASitem::RGB12)) + { + if (num_items == 2) + { + if (point_type) *point_type = 2; + if (record_length) assert(*record_length == 26); + return true; + } + else + { + if (items[2].is_type(LASitem::BYTE)) + { + if (num_items == 3) + { + if (point_type) *point_type = 2; + if (record_length) assert(*record_length == (26 + items[2].size)); + return true; + } + } + } + } + else if (items[1].is_type(LASitem::BYTE)) + { + if (num_items == 2) + { + if (point_type) *point_type = 0; + if (record_length) assert(*record_length == (20 + items[1].size)); + return true; + } + } + } + } + else if (items[0].is_type(LASitem::POINT14)) + { + // consider all the POINT14 combinations + if (num_items == 1) + { + if (point_type) *point_type = 6; + if (record_length) assert(*record_length == 30); + return true; + } + else + { + if (items[1].is_type(LASitem::RGB14)) + { + if (num_items == 2) + { + if (point_type) *point_type = 7; + if (record_length) assert(*record_length == 36); + return true; + } + else + { + if (items[2].is_type(LASitem::BYTE) || items[2].is_type(LASitem::BYTE14)) + { + if (num_items == 3) + { + if (point_type) *point_type = 7; + if (record_length) assert(*record_length == (36 + items[2].size)); + return true; + } + } + } + } + else if (items[1].is_type(LASitem::RGBNIR14)) + { + if (num_items == 2) + { + if (point_type) *point_type = 8; + if (record_length) assert(*record_length == 38); + return true; + } + else + { + if (items[2].is_type(LASitem::WAVEPACKET13) || items[1].is_type(LASitem::WAVEPACKET14)) + { + if (num_items == 3) + { + if (point_type) *point_type = 10; + if (record_length) assert(*record_length == 67); + return true; + } + else + { + if (items[3].is_type(LASitem::BYTE) || items[3].is_type(LASitem::BYTE14)) + { + if (num_items == 4) + { + if (point_type) *point_type = 10; + if (record_length) assert(*record_length == (67 + items[3].size)); + return true; + } + } + } + } + else if (items[2].is_type(LASitem::BYTE) || items[2].is_type(LASitem::BYTE14)) + { + if (num_items == 3) + { + if (point_type) *point_type = 8; + if (record_length) assert(*record_length == (38 + items[2].size)); + return true; + } + } + } + } + else if (items[1].is_type(LASitem::WAVEPACKET13) || items[1].is_type(LASitem::WAVEPACKET14)) + { + if (num_items == 2) + { + if (point_type) *point_type = 9; + if (record_length) assert(*record_length == 59); + return true; + } + else + { + if (items[2].is_type(LASitem::BYTE) || items[2].is_type(LASitem::BYTE14)) + { + if (num_items == 3) + { + if (point_type) *point_type = 9; + if (record_length) assert(*record_length == (59 + items[2].size)); + return true; + } + } + } + } + else if (items[1].is_type(LASitem::BYTE) || items[1].is_type(LASitem::BYTE14)) + { + if (num_items == 2) + { + if (point_type) *point_type = 6; + if (record_length) assert(*record_length == (30 + items[1].size)); + return true; + } + } + } + } + else + { + return_error("first LASitem is neither POINT10 nor POINT14"); + } + return return_error("LASitem array does not match LAS specification 1.4"); +} + +bool LASitem::is_type(LASitem::Type t) const +{ + if (t != type) return false; + switch (t) + { + case POINT10: + if (size != 20) return false; + break; + case POINT14: + if (size != 30) return false; + break; + case GPSTIME11: + if (size != 8) return false; + break; + case RGB12: + if (size != 6) return false; + break; + case BYTE: + if (size < 1) return false; + break; + case RGB14: + if (size != 6) return false; + break; + case RGBNIR14: + if (size != 8) return false; + break; + case BYTE14: + if (size < 1) return false; + break; + case WAVEPACKET13: + if (size != 29) return false; + break; + case WAVEPACKET14: + if (size != 29) return false; + break; + default: + return false; + } + return true; +} + +const char* LASitem::get_name() const +{ + switch (type) + { + case POINT10: + return "POINT10"; + break; + case POINT14: + return "POINT14"; + break; + case GPSTIME11: + return "GPSTIME11"; + break; + case RGB12: + return "RGB12"; + break; + case BYTE: + return "BYTE"; + break; + case RGB14: + return "RGB14"; + break; + case RGBNIR14: + return "RGBNIR14"; + break; + case BYTE14: + return "BYTE14"; + break; + case WAVEPACKET13: + return "WAVEPACKET13"; + break; + case WAVEPACKET14: + return "WAVEPACKET14"; + break; + default: + break; + } + return 0; +} + diff --git a/libs/laszip/src/laszip.hpp b/libs/laszip/src/laszip.hpp new file mode 100644 index 0000000..893219a --- /dev/null +++ b/libs/laszip/src/laszip.hpp @@ -0,0 +1,157 @@ +/* +=============================================================================== + + FILE: laszip.hpp + + CONTENTS: + + Contains LASitem and LASchunk structs as well as the IDs of the currently + supported entropy coding scheme + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2019, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 20 March 2019 -- upped to 3.3 r1 for consistent legacy and extended class check + 21 February 2019 -- bug fix when writing 4294967295+ points uncompressed to LAS + 28 December 2018 -- fix for v4 decompression of WavePacket part of PRDF 9 and 10 + 27 December 2018 -- upped to 3.2 r9 for bug fix in multi-channel NIR decompression + 7 November 2018 -- upped to 3.2 r8 for identical legacy and extended flags check + 20 October 2018 -- upped to 3.2 r7 for rare bug in LASinterval::merge_intervals() + 5 October 2018 -- upped to 3.2 r6 for corrected 'is_empty' return value + 28 September 2018 -- upped to 3.2 r5 for fix in extended classification writing + 9 February 2018 -- minor version increment as it can read v4 compressed items + 28 December 2017 -- fix incorrect 'context switch' reported by Wanwannodao + 23 August 2017 -- minor version increment for C++ stream-based read/write API + 28 May 2017 -- support for "LAS 1.4 selective decompression" added into DLL API + 8 April 2017 -- new check for whether point size and total size of items match + 30 March 2017 -- support for "native LAS 1.4 extension" added into main branch + 7 January 2017 -- set reserved VLR field from 0xAABB to 0x0 in DLL + 7 January 2017 -- consistent compatibility mode scan angle quantization in DLL + 7 January 2017 -- compatibility mode *decompression* fix for waveforms in DLL + 25 February 2016 -- depreciating old libLAS laszipper/lasunzipper binding + 29 July 2013 -- reorganized to create an easy-to-use LASzip DLL + 5 December 2011 -- learns the chunk table if it is missing (e.g. truncated LAZ) + 6 October 2011 -- large file support, ability to read with missing chunk table + 23 June 2011 -- turned on LASzip version 2.0 compressor with chunking + 8 May 2011 -- added an option for variable chunking via chunk() + 23 April 2011 -- changed interface for simplicity and chunking support + 20 March 2011 -- incrementing LASZIP_VERSION to 1.2 for improved compression + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- refactored from lasdefinitions after movies with silke + +=============================================================================== +*/ +#ifndef LASZIP_HPP +#define LASZIP_HPP + +#if defined(_MSC_VER) && (_MSC_VER < 1300) +#define LZ_WIN32_VC6 +typedef __int64 SIGNED_INT64; +#else +typedef long long SIGNED_INT64; +#endif + +#if defined(_MSC_VER) && (_MSC_FULL_VER >= 150000000) +#define LASCopyString _strdup +#else +#define LASCopyString strdup +#endif + +#define LASZIP_VERSION_MAJOR 3 +#define LASZIP_VERSION_MINOR 4 +#define LASZIP_VERSION_REVISION 3 +#define LASZIP_VERSION_BUILD_DATE 191111 + +#define LASZIP_COMPRESSOR_NONE 0 +#define LASZIP_COMPRESSOR_POINTWISE 1 +#define LASZIP_COMPRESSOR_POINTWISE_CHUNKED 2 +#define LASZIP_COMPRESSOR_LAYERED_CHUNKED 3 +#define LASZIP_COMPRESSOR_TOTAL_NUMBER_OF 4 + +#define LASZIP_COMPRESSOR_CHUNKED LASZIP_COMPRESSOR_POINTWISE_CHUNKED +#define LASZIP_COMPRESSOR_NOT_CHUNKED LASZIP_COMPRESSOR_POINTWISE + +#define LASZIP_COMPRESSOR_DEFAULT LASZIP_COMPRESSOR_CHUNKED + +#define LASZIP_CODER_ARITHMETIC 0 +#define LASZIP_CODER_TOTAL_NUMBER_OF 1 + +#define LASZIP_CHUNK_SIZE_DEFAULT 50000 + +class LASitem +{ +public: + enum Type { BYTE = 0, SHORT, INT, LONG, FLOAT, DOUBLE, POINT10, GPSTIME11, RGB12, WAVEPACKET13, POINT14, RGB14, RGBNIR14, WAVEPACKET14, BYTE14 } type; + unsigned short size; + unsigned short version; + bool is_type(LASitem::Type t) const; + const char* get_name() const; +}; + +class LASzip +{ +public: + + // supported version control + bool check_compressor(const unsigned short compressor); + bool check_coder(const unsigned short coder); + bool check_item(const LASitem* item); + bool check_items(const unsigned short num_items, const LASitem* items, const unsigned short point_size=0); + bool check(const unsigned short point_size=0); + + // go back and forth between item array and point type & size + bool setup(unsigned short* num_items, LASitem** items, const unsigned char point_type, const unsigned short point_size, const unsigned short compressor=LASZIP_COMPRESSOR_NONE); + bool is_standard(const unsigned short num_items, const LASitem* items, unsigned char* point_type=0, unsigned short* record_length=0); + bool is_standard(unsigned char* point_type=0, unsigned short* record_length=0); + + // pack to and unpack from VLR + unsigned char* bytes; + bool unpack(const unsigned char* bytes, const int num); + bool pack(unsigned char*& bytes, int& num); + + // setup + bool request_compatibility_mode(const unsigned short requested_compatibility_mode=0); // 0 = none, 1 = LAS 1.4 compatibility mode + bool setup(const unsigned char point_type, const unsigned short point_size, const unsigned short compressor=LASZIP_COMPRESSOR_DEFAULT); + bool setup(const unsigned short num_items, const LASitem* items, const unsigned short compressor); + bool set_chunk_size(const unsigned int chunk_size); /* for compressor only */ + bool request_version(const unsigned short requested_version); /* for compressor only */ + + // in case a function returns false this string describes the problem + const char* get_error() const; + + // stored in LASzip VLR data section + unsigned short compressor; + unsigned short coder; + unsigned char version_major; + unsigned char version_minor; + unsigned short version_revision; + unsigned int options; + unsigned int chunk_size; + SIGNED_INT64 number_of_special_evlrs; /* must be -1 if unused */ + SIGNED_INT64 offset_to_special_evlrs; /* must be -1 if unused */ + unsigned short num_items; + LASitem* items; + + LASzip(); + ~LASzip(); + +private: + bool return_error(const char* err); + char* error_string; +}; + +#endif diff --git a/libs/laszip/src/laszip_common_v1.hpp b/libs/laszip/src/laszip_common_v1.hpp new file mode 100644 index 0000000..1858dc4 --- /dev/null +++ b/libs/laszip/src/laszip_common_v1.hpp @@ -0,0 +1,106 @@ +/* +=============================================================================== + + FILE: laszip_common_v1.hpp + + CONTENTS: + + Common defines and functionalities for version 1 of LASitemReadCompressed + and LASitemwriteCompressed. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + uday.karan@gmail.com - http://github.com/verma + + COPYRIGHT: + + (c) 2007-2014, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 10 April 2014 - refactor LASwavepacket13 and add other functions to it + +=============================================================================== +*/ + +#ifndef LASZIP_COMMON_V1_HPP +#define LASZIP_COMMON_V1_HPP + +#include "mydefs.hpp" + +struct LASwavepacket13 +{ + U64 offset; + U32 packet_size; + U32I32F32 return_point; + U32I32F32 x; + U32I32F32 y; + U32I32F32 z; + + static inline LASwavepacket13 unpack(const U8* item) + { + // unpack a LAS wavepacket out of raw memory + LASwavepacket13 r; + + r.offset = makeU64(item); + r.packet_size = makeU32(item + 8); + r.return_point.u32 = makeU32(item + 12); + + r.x.u32 = makeU32(item + 16); + r.y.u32 = makeU32(item + 20); + r.z.u32 = makeU32(item + 24); + + return r; + } + + void inline pack(U8 *item) + { + // pack a LAS wavepacket into raw memory + packU32((U32)(offset & 0xFFFFFFFF), item); + packU32((U32)(offset >> 32), item+4); + + packU32(packet_size, item + 8); + packU32(return_point.u32, item + 12); + packU32(x.u32, item + 16); + packU32(y.u32, item + 20); + packU32(z.u32, item + 24); + } + +private: + + static inline U64 makeU64(const U8* item) + { + U64 dw0 = (U64)makeU32(item); + U64 dw1 = (U64)makeU32(item+4); + + return dw0 | (dw1 << 32); + } + + static inline U32 makeU32(const U8 *item) + { + U32 b0 = (U32)item[0]; + U32 b1 = (U32)item[1]; + U32 b2 = (U32)item[2]; + U32 b3 = (U32)item[3]; + + return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + } + + static inline void packU32(U32 v, U8 *item) + { + item[0] = v & 0xFF; + item[1] = (v >> 8) & 0xFF; + item[2] = (v >> 16) & 0xFF; + item[3] = (v >> 24) & 0xFF; + } +}; + +#endif // LASZIP_COMMON_V1_HPP diff --git a/libs/laszip/src/laszip_common_v2.hpp b/libs/laszip/src/laszip_common_v2.hpp new file mode 100644 index 0000000..3d5a80b --- /dev/null +++ b/libs/laszip/src/laszip_common_v2.hpp @@ -0,0 +1,186 @@ +/* +=============================================================================== + + FILE: laszip_common_v2.hpp + + CONTENTS: + + Common defines and functionalities for version 2 of LASitemReadCompressed + and LASitemwriteCompressed. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2012, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 16 March 2011 -- created after designing the "streaming median" algorithm + +=============================================================================== +*/ +#ifndef LASZIP_COMMON_V2_HPP +#define LASZIP_COMMON_V2_HPP + +class StreamingMedian5 +{ +public: + I32 values[5]; + BOOL high; + void init() + { + values[0] = values[1] = values[2] = values[3] = values[4] = 0; + high = true; + } + inline void add(I32 v) + { + if (high) + { + if (v < values[2]) + { + values[4] = values[3]; + values[3] = values[2]; + if (v < values[0]) + { + values[2] = values[1]; + values[1] = values[0]; + values[0] = v; + } + else if (v < values[1]) + { + values[2] = values[1]; + values[1] = v; + } + else + { + values[2] = v; + } + } + else + { + if (v < values[3]) + { + values[4] = values[3]; + values[3] = v; + } + else + { + values[4] = v; + } + high = false; + } + } + else + { + if (values[2] < v) + { + values[0] = values[1]; + values[1] = values[2]; + if (values[4] < v) + { + values[2] = values[3]; + values[3] = values[4]; + values[4] = v; + } + else if (values[3] < v) + { + values[2] = values[3]; + values[3] = v; + } + else + { + values[2] = v; + } + } + else + { + if (values[1] < v) + { + values[0] = values[1]; + values[1] = v; + } + else + { + values[0] = v; + } + high = true; + } + } + } + I32 get() const + { + return values[2]; + } + StreamingMedian5() + { + init(); + } +}; + +// for LAS files with the return (r) and the number (n) of +// returns field correctly populated the mapping should really +// be only the following. +// { 15, 15, 15, 15, 15, 15, 15, 15 }, +// { 15, 0, 15, 15, 15, 15, 15, 15 }, +// { 15, 1, 2, 15, 15, 15, 15, 15 }, +// { 15, 3, 4, 5, 15, 15, 15, 15 }, +// { 15, 6, 7, 8, 9, 15, 15, 15 }, +// { 15, 10, 11, 12, 13, 14, 15, 15 }, +// { 15, 15, 15, 15, 15, 15, 15, 15 }, +// { 15, 15, 15, 15, 15, 15, 15, 15 } +// however, some files start the numbering of r and n with 0, +// only have return counts r, or only have number of return +// counts n, or mix up the position of r and n. we therefore +// "complete" the table to also map those "undesired" r & n +// combinations to different contexts +const U8 number_return_map[8][8] = +{ + { 15, 14, 13, 12, 11, 10, 9, 8 }, + { 14, 0, 1, 3, 6, 10, 10, 9 }, + { 13, 1, 2, 4, 7, 11, 11, 10 }, + { 12, 3, 4, 5, 8, 12, 12, 11 }, + { 11, 6, 7, 8, 9, 13, 13, 12 }, + { 10, 10, 11, 12, 13, 14, 14, 13 }, + { 9, 10, 11, 12, 13, 14, 15, 14 }, + { 8, 9, 10, 11, 12, 13, 14, 15 } +}; + +// for LAS files with the return (r) and the number (n) of +// returns field correctly populated the mapping should really +// be only the following. +// { 0, 7, 7, 7, 7, 7, 7, 7 }, +// { 7, 0, 7, 7, 7, 7, 7, 7 }, +// { 7, 1, 0, 7, 7, 7, 7, 7 }, +// { 7, 2, 1, 0, 7, 7, 7, 7 }, +// { 7, 3, 2, 1, 0, 7, 7, 7 }, +// { 7, 4, 3, 2, 1, 0, 7, 7 }, +// { 7, 5, 4, 3, 2, 1, 0, 7 }, +// { 7, 6, 5, 4, 3, 2, 1, 0 } +// however, some files start the numbering of r and n with 0, +// only have return counts r, or only have number of return +// counts n, or mix up the position of r and n. we therefore +// "complete" the table to also map those "undesired" r & n +// combinations to different contexts +const U8 number_return_level[8][8] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 1, 0, 1, 2, 3, 4, 5, 6 }, + { 2, 1, 0, 1, 2, 3, 4, 5 }, + { 3, 2, 1, 0, 1, 2, 3, 4 }, + { 4, 3, 2, 1, 0, 1, 2, 3 }, + { 5, 4, 3, 2, 1, 0, 1, 2 }, + { 6, 5, 4, 3, 2, 1, 0, 1 }, + { 7, 6, 5, 4, 3, 2, 1, 0 } +}; + +#endif diff --git a/libs/laszip/src/laszip_common_v3.hpp b/libs/laszip/src/laszip_common_v3.hpp new file mode 100644 index 0000000..f65f277 --- /dev/null +++ b/libs/laszip/src/laszip_common_v3.hpp @@ -0,0 +1,325 @@ +/* +=============================================================================== + + FILE: laszip_common_v3.hpp + + CONTENTS: + + Common defines and functionalities for version 3 of LASitemReadCompressed + and LASitemwriteCompressed. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 27 June 2016 -- after Iceland forced England's BrExit with 2:1 at EURO2016 + +=============================================================================== +*/ +#ifndef LASZIP_COMMON_V3_HPP +#define LASZIP_COMMON_V3_HPP + +#include "laszip_common_v1.hpp" +#include "laszip_common_v2.hpp" + +#define DEBUG_OUTPUT_NUM_BYTES_DETAILS 0 + +class LAScontextPOINT14 +{ +public: + BOOL unused; + + U8 last_item[128]; + U16 last_intensity[8]; + StreamingMedian5 last_X_diff_median5[12]; + StreamingMedian5 last_Y_diff_median5[12]; + I32 last_Z[8]; + + ArithmeticModel* m_changed_values[8]; + ArithmeticModel* m_scanner_channel; + ArithmeticModel* m_number_of_returns[16]; + ArithmeticModel* m_return_number_gps_same; + ArithmeticModel* m_return_number[16]; + IntegerCompressor* ic_dX; + IntegerCompressor* ic_dY; + IntegerCompressor* ic_Z; + + ArithmeticModel* m_classification[64]; + + ArithmeticModel* m_flags[64]; + + ArithmeticModel* m_user_data[64]; + + IntegerCompressor* ic_intensity; + + IntegerCompressor* ic_scan_angle; + + IntegerCompressor* ic_point_source_ID; + + // GPS time stuff + U32 last, next; + U64I64F64 last_gpstime[4]; + I32 last_gpstime_diff[4]; + I32 multi_extreme_counter[4]; + + ArithmeticModel* m_gpstime_multi; + ArithmeticModel* m_gpstime_0diff; + IntegerCompressor* ic_gpstime; +}; + +class LAScontextRGB14 +{ +public: + BOOL unused; + + U16 last_item[3]; + + ArithmeticModel* m_byte_used; + ArithmeticModel* m_rgb_diff_0; + ArithmeticModel* m_rgb_diff_1; + ArithmeticModel* m_rgb_diff_2; + ArithmeticModel* m_rgb_diff_3; + ArithmeticModel* m_rgb_diff_4; + ArithmeticModel* m_rgb_diff_5; +}; + +class LAScontextRGBNIR14 +{ +public: + BOOL unused; + + U16 last_item[4]; + + ArithmeticModel* m_rgb_bytes_used; + ArithmeticModel* m_rgb_diff_0; + ArithmeticModel* m_rgb_diff_1; + ArithmeticModel* m_rgb_diff_2; + ArithmeticModel* m_rgb_diff_3; + ArithmeticModel* m_rgb_diff_4; + ArithmeticModel* m_rgb_diff_5; + + ArithmeticModel* m_nir_bytes_used; + ArithmeticModel* m_nir_diff_0; + ArithmeticModel* m_nir_diff_1; +}; + +class LAScontextWAVEPACKET14 +{ +public: + BOOL unused; + + U8 last_item[29]; + I32 last_diff_32; + U32 sym_last_offset_diff; + + ArithmeticModel* m_packet_index; + ArithmeticModel* m_offset_diff[4]; + IntegerCompressor* ic_offset_diff; + IntegerCompressor* ic_packet_size; + IntegerCompressor* ic_return_point; + IntegerCompressor* ic_xyz; +}; + +class LAScontextBYTE14 +{ +public: + BOOL unused; + + U8* last_item; + + ArithmeticModel** m_bytes; +}; + +// for LAS points with correctly populated return numbers (1 <= r <= n) and +// number of returns of given pulse (1 <= n <= 15) the return mapping that +// serializes the possible combinations into one number should be the following +// +// { ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, --- }, +// { ---, 0, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, --- }, +// { ---, 1, 2, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, --- }, +// { ---, 3, 4, 5, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, --- }, +// { ---, 6, 7, 8, 9, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, --- }, +// { ---, 10, 11, 12, 13, 14, ---, ---, ---, ---, ---, ---, ---, ---, ---, --- }, +// { ---, 15, 16, 17, 18, 19, 20, ---, ---, ---, ---, ---, ---, ---, ---, --- }, +// { ---, 21, 22, 23, 24, 25, 26, 27, ---, ---, ---, ---, ---, ---, ---, --- }, +// { ---, 28, 29, 30, 31, 32, 33, 34, 35, ---, ---, ---, ---, ---, ---, --- }, +// { ---, 36, 37, 38, 39, 40, 41, 42, 43, 44, ---, ---, ---, ---, ---, --- }, +// { ---, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, ---, ---, ---, ---, --- }, +// { ---, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, ---, ---, ---, --- }, +// { ---, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, ---, ---, --- }, +// { ---, 78, 89, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, ---, --- }, +// { ---, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, --- }, +// { ---, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119 } +// +// we drastically simplify the number of return combinations that we want to distinguish +// down to 16 as higher returns will not have significant entropy differences +// +// { --, --, --, --, --, --, --, --, --, --, --, --, --, --, --, -- }, +// { --, 0, --, --, --, --, --, --, --, --, --, --, --, --, --, -- }, +// { --, 1, 2, --, --, --, --, --, --, --, --, --, --, --, --, -- }, +// { --, 3, 4, 5, --, --, --, --, --, --, --, --, --, --, --, -- }, +// { --, 6, 7, 8, 9, --, --, --, --, --, --, --, --, --, --, -- }, +// { --, 10, 11, 12, 13, 14, --, --, --, --, --, --, --, --, --, -- }, +// { --, 10, 11, 12, 13, 14, 15, --, --, --, --, --, --, --, --, -- }, +// { --, 10, 11, 12, 12, 13, 14, 15, --, --, --, --, --, --, --, -- }, +// { --, 10, 11, 12, 12, 13, 13, 14, 15, --, --, --, --, --, --, -- }, +// { --, 10, 11, 11, 12, 12, 13, 13, 14, 15, --, --, --, --, --, -- }, +// { --, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, --, --, --, --, -- }, +// { --, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, --, --, --, -- }, +// { --, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, --, --, -- }, +// { --, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, --, -- }, +// { --, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, -- }, +// { --, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15 } + +// however, as some files start the numbering of r and n with 0, only have return counts +// r, only have number of return per pulse n, or mix up position of r and n, we complete +// the table to also map those "undesired" r and n combinations to different contexts +/* +const U8 number_return_map_4bit[16][16] = +{ + { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, + { 14, 0, 1, 3, 6, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }, + { 13, 1, 2, 4, 7, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10 }, + { 12, 3, 4, 5, 8, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11 }, + { 11, 6, 7, 8, 9, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 11 }, + { 10, 10, 11, 12, 13, 14, 14, 13, 13, 12, 12, 12, 12, 12, 12, 12 }, + { 9, 10, 11, 12, 13, 14, 15, 14, 13, 13, 13, 12, 12, 12, 12, 12 }, + { 8, 10, 11, 12, 12, 13, 14, 15, 14, 13, 13, 13, 13, 12, 12, 12 }, + { 7, 10, 11, 12, 12, 13, 13, 14, 15, 14, 14, 13, 13, 13, 13, 13 }, + { 6, 10, 11, 11, 12, 12, 13, 13, 14, 15, 14, 14, 14, 13, 13, 13 }, + { 5, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 14, 14, 14, 13, 13 }, + { 4, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 14, 14, 14 }, + { 3, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 14, 14 }, + { 2, 10, 10, 11, 11, 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 14 }, + { 1, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15 }, + { 0, 10, 10, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15 } +}; +// simplify down to 10 contexts +const U8 number_return_map_10ctx[16][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 9, 9, 9 }, + { 1, 0, 1, 3, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }, + { 2, 1, 2, 4, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6 }, + { 3, 3, 4, 5, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }, + { 4, 6, 7, 8, 9, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7 }, + { 5, 6, 7, 7, 8, 9, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7 }, + { 6, 6, 7, 7, 8, 8, 9, 8, 8, 8, 8, 7, 7, 7, 7, 7 }, + { 7, 6, 7, 7, 7, 8, 8, 9, 8, 8, 8, 8, 8, 7, 7, 7 }, + { 8, 6, 7, 7, 7, 8, 8, 8, 9, 8, 8, 8, 8, 8, 8, 8 }, + { 9, 6, 7, 7, 7, 7, 8, 8, 8, 9, 8, 8, 8, 8, 8, 8 }, + { 9, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 8, 8, 8, 8, 8 }, + { 9, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 8, 8, 8 }, + { 9, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 8, 8 }, + { 9, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 8 }, + { 9, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9 }, + { 9, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9 } +}; +// simplify even further down to 6 contexts +*/ +const U8 number_return_map_6ctx[16][16] = +{ + { 0, 1, 2, 3, 4, 5, 3, 4, 4, 5, 5, 5, 5, 5, 5, 5 }, + { 1, 0, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, + { 2, 1, 2, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3 }, + { 3, 3, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, + { 4, 3, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, + { 5, 3, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, + { 3, 3, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4 }, + { 4, 3, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4 }, + { 4, 3, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 4 }, + { 5, 3, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4 }, + { 5, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4 }, + { 5, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 4, 4, 4 }, + { 5, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 4, 4 }, + { 5, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 4 }, + { 5, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5 }, + { 5, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5 } +}; + +// for LAS points with return number (1 <= r <= n) and a number of returns +// of given pulse (1 <= n <= 15) the level of penetration counted in number +// of returns should really simply be n - r with all invalid combinations +// being mapped to 15 like shown below +// +// { 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 }, +// { 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 }, +// { 15, 1, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 }, +// { 15, 2, 1, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 }, +// { 15, 3, 2, 1, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 }, +// { 15, 4, 3, 2, 1, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15 }, +// { 15, 5, 4, 3, 2, 1, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15 }, +// { 15, 6, 5, 4, 3, 2, 1, 0, 15, 15, 15, 15, 15, 15, 15, 15 } +// { 15, 7, 6, 5, 4, 3, 2, 1, 0, 15, 15, 15, 15, 15, 15, 15 } +// { 15, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 15, 15, 15, 15, 15 } +// { 15, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 15, 15, 15, 15 } +// { 15, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 15, 15, 15 } +// { 15, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 15, 15 } +// { 15, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 15 } +// { 15, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15 } +// { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 } +// +// however, some files start the numbering of r and n with 0, only have +// return counts r, or only have number of returns of given pulse n, or +// mix up the position of r and n. we therefore "complete" the table to +// also map those "undesired" r & n combinations to different contexts. +// +// We also stop the enumeration of the levels of penetration at 7 and +// map all higher penetration levels also to 7 in order to keep the total +// number of contexts reasonably small. +// +/* +const U8 number_return_level_4bit[16][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, + { 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, + { 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }, + { 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, + { 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, + { 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, + { 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, + { 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8 }, + { 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6 }, + { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5 }, + { 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4 }, + { 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3 }, + { 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2 }, + { 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1 }, + { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 } +}; +*/ +// simplify down to 8 contexts +const U8 number_return_level_8ctx[16][16] = +{ + { 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7 }, + { 1, 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7 }, + { 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7 }, + { 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7 }, + { 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7 }, + { 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7 }, + { 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 7, 7 }, + { 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 7 }, + { 7, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7 }, + { 7, 7, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6 }, + { 7, 7, 7, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5 }, + { 7, 7, 7, 7, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4 }, + { 7, 7, 7, 7, 7, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3 }, + { 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2 }, + { 7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 3, 2, 1, 0, 1 }, + { 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 5, 4, 3, 2, 1, 0 } +}; + +#endif diff --git a/libs/laszip/src/laszip_decompress_selective_v3.hpp b/libs/laszip/src/laszip_decompress_selective_v3.hpp new file mode 100644 index 0000000..ffd2912 --- /dev/null +++ b/libs/laszip/src/laszip_decompress_selective_v3.hpp @@ -0,0 +1,58 @@ +/* +=============================================================================== + + FILE: laszip_decompress_selective_v3.hpp + + CONTENTS: + + Contains bit mask definitions for selective decompression. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2017, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 14 April 2017 -- created at Lo Que Hay where Gui was having birthday dinner + +=============================================================================== +*/ +#ifndef LASZIP_DECOMPRESS_SELECTIVE_V3_HPP +#define LASZIP_DECOMPRESS_SELECTIVE_V3_HPP + +#define LASZIP_DECOMPRESS_SELECTIVE_ALL 0xFFFFFFFF + +#define LASZIP_DECOMPRESS_SELECTIVE_CHANNEL_RETURNS_XY 0x00000000 +#define LASZIP_DECOMPRESS_SELECTIVE_Z 0x00000001 +#define LASZIP_DECOMPRESS_SELECTIVE_CLASSIFICATION 0x00000002 +#define LASZIP_DECOMPRESS_SELECTIVE_FLAGS 0x00000004 +#define LASZIP_DECOMPRESS_SELECTIVE_INTENSITY 0x00000008 +#define LASZIP_DECOMPRESS_SELECTIVE_SCAN_ANGLE 0x00000010 +#define LASZIP_DECOMPRESS_SELECTIVE_USER_DATA 0x00000020 +#define LASZIP_DECOMPRESS_SELECTIVE_POINT_SOURCE 0x00000040 +#define LASZIP_DECOMPRESS_SELECTIVE_GPS_TIME 0x00000080 +#define LASZIP_DECOMPRESS_SELECTIVE_RGB 0x00000100 +#define LASZIP_DECOMPRESS_SELECTIVE_NIR 0x00000200 +#define LASZIP_DECOMPRESS_SELECTIVE_WAVEPACKET 0x00000400 +#define LASZIP_DECOMPRESS_SELECTIVE_BYTE0 0x00010000 +#define LASZIP_DECOMPRESS_SELECTIVE_BYTE1 0x00020000 +#define LASZIP_DECOMPRESS_SELECTIVE_BYTE2 0x00040000 +#define LASZIP_DECOMPRESS_SELECTIVE_BYTE3 0x00080000 +#define LASZIP_DECOMPRESS_SELECTIVE_BYTE4 0x00100000 +#define LASZIP_DECOMPRESS_SELECTIVE_BYTE5 0x00200000 +#define LASZIP_DECOMPRESS_SELECTIVE_BYTE6 0x00400000 +#define LASZIP_DECOMPRESS_SELECTIVE_BYTE7 0x00800000 +#define LASZIP_DECOMPRESS_SELECTIVE_EXTRA_BYTES 0xFFFF0000 + +#endif // LASZIP_DECOMPRESS_SELECTIVE_V3_HPP diff --git a/libs/laszip/src/laszip_dll.cpp b/libs/laszip/src/laszip_dll.cpp new file mode 100644 index 0000000..36e4f5e --- /dev/null +++ b/libs/laszip/src/laszip_dll.cpp @@ -0,0 +1,4977 @@ +/* +=============================================================================== + + FILE: laszip_dll.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2019, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 15 October 2019 -- support reading from and writing to unicode file names under Windows + 20 March 2019 -- check consistent legacy and extended classification in laszip_write_point() + 7 November 2018 -- assure identical legacy and extended flags in laszip_write_point() + 20 October 2018 -- changed (U8*) to (const U8*) for all out->put___() calls + 5 October 2018 -- corrected 'is_empty' return value in laszip_inside_rectangle() + 29 September 2018 -- laszip_prepare_point_for_write() sets extended_point_type + 19 September 2018 -- removed tuples and triple support from attributes + 7 September 2018 -- replaced calls to _strdup with calls to the LASCopyString macro + 6 April 2018 -- added zero() function to laszip_dll struct to fix memory leak + 30 August 2017 -- completing stream-based writing (with writing LAS header) + 23 August 2017 -- turn on "native" by default + 3 August 2017 -- new 'laszip_create_laszip_vlr()' gets VLR as C++ std::vector + 29 July 2017 -- integrating minimal stream-based reading/writing into branch + 20 July 2017 -- Andrew Bell adds support for stream-based reading/writing + 28 May 2017 -- support for "LAS 1.4 selective decompression" added into DLL API + 25 April 2017 -- adding initial support for new "native LAS 1.4 extension" + 8 January 2017 -- changed from "laszip_dll.h" to "laszip_api.h" for hobu + +=============================================================================== +*/ + +#define LASZIP_DYN_LINK +#define LASZIP_SOURCE + +#include +#include +#include +#include +#include + +#include "laszip.hpp" +#include "lasattributer.hpp" +#include "bytestreamout_file.hpp" +#include "bytestreamin_file.hpp" +#include "bytestreamout_array.hpp" +#include "bytestreamin_array.hpp" +#include "bytestreamin_istream.hpp" +#include "bytestreamout_ostream.hpp" +#include "laswritepoint.hpp" +#include "lasreadpoint.hpp" +#include "lasquadtree.hpp" +#include "lasindex.hpp" + +class laszip_dll_inventory +{ +public: + BOOL active() const { return (first == FALSE); }; + U32 number_of_point_records; + U32 number_of_points_by_return[16]; + I32 max_X; + I32 min_X; + I32 max_Y; + I32 min_Y; + I32 max_Z; + I32 min_Z; + void add(const laszip_point_struct* point) + { + number_of_point_records++; + if (point->extended_point_type) + { + number_of_points_by_return[point->extended_return_number]++; + } + else + { + number_of_points_by_return[point->return_number]++; + } + if (first) + { + min_X = max_X = point->X; + min_Y = max_Y = point->Y; + min_Z = max_Z = point->Z; + first = FALSE; + } + else + { + if (point->X < min_X) min_X = point->X; + else if (point->X > max_X) max_X = point->X; + if (point->Y < min_Y) min_Y = point->Y; + else if (point->Y > max_Y) max_Y = point->Y; + if (point->Z < min_Z) min_Z = point->Z; + else if (point->Z > max_Z) max_Z = point->Z; + } + } + laszip_dll_inventory() + { + U32 i; + number_of_point_records = 0; + for (i = 0; i < 16; i++) number_of_points_by_return[i] = 0; + max_X = min_X = 0; + max_Y = min_Y = 0; + max_Z = min_Z = 0; + first = TRUE; + } +private: + BOOL first; +}; + +typedef struct laszip_dll { + laszip_header_struct header; + I64 p_count; + I64 npoints; + laszip_point_struct point; + U8** point_items; + FILE* file; + ByteStreamIn* streamin; + LASreadPoint* reader; + ByteStreamOut* streamout; + LASwritePoint* writer; + LASattributer* attributer; + CHAR error[1024]; + CHAR warning[1024]; + LASindex* lax_index; + F64 lax_r_min_x; + F64 lax_r_min_y; + F64 lax_r_max_x; + F64 lax_r_max_y; + CHAR* lax_file_name; + BOOL lax_create; + BOOL lax_append; + BOOL lax_exploit; + U32 las14_decompress_selective; + BOOL preserve_generating_software; + BOOL request_native_extension; + BOOL request_compatibility_mode; + BOOL compatibility_mode; + U32 set_chunk_size; + I32 start_scan_angle; + I32 start_extended_returns; + I32 start_classification; + I32 start_flags_and_channel; + I32 start_NIR_band; + laszip_dll_inventory* inventory; + std::vector buffers; + + void zero() + { + memset(&header, 0, sizeof(laszip_header_struct)); + p_count = 0; + npoints = 0; + memset(&point, 0, sizeof(laszip_point_struct)); + point_items = NULL; + file = NULL; + streamin = NULL; + reader = NULL; + streamout = NULL; + writer = NULL; + attributer = NULL; + memset(error, 0, 1024); + memset(warning, 0, 1024); + lax_index = NULL; + lax_r_min_x = 0.0; + lax_r_min_y = 0.0; + lax_r_max_x = 0.0; + lax_r_max_y = 0.0; + lax_file_name = NULL; + lax_create = FALSE; + lax_append = FALSE; + lax_exploit = FALSE; + las14_decompress_selective = 0; + preserve_generating_software = FALSE; + request_native_extension = FALSE; + request_compatibility_mode = FALSE; + compatibility_mode = FALSE; + set_chunk_size = 0; + start_scan_angle = 0; + start_extended_returns = 0; + start_classification = 0; + start_flags_and_channel = 0; + start_NIR_band = 0; + inventory = NULL; + }; +} laszip_dll_struct; + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_version( + laszip_U8* version_major + , laszip_U8* version_minor + , laszip_U16* version_revision + , laszip_U32* version_build +) +{ + try + { + *version_major = LASZIP_VERSION_MAJOR; + *version_minor = LASZIP_VERSION_MINOR; + *version_revision = LASZIP_VERSION_REVISION; + *version_build = LASZIP_VERSION_BUILD_DATE; + } + catch (...) + { + return 1; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_error( + laszip_POINTER pointer + , laszip_CHAR** error +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + *error = laszip_dll->error; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_get_error"); + return 1; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_warning( + laszip_POINTER pointer + , laszip_CHAR** warning +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + *warning = laszip_dll->warning; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_get_warning"); + return 1; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_create( + laszip_POINTER* pointer +) +{ + if (pointer == 0) return 1; + + try + { + laszip_dll_struct* laszip_dll = new laszip_dll_struct; + if (laszip_dll == 0) + { + return 1; + } + + // zero every field of the laszip_dll struct + + laszip_dll->zero(); + + // create the default + + laszip_clean(laszip_dll); + + *pointer = laszip_dll; + } + catch (...) + { + return 1; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_clean( + laszip_POINTER pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot clean while reader is open."); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "cannot clean while writer is open."); + return 1; + } + + // dealloc everything alloc in the header + + if (laszip_dll->header.user_data_in_header) + { + delete [] laszip_dll->header.user_data_in_header; + laszip_dll->header.user_data_in_header = 0; + } + + if (laszip_dll->header.vlrs) + { + U32 i; + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + if (laszip_dll->header.vlrs[i].data) + { + delete [] laszip_dll->header.vlrs[i].data; + } + } + free(laszip_dll->header.vlrs); + laszip_dll->header.vlrs = 0; + } + + if (laszip_dll->header.user_data_after_header) + { + delete [] laszip_dll->header.user_data_after_header; + laszip_dll->header.user_data_after_header = 0; + } + + // dealloc everything alloc in the point + + if (laszip_dll->point.extra_bytes) + { + delete [] laszip_dll->point.extra_bytes; + laszip_dll->point.extra_bytes = 0; + } + + // dealloc point items although close_reader() / close_writer() call should have done this already + + if (laszip_dll->point_items) + { + delete [] laszip_dll->point_items; + laszip_dll->point_items = 0; + } + + // close file although close_reader() / close_writer() call should have done this already + + if (laszip_dll->file) + { + fclose(laszip_dll->file); + laszip_dll->file = 0; + } + + // dealloc streamin although close_reader() call should have done this already + + if (laszip_dll->streamin) + { + delete laszip_dll->streamin; + laszip_dll->streamin = 0; + } + + // dealloc streamout although close_writer() call should have done this already + + if (laszip_dll->streamout) + { + delete laszip_dll->streamout; + laszip_dll->streamout = 0; + } + + // dealloc the attributer + + if (laszip_dll->attributer) + { + delete laszip_dll->attributer; + laszip_dll->attributer = 0; + } + + // dealloc lax_index although close_reader() / close_writer() call should have done this already + + if (laszip_dll->lax_index) + { + delete laszip_dll->lax_index; + laszip_dll->lax_index = 0; + } + + // dealloc lax_file_name although close_writer() call should have done this already + + if (laszip_dll->lax_file_name) + { + free(laszip_dll->lax_file_name); + laszip_dll->lax_file_name = 0; + } + + // dealloc the inventory although close_writer() call should have done this already + + if (laszip_dll->inventory == 0) + { + delete laszip_dll->inventory; + laszip_dll->inventory = 0; + } + + // dealloc any data fields that were kept around in memory for others + + if (laszip_dll->buffers.size()) + { + for (size_t i = 0; i < laszip_dll->buffers.size(); i++) + { + free(laszip_dll->buffers[i]); + } + laszip_dll->buffers.clear(); + } + + // zero every field of the laszip_dll struct + + laszip_dll->zero(); + + // create default header + + sprintf(laszip_dll->header.generating_software, "LASzip DLL %d.%d r%d (%d)", LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION, LASZIP_VERSION_BUILD_DATE); + laszip_dll->header.version_major = 1; + laszip_dll->header.version_minor = 2; + laszip_dll->header.header_size = 227; + laszip_dll->header.offset_to_point_data = 227; + laszip_dll->header.point_data_format = 1; + laszip_dll->header.point_data_record_length = 28; + laszip_dll->header.x_scale_factor = 0.01; + laszip_dll->header.y_scale_factor = 0.01; + laszip_dll->header.z_scale_factor = 0.01; + laszip_dll->set_chunk_size = LASZIP_CHUNK_SIZE_DEFAULT; + laszip_dll->request_native_extension = TRUE; + laszip_dll->las14_decompress_selective = LASZIP_DECOMPRESS_SELECTIVE_ALL; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_clean"); + return 1; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_destroy( + laszip_POINTER pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + int err = 0; + + try + { + err = laszip_clean(laszip_dll); + delete laszip_dll; + } + catch (...) + { + return 1; + } + + return err; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_header_pointer( + laszip_POINTER pointer + , laszip_header_struct** header_pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (header_pointer == 0) + { + sprintf(laszip_dll->error, "laszip_header_struct pointer 'header_pointer' is zero"); + return 1; + } + + *header_pointer = &laszip_dll->header; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_get_header_pointer"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_point_pointer( + laszip_POINTER pointer + , laszip_point_struct** point_pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (point_pointer == 0) + { + sprintf(laszip_dll->error, "laszip_point_struct pointer 'point_pointer' is zero"); + return 1; + } + + *point_pointer = &laszip_dll->point; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_get_point_pointer"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_point_count( + laszip_POINTER pointer + , laszip_I64* count +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (count == 0) + { + sprintf(laszip_dll->error, "laszip_I64 pointer 'count' is zero"); + return 1; + } + + if ((laszip_dll->reader == 0) && (laszip_dll->writer == 0)) + { + sprintf(laszip_dll->error, "getting count before reader or writer was opened"); + return 1; + } + + *count = laszip_dll->p_count; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_get_point_count"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_header( + laszip_POINTER pointer + , const laszip_header_struct* header +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (header == 0) + { + sprintf(laszip_dll->error, "laszip_header_struct pointer 'header' is zero"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot set header after reader was opened"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "cannot set header after writer was opened"); + return 1; + } + + // dealloc the attributer (if needed) + + if (laszip_dll->attributer) + { + delete laszip_dll->attributer; + laszip_dll->attributer = 0; + } + + // populate the header + + U32 i; + + laszip_dll->header.file_source_ID = header->file_source_ID; + laszip_dll->header.global_encoding = header->global_encoding; + laszip_dll->header.project_ID_GUID_data_1 = header->project_ID_GUID_data_1; + laszip_dll->header.project_ID_GUID_data_2 = header->project_ID_GUID_data_2; + laszip_dll->header.project_ID_GUID_data_3 = header->project_ID_GUID_data_3; + memcpy(laszip_dll->header.project_ID_GUID_data_4, header->project_ID_GUID_data_4, 8); + laszip_dll->header.version_major = header->version_major; + laszip_dll->header.version_minor = header->version_minor; + memcpy(laszip_dll->header.system_identifier, header->system_identifier, 32); + memcpy(laszip_dll->header.generating_software, header->generating_software, 32); + laszip_dll->header.file_creation_day = header->file_creation_day; + laszip_dll->header.file_creation_year = header->file_creation_year; + laszip_dll->header.header_size = header->header_size; + laszip_dll->header.offset_to_point_data = header->offset_to_point_data; + laszip_dll->header.number_of_variable_length_records = header->number_of_variable_length_records; + laszip_dll->header.point_data_format = header->point_data_format; + laszip_dll->header.point_data_record_length = header->point_data_record_length; + laszip_dll->header.number_of_point_records = header->number_of_point_records; + for (i = 0; i < 5; i++) laszip_dll->header.number_of_points_by_return[i] = header->number_of_points_by_return[i]; + laszip_dll->header.x_scale_factor = header->x_scale_factor; + laszip_dll->header.y_scale_factor = header->y_scale_factor; + laszip_dll->header.z_scale_factor = header->z_scale_factor; + laszip_dll->header.x_offset = header->x_offset; + laszip_dll->header.y_offset = header->y_offset; + laszip_dll->header.z_offset = header->z_offset; + laszip_dll->header.max_x = header->max_x; + laszip_dll->header.min_x = header->min_x; + laszip_dll->header.max_y = header->max_y; + laszip_dll->header.min_y = header->min_y; + laszip_dll->header.max_z = header->max_z; + laszip_dll->header.min_z = header->min_z; + + if (laszip_dll->header.version_minor >= 3) + { + laszip_dll->header.start_of_waveform_data_packet_record = header->start_of_first_extended_variable_length_record; + } + + if (laszip_dll->header.version_minor >= 4) + { + laszip_dll->header.start_of_first_extended_variable_length_record = header->start_of_first_extended_variable_length_record; + laszip_dll->header.number_of_extended_variable_length_records = header->number_of_extended_variable_length_records; + laszip_dll->header.extended_number_of_point_records = header->extended_number_of_point_records; + for (i = 0; i < 15; i++) laszip_dll->header.extended_number_of_points_by_return[i] = header->extended_number_of_points_by_return[i]; + } + + laszip_dll->header.user_data_in_header_size = header->user_data_in_header_size; + if (laszip_dll->header.user_data_in_header) + { + delete [] laszip_dll->header.user_data_in_header; + laszip_dll->header.user_data_in_header = 0; + } + if (header->user_data_in_header_size) + { + if (header->user_data_in_header == 0) + { + sprintf(laszip_dll->error, "header->user_data_in_header_size is %d but header->user_data_in_header is NULL", header->user_data_in_header_size); + return 1; + } + laszip_dll->header.user_data_in_header = new U8[header->user_data_in_header_size]; + memcpy(laszip_dll->header.user_data_in_header, header->user_data_in_header, header->user_data_in_header_size); + } + + if (laszip_dll->header.vlrs) + { + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + if (laszip_dll->header.vlrs[i].data) + { + delete [] laszip_dll->header.vlrs[i].data; + } + } + free(laszip_dll->header.vlrs); + laszip_dll->header.vlrs = 0; + } + if (header->number_of_variable_length_records) + { + laszip_dll->header.vlrs = (laszip_vlr*)malloc(sizeof(laszip_vlr)*header->number_of_variable_length_records); + for (i = 0; i < header->number_of_variable_length_records; i++) + { + laszip_dll->header.vlrs[i].reserved = header->vlrs[i].reserved; + memcpy(laszip_dll->header.vlrs[i].user_id, header->vlrs[i].user_id, 16); + laszip_dll->header.vlrs[i].record_id = header->vlrs[i].record_id; + laszip_dll->header.vlrs[i].record_length_after_header = header->vlrs[i].record_length_after_header; + memcpy(laszip_dll->header.vlrs[i].description, header->vlrs[i].description, 32); + if (header->vlrs[i].record_length_after_header) + { + if (header->vlrs[i].data == 0) + { + sprintf(laszip_dll->error, "header->vlrs[%d].record_length_after_header is %d but header->vlrs[%d].data is NULL", i, header->vlrs[i].record_length_after_header, i); + return 1; + } + laszip_dll->header.vlrs[i].data = new U8[header->vlrs[i].record_length_after_header]; + memcpy(laszip_dll->header.vlrs[i].data, header->vlrs[i].data, header->vlrs[i].record_length_after_header); + } + else + { + laszip_dll->header.vlrs[i].data = 0; + } + + // populate the attributer if needed + + if ((strcmp(laszip_dll->header.vlrs[i].user_id, "LASF_Spec") == 0) && (laszip_dll->header.vlrs[i].record_id == 4)) + { + if (laszip_dll->attributer == 0) + { + laszip_dll->attributer = new LASattributer; + if (laszip_dll->attributer == 0) + { + sprintf(laszip_dll->error, "cannot allocate LASattributer"); + return 1; + } + } + laszip_dll->attributer->init_attributes(laszip_dll->header.vlrs[i].record_length_after_header/sizeof(LASattribute), (LASattribute*)laszip_dll->header.vlrs[i].data); + } + } + } + + laszip_dll->header.user_data_after_header_size = header->user_data_after_header_size; + if (laszip_dll->header.user_data_after_header) + { + delete [] laszip_dll->header.user_data_after_header; + laszip_dll->header.user_data_after_header = 0; + } + if (header->user_data_after_header_size) + { + if (header->user_data_after_header == 0) + { + sprintf(laszip_dll->error, "header->user_data_after_header_size is %d but header->user_data_after_header is NULL", header->user_data_after_header_size); + return 1; + } + laszip_dll->header.user_data_after_header = new U8[header->user_data_after_header_size]; + memcpy(laszip_dll->header.user_data_after_header, header->user_data_after_header, header->user_data_after_header_size); + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_set_header"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_point_type_and_size( + laszip_POINTER pointer + , laszip_U8 point_type + , laszip_U16 point_size +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot set point format and point size after reader was opened"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "cannot set point format and point size after writer was opened"); + return 1; + } + + // check if point type and type are supported + + if (!LASzip().setup(point_type, point_size, LASZIP_COMPRESSOR_NONE)) + { + sprintf(laszip_dll->error, "invalid combination of point_type %d and point_size %d", (I32)point_type, (I32)point_size); + return 1; + } + + // set point type and point size + + laszip_dll->header.point_data_format = point_type; + laszip_dll->header.point_data_record_length = point_size; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_set_point_type_and_size"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_check_for_integer_overflow( + laszip_POINTER pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + // get a pointer to the header + + laszip_header_struct* header = &(laszip_dll->header); + + // quantize and dequantize the bounding box with current scale_factor and offset + + I32 quant_min_x = I32_QUANTIZE((header->min_x-header->x_offset)/header->x_scale_factor); + I32 quant_max_x = I32_QUANTIZE((header->max_x-header->x_offset)/header->x_scale_factor); + I32 quant_min_y = I32_QUANTIZE((header->min_y-header->y_offset)/header->y_scale_factor); + I32 quant_max_y = I32_QUANTIZE((header->max_y-header->y_offset)/header->y_scale_factor); + I32 quant_min_z = I32_QUANTIZE((header->min_z-header->z_offset)/header->z_scale_factor); + I32 quant_max_z = I32_QUANTIZE((header->max_z-header->z_offset)/header->z_scale_factor); + + F64 dequant_min_x = header->x_scale_factor*quant_min_x+header->x_offset; + F64 dequant_max_x = header->x_scale_factor*quant_max_x+header->x_offset; + F64 dequant_min_y = header->y_scale_factor*quant_min_y+header->y_offset; + F64 dequant_max_y = header->y_scale_factor*quant_max_y+header->y_offset; + F64 dequant_min_z = header->z_scale_factor*quant_min_z+header->z_offset; + F64 dequant_max_z = header->z_scale_factor*quant_max_z+header->z_offset; + + // make sure that there is not sign flip (a 32-bit integer overflow) for the bounding box + + if ((header->min_x > 0) != (dequant_min_x > 0)) + { + sprintf(laszip_dll->error, "quantization sign flip for min_x from %g to %g. set scale factor for x coarser than %g\n", header->min_x, dequant_min_x, header->x_scale_factor); + return 1; + } + if ((header->max_x > 0) != (dequant_max_x > 0)) + { + sprintf(laszip_dll->error, "quantization sign flip for max_x from %g to %g. set scale factor for x coarser than %g\n", header->max_x, dequant_max_x, header->x_scale_factor); + return 1; + } + if ((header->min_y > 0) != (dequant_min_y > 0)) + { + sprintf(laszip_dll->error, "quantization sign flip for min_y from %g to %g. set scale factor for y coarser than %g\n", header->min_y, dequant_min_y, header->y_scale_factor); + return 1; + } + if ((header->max_y > 0) != (dequant_max_y > 0)) + { + sprintf(laszip_dll->error, "quantization sign flip for max_y from %g to %g. set scale factor for y coarser than %g\n", header->max_y, dequant_max_y, header->y_scale_factor); + return 1; + } + if ((header->min_z > 0) != (dequant_min_z > 0)) + { + sprintf(laszip_dll->error, "quantization sign flip for min_z from %g to %g. set scale factor for z coarser than %g\n", header->min_z, dequant_min_z, header->z_scale_factor); + return 1; + } + if ((header->max_z > 0) != (dequant_max_z > 0)) + { + sprintf(laszip_dll->error, "quantization sign flip for max_z from %g to %g. set scale factor for z coarser than %g\n", header->max_z, dequant_max_z, header->z_scale_factor); + return 1; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_auto_offset"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_auto_offset( + laszip_POINTER pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot auto offset after reader was opened"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "cannot auto offset after writer was opened"); + return 1; + } + + // get a pointer to the header + + laszip_header_struct* header = &(laszip_dll->header); + + // check scale factor + + F64 x_scale_factor = header->x_scale_factor; + F64 y_scale_factor = header->y_scale_factor; + F64 z_scale_factor = header->z_scale_factor; + + if ((x_scale_factor <= 0) || !F64_IS_FINITE(x_scale_factor)) + { + sprintf(laszip_dll->error, "invalid x scale_factor %g in header", header->x_scale_factor); + return 1; + } + + if ((y_scale_factor <= 0) || !F64_IS_FINITE(y_scale_factor)) + { + sprintf(laszip_dll->error, "invalid y scale_factor %g in header", header->y_scale_factor); + return 1; + } + + if ((z_scale_factor <= 0) || !F64_IS_FINITE(z_scale_factor)) + { + sprintf(laszip_dll->error, "invalid z scale_factor %g in header", header->z_scale_factor); + return 1; + } + + F64 center_bb_x = (header->min_x + header->max_x) / 2; + F64 center_bb_y = (header->min_y + header->max_y) / 2; + F64 center_bb_z = (header->min_z + header->max_z) / 2; + + if (!F64_IS_FINITE(center_bb_x)) + { + sprintf(laszip_dll->error, "invalid x coordinate at center of bounding box (min: %g max: %g)", header->min_x, header->max_x); + return 1; + } + + if (!F64_IS_FINITE(center_bb_y)) + { + sprintf(laszip_dll->error, "invalid y coordinate at center of bounding box (min: %g max: %g)", header->min_y, header->max_y); + return 1; + } + + if (!F64_IS_FINITE(center_bb_z)) + { + sprintf(laszip_dll->error, "invalid z coordinate at center of bounding box (min: %g max: %g)", header->min_z, header->max_z); + return 1; + } + + F64 x_offset = header->x_offset; + F64 y_offset = header->y_offset; + F64 z_offset = header->z_offset; + + header->x_offset = (I64_FLOOR(center_bb_x/x_scale_factor/10000000))*10000000*x_scale_factor; + header->y_offset = (I64_FLOOR(center_bb_y/y_scale_factor/10000000))*10000000*y_scale_factor; + header->z_offset = (I64_FLOOR(center_bb_z/z_scale_factor/10000000))*10000000*z_scale_factor; + + if (laszip_check_for_integer_overflow(pointer)) + { + header->x_offset = x_offset; + header->y_offset = y_offset; + header->z_offset = z_offset; + return 1; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_auto_offset"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_point( + laszip_POINTER pointer + , const laszip_point_struct* point +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (point == 0) + { + sprintf(laszip_dll->error, "laszip_point_struct pointer 'point' is zero"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot set point for reader"); + return 1; + } + + memcpy(&laszip_dll->point, point, ((U8*)&(laszip_dll->point.extra_bytes)) - ((U8*)&(laszip_dll->point.X))); + + if (laszip_dll->point.extra_bytes) + { + if (point->extra_bytes) + { + if (laszip_dll->point.num_extra_bytes == point->num_extra_bytes) + { + memcpy(laszip_dll->point.extra_bytes, point->extra_bytes, laszip_dll->point.num_extra_bytes); + } + else + { + sprintf(laszip_dll->error, "target point has %d extra bytes but source point has %d", laszip_dll->point.num_extra_bytes, point->num_extra_bytes); + return 1; + } + } + else if (!laszip_dll->compatibility_mode) + { + sprintf(laszip_dll->error, "target point has extra bytes but source point does not"); + return 1; + } + } +/* + else + { + if (point->extra_bytes) + { + sprintf(laszip_dll->error, "source point has extra bytes but target point does not"); + return 1; + } + } +*/ + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_set_point"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_coordinates( + laszip_POINTER pointer + , const laszip_F64* coordinates +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (coordinates == 0) + { + sprintf(laszip_dll->error, "laszip_F64 pointer 'coordinates' is zero"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot set coordinates for reader"); + return 1; + } + + // get a pointer to the header + + laszip_header_struct* header = &(laszip_dll->header); + + // get a pointer to the point + + laszip_point_struct* point = &(laszip_dll->point); + + // set the coordinates + + point->X = I32_QUANTIZE((coordinates[0]-header->x_offset)/header->x_scale_factor); + point->Y = I32_QUANTIZE((coordinates[1]-header->y_offset)/header->y_scale_factor); + point->Z = I32_QUANTIZE((coordinates[2]-header->z_offset)/header->z_scale_factor); + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_set_coordinates"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_get_coordinates( + laszip_POINTER pointer + , laszip_F64* coordinates +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (coordinates == 0) + { + sprintf(laszip_dll->error, "laszip_F64 pointer 'coordinates' is zero"); + return 1; + } + + // get a pointer to the header + + laszip_header_struct* header = &(laszip_dll->header); + + // get a pointer to the point + + laszip_point_struct* point = &(laszip_dll->point); + + // get the coordinates + + coordinates[0] = header->x_scale_factor*point->X+header->x_offset; + coordinates[1] = header->y_scale_factor*point->Y+header->y_offset; + coordinates[2] = header->z_scale_factor*point->Z+header->z_offset; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_get_coordinates"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_geokeys( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_geokey_struct* key_entries +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (number == 0) + { + sprintf(laszip_dll->error, "number of key_entries is zero"); + return 1; + } + + if (key_entries == 0) + { + sprintf(laszip_dll->error, "laszip_geokey_struct pointer 'key_entries' is zero"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot set geokeys after reader was opened"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "cannot set geokeys after writer was opened"); + return 1; + } + + // create the geokey directory + + laszip_geokey_struct* key_entries_plus_one = new laszip_geokey_struct[number+1]; + if (key_entries_plus_one == 0) + { + sprintf(laszip_dll->error, "allocating laszip_geokey_struct[%u] array", number+1); + return 1; + } + key_entries_plus_one[0].key_id = 1; // aka key_directory_version + key_entries_plus_one[0].tiff_tag_location = 1; // aka key_revision + key_entries_plus_one[0].count = 0; // aka minor_revision + key_entries_plus_one[0].value_offset = number; // aka number_of_keys + memcpy(key_entries_plus_one + 1, key_entries, sizeof(laszip_geokey_struct)*number); + + // add the VLR + + if (laszip_add_vlr(laszip_dll, "LASF_Projection", 34735, (laszip_U16)(8 + number*8), 0, (laszip_U8*)key_entries_plus_one)) + { + sprintf(laszip_dll->error, "setting %u geodouble_params", number); + return 1; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_set_geokey_entries"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_geodouble_params( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_F64* geodouble_params +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (number == 0) + { + sprintf(laszip_dll->error, "number of geodouble_params is zero"); + return 1; + } + + if (geodouble_params == 0) + { + sprintf(laszip_dll->error, "laszip_F64 pointer 'geodouble_params' is zero"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot set geodouble_params after reader was opened"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "cannot set geodouble_params after writer was opened"); + return 1; + } + + // add the VLR + + if (laszip_add_vlr(laszip_dll, "LASF_Projection", 34736, (laszip_U16)(number*8), 0, (laszip_U8*)geodouble_params)) + { + sprintf(laszip_dll->error, "setting %u geodouble_params", number); + return 1; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_set_geodouble_params"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_geoascii_params( + laszip_POINTER pointer + , laszip_U32 number + , const laszip_CHAR* geoascii_params +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (number == 0) + { + sprintf(laszip_dll->error, "number of geoascii_params is zero"); + return 1; + } + + if (geoascii_params == 0) + { + sprintf(laszip_dll->error, "laszip_CHAR pointer 'geoascii_params' is zero"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot set geoascii_params after reader was opened"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "cannot set geoascii_params after writer was opened"); + return 1; + } + + // add the VLR + + if (laszip_add_vlr(laszip_dll, "LASF_Projection", 34737, (laszip_U16)(number), 0, (laszip_U8*)geoascii_params)) + { + sprintf(laszip_dll->error, "setting %u geoascii_params", number); + return 1; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_set_geoascii_params"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_add_attribute( + laszip_POINTER pointer + , laszip_U32 type + , const laszip_CHAR* name + , const laszip_CHAR* description + , laszip_F64 scale + , laszip_F64 offset +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (type > LAS_ATTRIBUTE_F64) + { + sprintf(laszip_dll->error, "laszip_U32 'type' is %u but needs to be between %d and %d", type, LAS_ATTRIBUTE_U8, LAS_ATTRIBUTE_F64); + return 1; + } + + if (name == 0) + { + sprintf(laszip_dll->error, "laszip_CHAR pointer 'name' is zero"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot add attribute after reader was opened"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "cannot add attribute after writer was opened"); + return 1; + } + + LASattribute lasattribute(type, name, description); + lasattribute.set_scale(scale); + lasattribute.set_offset(offset); + + if (laszip_dll->attributer == 0) + { + laszip_dll->attributer = new LASattributer; + if (laszip_dll->attributer == 0) + { + sprintf(laszip_dll->error, "cannot allocate LASattributer"); + return 1; + } + } + + if (laszip_dll->attributer->add_attribute(lasattribute) == -1) + { + sprintf(laszip_dll->error, "cannot add attribute '%s' to attributer", name); + return 1; + } + + if (laszip_add_vlr(laszip_dll, "LASF_Spec\0\0\0\0\0\0", 4, (laszip_U16)(laszip_dll->attributer->number_attributes*sizeof(LASattribute)), 0, (laszip_U8*)laszip_dll->attributer->attributes)) + { + sprintf(laszip_dll->error, "adding the new extra bytes VLR with the additional attribute '%s'", name); + return 1; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_add_attribute"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_add_vlr( + laszip_POINTER pointer + , const laszip_CHAR* user_id + , laszip_U16 record_id + , laszip_U16 record_length_after_header + , const laszip_CHAR* description + , const laszip_U8* data +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (user_id == 0) + { + sprintf(laszip_dll->error, "laszip_CHAR pointer 'user_id' is zero"); + return 1; + } + + if ((record_length_after_header > 0) && (data == 0)) + { + sprintf(laszip_dll->error, "record_length_after_header of VLR is %u but data pointer is zero", (U32)record_length_after_header); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot add vlr after reader was opened"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "cannot add vlr after writer was opened"); + return 1; + } + + U32 i = 0; + + if (laszip_dll->header.vlrs) + { + // overwrite existing VLR ? + + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + if ((strncmp(laszip_dll->header.vlrs[i].user_id, user_id, 16) == 0) && (laszip_dll->header.vlrs[i].record_id == record_id)) + { + if (laszip_dll->header.vlrs[i].record_length_after_header) + { + laszip_dll->header.offset_to_point_data -= laszip_dll->header.vlrs[i].record_length_after_header; + laszip_dll->header.vlrs[i].record_length_after_header = 0; + delete [] laszip_dll->header.vlrs[i].data; + laszip_dll->header.vlrs[i].data = 0; + } + break; + } + } + + // create new VLR + + if (i == laszip_dll->header.number_of_variable_length_records) + { + laszip_dll->header.number_of_variable_length_records++; + laszip_dll->header.offset_to_point_data += 54; + laszip_dll->header.vlrs = (laszip_vlr_struct*)realloc(laszip_dll->header.vlrs, sizeof(laszip_vlr_struct)*laszip_dll->header.number_of_variable_length_records); + if (laszip_dll->header.vlrs == 0) + { + sprintf(laszip_dll->error, "reallocating vlrs[%u] array", laszip_dll->header.number_of_variable_length_records); + return 1; + } + } + } + else + { + laszip_dll->header.number_of_variable_length_records = 1; + laszip_dll->header.offset_to_point_data += 54; + laszip_dll->header.vlrs = (laszip_vlr_struct*)malloc(sizeof(laszip_vlr_struct)); + if (laszip_dll->header.vlrs == 0) + { + sprintf(laszip_dll->error, "allocating vlrs[1] array"); + return 1; + } + } + + // zero the VLR + + memset(&(laszip_dll->header.vlrs[i]), 0, sizeof(laszip_vlr_struct)); + + // copy the VLR + + laszip_dll->header.vlrs[i].reserved = 0x0; + strncpy(laszip_dll->header.vlrs[i].user_id, user_id, 16); + laszip_dll->header.vlrs[i].record_id = record_id; + laszip_dll->header.vlrs[i].record_length_after_header = record_length_after_header; + if (description) + { + strncpy(laszip_dll->header.vlrs[i].description, description, 32); + } + else + { + sprintf(laszip_dll->header.vlrs[i].description, "LASzip DLL %d.%d r%d (%d)", LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION, LASZIP_VERSION_BUILD_DATE); + } + if (record_length_after_header) + { + laszip_dll->header.offset_to_point_data += record_length_after_header; + laszip_dll->header.vlrs[i].data = new U8[record_length_after_header]; + memcpy(laszip_dll->header.vlrs[i].data, data, record_length_after_header); + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_add_vlr"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_remove_vlr( + laszip_POINTER pointer + , const laszip_CHAR* user_id + , laszip_U16 record_id +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (user_id == 0) + { + sprintf(laszip_dll->error, "laszip_CHAR pointer 'user_id' is zero"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "cannot remove vlr after reader was opened"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "cannot remove vlr after writer was opened"); + return 1; + } + + U32 i = 0; + + if (laszip_dll->header.vlrs) + { + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + if ((strncmp(laszip_dll->header.vlrs[i].user_id, user_id, 16) == 0) && (laszip_dll->header.vlrs[i].record_id == record_id)) + { + if (laszip_dll->header.vlrs[i].record_length_after_header) + { + laszip_dll->header.offset_to_point_data -= (54 + laszip_dll->header.vlrs[i].record_length_after_header); + delete [] laszip_dll->header.vlrs[i].data; + laszip_dll->header.vlrs[i].data = 0; + } + laszip_dll->header.number_of_variable_length_records--; + for (/*i = i*/; i < laszip_dll->header.number_of_variable_length_records; i++) + { + laszip_dll->header.vlrs[i] = laszip_dll->header.vlrs[i+1]; + } + if (laszip_dll->header.number_of_variable_length_records) + { + laszip_dll->header.vlrs = (laszip_vlr_struct*)realloc(laszip_dll->header.vlrs, sizeof(laszip_vlr_struct)*laszip_dll->header.number_of_variable_length_records); + if (laszip_dll->header.vlrs == 0) + { + sprintf(laszip_dll->error, "reallocating vlrs[%u] array", laszip_dll->header.number_of_variable_length_records); + return 1; + } + } + else + { + free(laszip_dll->header.vlrs); + laszip_dll->header.vlrs = 0; + } + i = U32_MAX; + break; + } + } + if (i != U32_MAX) + { + sprintf(laszip_dll->error, "cannot find VLR with user_id '%s' and record_id %d among the %u VLRs in the header", user_id, (I32)record_id, laszip_dll->header.number_of_variable_length_records); + return 1; + } + } + else + { + sprintf(laszip_dll->error, "cannot remove VLR with user_id '%s' and record_id %d because header has no VLRs", user_id, (I32)record_id); + return 1; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_add_vlr"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_preserve_generating_software( + laszip_POINTER pointer + , const laszip_BOOL preserve +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + laszip_dll->preserve_generating_software = preserve; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_preserve_generating_software"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_request_native_extension( + laszip_POINTER pointer + , const laszip_BOOL request +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + laszip_dll->request_native_extension = request; + + if (request) // only one should be on + { + laszip_dll->request_compatibility_mode = FALSE; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_request_native_extension"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_request_compatibility_mode( + laszip_POINTER pointer + , const laszip_BOOL request +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + laszip_dll->request_compatibility_mode = request; + + if (request) // only one should be on + { + laszip_dll->request_native_extension = FALSE; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_request_compatibility_mode"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_set_chunk_size( + laszip_POINTER pointer + , const laszip_U32 chunk_size +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + laszip_dll->set_chunk_size = chunk_size; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_set_chunk_size"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_create_spatial_index( + laszip_POINTER pointer + , const laszip_BOOL create + , const laszip_BOOL append +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + if (append) + { + sprintf(laszip_dll->error, "appending of spatial index not (yet) supported in this version"); + return 1; + } + + laszip_dll->lax_create = create; + laszip_dll->lax_append = append; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_create_spatial_index"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +static I32 +laszip_prepare_header_for_write( + laszip_dll_struct* laszip_dll +) +{ + if ((laszip_dll->header.version_major != 1) || (laszip_dll->header.version_minor > 4)) + { + sprintf(laszip_dll->error, "unknown LAS version %d.%d", (I32)laszip_dll->header.version_major, (I32)laszip_dll->header.version_minor); + return 1; + } + + // check counters + U32 i; + + if (laszip_dll->header.point_data_format > 5) + { + // legacy counters are zero for new point types + + laszip_dll->header.number_of_point_records = 0; + for (i = 0; i < 5; i++) + { + laszip_dll->header.number_of_points_by_return[i] = 0; + } + } + else if (laszip_dll->header.version_minor > 3) + { + // legacy counters must be zero or consistent for old point types + + if (laszip_dll->header.number_of_point_records != laszip_dll->header.extended_number_of_point_records) + { + if (laszip_dll->header.number_of_point_records != 0) + { +#ifdef _WIN32 + sprintf(laszip_dll->error, "inconsistent number_of_point_records %u and extended_number_of_point_records %I64d", laszip_dll->header.number_of_point_records, laszip_dll->header.extended_number_of_point_records); +#else + sprintf(laszip_dll->error, "inconsistent number_of_point_records %u and extended_number_of_point_records %llu", laszip_dll->header.number_of_point_records, laszip_dll->header.extended_number_of_point_records); +#endif + return 1; + } + else if (laszip_dll->header.extended_number_of_point_records <= U32_MAX) + { + laszip_dll->header.number_of_point_records = (U32)laszip_dll->header.extended_number_of_point_records; + } + } + for (i = 0; i < 5; i++) + { + if (laszip_dll->header.number_of_points_by_return[i] != laszip_dll->header.extended_number_of_points_by_return[i]) + { + if (laszip_dll->header.number_of_points_by_return[i] != 0) + { +#ifdef _WIN32 + sprintf(laszip_dll->error, "inconsistent number_of_points_by_return[%u] %u and extended_number_of_points_by_return[%u] %I64d", i, laszip_dll->header.number_of_points_by_return[i], i, laszip_dll->header.extended_number_of_points_by_return[i]); +#else + sprintf(laszip_dll->error, "inconsistent number_of_points_by_return[%u] %u and extended_number_of_points_by_return[%u] %llu", i, laszip_dll->header.number_of_points_by_return[i], i, laszip_dll->header.extended_number_of_points_by_return[i]); +#endif + return 1; + } + else if (laszip_dll->header.extended_number_of_points_by_return[i] <= U32_MAX) + { + laszip_dll->header.number_of_points_by_return[i] = (U32)laszip_dll->header.extended_number_of_points_by_return[i]; + } + } + } + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +static I32 +laszip_prepare_point_for_write( + laszip_dll_struct* laszip_dll + , const laszip_BOOL compress +) +{ + U32 i; + + if (laszip_dll->header.point_data_format > 5) + { + // must be set for the new point types 6 or higher ... + + laszip_dll->point.extended_point_type = 1; + + if (laszip_dll->request_native_extension) + { + // we are *not* operating in compatibility mode + + laszip_dll->compatibility_mode = FALSE; + } + else if (laszip_dll->request_compatibility_mode) + { + // we are *not* using the native extension + + laszip_dll->request_native_extension = FALSE; + + // make sure there are no more than U32_MAX points + + if (laszip_dll->header.extended_number_of_point_records > U32_MAX) + { +#ifdef _WIN32 + sprintf(laszip_dll->error, "extended_number_of_point_records of %I64d is too much for 32-bit counters of compatibility mode", laszip_dll->header.extended_number_of_point_records); +#else + sprintf(laszip_dll->error, "extended_number_of_point_records of %llu is too much for 32-bit counters of compatibility mode", laszip_dll->header.extended_number_of_point_records); +#endif + return 1; + } + + // copy 64-bit extended counters back into 32-bit legacy counters + + laszip_dll->header.number_of_point_records = (U32)(laszip_dll->header.extended_number_of_point_records); + for (i = 0; i < 5; i++) + { + laszip_dll->header.number_of_points_by_return[i] = (U32)(laszip_dll->header.extended_number_of_points_by_return[i]); + } + + // are there any "extra bytes" already ... ? + + I32 number_of_existing_extrabytes = 0; + + switch (laszip_dll->header.point_data_format) + { + case 6: + number_of_existing_extrabytes = laszip_dll->header.point_data_record_length - 30; + break; + case 7: + number_of_existing_extrabytes = laszip_dll->header.point_data_record_length - 36; + break; + case 8: + number_of_existing_extrabytes = laszip_dll->header.point_data_record_length - 38; + break; + case 9: + number_of_existing_extrabytes = laszip_dll->header.point_data_record_length - 59; + break; + case 10: + number_of_existing_extrabytes = laszip_dll->header.point_data_record_length - 67; + break; + default: + sprintf(laszip_dll->error, "unknown point_data_format %d", laszip_dll->header.point_data_format); + return 1; + } + + if (number_of_existing_extrabytes < 0) + { + sprintf(laszip_dll->error, "bad point_data_format %d point_data_record_length %d combination", laszip_dll->header.point_data_format, laszip_dll->header.point_data_record_length); + return 1; + } + + // downgrade to LAS 1.2 or LAS 1.3 + if (laszip_dll->header.point_data_format <= 8) + { + laszip_dll->header.version_minor = 2; + // LAS 1.2 header is 148 bytes less than LAS 1.4+ header + laszip_dll->header.header_size -= 148; + laszip_dll->header.offset_to_point_data -= 148; + } + else + { + laszip_dll->header.version_minor = 3; + // LAS 1.3 header is 140 bytes less than LAS 1.4+ header + laszip_dll->header.header_size -= 140; + laszip_dll->header.offset_to_point_data -= 140; + } + // turn off the bit indicating the presence of the OGC WKT + laszip_dll->header.global_encoding &= ~(1<<4); + + // old point type is two bytes shorter + laszip_dll->header.point_data_record_length -= 2; + // but we add 5 bytes of attributes + laszip_dll->header.point_data_record_length += 5; + + // create 2+2+4+148 bytes payload for compatibility VLR + ByteStreamOutArray* out; + if (IS_LITTLE_ENDIAN()) + out = new ByteStreamOutArrayLE(); + else + out = new ByteStreamOutArrayBE(); + // write control info + U16 laszip_version = (U16)LASZIP_VERSION_BUILD_DATE; + out->put16bitsLE((U8*)&laszip_version); + U16 compatible_version = 3; + out->put16bitsLE((U8*)&compatible_version); + U32 unused = 0; + out->put32bitsLE((U8*)&unused); + // write the 148 bytes of the extended LAS 1.4 header + U64 start_of_waveform_data_packet_record = laszip_dll->header.start_of_waveform_data_packet_record; + if (start_of_waveform_data_packet_record != 0) + { +#ifdef _WIN32 + fprintf(stderr,"WARNING: header->start_of_waveform_data_packet_record is %I64d. writing 0 instead.\n", start_of_waveform_data_packet_record); +#else + fprintf(stderr,"WARNING: header->start_of_waveform_data_packet_record is %llu. writing 0 instead.\n", start_of_waveform_data_packet_record); +#endif + start_of_waveform_data_packet_record = 0; + } + out->put64bitsLE((U8*)&start_of_waveform_data_packet_record); + U64 start_of_first_extended_variable_length_record = laszip_dll->header.start_of_first_extended_variable_length_record; + if (start_of_first_extended_variable_length_record != 0) + { +#ifdef _WIN32 + fprintf(stderr,"WARNING: EVLRs not supported. header->start_of_first_extended_variable_length_record is %I64d. writing 0 instead.\n", start_of_first_extended_variable_length_record); +#else + fprintf(stderr,"WARNING: EVLRs not supported. header->start_of_first_extended_variable_length_record is %llu. writing 0 instead.\n", start_of_first_extended_variable_length_record); +#endif + start_of_first_extended_variable_length_record = 0; + } + out->put64bitsLE((U8*)&start_of_first_extended_variable_length_record); + U32 number_of_extended_variable_length_records = laszip_dll->header.number_of_extended_variable_length_records; + if (number_of_extended_variable_length_records != 0) + { + fprintf(stderr,"WARNING: EVLRs not supported. header->number_of_extended_variable_length_records is %u. writing 0 instead.\n", number_of_extended_variable_length_records); + number_of_extended_variable_length_records = 0; + } + out->put32bitsLE((U8*)&number_of_extended_variable_length_records); + U64 extended_number_of_point_records; + if (laszip_dll->header.number_of_point_records) + extended_number_of_point_records = laszip_dll->header.number_of_point_records; + else + extended_number_of_point_records = laszip_dll->header.extended_number_of_point_records; + out->put64bitsLE((U8*)&extended_number_of_point_records); + U64 extended_number_of_points_by_return; + for (i = 0; i < 15; i++) + { + if ((i < 5) && laszip_dll->header.number_of_points_by_return[i]) + extended_number_of_points_by_return = laszip_dll->header.number_of_points_by_return[i]; + else + extended_number_of_points_by_return = laszip_dll->header.extended_number_of_points_by_return[i]; + out->put64bitsLE((U8*)&extended_number_of_points_by_return); + } + + // add the compatibility VLR + + if (laszip_add_vlr(laszip_dll, "lascompatible\0\0", 22204, (laszip_U16)(2+2+4+148), 0, (laszip_U8*)out->takeData())) + { + sprintf(laszip_dll->error, "adding the compatibility VLR"); + return 1; + } + delete out; + + // if needed create an attributer to describe the "extra bytes" + + if (laszip_dll->attributer == 0) + { + laszip_dll->attributer = new LASattributer; + if (laszip_dll->attributer == 0) + { + sprintf(laszip_dll->error, "cannot allocate LASattributer"); + return 1; + } + } + + // were there any pre-existing extra bytes + + if (number_of_existing_extrabytes > 0) + { + // make sure the existing "extra bytes" are documented + + if (laszip_dll->attributer->get_attributes_size() > number_of_existing_extrabytes) + { + sprintf(laszip_dll->error, "bad \"extra bytes\" VLR describes %d bytes more than points actually have", laszip_dll->attributer->get_attributes_size() - number_of_existing_extrabytes); + return 1; + } + else if (laszip_dll->attributer->get_attributes_size() < number_of_existing_extrabytes) + { + // maybe the existing "extra bytes" are documented in a VLR + if (laszip_dll->header.vlrs) + { + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + if ((strcmp(laszip_dll->header.vlrs[i].user_id, "LASF_Spec") == 0) && (laszip_dll->header.vlrs[i].record_id == 4)) + { + laszip_dll->attributer->init_attributes(laszip_dll->header.vlrs[i].record_length_after_header/sizeof(LASattribute), (LASattribute*)laszip_dll->header.vlrs[i].data); + } + } + } + + // describe any undocumented "extra bytes" as "unknown" U8 attributes + for (I32 i = (I32)(laszip_dll->attributer->get_attributes_size()); i < number_of_existing_extrabytes; i++) + { + CHAR unknown_name[16]; + memset(unknown_name, 0, 16); + sprintf(unknown_name, "unknown %d", i); + LASattribute lasattribute_unknown(LAS_ATTRIBUTE_U8, unknown_name, unknown_name); + if (laszip_dll->attributer->add_attribute(lasattribute_unknown) == -1) + { + sprintf(laszip_dll->error, "cannot add unknown U8 attribute '%s' of %d to attributer", unknown_name, number_of_existing_extrabytes); + return 1; + } + } + } + } + + // create the "extra bytes" that store the newer LAS 1.4 point attributes + + // scan_angle (difference or remainder) is stored as a I16 + LASattribute lasattribute_scan_angle(LAS_ATTRIBUTE_I16, "LAS 1.4 scan angle", "additional attributes"); + lasattribute_scan_angle.set_scale(0.006); + I32 index_scan_angle = laszip_dll->attributer->add_attribute(lasattribute_scan_angle); + laszip_dll->start_scan_angle = laszip_dll->attributer->get_attribute_start(index_scan_angle); + // extended returns stored as a U8 + LASattribute lasattribute_extended_returns(LAS_ATTRIBUTE_U8, "LAS 1.4 extended returns", "additional attributes"); + I32 index_extended_returns = laszip_dll->attributer->add_attribute(lasattribute_extended_returns); + laszip_dll->start_extended_returns = laszip_dll->attributer->get_attribute_start(index_extended_returns); + // classification stored as a U8 + LASattribute lasattribute_classification(LAS_ATTRIBUTE_U8, "LAS 1.4 classification", "additional attributes"); + I32 index_classification = laszip_dll->attributer->add_attribute(lasattribute_classification); + laszip_dll->start_classification = laszip_dll->attributer->get_attribute_start(index_classification); + // flags and channel stored as a U8 + LASattribute lasattribute_flags_and_channel(LAS_ATTRIBUTE_U8, "LAS 1.4 flags and channel", "additional attributes"); + I32 index_flags_and_channel = laszip_dll->attributer->add_attribute(lasattribute_flags_and_channel); + laszip_dll->start_flags_and_channel = laszip_dll->attributer->get_attribute_start(index_flags_and_channel); + // maybe store the NIR band as a U16 + if (laszip_dll->header.point_data_format == 8 || laszip_dll->header.point_data_format == 10) + { + // the NIR band is stored as a U16 + LASattribute lasattribute_NIR_band(LAS_ATTRIBUTE_U16, "LAS 1.4 NIR band", "additional attributes"); + I32 index_NIR_band = laszip_dll->attributer->add_attribute(lasattribute_NIR_band); + laszip_dll->start_NIR_band = laszip_dll->attributer->get_attribute_start(index_NIR_band); + } + else + { + laszip_dll->start_NIR_band = -1; + } + + // add the extra bytes VLR with the additional attributes + + if (laszip_add_vlr(laszip_dll, "LASF_Spec\0\0\0\0\0\0", 4, (laszip_U16)(laszip_dll->attributer->number_attributes*sizeof(LASattribute)), 0, (laszip_U8*)laszip_dll->attributer->attributes)) + { + sprintf(laszip_dll->error, "adding the extra bytes VLR with the additional attributes"); + return 1; + } + + // update point type + + if (laszip_dll->header.point_data_format == 6) + { + laszip_dll->header.point_data_format = 1; + } + else if (laszip_dll->header.point_data_format <= 8) + { + laszip_dll->header.point_data_format = 3; + } + else // 9->4 and 10->5 + { + laszip_dll->header.point_data_format -= 5; + } + + // we are operating in compatibility mode + laszip_dll->compatibility_mode = TRUE; + } + else if (compress) + { + sprintf(laszip_dll->error, "LASzip DLL %d.%d r%d (%d) cannot compress point data format %d without requesting 'compatibility mode'", LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION, LASZIP_VERSION_BUILD_DATE, (I32)laszip_dll->header.point_data_format); + return 1; + } + } + else + { + // must *not* be set for the old point type 5 or lower + + laszip_dll->point.extended_point_type = 0; + + // we are *not* operating in compatibility mode + + laszip_dll->compatibility_mode = FALSE; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +static I32 +laszip_prepare_vlrs_for_write( + laszip_dll_struct* laszip_dll +) +{ + U32 i, vlrs_size = 0; + + if (laszip_dll->header.number_of_variable_length_records) + { + if (laszip_dll->header.vlrs == 0) + { + sprintf(laszip_dll->error, "number_of_variable_length_records is %u but vlrs pointer is zero", laszip_dll->header.number_of_variable_length_records); + return 1; + } + + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + vlrs_size += 54; + if (laszip_dll->header.vlrs[i].record_length_after_header) + { + if (laszip_dll->header.vlrs == 0) + { + sprintf(laszip_dll->error, "vlrs[%u].record_length_after_header is %u but vlrs[%u].data pointer is zero", i, laszip_dll->header.vlrs[i].record_length_after_header, i); + return 1; + } + vlrs_size += laszip_dll->header.vlrs[i].record_length_after_header; + } + } + } + + if ((vlrs_size + laszip_dll->header.header_size + laszip_dll->header.user_data_after_header_size) != laszip_dll->header.offset_to_point_data) + { + sprintf(laszip_dll->error,"header_size (%u) plus vlrs_size (%u) plus user_data_after_header_size (%u) does not equal offset_to_point_data (%u)", (U32)laszip_dll->header.header_size, vlrs_size, laszip_dll->header.user_data_after_header_size, laszip_dll->header.offset_to_point_data); + return 1; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +static U32 +laszip_vrl_payload_size( + const LASzip* laszip +) +{ + return 34 + (6 * laszip->num_items); +} + +/*---------------------------------------------------------------------------*/ +static I32 +write_laszip_vlr_header( + laszip_dll_struct* laszip_dll + , const LASzip* laszip + , ByteStreamOut* out +) +{ + // write the LASzip VLR header + + U16 reserved = 0x0; + try { out->put16bitsLE((U8*)&reserved); } catch(...) + { + sprintf(laszip_dll->error, "writing LASzip VLR header.reserved"); + return 1; + } + U8 user_id[16] = "laszip encoded\0"; + try { out->putBytes((U8*)user_id, 16); } catch(...) + { + sprintf(laszip_dll->error, "writing LASzip VLR header.user_id"); + return 1; + } + U16 record_id = 22204; + try { out->put16bitsLE((U8*)&record_id); } catch(...) + { + sprintf(laszip_dll->error, "writing LASzip VLR header.record_id"); + return 1; + } + U16 record_length_after_header = (U16)laszip_vrl_payload_size(laszip); + try { out->put16bitsLE((U8*)&record_length_after_header); } catch(...) + { + sprintf(laszip_dll->error, "writing LASzip VLR header.record_length_after_header"); + return 1; + } + CHAR description[32]; + memset(description, 0, 32); + sprintf(description, "LASzip DLL %d.%d r%d (%d)", LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION, LASZIP_VERSION_BUILD_DATE); + try { out->putBytes((U8*)description, 32); } catch(...) + { + sprintf(laszip_dll->error, "writing LASzip VLR header.description"); + return 1; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +static I32 +write_laszip_vlr_payload( + laszip_dll_struct* laszip_dll + , const LASzip* laszip + , ByteStreamOut* out +) +{ + // write the LASzip VLR payload + + // U16 compressor 2 bytes + // U32 coder 2 bytes + // U8 version_major 1 byte + // U8 version_minor 1 byte + // U16 version_revision 2 bytes + // U32 options 4 bytes + // I32 chunk_size 4 bytes + // I64 number_of_special_evlrs 8 bytes + // I64 offset_to_special_evlrs 8 bytes + // U16 num_items 2 bytes + // U16 type 2 bytes * num_items + // U16 size 2 bytes * num_items + // U16 version 2 bytes * num_items + // which totals 34+6*num_items + + try { out->put16bitsLE((const U8*)&(laszip->compressor)); } catch(...) + { + sprintf(laszip_dll->error, "writing compressor %d", (I32)laszip->compressor); + return 1; + } + try { out->put16bitsLE((const U8*)&(laszip->coder)); } catch(...) + { + sprintf(laszip_dll->error, "writing coder %d", (I32)laszip->coder); + return 1; + } + try { out->putBytes((const U8*)&(laszip->version_major), 1); } catch(...) + { + sprintf(laszip_dll->error, "writing version_major %d", (I32)laszip->version_major); + return 1; + } + try { out->putBytes((const U8*)&(laszip->version_minor), 1); } catch(...) + { + sprintf(laszip_dll->error, "writing version_minor %d", (I32)laszip->version_minor); + return 1; + } + try { out->put16bitsLE((const U8*)&(laszip->version_revision)); } catch(...) + { + sprintf(laszip_dll->error, "writing version_revision %d", (I32)laszip->version_revision); + return 1; + } + try { out->put32bitsLE((const U8*)&(laszip->options)); } catch(...) + { + sprintf(laszip_dll->error, "writing options %u", laszip->options); + return 1; + } + try { out->put32bitsLE((const U8*)&(laszip->chunk_size)); } catch(...) + { + sprintf(laszip_dll->error, "writing chunk_size %u", laszip->chunk_size); + return 1; + } + try { out->put64bitsLE((const U8*)&(laszip->number_of_special_evlrs)); } catch(...) + { + sprintf(laszip_dll->error, "writing number_of_special_evlrs %d", (I32)laszip->number_of_special_evlrs); + return 1; + } + try { out->put64bitsLE((const U8*)&(laszip->offset_to_special_evlrs)); } catch(...) + { + sprintf(laszip_dll->error, "writing offset_to_special_evlrs %d", (I32)laszip->offset_to_special_evlrs); + return 1; + } + try { out->put16bitsLE((const U8*)&(laszip->num_items)); } catch(...) + { + sprintf(laszip_dll->error, "writing num_items %d", (I32)laszip->num_items); + return 1; + } + + U32 j; + for (j = 0; j < laszip->num_items; j++) + { + U16 type = (U16)(laszip->items[j].type); + try { out->put16bitsLE((const U8*)&type); } catch(...) + { + sprintf(laszip_dll->error, "writing type %d of item %d", (I32)laszip->items[j].type, j); + return 1; + } + try { out->put16bitsLE((const U8*)&(laszip->items[j].size)); } catch(...) + { + sprintf(laszip_dll->error, "writing size %d of item %d", (I32)laszip->items[j].size, j); + return 1; + } + try { out->put16bitsLE((const U8*)&(laszip->items[j].version)); } catch(...) + { + sprintf(laszip_dll->error, "writing version %d of item %d", (I32)laszip->items[j].version, j); + return 1; + } + } + return 0; +} + +/*---------------------------------------------------------------------------*/ +static I32 +laszip_write_header( + laszip_dll_struct* laszip_dll + , const LASzip* laszip + , const laszip_BOOL compress +) +{ + U32 i; + + try { laszip_dll->streamout->putBytes((const U8*)"LASF", 4); } catch(...) + { + sprintf(laszip_dll->error, "writing header.file_signature"); + return 1; + } + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.file_source_ID)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.file_source_ID"); + return 1; + } + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.global_encoding)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.global_encoding"); + return 1; + } + try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.project_ID_GUID_data_1)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.project_ID_GUID_data_1"); + return 1; + } + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.project_ID_GUID_data_2)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.project_ID_GUID_data_2"); + return 1; + } + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.project_ID_GUID_data_3)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.project_ID_GUID_data_3"); + return 1; + } + try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.project_ID_GUID_data_4, 8); } catch(...) + { + sprintf(laszip_dll->error, "writing header.project_ID_GUID_data_4"); + return 1; + } + try { laszip_dll->streamout->putBytes((const U8*)&(laszip_dll->header.version_major), 1); } catch(...) + { + sprintf(laszip_dll->error, "writing header.version_major"); + return 1; + } + try { laszip_dll->streamout->putBytes((const U8*)&(laszip_dll->header.version_minor), 1); } catch(...) + { + sprintf(laszip_dll->error, "writing header.version_minor"); + return 1; + } + try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.system_identifier, 32); } catch(...) + { + sprintf(laszip_dll->error, "writing header.system_identifier"); + return 1; + } + if (!laszip_dll->preserve_generating_software) + { + memset(laszip_dll->header.generating_software, 0, 32); + sprintf(laszip_dll->header.generating_software, "LASzip DLL %d.%d r%d (%d)", LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION, LASZIP_VERSION_BUILD_DATE); + } + try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.generating_software, 32); } catch(...) + { + sprintf(laszip_dll->error, "writing header.generating_software"); + return 1; + } + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.file_creation_day)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.file_creation_day"); + return 1; + } + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.file_creation_year)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.file_creation_year"); + return 1; + } + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.header_size)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.header_size"); + return 1; + } + if (compress) + { + laszip_dll->header.offset_to_point_data += (54 + laszip_vrl_payload_size(laszip)); + } + try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.offset_to_point_data)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.offset_to_point_data"); + return 1; + } + if (compress) + { + laszip_dll->header.offset_to_point_data -= (54 + laszip_vrl_payload_size(laszip)); + laszip_dll->header.number_of_variable_length_records += 1; + } + try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.number_of_variable_length_records)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.number_of_variable_length_records"); + return 1; + } + if (compress) + { + laszip_dll->header.number_of_variable_length_records -= 1; + laszip_dll->header.point_data_format |= 128; + } + try { laszip_dll->streamout->putBytes((const U8*)&(laszip_dll->header.point_data_format), 1); } catch(...) + { + sprintf(laszip_dll->error, "writing header.point_data_format"); + return 1; + } + if (compress) + { + laszip_dll->header.point_data_format &= 127; + } + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.point_data_record_length)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.point_data_record_length"); + return 1; + } + try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.number_of_point_records)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.number_of_point_records"); + return 1; + } + for (i = 0; i < 5; i++) + { + try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.number_of_points_by_return[i])); } catch(...) + { + sprintf(laszip_dll->error, "writing header.number_of_points_by_return %d", i); + return 1; + } + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.x_scale_factor)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.x_scale_factor"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.y_scale_factor)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.y_scale_factor"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.z_scale_factor)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.z_scale_factor"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.x_offset)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.x_offset"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.y_offset)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.y_offset"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.z_offset)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.z_offset"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.max_x)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.max_x"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.min_x)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.min_x"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.max_y)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.max_y"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.min_y)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.min_y"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.max_z)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.max_z"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.min_z)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.min_z"); + return 1; + } + + // special handling for LAS 1.3 + if ((laszip_dll->header.version_major == 1) && (laszip_dll->header.version_minor >= 3)) + { + if (laszip_dll->header.header_size < 235) + { + sprintf(laszip_dll->error, "for LAS 1.%d header_size should at least be 235 but it is only %d", laszip_dll->header.version_minor, laszip_dll->header.header_size); + return 1; + } + else + { + if (laszip_dll->header.start_of_waveform_data_packet_record != 0) + { +#ifdef _WIN32 + sprintf(laszip_dll->warning, "header.start_of_waveform_data_packet_record is %I64d. writing 0 instead.", laszip_dll->header.start_of_waveform_data_packet_record); +#else + sprintf(laszip_dll->warning, "header.start_of_waveform_data_packet_record is %llu. writing 0 instead.", laszip_dll->header.start_of_waveform_data_packet_record); +#endif + laszip_dll->header.start_of_waveform_data_packet_record = 0; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.start_of_waveform_data_packet_record)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.start_of_waveform_data_packet_record"); + return 1; + } + laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 235; + } + } + else + { + laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 227; + } + + // special handling for LAS 1.4 + if ((laszip_dll->header.version_major == 1) && (laszip_dll->header.version_minor >= 4)) + { + if (laszip_dll->header.header_size < 375) + { + sprintf(laszip_dll->error, "for LAS 1.%d header_size should at least be 375 but it is only %d", laszip_dll->header.version_minor, laszip_dll->header.header_size); + return 1; + } + else + { + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.start_of_first_extended_variable_length_record)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.start_of_first_extended_variable_length_record"); + return 1; + } + try { laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->header.number_of_extended_variable_length_records)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.number_of_extended_variable_length_records"); + return 1; + } + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.extended_number_of_point_records)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.extended_number_of_point_records"); + return 1; + } + for (i = 0; i < 15; i++) + { + try { laszip_dll->streamout->put64bitsLE((const U8*)&(laszip_dll->header.extended_number_of_points_by_return[i])); } catch(...) + { + sprintf(laszip_dll->error, "writing header.extended_number_of_points_by_return[%d]", i); + return 1; + } + } + laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 375; + } + } + + // write any number of user-defined bytes that might have been added to the header + if (laszip_dll->header.user_data_in_header_size) + { + try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.user_data_in_header, laszip_dll->header.user_data_in_header_size); } catch(...) + { + sprintf(laszip_dll->error, "writing %d bytes of data into header.user_data_in_header", laszip_dll->header.user_data_in_header_size); + return 1; + } + } + + // write variable length records into the header + + if (laszip_dll->header.number_of_variable_length_records) + { + U32 i; + + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + // write variable length records variable after variable (to avoid alignment issues) + + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.vlrs[i].reserved)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.vlrs[%d].reserved", i); + return 1; + } + + try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.vlrs[i].user_id, 16); } catch(...) + { + sprintf(laszip_dll->error, "writing header.vlrs[%d].user_id", i); + return 1; + } + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.vlrs[i].record_id)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.vlrs[%d].record_id", i); + return 1; + } + try { laszip_dll->streamout->put16bitsLE((const U8*)&(laszip_dll->header.vlrs[i].record_length_after_header)); } catch(...) + { + sprintf(laszip_dll->error, "writing header.vlrs[%d].record_length_after_header", i); + return 1; + } + try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.vlrs[i].description, 32); } catch(...) + { + sprintf(laszip_dll->error, "writing header.vlrs[%d].description", i); + return 1; + } + + // write data following the header of the variable length record + + if (laszip_dll->header.vlrs[i].record_length_after_header) + { + try { laszip_dll->streamout->putBytes(laszip_dll->header.vlrs[i].data, laszip_dll->header.vlrs[i].record_length_after_header); } catch(...) + { + sprintf(laszip_dll->error, "writing %d bytes of data into header.vlrs[%d].data", laszip_dll->header.vlrs[i].record_length_after_header, i); + return 1; + } + } + } + } + + if (compress) + { + // write the LASzip VLR header + + if (write_laszip_vlr_header(laszip_dll, laszip, laszip_dll->streamout)) + { + return 1; + } + + // write the LASzip VLR payload + + if (write_laszip_vlr_payload(laszip_dll, laszip, laszip_dll->streamout)) + { + return 1; + } + } + + // write any number of user-defined bytes that might have been added after the header + + if (laszip_dll->header.user_data_after_header_size) + { + try { laszip_dll->streamout->putBytes((const U8*)laszip_dll->header.user_data_after_header, laszip_dll->header.user_data_after_header_size); } catch(...) + { + sprintf(laszip_dll->error, "writing %u bytes of data into header.user_data_after_header", laszip_dll->header.user_data_after_header_size); + return 1; + } + } + + return 0; +} + +/*----------------------------------------------------------------------------*/ +laszip_I32 create_point_writer +( + laszip_dll_struct *laszip_dll + , const LASzip *laszip +) +{ + // create the point writer + laszip_dll->writer = new LASwritePoint(); + if (laszip_dll->writer == 0) + { + sprintf(laszip_dll->error, "could not alloc LASwritePoint"); + return 1; + } + + if (!laszip_dll->writer->setup(laszip->num_items, laszip->items, laszip)) + { + sprintf(laszip_dll->error, "setup of LASwritePoint failed"); + return 1; + } + + if (!laszip_dll->writer->init(laszip_dll->streamout)) + { + sprintf(laszip_dll->error, "init of LASwritePoint failed"); + return 1; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ +static I32 +setup_laszip_items( + laszip_dll_struct* laszip_dll + , LASzip* laszip + , laszip_BOOL compress +) +{ + laszip_U8 point_type = laszip_dll->header.point_data_format; + laszip_U16 point_size = laszip_dll->header.point_data_record_length; + + if ((point_type > 5) && laszip_dll->request_compatibility_mode) + { + if (!laszip->request_compatibility_mode(1)) + { + sprintf(laszip_dll->error, "requesting 'compatibility mode' has failed"); + return 1; + } + } + + // create point items in the LASzip structure from point format and size + + if (!laszip->setup(point_type, point_size, LASZIP_COMPRESSOR_NONE)) + { + sprintf(laszip_dll->error, "invalid combination of point_type %d and point_size %d", (I32)point_type, (I32)point_size); + return 1; + } + + // compute offsets (or points item pointers) for data transfer from the point items + + if (laszip_dll->point_items) + { + delete [] laszip_dll->point_items; + } + + laszip_dll->point_items = new U8*[laszip->num_items]; + + if (laszip_dll->point_items == 0) + { + sprintf(laszip_dll->error, "could not alloc point_items"); + return 1; + } + + for (size_t i = 0; i < laszip->num_items; i++) + { + switch (laszip->items[i].type) + { + case LASitem::POINT10: + case LASitem::POINT14: + laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.X); + break; + case LASitem::GPSTIME11: + laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.gps_time); + break; + case LASitem::RGB12: + case LASitem::RGB14: + case LASitem::RGBNIR14: + laszip_dll->point_items[i] = (U8*)laszip_dll->point.rgb; + break; + case LASitem::BYTE: + case LASitem::BYTE14: + laszip_dll->point.num_extra_bytes = laszip->items[i].size; + if (laszip_dll->point.extra_bytes) delete [] laszip_dll->point.extra_bytes; + laszip_dll->point.extra_bytes = new U8[laszip_dll->point.num_extra_bytes]; + laszip_dll->point_items[i] = laszip_dll->point.extra_bytes; + break; + case LASitem::WAVEPACKET13: + case LASitem::WAVEPACKET14: + laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.wave_packet); + break; + default: + sprintf(laszip_dll->error, "unknown LASitem type %d", (I32)laszip->items[i].type); + return 1; + } + } + + if (compress) + { + if ((point_type > 5) && laszip_dll->request_native_extension) + { + if (!laszip->setup(point_type, point_size, LASZIP_COMPRESSOR_LAYERED_CHUNKED)) + { + sprintf(laszip_dll->error, "cannot compress point_type %d with point_size %d using native", (I32)point_type, (I32)point_size); + return 1; + } + } + else + { + if (!laszip->setup(point_type, point_size, LASZIP_COMPRESSOR_DEFAULT)) + { + sprintf(laszip_dll->error, "cannot compress point_type %d with point_size %d", (I32)point_type, (I32)point_size); + return 1; + } + } + + // request version (old point types only, new point types always use version 3) + + laszip->request_version(2); + + // maybe we should change the chunk size + + if (laszip_dll->set_chunk_size != LASZIP_CHUNK_SIZE_DEFAULT) + { + if (!laszip->set_chunk_size(laszip_dll->set_chunk_size)) + { + sprintf(laszip_dll->error, "setting chunk size %d has failed", laszip_dll->set_chunk_size); + return 1; + } + } + } + else + { + laszip->request_version(0); + } + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_open_writer( + laszip_POINTER pointer + , const laszip_CHAR* file_name + , laszip_BOOL compress +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (file_name == 0) + { + sprintf(laszip_dll->error, "laszip_CHAR pointer 'file_name' is zero"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + // open the file + +#ifdef _MSC_VER + wchar_t* utf16_file_name = UTF8toUTF16(file_name); + laszip_dll->file = _wfopen(utf16_file_name, L"wb"); + delete [] utf16_file_name; +#else + laszip_dll->file = fopen(file_name, "wb"); +#endif + + if (laszip_dll->file == 0) + { + sprintf(laszip_dll->error, "cannot open file '%s'", file_name); + return 1; + } + + if (setvbuf(laszip_dll->file, NULL, _IOFBF, 262144) != 0) + { + sprintf(laszip_dll->warning, "setvbuf() failed with buffer size 262144\n"); + } + + // create the outstream + + if (IS_LITTLE_ENDIAN()) + laszip_dll->streamout = new ByteStreamOutFileLE(laszip_dll->file); + else + laszip_dll->streamout = new ByteStreamOutFileBE(laszip_dll->file); + + if (laszip_dll->streamout == 0) + { + sprintf(laszip_dll->error, "could not alloc ByteStreamOutFile"); + return 1; + } + + // setup the items that make up the point + + LASzip laszip; + if (setup_laszip_items(laszip_dll, &laszip, compress)) + { + return 1; + } + + // prepare header + + if (laszip_prepare_header_for_write(laszip_dll)) + { + return 1; + } + + // prepare point + + if (laszip_prepare_point_for_write(laszip_dll, compress)) + { + return 1; + } + + // prepare VLRs + + if (laszip_prepare_vlrs_for_write(laszip_dll)) + { + return 1; + } + + // write header variable after variable + + if (laszip_write_header(laszip_dll, &laszip, compress)) + { + return 1; + } + + // create the point writer + + if (create_point_writer(laszip_dll, &laszip)) + { + return 1; + } + + if (laszip_dll->lax_create) + { + // create spatial indexing information using cell_size = 100.0f and threshold = 1000 + + LASquadtree* lasquadtree = new LASquadtree; + lasquadtree->setup(laszip_dll->header.min_x, laszip_dll->header.max_x, laszip_dll->header.min_y, laszip_dll->header.max_y, 100.0f); + + laszip_dll->lax_index = new LASindex; + laszip_dll->lax_index->prepare(lasquadtree, 1000); + + // copy the file name for later + + laszip_dll->lax_file_name = LASCopyString(file_name); + } + + // set the point number and point count + + laszip_dll->npoints = (laszip_dll->header.number_of_point_records ? laszip_dll->header.number_of_point_records : laszip_dll->header.extended_number_of_point_records); + laszip_dll->p_count = 0; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_open_writer '%s'", file_name); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_write_point( + laszip_POINTER pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + // temporary fix to avoid corrupt LAZ files + + if (laszip_dll->point.extended_point_type) + { + // make sure legacy flags and extended flags are identical + if ((laszip_dll->point.extended_classification_flags & 0x7) != ((((U8*)&(laszip_dll->point.intensity))[3]) >> 5)) + { + sprintf(laszip_dll->error, "legacy flags and extended flags are not identical"); + return 1; + } + + // make sure legacy classification is zero or identical to extended classification + if (laszip_dll->point.classification != 0) + { + if (laszip_dll->point.classification != laszip_dll->point.extended_classification) + { + sprintf(laszip_dll->error, "legacy classification %d and extended classification %d are not consistent", laszip_dll->point.classification, laszip_dll->point.extended_classification); + return 1; + } + } + } + + // special recoding of points (in compatibility mode only) + + if (laszip_dll->compatibility_mode) + { + I32 scan_angle_remainder; + I32 number_of_returns_increment; + I32 return_number_increment; + I32 return_count_difference; + I32 overlap_bit; + I32 scanner_channel; + + // distill extended attributes + struct laszip_point* point = &laszip_dll->point; + + point->scan_angle_rank = I8_CLAMP(I16_QUANTIZE(0.006f*point->extended_scan_angle)); + scan_angle_remainder = point->extended_scan_angle - I16_QUANTIZE(((F32)point->scan_angle_rank)/0.006f); + if (point->extended_number_of_returns <= 7) + { + point->number_of_returns = point->extended_number_of_returns; + if (point->extended_return_number <= 7) + { + point->return_number = point->extended_return_number; + } + else + { + point->return_number = 7; + } + } + else + { + point->number_of_returns = 7; + if (point->extended_return_number <= 4) + { + point->return_number = point->extended_return_number; + } + else + { + return_count_difference = point->extended_number_of_returns - point->extended_return_number; + if (return_count_difference <= 0) + { + point->return_number = 7; + } + else if (return_count_difference >= 3) + { + point->return_number = 4; + } + else + { + point->return_number = 7 - return_count_difference; + } + } + } + return_number_increment = point->extended_return_number - point->return_number; + number_of_returns_increment = point->extended_number_of_returns - point->number_of_returns; + if (point->extended_classification > 31) + { + point->classification = 0; + } + else + { + point->extended_classification = 0; + } + scanner_channel = point->extended_scanner_channel; + overlap_bit = (point->extended_classification_flags >> 3); + + // write distilled extended attributes into extra bytes + + *((I16*)(point->extra_bytes + laszip_dll->start_scan_angle)) = ((I16)scan_angle_remainder); + point->extra_bytes[laszip_dll->start_extended_returns] = (U8)((return_number_increment << 4) | number_of_returns_increment); + point->extra_bytes[laszip_dll->start_classification] = (U8)(point->extended_classification); + point->extra_bytes[laszip_dll->start_flags_and_channel] = (U8)((scanner_channel << 1) | overlap_bit); + if (laszip_dll->start_NIR_band != -1) + { + *((U16*)(point->extra_bytes + laszip_dll->start_NIR_band)) = point->rgb[3]; + } + } + + // write the point + if (!laszip_dll->writer->write(laszip_dll->point_items)) + { +#ifdef _WIN32 + sprintf(laszip_dll->error, "writing point %I64d of %I64d total points", laszip_dll->p_count, laszip_dll->npoints); +#else + sprintf(laszip_dll->error, "writing point %lld of %lld total points", laszip_dll->p_count, laszip_dll->npoints); +#endif + return 1; + } + + laszip_dll->p_count++; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_write_point"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_write_indexed_point( + laszip_POINTER pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + // write the point + if (!laszip_dll->writer->write(laszip_dll->point_items)) + { +#ifdef _WIN32 + sprintf(laszip_dll->error, "writing point %I64d of %I64d total points", laszip_dll->p_count, laszip_dll->npoints); +#else + sprintf(laszip_dll->error, "writing point %lld of %lld total points", laszip_dll->p_count, laszip_dll->npoints); +#endif + return 1; + } + // index the point + F64 x = laszip_dll->header.x_scale_factor*laszip_dll->point.X+laszip_dll->header.x_offset; + F64 y = laszip_dll->header.y_scale_factor*laszip_dll->point.Y+laszip_dll->header.y_offset; + laszip_dll->lax_index->add(x, y, (U32)laszip_dll->p_count); + laszip_dll->p_count++; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_write_indexed_point"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_update_inventory( + laszip_POINTER pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->inventory == 0) + { + laszip_dll->inventory = new laszip_dll_inventory; + } + + laszip_dll->inventory->add(&laszip_dll->point); + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_update_inventory"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_close_writer( + laszip_POINTER pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->writer == 0) + { + sprintf(laszip_dll->error, "closing writer before it was opened"); + return 1; + } + + if (!laszip_dll->writer->done()) + { + sprintf(laszip_dll->error, "done of LASwritePoint failed"); + return 1; + } + + delete laszip_dll->writer; + laszip_dll->writer = 0; + + delete [] laszip_dll->point_items; + laszip_dll->point_items = 0; + + // maybe update the header + + if (laszip_dll->inventory) + { + if (laszip_dll->header.point_data_format <= 5) // only update legacy counters for old point types + { + laszip_dll->streamout->seek(107); + if (!laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->inventory->number_of_point_records))) + { + sprintf(laszip_dll->error, "updating laszip_dll->inventory->number_of_point_records"); + return 1; + } + for (I32 i = 0; i < 5; i++) + { + if (!laszip_dll->streamout->put32bitsLE((const U8*)&(laszip_dll->inventory->number_of_points_by_return[i+1]))) + { + sprintf(laszip_dll->error, "updating laszip_dll->inventory->number_of_points_by_return[%d]\n", i); + return 1; + } + } + } + laszip_dll->streamout->seek(179); + F64 value; + value = laszip_dll->header.x_scale_factor*laszip_dll->inventory->max_X+laszip_dll->header.x_offset; + if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) + { + sprintf(laszip_dll->error, "updating laszip_dll->inventory->max_X"); + return 1; + } + value = laszip_dll->header.x_scale_factor*laszip_dll->inventory->min_X+laszip_dll->header.x_offset; + if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) + { + sprintf(laszip_dll->error, "updating laszip_dll->inventory->min_X"); + return 1; + } + value = laszip_dll->header.y_scale_factor*laszip_dll->inventory->max_Y+laszip_dll->header.y_offset; + if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) + { + sprintf(laszip_dll->error, "updating laszip_dll->inventory->max_Y"); + return 1; + } + value = laszip_dll->header.y_scale_factor*laszip_dll->inventory->min_Y+laszip_dll->header.y_offset; + if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) + { + sprintf(laszip_dll->error, "updating laszip_dll->inventory->min_Y"); + return 1; + } + value = laszip_dll->header.z_scale_factor*laszip_dll->inventory->max_Z+laszip_dll->header.z_offset; + if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) + { + sprintf(laszip_dll->error, "updating laszip_dll->inventory->max_Z"); + return 1; + } + value = laszip_dll->header.z_scale_factor*laszip_dll->inventory->min_Z+laszip_dll->header.z_offset; + if (!laszip_dll->streamout->put64bitsLE((const U8*)&value)) + { + sprintf(laszip_dll->error, "updating laszip_dll->inventory->min_Z"); + return 1; + } + if (laszip_dll->header.version_minor >= 4) // only update extended counters for LAS 1.4 + { + laszip_dll->streamout->seek(247); + I64 number = laszip_dll->inventory->number_of_point_records; + if (!laszip_dll->streamout->put64bitsLE((const U8*)&number)) + { + sprintf(laszip_dll->error, "updating laszip_dll->inventory->extended_number_of_point_records"); + return 1; + } + for (I32 i = 0; i < 15; i++) + { + number = laszip_dll->inventory->number_of_points_by_return[i+1]; + if (!laszip_dll->streamout->put64bitsLE((const U8*)&number)) + { + sprintf(laszip_dll->error, "updating laszip_dll->inventory->extended_number_of_points_by_return[%d]\n", i); + return 1; + } + } + } + laszip_dll->streamout->seekEnd(); + + delete laszip_dll->inventory; + laszip_dll->inventory = 0; + } + + if (laszip_dll->lax_index) + { + laszip_dll->lax_index->complete(100000, -20, FALSE); + + if (!laszip_dll->lax_index->write(laszip_dll->lax_file_name)) + { + sprintf(laszip_dll->error, "writing LAX file to '%s'", laszip_dll->lax_file_name); + return 1; + } + + free(laszip_dll->lax_file_name); + laszip_dll->lax_file_name = 0; + + delete laszip_dll->lax_index; + laszip_dll->lax_index = 0; + } + + delete laszip_dll->streamout; + laszip_dll->streamout = 0; + + if (laszip_dll->file) + { + fclose(laszip_dll->file); + laszip_dll->file = 0; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_writer_close"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_exploit_spatial_index( + laszip_POINTER pointer + , const laszip_BOOL exploit +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + laszip_dll->lax_exploit = exploit; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_exploit_spatial_index"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_decompress_selective( + laszip_POINTER pointer + , const laszip_U32 decompress_selective +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + laszip_dll->las14_decompress_selective = decompress_selective; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_decompress_selective"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +static I32 +laszip_read_header( + laszip_dll_struct* laszip_dll + , laszip_BOOL* is_compressed +) +{ + U32 i; + + // read the header variable after variable + + CHAR file_signature[5]; + try { laszip_dll->streamin->getBytes((U8*)file_signature, 4); } catch(...) + { + sprintf(laszip_dll->error, "reading header.file_signature"); + return 1; + } + if (strncmp(file_signature, "LASF", 4) != 0) + { + sprintf(laszip_dll->error, "wrong file_signature. not a LAS/LAZ file."); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.file_source_ID)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.file_source_ID"); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.global_encoding)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.global_encoding"); + return 1; + } + try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.project_ID_GUID_data_1)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.project_ID_GUID_data_1"); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.project_ID_GUID_data_2)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.project_ID_GUID_data_2"); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.project_ID_GUID_data_3)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.project_ID_GUID_data_3"); + return 1; + } + try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.project_ID_GUID_data_4, 8); } catch(...) + { + sprintf(laszip_dll->error, "reading header.project_ID_GUID_data_4"); + return 1; + } + try { laszip_dll->streamin->getBytes((U8*)&(laszip_dll->header.version_major), 1); } catch(...) + { + sprintf(laszip_dll->error, "reading header.version_major"); + return 1; + } + try { laszip_dll->streamin->getBytes((U8*)&(laszip_dll->header.version_minor), 1); } catch(...) + { + sprintf(laszip_dll->error, "reading header.version_minor"); + return 1; + } + try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.system_identifier, 32); } catch(...) + { + sprintf(laszip_dll->error, "reading header.system_identifier"); + return 1; + } + try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.generating_software, 32); } catch(...) + { + sprintf(laszip_dll->error, "reading header.generating_software"); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.file_creation_day)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.file_creation_day"); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.file_creation_year)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.file_creation_year"); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.header_size)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.header_size"); + return 1; + } + try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.offset_to_point_data)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.offset_to_point_data"); + return 1; + } + try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.number_of_variable_length_records)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.number_of_variable_length_records"); + return 1; + } + try { laszip_dll->streamin->getBytes((U8*)&(laszip_dll->header.point_data_format), 1); } catch(...) + { + sprintf(laszip_dll->error, "reading header.point_data_format"); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.point_data_record_length)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.point_data_record_length"); + return 1; + } + try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.number_of_point_records)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.number_of_point_records"); + return 1; + } + for (i = 0; i < 5; i++) + { + try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.number_of_points_by_return[i])); } catch(...) + { + sprintf(laszip_dll->error, "reading header.number_of_points_by_return %d", i); + return 1; + } + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.x_scale_factor)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.x_scale_factor"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.y_scale_factor)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.y_scale_factor"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.z_scale_factor)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.z_scale_factor"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.x_offset)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.x_offset"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.y_offset)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.y_offset"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.z_offset)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.z_offset"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.max_x)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.max_x"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.min_x)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.min_x"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.max_y)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.max_y"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.min_y)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.min_y"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.max_z)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.max_z"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.min_z)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.min_z"); + return 1; + } + + // special handling for LAS 1.3 + if ((laszip_dll->header.version_major == 1) && (laszip_dll->header.version_minor >= 3)) + { + if (laszip_dll->header.header_size < 235) + { + sprintf(laszip_dll->error, "for LAS 1.%d header_size should at least be 235 but it is only %d", laszip_dll->header.version_minor, laszip_dll->header.header_size); + return 1; + } + else + { + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.start_of_waveform_data_packet_record)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.start_of_waveform_data_packet_record"); + return 1; + } + laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 235; + } + } + else + { + laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 227; + } + + // special handling for LAS 1.4 + if ((laszip_dll->header.version_major == 1) && (laszip_dll->header.version_minor >= 4)) + { + if (laszip_dll->header.header_size < 375) + { + sprintf(laszip_dll->error, "for LAS 1.%d header_size should at least be 375 but it is only %d", laszip_dll->header.version_minor, laszip_dll->header.header_size); + return 1; + } + else + { + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.start_of_first_extended_variable_length_record)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.start_of_first_extended_variable_length_record"); + return 1; + } + try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip_dll->header.number_of_extended_variable_length_records)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.number_of_extended_variable_length_records"); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.extended_number_of_point_records)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.extended_number_of_point_records"); + return 1; + } + for (i = 0; i < 15; i++) + { + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip_dll->header.extended_number_of_points_by_return[i])); } catch(...) + { + sprintf(laszip_dll->error, "reading header.extended_number_of_points_by_return[%d]", i); + return 1; + } + } + laszip_dll->header.user_data_in_header_size = laszip_dll->header.header_size - 375; + } + } + + // load any number of user-defined bytes that might have been added to the header + if (laszip_dll->header.user_data_in_header_size) + { + if (laszip_dll->header.user_data_in_header) + { + delete [] laszip_dll->header.user_data_in_header; + } + laszip_dll->header.user_data_in_header = new U8[laszip_dll->header.user_data_in_header_size]; + + try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.user_data_in_header, laszip_dll->header.user_data_in_header_size); } catch(...) + { + sprintf(laszip_dll->error, "reading %u bytes of data into header.user_data_in_header", laszip_dll->header.user_data_in_header_size); + return 1; + } + } + + // read variable length records into the header + + U32 vlrs_size = 0; + LASzip* laszip = 0; + + if (laszip_dll->header.number_of_variable_length_records) + { + U32 i; + + laszip_dll->header.vlrs = (laszip_vlr*)malloc(sizeof(laszip_vlr)*laszip_dll->header.number_of_variable_length_records); + + if (laszip_dll->header.vlrs == 0) + { + sprintf(laszip_dll->error, "allocating %u VLRs", laszip_dll->header.number_of_variable_length_records); + return 1; + } + + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + // make sure there are enough bytes left to read a variable length record before the point block starts + + if (((int)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size) < 54) + { + sprintf(laszip_dll->warning, "only %d bytes until point block after reading %d of %d vlrs. skipping remaining vlrs ...", (int)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size, i, laszip_dll->header.number_of_variable_length_records); + laszip_dll->header.number_of_variable_length_records = i; + break; + } + + // read variable length records variable after variable (to avoid alignment issues) + + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.vlrs[i].reserved)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.vlrs[%u].reserved", i); + return 1; + } + + try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.vlrs[i].user_id, 16); } catch(...) + { + sprintf(laszip_dll->error, "reading header.vlrs[%u].user_id", i); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.vlrs[i].record_id)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.vlrs[%u].record_id", i); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip_dll->header.vlrs[i].record_length_after_header)); } catch(...) + { + sprintf(laszip_dll->error, "reading header.vlrs[%u].record_length_after_header", i); + return 1; + } + try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.vlrs[i].description, 32); } catch(...) + { + sprintf(laszip_dll->error, "reading header.vlrs[%u].description", i); + return 1; + } + + // keep track on the number of bytes we have read so far + + vlrs_size += 54; + + // check variable length record contents + + if ((laszip_dll->header.vlrs[i].reserved != 0xAABB) && (laszip_dll->header.vlrs[i].reserved != 0x0)) + { + sprintf(laszip_dll->warning,"wrong header.vlrs[%d].reserved: %d != 0xAABB and %d != 0x0", i, laszip_dll->header.vlrs[i].reserved, laszip_dll->header.vlrs[i].reserved); + } + + // make sure there are enough bytes left to read the data of the variable length record before the point block starts + + if (((int)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size) < laszip_dll->header.vlrs[i].record_length_after_header) + { + sprintf(laszip_dll->warning, "only %d bytes until point block when trying to read %d bytes into header.vlrs[%d].data", (int)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size, laszip_dll->header.vlrs[i].record_length_after_header, i); + laszip_dll->header.vlrs[i].record_length_after_header = (int)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size; + } + + // load data following the header of the variable length record + + if (laszip_dll->header.vlrs[i].record_length_after_header) + { + if ((strcmp(laszip_dll->header.vlrs[i].user_id, "laszip encoded") == 0) && (laszip_dll->header.vlrs[i].record_id == 22204)) + { + if (laszip) + { + delete laszip; + } + + laszip = new LASzip(); + + if (laszip == 0) + { + sprintf(laszip_dll->error, "could not alloc LASzip"); + return 1; + } + + // read the LASzip VLR payload + + // U16 compressor 2 bytes + // U32 coder 2 bytes + // U8 version_major 1 byte + // U8 version_minor 1 byte + // U16 version_revision 2 bytes + // U32 options 4 bytes + // I32 chunk_size 4 bytes + // I64 number_of_special_evlrs 8 bytes + // I64 offset_to_special_evlrs 8 bytes + // U16 num_items 2 bytes + // U16 type 2 bytes * num_items + // U16 size 2 bytes * num_items + // U16 version 2 bytes * num_items + // which totals 34+6*num_items + + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->compressor)); } catch(...) + { + sprintf(laszip_dll->error, "reading compressor %d", (I32)laszip->compressor); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->coder)); } catch(...) + { + sprintf(laszip_dll->error, "reading coder %d", (I32)laszip->coder); + return 1; + } + try { laszip_dll->streamin->getBytes((U8*)&(laszip->version_major), 1); } catch(...) + { + sprintf(laszip_dll->error, "reading version_major %d", (I32)laszip->version_major); + return 1; + } + try { laszip_dll->streamin->getBytes((U8*)&(laszip->version_minor), 1); } catch(...) + { + sprintf(laszip_dll->error, "reading version_minor %d", (I32)laszip->version_minor); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->version_revision)); } catch(...) + { + sprintf(laszip_dll->error, "reading version_revision %d", (I32)laszip->version_revision); + return 1; + } + try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip->options)); } catch(...) + { + sprintf(laszip_dll->error, "reading options %u", laszip->options); + return 1; + } + try { laszip_dll->streamin->get32bitsLE((U8*)&(laszip->chunk_size)); } catch(...) + { + sprintf(laszip_dll->error, "reading chunk_size %u", laszip->chunk_size); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip->number_of_special_evlrs)); } catch(...) + { + sprintf(laszip_dll->error, "reading number_of_special_evlrs %d", (I32)laszip->number_of_special_evlrs); + return 1; + } + try { laszip_dll->streamin->get64bitsLE((U8*)&(laszip->offset_to_special_evlrs)); } catch(...) + { + sprintf(laszip_dll->error, "reading offset_to_special_evlrs %d", (I32)laszip->offset_to_special_evlrs); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->num_items)); } catch(...) + { + sprintf(laszip_dll->error, "reading num_items %d", (I32)laszip->num_items); + return 1; + } + laszip->items = new LASitem[laszip->num_items]; + U32 j; + for (j = 0; j < laszip->num_items; j++) + { + U16 type; + try { laszip_dll->streamin->get16bitsLE((U8*)&type); } catch(...) + { + sprintf(laszip_dll->error, "reading type of item %u", j); + return 1; + } + laszip->items[j].type = (LASitem::Type)type; + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->items[j].size)); } catch(...) + { + sprintf(laszip_dll->error, "reading size of item %u", j); + return 1; + } + try { laszip_dll->streamin->get16bitsLE((U8*)&(laszip->items[j].version)); } catch(...) + { + sprintf(laszip_dll->error, "reading version of item %u", j); + return 1; + } + } + } + else + { + laszip_dll->header.vlrs[i].data = new U8[laszip_dll->header.vlrs[i].record_length_after_header]; + + try { laszip_dll->streamin->getBytes(laszip_dll->header.vlrs[i].data, laszip_dll->header.vlrs[i].record_length_after_header); } catch(...) + { + sprintf(laszip_dll->error, "reading %d bytes of data into header.vlrs[%u].data", (I32)laszip_dll->header.vlrs[i].record_length_after_header, i); + return 1; + } + } + } + else + { + laszip_dll->header.vlrs[i].data = 0; + } + + // keep track on the number of bytes we have read so far + + vlrs_size += laszip_dll->header.vlrs[i].record_length_after_header; + + // special handling for LASzip VLR + + if ((strcmp(laszip_dll->header.vlrs[i].user_id, "laszip encoded") == 0) && (laszip_dll->header.vlrs[i].record_id == 22204)) + { + // we take our the VLR for LASzip away + laszip_dll->header.offset_to_point_data -= (54+laszip_dll->header.vlrs[i].record_length_after_header); + vlrs_size -= (54+laszip_dll->header.vlrs[i].record_length_after_header); + i--; + laszip_dll->header.number_of_variable_length_records--; + // free or resize the VLR array + if (laszip_dll->header.number_of_variable_length_records == 0) + { + free(laszip_dll->header.vlrs); + laszip_dll->header.vlrs = 0; + } + else + { + laszip_dll->header.vlrs = (laszip_vlr*)realloc(laszip_dll->header.vlrs, sizeof(laszip_vlr)*laszip_dll->header.number_of_variable_length_records); + } + } + } + } + + // load any number of user-defined bytes that might have been added after the header + + laszip_dll->header.user_data_after_header_size = (I32)laszip_dll->header.offset_to_point_data - vlrs_size - laszip_dll->header.header_size; + if (laszip_dll->header.user_data_after_header_size) + { + if (laszip_dll->header.user_data_after_header) + { + delete [] laszip_dll->header.user_data_after_header; + } + laszip_dll->header.user_data_after_header = new U8[laszip_dll->header.user_data_after_header_size]; + + try { laszip_dll->streamin->getBytes((U8*)laszip_dll->header.user_data_after_header, laszip_dll->header.user_data_after_header_size); } catch(...) + { + sprintf(laszip_dll->error, "reading %u bytes of data into header.user_data_after_header", laszip_dll->header.user_data_after_header_size); + return 1; + } + } + + // remove extra bits in point data type + + if ((laszip_dll->header.point_data_format & 128) || (laszip_dll->header.point_data_format & 64)) + { + if (!laszip) + { + sprintf(laszip_dll->error, "this file was compressed with an experimental version of LASzip. contact 'martin.isenburg@rapidlasso.com' for assistance"); + return 1; + } + laszip_dll->header.point_data_format &= 127; + } + + // check if file is compressed + + if (laszip) + { + // yes. check the compressor state + *is_compressed = 1; + if (!laszip->check(laszip_dll->header.point_data_record_length)) + { + sprintf(laszip_dll->error, "%s upgrade to the latest release of LASzip or contact 'martin.isenburg@rapidlasso.com' for assistance", laszip->get_error()); + return 1; + } + } + else + { + // no. setup an un-compressed read + *is_compressed = 0; + laszip = new LASzip; + if (laszip == 0) + { + sprintf(laszip_dll->error, "could not alloc LASzip"); + return 1; + } + if (!laszip->setup(laszip_dll->header.point_data_format, laszip_dll->header.point_data_record_length, LASZIP_COMPRESSOR_NONE)) + { + sprintf(laszip_dll->error, "invalid combination of point_data_format %d and point_data_record_length %d", (I32)laszip_dll->header.point_data_format, (I32)laszip_dll->header.point_data_record_length); + return 1; + } + } + + // create point's item pointers + + if (laszip_dll->point_items) + { + delete [] laszip_dll->point_items; + } + + laszip_dll->point_items = new U8*[laszip->num_items]; + + if (laszip_dll->point_items == 0) + { + sprintf(laszip_dll->error, "could not alloc point_items"); + return 1; + } + + for (i = 0; i < laszip->num_items; i++) + { + switch (laszip->items[i].type) + { + case LASitem::POINT10: + case LASitem::POINT14: + laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.X); + break; + case LASitem::GPSTIME11: + laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.gps_time); + break; + case LASitem::RGB12: + case LASitem::RGB14: + case LASitem::RGBNIR14: + laszip_dll->point_items[i] = (U8*)laszip_dll->point.rgb; + break; + case LASitem::BYTE: + case LASitem::BYTE14: + laszip_dll->point.num_extra_bytes = laszip->items[i].size; + if (laszip_dll->point.extra_bytes) delete [] laszip_dll->point.extra_bytes; + laszip_dll->point.extra_bytes = new U8[laszip_dll->point.num_extra_bytes]; + laszip_dll->point_items[i] = laszip_dll->point.extra_bytes; + break; + case LASitem::WAVEPACKET13: + case LASitem::WAVEPACKET14: + laszip_dll->point_items[i] = (U8*)&(laszip_dll->point.wave_packet); + break; + default: + sprintf(laszip_dll->error, "unknown LASitem type %d", (I32)laszip->items[i].type); + return 1; + } + } + + // did the user request to recode the compatibility mode points? + + laszip_dll->compatibility_mode = FALSE; + + if (laszip_dll->request_compatibility_mode && (laszip_dll->header.version_minor < 4)) + { + // does this file contain compatibility mode recoded LAS 1.4 content + + struct laszip_vlr* compatibility_VLR = 0; + + if (laszip_dll->header.point_data_format == 1 || laszip_dll->header.point_data_format == 3 || laszip_dll->header.point_data_format == 4 || laszip_dll->header.point_data_format == 5) + { + // if we find the compatibility VLR + + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + if ((strncmp(laszip_dll->header.vlrs[i].user_id, "lascompatible\0\0", 16) == 0) && (laszip_dll->header.vlrs[i].record_id == 22204)) + { + if (laszip_dll->header.vlrs[i].record_length_after_header == 2+2+4+148) + { + compatibility_VLR = &(laszip_dll->header.vlrs[i]); + break; + } + } + } + + if (compatibility_VLR) + { + // and we also find the extra bytes VLR with the right attributes + + LASattributer attributer; + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + if ((strncmp(laszip_dll->header.vlrs[i].user_id, "LASF_Spec\0\0\0\0\0\0", 16) == 0) && (laszip_dll->header.vlrs[i].record_id == 4)) + { + attributer.init_attributes(laszip_dll->header.vlrs[i].record_length_after_header/192, (LASattribute*)laszip_dll->header.vlrs[i].data); + laszip_dll->start_scan_angle = attributer.get_attribute_start("LAS 1.4 scan angle"); + laszip_dll->start_extended_returns = attributer.get_attribute_start("LAS 1.4 extended returns"); + laszip_dll->start_classification = attributer.get_attribute_start("LAS 1.4 classification"); + laszip_dll->start_flags_and_channel = attributer.get_attribute_start("LAS 1.4 flags and channel"); + laszip_dll->start_NIR_band = attributer.get_attribute_start("LAS 1.4 NIR band"); + break; + } + } + + // can we do it ... ? + + if ((laszip_dll->start_scan_angle != -1) && (laszip_dll->start_extended_returns != -1) && (laszip_dll->start_classification != -1) && (laszip_dll->start_flags_and_channel != -1)) + { + // yes ... so let's fix the header (using the content from the compatibility VLR) + + ByteStreamInArray* in; + if (IS_LITTLE_ENDIAN()) + in = new ByteStreamInArrayLE(compatibility_VLR->data, compatibility_VLR->record_length_after_header); + else + in = new ByteStreamInArrayBE(compatibility_VLR->data, compatibility_VLR->record_length_after_header); + // read control info + U16 laszip_version; + in->get16bitsLE((U8*)&laszip_version); + U16 compatible_version; + in->get16bitsLE((U8*)&compatible_version); + U32 unused; + in->get32bitsLE((U8*)&unused); + // read the 148 bytes of the extended LAS 1.4 header + U64 start_of_waveform_data_packet_record; + in->get64bitsLE((U8*)&start_of_waveform_data_packet_record); + if (start_of_waveform_data_packet_record != 0) + { +#ifdef _WIN32 + fprintf(stderr,"WARNING: start_of_waveform_data_packet_record is %I64d. reading 0 instead.\n", start_of_waveform_data_packet_record); +#else + fprintf(stderr,"WARNING: start_of_waveform_data_packet_record is %llu. reading 0 instead.\n", start_of_waveform_data_packet_record); +#endif + } + laszip_dll->header.start_of_waveform_data_packet_record = 0; + U64 start_of_first_extended_variable_length_record; + in->get64bitsLE((U8*)&start_of_first_extended_variable_length_record); + if (start_of_first_extended_variable_length_record != 0) + { +#ifdef _WIN32 + fprintf(stderr,"WARNING: EVLRs not supported. start_of_first_extended_variable_length_record is %I64d. reading 0 instead.\n", start_of_first_extended_variable_length_record); +#else + fprintf(stderr,"WARNING: EVLRs not supported. start_of_first_extended_variable_length_record is %llu. reading 0 instead.\n", start_of_first_extended_variable_length_record); +#endif + } + laszip_dll->header.start_of_first_extended_variable_length_record = 0; + U32 number_of_extended_variable_length_records ; + in->get32bitsLE((U8*)&number_of_extended_variable_length_records); + if (number_of_extended_variable_length_records != 0) + { + fprintf(stderr,"WARNING: EVLRs not supported. number_of_extended_variable_length_records is %u. reading 0 instead.\n", number_of_extended_variable_length_records); + } + laszip_dll->header.number_of_extended_variable_length_records = 0; + U64 extended_number_of_point_records = 0; + in->get64bitsLE((U8*)&extended_number_of_point_records); + if (laszip_dll->header.number_of_point_records != 0 && ((U64)(laszip_dll->header.number_of_point_records)) != extended_number_of_point_records) + { +#ifdef _WIN32 + fprintf(stderr,"WARNING: number_of_point_records is %u. but extended_number_of_point_records is %I64u.\n", laszip_dll->header.number_of_point_records, extended_number_of_point_records); +#else + fprintf(stderr,"WARNING: number_of_point_records is %u. but extended_number_of_point_records is %llu.\n", laszip_dll->header.number_of_point_records, extended_number_of_point_records); +#endif + } + laszip_dll->header.extended_number_of_point_records = extended_number_of_point_records; + U64 extended_number_of_points_by_return; + for (U32 r = 0; r < 15; r++) + { + in->get64bitsLE((U8*)&extended_number_of_points_by_return); + if ((r < 5) && laszip_dll->header.number_of_points_by_return[r] != 0 && ((U64)(laszip_dll->header.number_of_points_by_return[r])) != extended_number_of_points_by_return) + { +#ifdef _WIN32 + fprintf(stderr,"WARNING: number_of_points_by_return[%u] is %u. but extended_number_of_points_by_return[%u] is %I64u.\n", r, laszip_dll->header.number_of_points_by_return[r], r, extended_number_of_points_by_return); +#else + fprintf(stderr,"WARNING: number_of_points_by_return[%u] is %u. but extended_number_of_points_by_return[%u] is %llu.\n", r, laszip_dll->header.number_of_points_by_return[r], r, extended_number_of_points_by_return); +#endif + } + laszip_dll->header.extended_number_of_points_by_return[r] = extended_number_of_points_by_return; + } + delete in; + + // remove the compatibility VLR + + if (laszip_remove_vlr(laszip_dll, "lascompatible\0\0", 22204)) + { + sprintf(laszip_dll->error, "removing the compatibility VLR"); + return 1; + } + + // remove the LAS 1.4 attributes from the "extra bytes" description + + if (laszip_dll->start_NIR_band != -1) attributer.remove_attribute("LAS 1.4 NIR band"); + attributer.remove_attribute("LAS 1.4 flags and channel"); + attributer.remove_attribute("LAS 1.4 classification"); + attributer.remove_attribute("LAS 1.4 extended returns"); + attributer.remove_attribute("LAS 1.4 scan angle"); + + // either rewrite or remove the "extra bytes" VLR + + if (attributer.number_attributes) + { + if (laszip_add_vlr(laszip_dll, "LASF_Spec\0\0\0\0\0\0", 4, (laszip_U16)(attributer.number_attributes*sizeof(LASattribute)), 0, (laszip_U8*)attributer.attributes)) + { + sprintf(laszip_dll->error, "rewriting the extra bytes VLR without 'LAS 1.4 compatibility mode' attributes"); + return 1; + } + } + else + { + if (laszip_remove_vlr(laszip_dll, "LASF_Spec\0\0\0\0\0\0", 4)) + { + sprintf(laszip_dll->error, "removing the LAS 1.4 attribute VLR"); + return 1; + } + } + + // upgrade to LAS 1.4 + if (laszip_dll->header.version_minor < 3) + { + // LAS 1.2 header is 148 bytes less than LAS 1.4+ header + laszip_dll->header.header_size += 148; + laszip_dll->header.offset_to_point_data += 148; + } + else + { + // LAS 1.3 header is 140 bytes less than LAS 1.4+ header + laszip_dll->header.header_size += 140; + laszip_dll->header.offset_to_point_data += 140; + } + laszip_dll->header.version_minor = 4; + + // maybe turn on the bit indicating the presence of the OGC WKT + for (i = 0; i < laszip_dll->header.number_of_variable_length_records; i++) + { + if ((strncmp(laszip_dll->header.vlrs[i].user_id, "LASF_Projection", 16) == 0) && (laszip_dll->header.vlrs[i].record_id == 2112)) + { + laszip_dll->header.global_encoding |= (1<<4); + break; + } + } + + // update point type and size + + laszip_dll->point.extended_point_type = 1; + + if (laszip_dll->header.point_data_format == 1) + { + laszip_dll->header.point_data_format = 6; + laszip_dll->header.point_data_record_length += (2 - 5); // record is 2 bytes larger but minus 5 extra bytes + } + else if (laszip_dll->header.point_data_format == 3) + { + if (laszip_dll->start_NIR_band == -1) + { + laszip_dll->header.point_data_format = 7; + laszip_dll->header.point_data_record_length += (2 - 5); // record is 2 bytes larger but minus 5 extra bytes + } + else + { + laszip_dll->header.point_data_format = 8; + laszip_dll->header.point_data_record_length += (4 - 7); // record is 4 bytes larger but minus 7 extra bytes + } + } + else + { + if (laszip_dll->start_NIR_band == -1) + { + laszip_dll->header.point_data_format = 9; + laszip_dll->header.point_data_record_length += (2 - 5); + } + else + { + laszip_dll->header.point_data_format = 10; + laszip_dll->header.point_data_record_length += (4 - 7); + } + } + + // we are operating in compatibility mode + + laszip_dll->compatibility_mode = TRUE; + } + } + } + } + else if (laszip_dll->header.point_data_format > 5) + { + laszip_dll->point.extended_point_type = 1; + } + + // create the point reader + + laszip_dll->reader = new LASreadPoint(laszip_dll->las14_decompress_selective); + if (laszip_dll->reader == 0) + { + sprintf(laszip_dll->error, "could not alloc LASreadPoint"); + return 1; + } + + if (!laszip_dll->reader->setup(laszip->num_items, laszip->items, laszip)) + { + sprintf(laszip_dll->error, "setup of LASreadPoint failed"); + return 1; + } + + if (!laszip_dll->reader->init(laszip_dll->streamin)) + { + sprintf(laszip_dll->error, "init of LASreadPoint failed"); + return 1; + } + + delete laszip; + + // set the point number and point count + + laszip_dll->npoints = (laszip_dll->header.number_of_point_records ? laszip_dll->header.number_of_point_records : laszip_dll->header.extended_number_of_point_records); + laszip_dll->p_count = 0; + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_open_reader( + laszip_POINTER pointer + , const laszip_CHAR* file_name + , laszip_BOOL* is_compressed +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (file_name == 0) + { + sprintf(laszip_dll->error, "laszip_CHAR pointer 'file_name' is zero"); + return 1; + } + + if (is_compressed == 0) + { + sprintf(laszip_dll->error, "laszip_BOOL pointer 'is_compressed' is zero"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + // open the file + +#ifdef _MSC_VER + wchar_t* utf16_file_name = UTF8toUTF16(file_name); + laszip_dll->file = _wfopen(utf16_file_name, L"rb"); + delete [] utf16_file_name; +#else + laszip_dll->file = fopen(file_name, "rb"); +#endif + + if (laszip_dll->file == 0) + { + sprintf(laszip_dll->error, "cannot open file '%s'", file_name); + return 1; + } + + if (setvbuf(laszip_dll->file, NULL, _IOFBF, 262144) != 0) + { + sprintf(laszip_dll->warning, "setvbuf() failed with buffer size 262144\n"); + } + + if (IS_LITTLE_ENDIAN()) + laszip_dll->streamin = new ByteStreamInFileLE(laszip_dll->file); + else + laszip_dll->streamin = new ByteStreamInFileBE(laszip_dll->file); + + if (laszip_dll->streamin == 0) + { + sprintf(laszip_dll->error, "could not alloc ByteStreamInFile"); + return 1; + } + + // read the header variable after variable + + if (laszip_read_header(laszip_dll, is_compressed)) + { + return 1; + } + + // should we try to exploit existing spatial indexing information + + if (laszip_dll->lax_exploit) + { + laszip_dll->lax_index = new LASindex(); + + if (!laszip_dll->lax_index->read(file_name)) + { + delete laszip_dll->lax_index; + laszip_dll->lax_index = 0; + } + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_open_reader"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_has_spatial_index( + laszip_POINTER pointer + , laszip_BOOL* is_indexed + , laszip_BOOL* is_appended +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (is_indexed == 0) + { + sprintf(laszip_dll->error, "laszip_BOOL pointer 'is_indexed' is zero"); + return 1; + } + + if (laszip_dll->reader == 0) + { + sprintf(laszip_dll->error, "reader is not open"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + if (laszip_dll->lax_exploit == 0) + { + sprintf(laszip_dll->error, "exploiting of spatial indexing not enabled before opening reader"); + return 1; + } + + // check if reader found spatial indexing information when opening file + + if (laszip_dll->lax_index) + { + *is_indexed = 1; + } + else + { + *is_indexed = 0; + } + + // optional: inform whether spatial index is appended to LAZ file or in separate LAX file + + if (is_appended) + { + *is_appended = 0; + } + + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_have_spatial_index"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_inside_rectangle( + laszip_POINTER pointer + , const laszip_F64 r_min_x + , const laszip_F64 r_min_y + , const laszip_F64 r_max_x + , const laszip_F64 r_max_y + , laszip_BOOL* is_empty +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->reader == 0) + { + sprintf(laszip_dll->error, "reader is not open"); + return 1; + } + + if (is_empty == 0) + { + sprintf(laszip_dll->error, "laszip_BOOL pointer 'is_empty' is zero"); + return 1; + } + + if (laszip_dll->lax_exploit == FALSE) + { + sprintf(laszip_dll->error, "exploiting of spatial indexing not enabled before opening reader"); + return 1; + } + + laszip_dll->lax_r_min_x = r_min_x; + laszip_dll->lax_r_min_y = r_min_y; + laszip_dll->lax_r_max_x = r_max_x; + laszip_dll->lax_r_max_y = r_max_y; + + if (laszip_dll->lax_index) + { + if (laszip_dll->lax_index->intersect_rectangle(r_min_x, r_min_y, r_max_x, r_max_y)) + { + *is_empty = 0; + } + else + { + // no overlap between spatial indexing cells and query reactangle + *is_empty = 1; + } + } + else + { + if ((laszip_dll->header.min_x > r_max_x) || (laszip_dll->header.min_y > r_max_y) || (laszip_dll->header.max_x < r_min_x) || (laszip_dll->header.max_y < r_min_y)) + { + // no overlap between header bouding box and query reactangle + *is_empty = 1; + } + else + { + *is_empty = 0; + } + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_inside_rectangle"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_seek_point( + laszip_POINTER pointer + , laszip_I64 index +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + // seek to the point + if (!laszip_dll->reader->seek((U32)laszip_dll->p_count, (U32)index)) + { +#ifdef _WIN32 + sprintf(laszip_dll->error, "seeking from index %I64d to index %I64d for file with %I64d points", laszip_dll->p_count, index, laszip_dll->npoints); +#else + sprintf(laszip_dll->error, "seeking from index %lld to index %lld for file with %lld points", laszip_dll->p_count, index, laszip_dll->npoints); +#endif + return 1; + } + laszip_dll->p_count = index; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_seek_point"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_read_point( + laszip_POINTER pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + // read the point + if (!laszip_dll->reader->read(laszip_dll->point_items)) + { +#ifdef _WIN32 + sprintf(laszip_dll->error, "reading point %I64d of %I64d total points", laszip_dll->p_count, laszip_dll->npoints); +#else + sprintf(laszip_dll->error, "reading point %lld of %lld total points", laszip_dll->p_count, laszip_dll->npoints); +#endif + return 1; + } + + // special recoding of points (in compatibility mode only) + + if (laszip_dll->compatibility_mode) + { + I16 scan_angle_remainder; + U8 extended_returns; + U8 classification; + U8 flags_and_channel; + I32 return_number_increment; + I32 number_of_returns_increment; + I32 overlap_bit; + I32 scanner_channel; + + // instill extended attributes + struct laszip_point* point = &laszip_dll->point; + + // get extended attributes from extra bytes + scan_angle_remainder = *((I16*)(point->extra_bytes + laszip_dll->start_scan_angle)); + extended_returns = point->extra_bytes[laszip_dll->start_extended_returns]; + classification = point->extra_bytes[laszip_dll->start_classification]; + flags_and_channel = point->extra_bytes[laszip_dll->start_flags_and_channel]; + if (laszip_dll->start_NIR_band != -1) + { + point->rgb[3] = *((U16*)(point->extra_bytes + laszip_dll->start_NIR_band)); + } + + // decompose into individual attributes + return_number_increment = (extended_returns >> 4) & 0x0F; + number_of_returns_increment = extended_returns & 0x0F; + scanner_channel = (flags_and_channel >> 1) & 0x03; + overlap_bit = flags_and_channel & 0x01; + + // instill into point + point->extended_scan_angle = scan_angle_remainder + I16_QUANTIZE(((F32)point->scan_angle_rank) / 0.006f); + point->extended_return_number = return_number_increment + point->return_number; + point->extended_number_of_returns = number_of_returns_increment + point->number_of_returns; + point->extended_classification = classification + point->classification; + point->extended_scanner_channel = scanner_channel; + point->extended_classification_flags = (overlap_bit << 3) | ((point->withheld_flag) << 2) | ((point->keypoint_flag) << 1) | (point->synthetic_flag); + } + + laszip_dll->p_count++; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_read_point"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_read_inside_point( + laszip_POINTER pointer + , laszip_BOOL* is_done +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + laszip_F64 xy; + + *is_done = 1; + + if (laszip_dll->lax_index) + { + while (laszip_dll->lax_index->seek_next(laszip_dll->reader, laszip_dll->p_count)) + { + if (laszip_dll->reader->read(laszip_dll->point_items)) + { + laszip_dll->p_count++; + xy = laszip_dll->header.x_scale_factor*laszip_dll->point.X+laszip_dll->header.x_offset; + if (xy < laszip_dll->lax_r_min_x || xy >= laszip_dll->lax_r_max_x) continue; + xy = laszip_dll->header.y_scale_factor*laszip_dll->point.Y+laszip_dll->header.y_offset; + if (xy < laszip_dll->lax_r_min_y || xy >= laszip_dll->lax_r_max_y) continue; + *is_done = 0; + break; + } + } + } + else + { + while (laszip_dll->reader->read(laszip_dll->point_items)) + { + laszip_dll->p_count++; + xy = laszip_dll->header.x_scale_factor*laszip_dll->point.X+laszip_dll->header.x_offset; + if (xy < laszip_dll->lax_r_min_x || xy >= laszip_dll->lax_r_max_x) continue; + xy = laszip_dll->header.y_scale_factor*laszip_dll->point.Y+laszip_dll->header.y_offset; + if (xy < laszip_dll->lax_r_min_y || xy >= laszip_dll->lax_r_max_y) continue; + *is_done = 0; + break; + } + + if (*is_done) + { + if (laszip_dll->p_count < laszip_dll->npoints) + { +#ifdef _WIN32 + sprintf(laszip_dll->error, "reading point %I64d of %I64d total points", laszip_dll->p_count, laszip_dll->npoints); +#else + sprintf(laszip_dll->error, "reading point %lld of %lld total points", laszip_dll->p_count, laszip_dll->npoints); +#endif + return 1; + } + } + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_read_inside_point"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_close_reader( + laszip_POINTER pointer +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + try + { + if (laszip_dll->reader == 0) + { + sprintf(laszip_dll->error, "closing reader before it was opened"); + return 1; + } + + if (!laszip_dll->reader->done()) + { + sprintf(laszip_dll->error, "done of LASreadPoint failed"); + return 1; + } + + delete laszip_dll->reader; + laszip_dll->reader = 0; + + delete [] laszip_dll->point_items; + laszip_dll->point_items = 0; + + delete laszip_dll->streamin; + laszip_dll->streamin = 0; + + if (laszip_dll->lax_index) + { + delete laszip_dll->lax_index; + laszip_dll->lax_index = 0; + } + + if (laszip_dll->file) + { + fclose(laszip_dll->file); + laszip_dll->file = 0; + } + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_close_reader"); + return 1; + } + + laszip_dll->error[0] = '\0'; + return 0; +} + +#ifdef __cplusplus + +/*---------------------------------------------------------------------------*/ +LASZIP_API laszip_I32 +laszip_open_reader_stream( + laszip_POINTER pointer + , istream& stream + , laszip_BOOL* is_compressed +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (is_compressed == 0) + { + sprintf(laszip_dll->error, "laszip_BOOL pointer 'is_compressed' is zero"); + return 1; + } + + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + // open the file + + if (IS_LITTLE_ENDIAN()) + laszip_dll->streamin = new ByteStreamInIstreamLE(stream); + else + laszip_dll->streamin = new ByteStreamInIstreamBE(stream); + + if (laszip_dll->streamin == 0) + { + sprintf(laszip_dll->error, "could not alloc ByteStreamInIstream"); + return 1; + } + + return laszip_read_header(laszip_dll, is_compressed); + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_open_reader"); + return 1; + } +} + +/*---------------------------------------------------------------------------*/ +// The stream writer also supports software that writes the LAS header on its +// own simply by setting the BOOL 'do_not_write_header' to TRUE. This function +// should then be called just prior to writing points as data is then written +// to the current stream position +LASZIP_API laszip_I32 +laszip_open_writer_stream( + laszip_POINTER pointer + , ostream& stream + , laszip_BOOL compress + , laszip_BOOL do_not_write_header +) +{ + if (pointer == 0) return 1; + laszip_dll_struct* laszip_dll = (laszip_dll_struct*)pointer; + + try + { + if (laszip_dll->writer) + { + sprintf(laszip_dll->error, "writer is already open"); + return 1; + } + + if (laszip_dll->reader) + { + sprintf(laszip_dll->error, "reader is already open"); + return 1; + } + + // create the outstream + + if (IS_LITTLE_ENDIAN()) + laszip_dll->streamout = new ByteStreamOutOstreamLE(stream); + else + laszip_dll->streamout = new ByteStreamOutOstreamBE(stream); + + if (laszip_dll->streamout == 0) + { + sprintf(laszip_dll->error, "could not alloc ByteStreamOutOstream"); + return 1; + } + + // setup the items that make up the point + + LASzip laszip; + if (setup_laszip_items(laszip_dll, &laszip, compress)) + { + return 1; + } + + // this supports software that writes the LAS header on its own + + if (do_not_write_header == FALSE) + { + // prepare header + + if (laszip_prepare_header_for_write(laszip_dll)) + { + return 1; + } + + // prepare point + + if (laszip_prepare_point_for_write(laszip_dll, compress)) + { + return 1; + } + + // prepare VLRs + + if (laszip_prepare_vlrs_for_write(laszip_dll)) + { + return 1; + } + + // write header variable after variable + + if (laszip_write_header(laszip_dll, &laszip, compress)) + { + return 1; + } + } + + // create the point writer + + if (create_point_writer(laszip_dll, &laszip)) + { + return 1; + } + + // set the point number and point count + + laszip_dll->npoints = (laszip_dll->header.number_of_point_records ? laszip_dll->header.number_of_point_records : laszip_dll->header.extended_number_of_point_records); + laszip_dll->p_count = 0; + } + catch (...) + { + sprintf(laszip_dll->error, "internal error in laszip_open_writer_stream."); + return 1; + } + laszip_dll->error[0] = '\0'; + return 0; +} + +/*---------------------------------------------------------------------------*/ +// creates complete LASzip VLR for currently selected point type and compression +// The VLR data is valid until the laszip_dll pointer is destroyed. +LASZIP_API laszip_I32 +laszip_create_laszip_vlr( + laszip_POINTER pointer + , laszip_U8** vlr + , laszip_U32* vlr_size +) +{ + if (pointer == 0) return 1; + laszip_dll_struct *laszip_dll = (laszip_dll_struct *)pointer; + + LASzip laszip; + if (setup_laszip_items(laszip_dll, &laszip, TRUE)) + { + return 1; + } + + ByteStreamOutArray* out = 0; + + if (IS_LITTLE_ENDIAN()) + out = new ByteStreamOutArrayLE(); + else + out = new ByteStreamOutArrayBE(); + + if (out == 0) + { + sprintf(laszip_dll->error, "could not alloc ByteStreamOutArray"); + return 1; + } + + if (write_laszip_vlr_header(laszip_dll, &laszip, out)) + { + return 1; + } + + if (write_laszip_vlr_payload(laszip_dll, &laszip, out)) + { + return 1; + } + + *vlr = (laszip_U8*)malloc(out->getSize()); + *vlr_size = (U32)out->getSize(); + laszip_dll->buffers.push_back(*vlr); + memcpy(*vlr, out->getData(), out->getSize()); + + delete out; + + laszip_dll->error[0] = '\0'; + return 0; +} + +#endif // __cplusplus diff --git a/libs/laszip/src/laszipper.cpp b/libs/laszip/src/laszipper.cpp new file mode 100644 index 0000000..c04cc45 --- /dev/null +++ b/libs/laszip/src/laszipper.cpp @@ -0,0 +1,133 @@ +/* +=============================================================================== + + FILE: laszipper.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2013, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ +#include "laszipper.hpp" + +#include +#include + +#include "bytestreamout_file.hpp" +#include "bytestreamout_ostream.hpp" +#include "laswritepoint.hpp" + +bool LASzipper::open(FILE* outfile, const LASzip* laszip) +{ + if (!outfile) return return_error("FILE* outfile pointer is NULL"); + if (!laszip) return return_error("const LASzip* laszip pointer is NULL"); + count = 0; + if (writer) delete writer; + writer = new LASwritePoint(); + if (!writer) return return_error("alloc of LASwritePoint failed"); + if (!writer->setup(laszip->num_items, laszip->items, laszip)) return return_error("setup() of LASwritePoint failed"); + if (stream) delete stream; + if (IS_LITTLE_ENDIAN()) + stream = new ByteStreamOutFileLE(outfile); + else + stream = new ByteStreamOutFileBE(outfile); + if (!stream) return return_error("alloc of ByteStreamOutFile failed"); + if (!writer->init(stream)) return return_error("init() of LASwritePoint failed"); + return true; +} + +bool LASzipper::open(ostream& outstream, const LASzip* laszip) +{ + if (!laszip) return return_error("const LASzip* laszip pointer is NULL"); + count = 0; + if (writer) delete writer; + writer = new LASwritePoint(); + if (!writer) return return_error("alloc of LASwritePoint failed"); + if (!writer->setup(laszip->num_items, laszip->items, laszip)) return return_error("setup() of LASwritePoint failed"); + if (stream) delete stream; + if (IS_LITTLE_ENDIAN()) + stream = new ByteStreamOutOstreamLE(outstream); + else + stream = new ByteStreamOutOstreamBE(outstream); + if (!stream) return return_error("alloc of ByteStreamOutStream failed"); + if (!writer->init(stream)) return return_error("init() of LASwritePoint failed"); + return true; +} + +bool LASzipper::write(const unsigned char * const * point) +{ + count++; + return (writer->write(point) == TRUE); +} + +bool LASzipper::chunk() +{ + if (!writer->chunk()) return return_error("chunk() of LASwritePoint failed"); + return true; +} + +bool LASzipper::close() +{ + BOOL done = TRUE; + if (writer) + { + done = writer->done(); + delete writer; + writer = 0; + } + if (stream) + { + delete stream; + stream = 0; + } + if (!done) return return_error("done() of LASwritePoint failed"); + return true; +} + +const char* LASzipper::get_error() const +{ + return error_string; +} + +bool LASzipper::return_error(const char* error) +{ + char err[256]; + sprintf(err, "%s (LASzip v%d.%dr%d)", error, LASZIP_VERSION_MAJOR, LASZIP_VERSION_MINOR, LASZIP_VERSION_REVISION); + if (error_string) free(error_string); + error_string = LASCopyString(err); + return false; +} + +LASzipper::LASzipper() +{ + error_string = 0; + count = 0; + stream = 0; + writer = 0; +} + +LASzipper::~LASzipper() +{ + if (error_string) free(error_string); + if (writer || stream) close(); +} diff --git a/libs/laszip/src/laszipper.hpp b/libs/laszip/src/laszipper.hpp new file mode 100644 index 0000000..d349845 --- /dev/null +++ b/libs/laszip/src/laszipper.hpp @@ -0,0 +1,77 @@ +/* +=============================================================================== + + FILE: laszipper.hpp + + CONTENTS: + + Writes (optionally compressed) LIDAR points to LAS formats 1.0 - 1.3. This + particular class is only used for adding LASzip to libLAS (not to LASlib). + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2007-2013, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 8 May 2011 -- added an option for variable chunking via chunk() + 23 April 2011 -- changed interface for simplicity and chunking support + 10 January 2011 -- licensing change for LGPL release and liblas integration + 12 December 2010 -- created from LASwriter/LASreader after Howard got pushy (-; + +=============================================================================== +*/ +#ifndef LAS_ZIPPER_HPP +#define LAS_ZIPPER_HPP + +#include + +#include "laszip.hpp" + +#ifdef LZ_WIN32_VC6 +#include +#else +#include +#include +using namespace std; +#endif + +class ByteStreamOut; +class LASwritePoint; + +class LASzipper +{ +public: + bool open(FILE* outfile, const LASzip* laszip); + bool open(ostream& outstream, const LASzip* laszip); + + bool write(const unsigned char* const * point); + bool chunk(); + bool close(); + + LASzipper(); + ~LASzipper(); + + // in case a function returns false this string describes the problem + const char* get_error() const; + +private: + unsigned int count; + ByteStreamOut* stream; + LASwritePoint* writer; + bool return_error(const char* err); + char* error_string; +}; + +#endif diff --git a/libs/laszip/src/mydefs.cpp b/libs/laszip/src/mydefs.cpp new file mode 100644 index 0000000..0051eca --- /dev/null +++ b/libs/laszip/src/mydefs.cpp @@ -0,0 +1,46 @@ +/* +=============================================================================== + + FILE: mydefs.cpp + + CONTENTS: + + see corresponding header file + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2011-2019, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the LICENSE.txt file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + see corresponding header file + +=============================================================================== +*/ +#include "mydefs.hpp" + +#if defined(_MSC_VER) +#include +wchar_t* UTF8toUTF16(const char* utf8) +{ + wchar_t* utf16 = 0; + int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, 0, 0); + if (len > 0) + { + utf16 = new wchar_t[len]; + MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, len); + } + return utf16; +} +#endif diff --git a/libs/laszip/src/mydefs.hpp b/libs/laszip/src/mydefs.hpp new file mode 100644 index 0000000..f2074e6 --- /dev/null +++ b/libs/laszip/src/mydefs.hpp @@ -0,0 +1,254 @@ +/* +=============================================================================== + + FILE: mydefs.hpp + + CONTENTS: + + Basic data type definitions and operations to be robust across platforms. + + PROGRAMMERS: + + martin.isenburg@rapidlasso.com - http://rapidlasso.com + + COPYRIGHT: + + (c) 2005-2015, martin isenburg, rapidlasso - fast tools to catch reality + + This is free software; you can redistribute and/or modify it under the + terms of the GNU Lesser General Licence as published by the Free Software + Foundation. See the COPYING file for more information. + + This software is distributed WITHOUT ANY WARRANTY and without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + CHANGE HISTORY: + + 28 October 2015 -- adding DLL bindings via 'COMPILE_AS_DLL' and 'USE_AS_DLL' + 10 January 2011 -- licensing change for LGPL release and libLAS integration + 13 July 2005 -- created after returning with many mosquito bites from OBX + +=============================================================================== +*/ +#ifndef MYDEFS_HPP +#define MYDEFS_HPP + +#ifndef _WIN32 +#define LASLIB_DLL +#else // _WIN32 +#ifdef COMPILE_AS_DLL +#define LASLIB_DLL __declspec(dllexport) +#elif USE_AS_DLL +#define LASLIB_DLL __declspec(dllimport) +#else +#define LASLIB_DLL +#endif +#endif // _WIN32 + +typedef char CHAR; + +typedef int I32; +typedef short I16; +typedef char I8; + +typedef unsigned int U32; +typedef unsigned short U16; +typedef unsigned char U8; + +#if defined(_WIN32) && ! defined (__MINGW32__) // 64 byte integer under Windows +typedef unsigned __int64 U64; +typedef __int64 I64; +#else // 64 byte integer elsewhere ... +typedef unsigned long long U64; +typedef long long I64; +#endif + +typedef float F32; +typedef double F64; + +#if defined(_MSC_VER) || defined (__MINGW32__) +typedef int BOOL; +#else +typedef bool BOOL; +#endif + +typedef union U32I32F32 { U32 u32; I32 i32; F32 f32; } U32I32F32; +typedef union U64I64F64 { U64 u64; I64 i64; F64 f64; } U64I64F64; +typedef union I64U32I32F32 { I64 i64; U32 u32[2]; I32 i32[2]; F32 f32[2]; } I64U32I32F32; + +#define F32_MAX +2.0e+37f +#define F32_MIN -2.0e+37f + +#define F64_MAX +2.0e+307 +#define F64_MIN -2.0e+307 + +#define U8_MIN ((U8)0x0) // 0 +#define U8_MAX ((U8)0xFF) // 255 +#define U8_MAX_MINUS_ONE ((U8)0xFE) // 254 +#define U8_MAX_PLUS_ONE 0x0100 // 256 + +#define U16_MIN ((U16)0x0) // 0 +#define U16_MAX ((U16)0xFFFF) // 65535 +#define U16_MAX_MINUS_ONE ((U16)0xFFFE) // 65534 +#define U16_MAX_PLUS_ONE 0x00010000 // 65536 + +#define U32_MIN ((U32)0x0) // 0 +#define U32_MAX ((U32)0xFFFFFFFF) // 4294967295 +#define U32_MAX_MINUS_ONE ((U32)0xFFFFFFFE) // 4294967294 +#if defined(WIN32) // 64 byte unsigned int constant under Windows +#define U32_MAX_PLUS_ONE 0x0000000100000000 // 4294967296 +#else // 64 byte unsigned int constant elsewhere ... +#define U32_MAX_PLUS_ONE 0x0000000100000000ull // 4294967296 +#endif + +#define I8_MIN ((I8)0x80) // -128 +#define I8_MAX ((I8)0x7F) // 127 + +#define I16_MIN ((I16)0x8000) // -32768 +#define I16_MAX ((I16)0x7FFF) // 32767 + +#define I32_MIN ((I32)0x80000000) // -2147483648 +#define I32_MAX ((I32)0x7FFFFFFF) // 2147483647 + +#define I64_MIN ((I64)0x8000000000000000) +#define I64_MAX ((I64)0x7FFFFFFFFFFFFFFF) + +#define U8_FOLD(n) (((n) < U8_MIN) ? (n+U8_MAX_PLUS_ONE) : (((n) > U8_MAX) ? (n-U8_MAX_PLUS_ONE) : (n))) + +#define I8_CLAMP(n) (((n) <= I8_MIN) ? I8_MIN : (((n) >= I8_MAX) ? I8_MAX : ((I8)(n)))) +#define U8_CLAMP(n) (((n) <= U8_MIN) ? U8_MIN : (((n) >= U8_MAX) ? U8_MAX : ((U8)(n)))) + +#define I16_CLAMP(n) (((n) <= I16_MIN) ? I16_MIN : (((n) >= I16_MAX) ? I16_MAX : ((I16)(n)))) +#define U16_CLAMP(n) (((n) <= U16_MIN) ? U16_MIN : (((n) >= U16_MAX) ? U16_MAX : ((U16)(n)))) + +#define I32_CLAMP(n) (((n) <= I32_MIN) ? I32_MIN : (((n) >= I32_MAX) ? I32_MAX : ((I32)(n)))) +#define U32_CLAMP(n) (((n) <= U32_MIN) ? U32_MIN : (((n) >= U32_MAX) ? U32_MAX : ((U32)(n)))) + +#define I8_QUANTIZE(n) (((n) >= 0) ? (I8)((n)+0.5) : (I8)((n)-0.5)) +#define U8_QUANTIZE(n) (((n) >= 0) ? (U8)((n)+0.5) : (U8)(0)) + +#define I16_QUANTIZE(n) (((n) >= 0) ? (I16)((n)+0.5) : (I16)((n)-0.5)) +#define U16_QUANTIZE(n) (((n) >= 0) ? (U16)((n)+0.5) : (U16)(0)) + +#define I32_QUANTIZE(n) (((n) >= 0) ? (I32)((n)+0.5) : (I32)((n)-0.5)) +#define U32_QUANTIZE(n) (((n) >= 0) ? (U32)((n)+0.5) : (U32)(0)) + +#define I64_QUANTIZE(n) (((n) >= 0) ? (I64)((n)+0.5) : (I64)((n)-0.5)) +#define U64_QUANTIZE(n) (((n) >= 0) ? (U64)((n)+0.5) : (U64)(0)) + +#define I16_FLOOR(n) ((((I16)(n)) > (n)) ? (((I16)(n))-1) : ((I16)(n))) +#define I32_FLOOR(n) ((((I32)(n)) > (n)) ? (((I32)(n))-1) : ((I32)(n))) +#define I64_FLOOR(n) ((((I64)(n)) > (n)) ? (((I64)(n))-1) : ((I64)(n))) + +#define I16_CEIL(n) ((((I16)(n)) < (n)) ? (((I16)(n))+1) : ((I16)(n))) +#define I32_CEIL(n) ((((I32)(n)) < (n)) ? (((I32)(n))+1) : ((I32)(n))) +#define I64_CEIL(n) ((((I64)(n)) < (n)) ? (((I64)(n))+1) : ((I64)(n))) + +#define I8_FITS_IN_RANGE(n) (((n) >= I8_MIN) && ((n) <= I8_MAX) ? TRUE : FALSE) +#define U8_FITS_IN_RANGE(n) (((n) >= U8_MIN) && ((n) <= U8_MAX) ? TRUE : FALSE) +#define I16_FITS_IN_RANGE(n) (((n) >= I16_MIN) && ((n) <= I16_MAX) ? TRUE : FALSE) +#define U16_FITS_IN_RANGE(n) (((n) >= U16_MIN) && ((n) <= U16_MAX) ? TRUE : FALSE) +#define I32_FITS_IN_RANGE(n) (((n) >= I32_MIN) && ((n) <= I32_MAX) ? TRUE : FALSE) +#define U32_FITS_IN_RANGE(n) (((n) >= U32_MIN) && ((n) <= U32_MAX) ? TRUE : FALSE) + +#define F32_IS_FINITE(n) ((F32_MIN < (n)) && ((n) < F32_MAX)) +#define F64_IS_FINITE(n) ((F64_MIN < (n)) && ((n) < F64_MAX)) + +#define U32_ZERO_BIT_0(n) (((n)&(U32)0xFFFFFFFE)) +#define U32_ZERO_BIT_0_1(n) (((n)&(U32)0xFFFFFFFC)) + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef NULL +#define NULL 0 +#endif + +inline BOOL IS_LITTLE_ENDIAN() +{ + const U32 i = 1; + return (*((const U8*)&i) == 1); +} + +#define ENDIANSWAP16(n) \ + ( ((((U16) n) << 8) & 0xFF00) | \ + ((((U16) n) >> 8) & 0x00FF) ) + +#define ENDIANSWAP32(n) \ + ( ((((U32) n) << 24) & 0xFF000000) | \ + ((((U32) n) << 8) & 0x00FF0000) | \ + ((((U32) n) >> 8) & 0x0000FF00) | \ + ((((U32) n) >> 24) & 0x000000FF) ) + +inline void ENDIAN_SWAP_16(U8* field) +{ + U8 help = field[0]; + field[0] = field[1]; + field[1] = help; +} + +inline void ENDIAN_SWAP_32(U8* field) +{ + U8 help; + help = field[0]; + field[0] = field[3]; + field[3] = help; + help = field[1]; + field[1] = field[2]; + field[2] = help; +} + +inline void ENDIAN_SWAP_64(U8* field) +{ + U8 help; + help = field[0]; + field[0] = field[7]; + field[7] = help; + help = field[1]; + field[1] = field[6]; + field[6] = help; + help = field[2]; + field[2] = field[5]; + field[5] = help; + help = field[3]; + field[3] = field[4]; + field[4] = help; +} + +inline void ENDIAN_SWAP_16(const U8* from, U8* to) +{ + to[0] = from[1]; + to[1] = from[0]; +} + +inline void ENDIAN_SWAP_32(const U8* from, U8* to) +{ + to[0] = from[3]; + to[1] = from[2]; + to[2] = from[1]; + to[3] = from[0]; +} + +inline void ENDIAN_SWAP_64(const U8* from, U8* to) +{ + to[0] = from[7]; + to[1] = from[6]; + to[2] = from[5]; + to[3] = from[4]; + to[4] = from[3]; + to[5] = from[2]; + to[6] = from[1]; + to[7] = from[0]; +} + +#if defined(_MSC_VER) +#include +wchar_t* UTF8toUTF16(const char* utf8); +#endif + +#endif diff --git a/modules/compute/LasLoaderSparse.cpp b/modules/compute/LasLoaderSparse.cpp new file mode 100644 index 0000000..872c45e --- /dev/null +++ b/modules/compute/LasLoaderSparse.cpp @@ -0,0 +1,711 @@ + +#include "LasLoaderSparse.h" +#include "unsuck.hpp" + + +#define STEPS_30BIT 1073741824 +#define MASK_30BIT 1073741823 +#define STEPS_20BIT 1048576 +#define MASK_20BIT 1048575 +#define STEPS_10BIT 1024 +#define MASK_10BIT 1023 + +mutex mtx_debug; + + +struct LoadResult{ + shared_ptr bBatches; + shared_ptr bXyzLow; + shared_ptr bXyzMed; + shared_ptr bXyzHig; + shared_ptr bColors; + int64_t sparse_pointOffset; + int64_t numBatches; +}; + +struct Batch{ + int64_t chunk_pointOffset; + int64_t file_pointOffset; + int64_t sparse_pointOffset; + int64_t numPoints; + int64_t file_index; + + dvec3 min = {Infinity, Infinity, Infinity}; + dvec3 max = {-Infinity, -Infinity, -Infinity}; +}; + +shared_ptr loadLas(shared_ptr lasfile, int64_t firstPoint, int64_t numPoints){ + + string path = lasfile->path; + int64_t file_byteOffset = lasfile->offsetToPointData + firstPoint * lasfile->bytesPerPoint; + int64_t file_byteSize = numPoints * lasfile->bytesPerPoint; + auto source = readBinaryFile(path, file_byteOffset, file_byteSize); + int64_t sparse_pointOffset = lasfile->sparse_point_offset + firstPoint; + + // compute batch metadata + int64_t numBatches = numPoints / POINTS_PER_WORKGROUP; + if((numPoints % POINTS_PER_WORKGROUP) != 0){ + numBatches++; + } + + vector batches; + + int64_t chunk_pointsProcessed = 0; + for(int i = 0; i < numBatches; i++){ + + int64_t remaining = numPoints - chunk_pointsProcessed; + int64_t numPointsInBatch = std::min(int64_t(POINTS_PER_WORKGROUP), remaining); + + Batch batch; + + batch.min = {Infinity, Infinity, Infinity}; + batch.max = {-Infinity, -Infinity, -Infinity}; + batch.chunk_pointOffset = chunk_pointsProcessed; + batch.file_pointOffset = firstPoint + chunk_pointsProcessed; + batch.sparse_pointOffset = sparse_pointOffset + chunk_pointsProcessed; + batch.numPoints = numPointsInBatch; + + batches.push_back(batch); + + chunk_pointsProcessed += numPointsInBatch; + } + + auto bBatches = make_shared(64 * numBatches); + auto bXyzLow = make_shared(4 * numPoints); + auto bXyzMed = make_shared(4 * numPoints); + auto bXyzHig = make_shared(4 * numPoints); + auto bColors = make_shared(4 * numPoints); + + dvec3 boxMin = lasfile->boxMin; + dvec3 cScale = lasfile->scale; + dvec3 cOffset = lasfile->offset; + + // load batches/points + for(int batchIndex = 0; batchIndex < numBatches; batchIndex++){ + Batch& batch = batches[batchIndex]; + + // compute batch bounding box + for(int i = 0; i < batch.numPoints; i++){ + int index_pointFile = batch.chunk_pointOffset + i; + + int32_t X = source->get(index_pointFile * lasfile->bytesPerPoint + 0); + int32_t Y = source->get(index_pointFile * lasfile->bytesPerPoint + 4); + int32_t Z = source->get(index_pointFile * lasfile->bytesPerPoint + 8); + + double x = double(X) * cScale.x + cOffset.x - boxMin.x; + double y = double(Y) * cScale.y + cOffset.y - boxMin.y; + double z = double(Z) * cScale.z + cOffset.z - boxMin.z; + + batch.min.x = std::min(batch.min.x, x); + batch.min.y = std::min(batch.min.y, y); + batch.min.z = std::min(batch.min.z, z); + batch.max.x = std::max(batch.max.x, x); + batch.max.y = std::max(batch.max.y, y); + batch.max.z = std::max(batch.max.z, z); + } + + dvec3 batchBoxSize = batch.max - batch.min; + + { + int64_t batchByteOffset = 64 * batchIndex; + + bBatches->set(batch.min.x , batchByteOffset + 4); + bBatches->set(batch.min.y , batchByteOffset + 8); + bBatches->set(batch.min.z , batchByteOffset + 12); + bBatches->set(batch.max.x , batchByteOffset + 16); + bBatches->set(batch.max.y , batchByteOffset + 20); + bBatches->set(batch.max.z , batchByteOffset + 24); + bBatches->set(batch.numPoints , batchByteOffset + 28); + bBatches->set(batch.sparse_pointOffset , batchByteOffset + 32); + bBatches->set(lasfile->fileIndex , batchByteOffset + 36); + } + + int offset_rgb = 0; + if(lasfile->pointFormat == 2){ + offset_rgb = 20; + }else if(lasfile->pointFormat == 3){ + offset_rgb = 28; + }else if(lasfile->pointFormat == 7){ + offset_rgb = 30; + }else if(lasfile->pointFormat == 8){ + offset_rgb = 30; + } + + // load data + for(int i = 0; i < batch.numPoints; i++){ + int index_pointFile = batch.chunk_pointOffset + i; + + int32_t X = source->get(index_pointFile * lasfile->bytesPerPoint + 0); + int32_t Y = source->get(index_pointFile * lasfile->bytesPerPoint + 4); + int32_t Z = source->get(index_pointFile * lasfile->bytesPerPoint + 8); + + double x = double(X) * cScale.x + cOffset.x - boxMin.x; + double y = double(Y) * cScale.y + cOffset.y - boxMin.y; + double z = double(Z) * cScale.z + cOffset.z - boxMin.z; + + uint32_t X30 = uint32_t(((x - batch.min.x) / batchBoxSize.x) * STEPS_30BIT); + uint32_t Y30 = uint32_t(((y - batch.min.y) / batchBoxSize.y) * STEPS_30BIT); + uint32_t Z30 = uint32_t(((z - batch.min.z) / batchBoxSize.z) * STEPS_30BIT); + + X30 = min(X30, uint32_t(STEPS_30BIT - 1)); + Y30 = min(Y30, uint32_t(STEPS_30BIT - 1)); + Z30 = min(Z30, uint32_t(STEPS_30BIT - 1)); + + { // low + uint32_t X_low = (X30 >> 20) & MASK_10BIT; + uint32_t Y_low = (Y30 >> 20) & MASK_10BIT; + uint32_t Z_low = (Z30 >> 20) & MASK_10BIT; + + uint32_t encoded = X_low | (Y_low << 10) | (Z_low << 20); + + bXyzLow->set(encoded, 4 * index_pointFile); + } + + { // med + uint32_t X_med = (X30 >> 10) & MASK_10BIT; + uint32_t Y_med = (Y30 >> 10) & MASK_10BIT; + uint32_t Z_med = (Z30 >> 10) & MASK_10BIT; + + uint32_t encoded = X_med | (Y_med << 10) | (Z_med << 20); + + bXyzMed->set(encoded, 4 * index_pointFile); + } + + { // hig + uint32_t X_hig = (X30 >> 0) & MASK_10BIT; + uint32_t Y_hig = (Y30 >> 0) & MASK_10BIT; + uint32_t Z_hig = (Z30 >> 0) & MASK_10BIT; + + uint32_t encoded = X_hig | (Y_hig << 10) | (Z_hig << 20); + + bXyzHig->set(encoded, 4 * index_pointFile); + } + + { // RGB + + + int R = source->get(index_pointFile * lasfile->bytesPerPoint + offset_rgb + 0); + int G = source->get(index_pointFile * lasfile->bytesPerPoint + offset_rgb + 2); + int B = source->get(index_pointFile * lasfile->bytesPerPoint + offset_rgb + 4); + + R = R < 256 ? R : R / 256; + G = G < 256 ? G : G / 256; + B = B < 256 ? B : B / 256; + + uint32_t color = R | (G << 8) | (B << 16); + + bColors->set(color, 4 * index_pointFile); + } + } + } + + auto result = make_shared(); + result->bXyzLow = bXyzLow; + result->bXyzMed = bXyzMed; + result->bXyzHig = bXyzHig; + result->bColors = bColors; + result->bBatches = bBatches; + result->numBatches = numBatches; + result->sparse_pointOffset = sparse_pointOffset; + + return result; +} + +shared_ptr loadLaz(shared_ptr lasfile, int64_t firstPoint, int64_t numPoints){ + + string path = lasfile->path; + int64_t sparse_pointOffset = lasfile->sparse_point_offset + firstPoint; + + // compute batch metadata + int64_t numBatches = numPoints / POINTS_PER_WORKGROUP; + if((numPoints % POINTS_PER_WORKGROUP) != 0){ + numBatches++; + } + + vector batches; + int64_t chunk_pointsProcessed = 0; + for(int i = 0; i < numBatches; i++){ + + int64_t remaining = numPoints - chunk_pointsProcessed; + int64_t numPointsInBatch = std::min(int64_t(POINTS_PER_WORKGROUP), remaining); + + Batch batch; + + batch.min = {Infinity, Infinity, Infinity}; + batch.max = {-Infinity, -Infinity, -Infinity}; + batch.chunk_pointOffset = chunk_pointsProcessed; + batch.file_pointOffset = firstPoint + chunk_pointsProcessed; + batch.sparse_pointOffset = sparse_pointOffset + chunk_pointsProcessed; + batch.numPoints = numPointsInBatch; + + batches.push_back(batch); + + chunk_pointsProcessed += numPointsInBatch; + } + + + laszip_POINTER laszip_reader; + laszip_header* laz_header; + laszip_point* laz_point; + { + laszip_BOOL is_compressed = true; + laszip_BOOL request_reader = 1; + + laszip_create(&laszip_reader); + laszip_request_compatibility_mode(laszip_reader, request_reader); + laszip_open_reader(laszip_reader, path.c_str(), &is_compressed); + laszip_seek_point(laszip_reader, firstPoint); + + laszip_get_header_pointer(laszip_reader, &laz_header); + laszip_get_point_pointer(laszip_reader, &laz_point); + } + + auto bBatches = make_shared(64 * numBatches); + auto bXyzLow = make_shared(4 * numPoints); + auto bXyzMed = make_shared(4 * numPoints); + auto bXyzHig = make_shared(4 * numPoints); + auto bColors = make_shared(4 * numPoints); + + dvec3 boxMin = lasfile->boxMin; + dvec3 cScale = lasfile->scale; + dvec3 cOffset = lasfile->offset; + + struct Point{ + double x = 0.0; + double y = 0.0; + double z = 0.0; + uint16_t R = 0; + uint16_t G = 0; + uint16_t B = 0; + }; + + vector points(numPoints); + + double XYZ[3]; + + // load batches/points + for(int batchIndex = 0; batchIndex < numBatches; batchIndex++){ + Batch& batch = batches[batchIndex]; + + // compute batch bounding box + for(int i = 0; i < batch.numPoints; i++){ + int index_pointFile = batch.chunk_pointOffset + i; + + laszip_read_point(laszip_reader); + laszip_get_coordinates(laszip_reader, XYZ); + + Point point; + point.x = XYZ[0]; + point.y = XYZ[1]; + point.z = XYZ[2]; + point.R = laz_point->rgb[0]; + point.G = laz_point->rgb[1]; + point.B = laz_point->rgb[2]; + + points[index_pointFile] = point; + + batch.min.x = std::min(batch.min.x, point.x - boxMin.x); + batch.min.y = std::min(batch.min.y, point.y - boxMin.y); + batch.min.z = std::min(batch.min.z, point.z - boxMin.z); + batch.max.x = std::max(batch.max.x, point.x - boxMin.x); + batch.max.y = std::max(batch.max.y, point.y - boxMin.y); + batch.max.z = std::max(batch.max.z, point.z - boxMin.z); + } + + dvec3 batchBoxSize = batch.max - batch.min; + + { + int64_t batchByteOffset = 64 * batchIndex; + + bBatches->set(batch.min.x , batchByteOffset + 4); + bBatches->set(batch.min.y , batchByteOffset + 8); + bBatches->set(batch.min.z , batchByteOffset + 12); + bBatches->set(batch.max.x , batchByteOffset + 16); + bBatches->set(batch.max.y , batchByteOffset + 20); + bBatches->set(batch.max.z , batchByteOffset + 24); + bBatches->set(batch.numPoints , batchByteOffset + 28); + bBatches->set(batch.sparse_pointOffset , batchByteOffset + 32); + bBatches->set(lasfile->fileIndex , batchByteOffset + 36); + } + + int offset_rgb = 0; + if(lasfile->pointFormat == 2){ + offset_rgb = 20; + }else if(lasfile->pointFormat == 3){ + offset_rgb = 28; + }else if(lasfile->pointFormat == 7){ + offset_rgb = 30; + }else if(lasfile->pointFormat == 8){ + offset_rgb = 30; + } + + // load data + for(int i = 0; i < batch.numPoints; i++){ + int index_pointFile = batch.chunk_pointOffset + i; + + Point point = points[index_pointFile]; + + // int32_t X = source->get(index_pointFile * lasfile->bytesPerPoint + 0); + // int32_t Y = source->get(index_pointFile * lasfile->bytesPerPoint + 4); + // int32_t Z = source->get(index_pointFile * lasfile->bytesPerPoint + 8); + + // double x = double(X) * cScale.x + cOffset.x - boxMin.x; + // double y = double(Y) * cScale.y + cOffset.y - boxMin.y; + // double z = double(Z) * cScale.z + cOffset.z - boxMin.z; + + double x = point.x - boxMin.x; + double y = point.y - boxMin.y; + double z = point.z - boxMin.z; + + uint32_t X30 = uint32_t(((x - batch.min.x) / batchBoxSize.x) * STEPS_30BIT); + uint32_t Y30 = uint32_t(((y - batch.min.y) / batchBoxSize.y) * STEPS_30BIT); + uint32_t Z30 = uint32_t(((z - batch.min.z) / batchBoxSize.z) * STEPS_30BIT); + + X30 = min(X30, uint32_t(STEPS_30BIT - 1)); + Y30 = min(Y30, uint32_t(STEPS_30BIT - 1)); + Z30 = min(Z30, uint32_t(STEPS_30BIT - 1)); + + { // low + uint32_t X_low = (X30 >> 20) & MASK_10BIT; + uint32_t Y_low = (Y30 >> 20) & MASK_10BIT; + uint32_t Z_low = (Z30 >> 20) & MASK_10BIT; + + uint32_t encoded = X_low | (Y_low << 10) | (Z_low << 20); + + bXyzLow->set(encoded, 4 * index_pointFile); + } + + { // med + uint32_t X_med = (X30 >> 10) & MASK_10BIT; + uint32_t Y_med = (Y30 >> 10) & MASK_10BIT; + uint32_t Z_med = (Z30 >> 10) & MASK_10BIT; + + uint32_t encoded = X_med | (Y_med << 10) | (Z_med << 20); + + bXyzMed->set(encoded, 4 * index_pointFile); + } + + { // hig + uint32_t X_hig = (X30 >> 0) & MASK_10BIT; + uint32_t Y_hig = (Y30 >> 0) & MASK_10BIT; + uint32_t Z_hig = (Z30 >> 0) & MASK_10BIT; + + uint32_t encoded = X_hig | (Y_hig << 10) | (Z_hig << 20); + + bXyzHig->set(encoded, 4 * index_pointFile); + } + + { // RGB + + + // int R = source->get(index_pointFile * lasfile->bytesPerPoint + offset_rgb + 0); + // int G = source->get(index_pointFile * lasfile->bytesPerPoint + offset_rgb + 2); + // int B = source->get(index_pointFile * lasfile->bytesPerPoint + offset_rgb + 4); + + int R = point.R < 256 ? point.R : point.R / 256; + int G = point.G < 256 ? point.G : point.G / 256; + int B = point.B < 256 ? point.B : point.B / 256; + + uint32_t color = R | (G << 8) | (B << 16); + + bColors->set(color, 4 * index_pointFile); + } + } + } + + laszip_close_reader(laszip_reader); + laszip_destroy(laszip_reader); + + auto result = make_shared(); + result->bXyzLow = bXyzLow; + result->bXyzMed = bXyzMed; + result->bXyzHig = bXyzHig; + result->bColors = bColors; + result->bBatches = bBatches; + result->numBatches = numBatches; + result->sparse_pointOffset = sparse_pointOffset; + + return result; +} + + +LasLoaderSparse::LasLoaderSparse(shared_ptr renderer){ + + this->renderer = renderer; + + int pageSize = 0; + glGetIntegerv(GL_SPARSE_BUFFER_PAGE_SIZE_ARB, &pageSize); + PAGE_SIZE = pageSize; + + { // create (sparse) buffers + this->ssBatches = renderer->createBuffer(64 * 200'000); + this->ssXyzLow = renderer->createSparseBuffer(4 * MAX_POINTS); + this->ssXyzMed = renderer->createSparseBuffer(4 * MAX_POINTS); + this->ssXyzHig = renderer->createSparseBuffer(4 * MAX_POINTS); + this->ssColors = renderer->createSparseBuffer(4 * MAX_POINTS); + this->ssLoadBuffer = renderer->createBuffer(200 * MAX_POINTS_PER_BATCH); + + GLuint zero = 0; + glClearNamedBufferData(this->ssBatches.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); + } + + int numThreads = 1; + auto cpuData = getCpuData(); + + if(cpuData.numProcessors == 1) numThreads = 1; + if(cpuData.numProcessors == 2) numThreads = 1; + if(cpuData.numProcessors == 3) numThreads = 2; + if(cpuData.numProcessors == 4) numThreads = 3; + if(cpuData.numProcessors == 5) numThreads = 4; + if(cpuData.numProcessors == 6) numThreads = 4; + if(cpuData.numProcessors == 7) numThreads = 5; + if(cpuData.numProcessors == 8) numThreads = 5; + if(cpuData.numProcessors > 8) numThreads = (cpuData.numProcessors / 2) + 1; + + cout << "start loading points with " << numThreads << " threads" << endl; + + for(int i = 0; i < 12; i++) + //for(int i = 0; i < numThreads; i++) + { + // spawnLoader(); + } + spawnLoader(); +} + +void LasLoaderSparse::add(vector files, std::function>)> callback){ + + vector> lasfiles; + static mutex mtx_lasfiles; + + struct Task{ + string file; + int fileIndex; + }; + + auto ref = this; + + auto processor = [ref, &lasfiles](shared_ptr task){ + + auto lasfile = make_shared(); + lasfile->fileIndex = task->fileIndex; + lasfile->path = task->file; + + auto buffer_header = readBinaryFile(lasfile->path, 0, 375); + + int versionMajor = buffer_header->get(24); + int versionMinor = buffer_header->get(25); + + if(versionMajor == 1 && versionMinor < 4){ + lasfile->numPoints = buffer_header->get(107); + }else{ + lasfile->numPoints = buffer_header->get(247); + } + + lasfile->numPoints = min(lasfile->numPoints, 1'000'000'000ll); + + lasfile->offsetToPointData = buffer_header->get(96); + lasfile->pointFormat = buffer_header->get(104) % 128; + lasfile->bytesPerPoint = buffer_header->get(105); + + lasfile->scale.x = buffer_header->get(131); + lasfile->scale.y = buffer_header->get(139); + lasfile->scale.z = buffer_header->get(147); + + lasfile->offset.x = buffer_header->get(155); + lasfile->offset.y = buffer_header->get(163); + lasfile->offset.z = buffer_header->get(171); + + lasfile->boxMin.x = buffer_header->get(187); + lasfile->boxMin.y = buffer_header->get(203); + lasfile->boxMin.z = buffer_header->get(219); + + lasfile->boxMax.x = buffer_header->get(179); + lasfile->boxMax.y = buffer_header->get(195); + lasfile->boxMax.z = buffer_header->get(211); + + + + { + unique_lock lock1(mtx_lasfiles); + + lasfile->numBatches = lasfile->numPoints / POINTS_PER_WORKGROUP + 1; + + lasfile->sparse_point_offset = ref->numPoints; + + ref->files.push_back(lasfile); + ref->numPoints += lasfile->numPoints; + ref->numBatches += lasfile->numBatches; + + lasfiles.push_back(lasfile); + + stringstream ss; + ss << "load file " << task->file << endl; + ss << "numPoints: " << lasfile->numPoints << "\n"; + ss << "numBatches: " << lasfile->numBatches << "\n"; + ss << "sparse_point_offset: " << lasfile->sparse_point_offset << "\n"; + + cout << ss.str() << endl; + } + + { // create load tasks + + unique_lock lock2(ref->mtx_load); + + int64_t pointOffset = 0; + + while(pointOffset < lasfile->numPoints){ + + int64_t remaining = lasfile->numPoints - pointOffset; + int64_t pointsInBatch = min(int64_t(MAX_POINTS_PER_BATCH), remaining); + + LoadTask task; + task.lasfile = lasfile; + task.firstPoint = pointOffset; + task.numPoints = pointsInBatch; + + ref->loadTasks.push_back(task); + + pointOffset += pointsInBatch; + } + } + + }; + + auto cpuData = getCpuData(); + int numThreads = cpuData.numProcessors; + + TaskPool pool(numThreads, processor); + + for(auto file : files){ + auto task = make_shared(); + task->file = file; + task->fileIndex = this->numFiles; + this->numFiles++; + + pool.addTask(task); + } + + pool.close(); + pool.waitTillEmpty(); + + callback(lasfiles); + +} + +void LasLoaderSparse::spawnLoader(){ + + auto ref = this; + + thread t([ref](){ + + while(true){ + + std::this_thread::sleep_for(10ms); + + unique_lock lock_load(ref->mtx_load); + //unique_lock lock_dbg(mtx_debug); + + if(ref->loadTasks.size() == 0){ + lock_load.unlock(); + + continue; + } + + auto task = ref->loadTasks.back(); + ref->loadTasks.pop_back(); + + lock_load.unlock(); + + shared_ptr result = nullptr; + + //unique_lock lock_dbg(mtx_debug); + + if(iEndsWith(task.lasfile->path, "las")){ + result = loadLas(task.lasfile, task.firstPoint, task.numPoints); + }else if(iEndsWith(task.lasfile->path, "laz")){ + result = loadLaz(task.lasfile, task.firstPoint, task.numPoints); + } + + UploadTask uploadTask; + uploadTask.lasfile = task.lasfile; + uploadTask.sparse_pointOffset = result->sparse_pointOffset; + uploadTask.numPoints = task.numPoints; + uploadTask.numBatches = result->numBatches; + uploadTask.bXyzLow = result->bXyzLow; + uploadTask.bXyzMed = result->bXyzMed; + uploadTask.bXyzHig = result->bXyzHig; + uploadTask.bColors = result->bColors; + uploadTask.bBatches = result->bBatches; + + unique_lock lock_upload(ref->mtx_upload); + ref->uploadTasks.push_back(uploadTask); + lock_upload.unlock(); + + } + + }); + t.detach(); + +} + +void LasLoaderSparse::process(){ + + // static int numProcessed = 0; + + // FETCH TASK + unique_lock lock(mtx_upload); + // unique_lock lock_dbg(mtx_debug); + + if(uploadTasks.size() == 0){ + return; + } + + auto task = uploadTasks.back(); + uploadTasks.pop_back(); + + lock.unlock(); + + // UPLOAD DATA TO GPU + + { // commit physical memory in sparse buffers + int64_t offset = 4 * task.sparse_pointOffset; + int64_t pageAlignedOffset = offset - (offset % PAGE_SIZE); + + int64_t size = 4 * task.numPoints; + int64_t pageAlignedSize = size - (size % PAGE_SIZE) + PAGE_SIZE; + pageAlignedSize = std::min(pageAlignedSize, 4 * MAX_POINTS); + + //cout << "commiting, offset: " << formatNumber(pageAlignedOffset) << ", size: " << formatNumber(pageAlignedSize) << endl; + + for(auto glBuffer : {ssXyzLow, ssXyzMed, ssXyzHig, ssColors}){ + glBindBuffer(GL_SHADER_STORAGE_BUFFER, glBuffer.handle); + glBufferPageCommitmentARB(GL_SHADER_STORAGE_BUFFER, pageAlignedOffset, pageAlignedSize, GL_TRUE); + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + } + } + + //static int64_t numBatchesLoaded = 0; + + // upload batch metadata + glNamedBufferSubData(ssBatches.handle, + 64 * this->numBatchesLoaded, + task.bBatches->size, + task.bBatches->data); + + //numBatchesLoaded += task.numBatches; + + // upload batch points + glNamedBufferSubData(ssXyzLow.handle, 4 * task.sparse_pointOffset, 4 * task.numPoints, task.bXyzLow->data); + glNamedBufferSubData(ssXyzMed.handle, 4 * task.sparse_pointOffset, 4 * task.numPoints, task.bXyzMed->data); + glNamedBufferSubData(ssXyzHig.handle, 4 * task.sparse_pointOffset, 4 * task.numPoints, task.bXyzHig->data); + glNamedBufferSubData(ssColors.handle, 4 * task.sparse_pointOffset, 4 * task.numPoints, task.bColors->data); + + //cout << "uploading, offset: " << formatNumber(4 * task.sparse_pointOffset) << ", size: " << formatNumber(4 * task.numPoints) << endl; + + this->numBatchesLoaded += task.numBatches; + this->numPointsLoaded += task.numPoints; + task.lasfile->numPointsLoaded += task.numPoints; + + //cout << "numBatchesLoaded: " << numBatchesLoaded << endl; + +} \ No newline at end of file diff --git a/modules/compute/LasLoaderSparse.h b/modules/compute/LasLoaderSparse.h index 2c9f020..934b127 100644 --- a/modules/compute/LasLoaderSparse.h +++ b/modules/compute/LasLoaderSparse.h @@ -11,49 +11,46 @@ #include "unsuck.hpp" #include "Shader.h" #include "Resources.h" +#include "TaskPool.h" +#include "laszip_api.h" using namespace std; using glm::vec3; namespace fs = std::filesystem; + +struct LasFile{ + int64_t fileIndex = 0; + string path; + int64_t numPoints = 0; + int64_t numPointsLoaded = 0; + uint32_t offsetToPointData = 0; + int pointFormat = 0; + uint32_t bytesPerPoint = 0; + dvec3 scale = {1.0, 1.0, 1.0}; + dvec3 offset = {0.0, 0.0, 0.0}; + dvec3 boxMin; + dvec3 boxMax; + + int64_t numBatches = 0; + + // index of first point in the sparse gpu buffer + int64_t sparse_point_offset = 0; + + bool isSelected = false; + bool isHovered = false; + bool isDoubleClicked = false; +}; + struct LasLoaderSparse { int64_t MAX_POINTS = 1'000'000'000; int64_t PAGE_SIZE = 0; -#define STEPS_30BIT 1073741824 -#define MASK_30BIT 1073741823 -#define STEPS_20BIT 1048576 -#define MASK_20BIT 1048575 -#define STEPS_10BIT 1024 -#define MASK_10BIT 1023 - mutex mtx_upload; mutex mtx_load; - struct LasFile{ - string path; - int64_t numPoints = 0; - int64_t numPointsLoaded = 0; - uint32_t offsetToPointData = 0; - int pointFormat = 0; - uint32_t bytesPerPoint = 0; - dvec3 scale = {1.0, 1.0, 1.0}; - dvec3 offset = {0.0, 0.0, 0.0}; - dvec3 boxMin; - dvec3 boxMax; - - int64_t numBatches = 0; - - // index of first point in the sparse gpu buffer - int64_t sparse_point_offset = 0; - - bool isSelected = false; - bool isHovered = false; - bool isDoubleClicked = false; - }; - struct LoadTask{ shared_ptr lasfile; int64_t firstPoint; @@ -73,16 +70,6 @@ struct LasLoaderSparse { shared_ptr bBatches; }; - struct Batch{ - int64_t chunk_pointOffset; - int64_t file_pointOffset; - int64_t sparse_pointOffset; - int64_t numPoints; - - dvec3 min = {Infinity, Infinity, Infinity}; - dvec3 max = {-Infinity, -Infinity, -Infinity}; - }; - vector> files; vector loadTasks; vector uploadTasks; @@ -92,6 +79,7 @@ struct LasLoaderSparse { int64_t numBatches = 0; int64_t numBatchesLoaded = 0; int64_t bytesReserved = 0; + int64_t numFiles = 0; shared_ptr renderer = nullptr; @@ -102,408 +90,12 @@ struct LasLoaderSparse { GLBuffer ssColors; GLBuffer ssLoadBuffer; + LasLoaderSparse(shared_ptr renderer); - LasLoaderSparse(shared_ptr renderer){ - - this->renderer = renderer; - - int pageSize = 0; - glGetIntegerv(GL_SPARSE_BUFFER_PAGE_SIZE_ARB, &pageSize); - PAGE_SIZE = pageSize; - - { // create (sparse) buffers - this->ssBatches = renderer->createBuffer(64 * 200'000); - this->ssXyzLow = renderer->createSparseBuffer(4 * MAX_POINTS); - this->ssXyzMed = renderer->createSparseBuffer(4 * MAX_POINTS); - this->ssXyzHig = renderer->createSparseBuffer(4 * MAX_POINTS); - this->ssColors = renderer->createSparseBuffer(4 * MAX_POINTS); - this->ssLoadBuffer = renderer->createBuffer(200 * MAX_POINTS_PER_BATCH); - - GLuint zero = 0; - glClearNamedBufferData(this->ssBatches.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); - } - - // for(int i = 0; i < 10; i++){ - // spawnLoader(); - // } - spawnLoader(); - } - - // add las files that are to be loaded progressively - shared_ptr add(string path){ - - auto lasfile = make_shared(); - - { // load lasfile metadata - lasfile->path = path; - - auto buffer_header = readBinaryFile(path, 0, 375); - - int versionMajor = buffer_header->get(24); - int versionMinor = buffer_header->get(25); - - if(versionMajor == 1 && versionMinor < 4){ - lasfile->numPoints = buffer_header->get(107); - }else{ - lasfile->numPoints = buffer_header->get(247); - } - - lasfile->numPoints = min(lasfile->numPoints, 1'000'000'000ll); - - lasfile->offsetToPointData = buffer_header->get(96); - lasfile->pointFormat = buffer_header->get(104); - lasfile->bytesPerPoint = buffer_header->get(105); - - lasfile->scale.x = buffer_header->get(131); - lasfile->scale.y = buffer_header->get(139); - lasfile->scale.z = buffer_header->get(147); - - lasfile->offset.x = buffer_header->get(155); - lasfile->offset.y = buffer_header->get(163); - lasfile->offset.z = buffer_header->get(171); - - lasfile->boxMin.x = buffer_header->get(187); - lasfile->boxMin.y = buffer_header->get(203); - lasfile->boxMin.z = buffer_header->get(219); - - lasfile->boxMax.x = buffer_header->get(179); - lasfile->boxMax.y = buffer_header->get(195); - lasfile->boxMax.z = buffer_header->get(211); - - lasfile->numBatches = lasfile->numPoints / POINTS_PER_WORKGROUP + 1; - - lasfile->sparse_point_offset = this->numPoints; - - this->files.push_back(lasfile); - this->numPoints += lasfile->numPoints; - this->numBatches += lasfile->numBatches; - } - - - { // create load tasks - - int64_t pointOffset = 0; - - unique_lock lock(mtx_load); - - while(pointOffset < lasfile->numPoints){ - - int64_t remaining = lasfile->numPoints - pointOffset; - int64_t pointsInBatch = min(int64_t(MAX_POINTS_PER_BATCH), remaining); - - LoadTask task; - task.lasfile = lasfile; - task.firstPoint = pointOffset; - task.numPoints = pointsInBatch; - - loadTasks.push_back(task); - - pointOffset += pointsInBatch; - } - - // vector cropped; - // cropped.push_back(loadTasks[0]); - // cropped.push_back(loadTasks[1]); - // cropped.push_back(loadTasks[2]); - // cropped.push_back(loadTasks[3]); - // cropped.push_back(loadTasks[4]); - // loadTasks = cropped; - - // std::reverse(loadTasks.begin(), loadTasks.end()); - } - - return lasfile; - } - - // continue progressively loading some data - // batch: workgroup batch - // chunk: multiple(~100) workgroup batches loaded from file at once - void spawnLoader(){ - - auto ref = this; - - thread t([ref](){ - - while(true){ - - std::this_thread::sleep_for(10ms); - - unique_lock lock_load(ref->mtx_load); - - if(ref->loadTasks.size() == 0){ - lock_load.unlock(); - - continue; - } - - auto task = ref->loadTasks.back(); - ref->loadTasks.pop_back(); - - lock_load.unlock(); - - static int64_t numBatchesLoaded = 0; - - auto lasfile = task.lasfile; - string path = lasfile->path; - int64_t file_byteOffset = lasfile->offsetToPointData + task.firstPoint * lasfile->bytesPerPoint; - int64_t file_byteSize = task.numPoints * lasfile->bytesPerPoint; - auto source = readBinaryFile(path, file_byteOffset, file_byteSize); - // int64_t sparse_batchOffset = numBatchesLoaded; - int64_t sparse_pointOffset = lasfile->sparse_point_offset + task.firstPoint; - - // compute batch metadata - int64_t numBatches = task.numPoints / POINTS_PER_WORKGROUP; - if((task.numPoints % POINTS_PER_WORKGROUP) != 0){ - numBatches++; - } - - - vector batches; - - int64_t chunk_pointsProcessed = 0; - for(int i = 0; i < numBatches; i++){ - - int64_t remaining = task.numPoints - chunk_pointsProcessed; - int64_t numPointsInBatch = std::min(int64_t(POINTS_PER_WORKGROUP), remaining); - - Batch batch; - - batch.min = {Infinity, Infinity, Infinity}; - batch.max = {-Infinity, -Infinity, -Infinity}; - batch.chunk_pointOffset = chunk_pointsProcessed; - batch.file_pointOffset = task.firstPoint + chunk_pointsProcessed; - batch.sparse_pointOffset = sparse_pointOffset + chunk_pointsProcessed; - batch.numPoints = numPointsInBatch; - - batches.push_back(batch); - - chunk_pointsProcessed += numPointsInBatch; - } - - auto bBatches = make_shared(64 * numBatches); - auto bXyzLow = make_shared(4 * task.numPoints); - auto bXyzMed = make_shared(4 * task.numPoints); - auto bXyzHig = make_shared(4 * task.numPoints); - auto bColors = make_shared(4 * task.numPoints); - - dvec3 boxMin = lasfile->boxMin; - dvec3 cScale = lasfile->scale; - dvec3 cOffset = lasfile->offset; - - // load batches/points - for(int batchIndex = 0; batchIndex < numBatches; batchIndex++){ - Batch& batch = batches[batchIndex]; - - // compute batch bounding box - for(int i = 0; i < batch.numPoints; i++){ - int index_pointFile = batch.chunk_pointOffset + i; - - int32_t X = source->get(index_pointFile * lasfile->bytesPerPoint + 0); - int32_t Y = source->get(index_pointFile * lasfile->bytesPerPoint + 4); - int32_t Z = source->get(index_pointFile * lasfile->bytesPerPoint + 8); - - double x = double(X) * cScale.x + cOffset.x - boxMin.x; - double y = double(Y) * cScale.y + cOffset.y - boxMin.y; - double z = double(Z) * cScale.z + cOffset.z - boxMin.z; - - batch.min.x = std::min(batch.min.x, x); - batch.min.y = std::min(batch.min.y, y); - batch.min.z = std::min(batch.min.z, z); - batch.max.x = std::max(batch.max.x, x); - batch.max.y = std::max(batch.max.y, y); - batch.max.z = std::max(batch.max.z, z); - } - - dvec3 batchBoxSize = batch.max - batch.min; - - { - int64_t batchByteOffset = 64 * batchIndex; - - bBatches->set(batch.min.x , batchByteOffset + 4); - bBatches->set(batch.min.y , batchByteOffset + 8); - bBatches->set(batch.min.z , batchByteOffset + 12); - bBatches->set(batch.max.x , batchByteOffset + 16); - bBatches->set(batch.max.y , batchByteOffset + 20); - bBatches->set(batch.max.z , batchByteOffset + 24); - bBatches->set(batch.numPoints , batchByteOffset + 28); - bBatches->set(batch.sparse_pointOffset, batchByteOffset + 32); - } - - int offset_rgb = 0; - if(lasfile->pointFormat == 2){ - offset_rgb = 20; - }else if(lasfile->pointFormat == 3){ - offset_rgb = 28; - }else if(lasfile->pointFormat == 7){ - offset_rgb = 30; - }else if(lasfile->pointFormat == 8){ - offset_rgb = 30; - } - - // load data - for(int i = 0; i < batch.numPoints; i++){ - int index_pointFile = batch.chunk_pointOffset + i; - - int32_t X = source->get(index_pointFile * lasfile->bytesPerPoint + 0); - int32_t Y = source->get(index_pointFile * lasfile->bytesPerPoint + 4); - int32_t Z = source->get(index_pointFile * lasfile->bytesPerPoint + 8); - - double x = double(X) * cScale.x + cOffset.x - boxMin.x; - double y = double(Y) * cScale.y + cOffset.y - boxMin.y; - double z = double(Z) * cScale.z + cOffset.z - boxMin.z; - - uint32_t X30 = uint32_t(((x - batch.min.x) / batchBoxSize.x) * STEPS_30BIT); - uint32_t Y30 = uint32_t(((y - batch.min.y) / batchBoxSize.y) * STEPS_30BIT); - uint32_t Z30 = uint32_t(((z - batch.min.z) / batchBoxSize.z) * STEPS_30BIT); - - X30 = min(X30, uint32_t(STEPS_30BIT - 1)); - Y30 = min(Y30, uint32_t(STEPS_30BIT - 1)); - Z30 = min(Z30, uint32_t(STEPS_30BIT - 1)); - - { // low - uint32_t X_low = (X30 >> 20) & MASK_10BIT; - uint32_t Y_low = (Y30 >> 20) & MASK_10BIT; - uint32_t Z_low = (Z30 >> 20) & MASK_10BIT; - - uint32_t encoded = X_low | (Y_low << 10) | (Z_low << 20); - - bXyzLow->set(encoded, 4 * index_pointFile); - } - - { // med - uint32_t X_med = (X30 >> 10) & MASK_10BIT; - uint32_t Y_med = (Y30 >> 10) & MASK_10BIT; - uint32_t Z_med = (Z30 >> 10) & MASK_10BIT; - - uint32_t encoded = X_med | (Y_med << 10) | (Z_med << 20); - - bXyzMed->set(encoded, 4 * index_pointFile); - } - - { // hig - uint32_t X_hig = (X30 >> 0) & MASK_10BIT; - uint32_t Y_hig = (Y30 >> 0) & MASK_10BIT; - uint32_t Z_hig = (Z30 >> 0) & MASK_10BIT; - - uint32_t encoded = X_hig | (Y_hig << 10) | (Z_hig << 20); - - bXyzHig->set(encoded, 4 * index_pointFile); - } - - { // RGB - - - int R = source->get(index_pointFile * lasfile->bytesPerPoint + offset_rgb + 0); - int G = source->get(index_pointFile * lasfile->bytesPerPoint + offset_rgb + 2); - int B = source->get(index_pointFile * lasfile->bytesPerPoint + offset_rgb + 4); - - R = R < 256 ? R : R / 256; - G = G < 256 ? G : G / 256; - B = B < 256 ? B : B / 256; - - uint32_t color = R | (G << 8) | (B << 16); - - bColors->set(color, 4 * index_pointFile); - } - } - - - } - - // numBatchesLoaded += numBatches; - - UploadTask uploadTask; - uploadTask.lasfile = lasfile; - uploadTask.sparse_pointOffset = sparse_pointOffset; - uploadTask.numPoints = task.numPoints; - uploadTask.numBatches = numBatches; - uploadTask.bXyzLow = bXyzLow; - uploadTask.bXyzMed = bXyzMed; - uploadTask.bXyzHig = bXyzHig; - uploadTask.bColors = bColors; - uploadTask.bBatches = bBatches; - - unique_lock lock_upload(ref->mtx_upload); - ref->uploadTasks.push_back(uploadTask); - lock_upload.unlock(); - - } - - }); - t.detach(); - - } - - void process(){ - - // static int numProcessed = 0; - - // FETCH TASK - unique_lock lock(mtx_upload); - - if(uploadTasks.size() == 0){ - return; - } - - auto task = uploadTasks.back(); - uploadTasks.pop_back(); - - lock.unlock(); - - // if(numProcessed > 0){ - // return; - // } - - // numProcessed++; - - - - // UPLOAD DATA TO GPU - - { // commit physical memory in sparse buffers - int64_t offset = 4 * task.sparse_pointOffset; - int64_t pageAlignedOffset = offset - (offset % PAGE_SIZE); - - int64_t size = 4 * task.numPoints; - int64_t pageAlignedSize = size - (size % PAGE_SIZE) + PAGE_SIZE; - pageAlignedSize = std::min(pageAlignedSize, 4 * MAX_POINTS); - - // pageAlignedOffset = 0; - // pageAlignedSize = 10 * 4'128'768; - - cout << "commiting, offset: " << formatNumber(pageAlignedOffset) << ", size: " << formatNumber(pageAlignedSize) << endl; - - for(auto glBuffer : {ssXyzLow, ssXyzMed, ssXyzHig, ssColors}){ - glBindBuffer(GL_SHADER_STORAGE_BUFFER, glBuffer.handle); - glBufferPageCommitmentARB(GL_SHADER_STORAGE_BUFFER, pageAlignedOffset, pageAlignedSize, GL_TRUE); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); - } - } - - static int64_t numBatchesLoaded = 0; - - // upload batch metadata - glNamedBufferSubData(ssBatches.handle, - 64 * numBatchesLoaded, - task.bBatches->size, - task.bBatches->data); - - numBatchesLoaded += task.numBatches; - - // upload batch points - glNamedBufferSubData(ssXyzLow.handle, 4 * task.sparse_pointOffset, 4 * task.numPoints, task.bXyzLow->data); - glNamedBufferSubData(ssXyzMed.handle, 4 * task.sparse_pointOffset, 4 * task.numPoints, task.bXyzMed->data); - glNamedBufferSubData(ssXyzHig.handle, 4 * task.sparse_pointOffset, 4 * task.numPoints, task.bXyzHig->data); - glNamedBufferSubData(ssColors.handle, 4 * task.sparse_pointOffset, 4 * task.numPoints, task.bColors->data); - - cout << "uploading, offset: " << formatNumber(4 * task.sparse_pointOffset) << ", size: " << formatNumber(4 * task.numPoints) << endl; - - this->numBatchesLoaded += task.numBatches; - this->numPointsLoaded += task.numPoints; - task.lasfile->numPointsLoaded += task.numPoints; + void add(vector files, std::function>)> callback); - cout << "numBatchesLoaded: " << numBatchesLoaded << endl; + void spawnLoader(); - } + void process(); }; \ No newline at end of file diff --git a/modules/compute/PotreeData.h b/modules/compute/PotreeData.h index c5fe68c..0ac0702 100644 --- a/modules/compute/PotreeData.h +++ b/modules/compute/PotreeData.h @@ -22,12 +22,12 @@ using nlohmann::json; struct PotreeData : public Resource { - //uint32_t STEPS_30BIT = 1073741824; - //uint32_t MASK_30BIT = 1073741823; - //uint32_t STEPS_20BIT = 1048576; - //uint32_t MASK_20BIT = 1048575; - //uint32_t STEPS_10BIT = 1024; - //uint32_t MASK_10BIT = 1023; +#define STEPS_30BIT 1073741824 +#define MASK_30BIT 1073741823 +#define STEPS_20BIT 1048576 +#define MASK_20BIT 1048575 +#define STEPS_10BIT 1024 +#define MASK_10BIT 1023 struct LoaderTask{ shared_ptr buffer = nullptr; diff --git a/modules/compute_loop_las/compute_loop_las.h b/modules/compute_loop_las/compute_loop_las.h index d8469e9..9960322 100644 --- a/modules/compute_loop_las/compute_loop_las.h +++ b/modules/compute_loop_las/compute_loop_las.h @@ -12,6 +12,7 @@ #include #include "nlohmann/json.hpp" #include +#include #include "unsuck.hpp" @@ -25,7 +26,6 @@ #include "Renderer.h" #include "GLTimerQueries.h" #include "Method.h" -#include "Runtime.h" #include "compute/LasLoaderSparse.h" using namespace std; @@ -68,8 +68,10 @@ struct ComputeLoopLas : public Method{ GLBuffer ssFramebuffer; GLBuffer ssDebug; GLBuffer ssBoundingBoxes; + GLBuffer ssFiles; GLBuffer uniformBuffer; UniformData uniformData; + shared_ptr ssFilesBuffer; shared_ptr las = nullptr; @@ -90,28 +92,25 @@ struct ComputeLoopLas : public Method{ csRender = new Shader({ {"./modules/compute_loop_las/render.cs", GL_COMPUTE_SHADER} }); csResolve = new Shader({ {"./modules/compute_loop_las/resolve.cs", GL_COMPUTE_SHADER} }); + + ssFramebuffer = renderer->createBuffer(8 * 2048 * 2048); this->renderer = renderer; - ssFramebuffer = renderer->createBuffer(8 * 2048 * 2048); + ssFilesBuffer = make_shared(10'000 * 128); + ssDebug = renderer->createBuffer(256); ssBoundingBoxes = renderer->createBuffer(48 * 1'000'000); + ssFiles = renderer->createBuffer(ssFilesBuffer->size); uniformBuffer = renderer->createUniformBuffer(512); GLuint zero = 0; glClearNamedBufferData(ssDebug.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); glClearNamedBufferData(ssBoundingBoxes.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); + glClearNamedBufferData(ssFiles.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); } - - void update(Renderer* renderer){ - - if(Runtime::resource != (Resource*)las.get()){ - if(Runtime::resource != nullptr){ - Runtime::resource->unload(renderer); - } - - } + void update(Renderer* renderer){ } @@ -126,9 +125,8 @@ struct ComputeLoopLas : public Method{ } auto fbo = renderer->views[0].framebuffer; - - // Update Uniform Buffer - { + + { // Update Uniform Buffer mat4 world; mat4 view = renderer->views[0].view; mat4 proj = renderer->views[0].proj; @@ -153,6 +151,42 @@ struct ComputeLoopLas : public Method{ glNamedBufferSubData(uniformBuffer.handle, 0, sizeof(UniformData), &uniformData); } + { // update file buffer + + for(int i = 0; i < las->files.size(); i++){ + auto lasfile = las->files[i]; + + dmat4 world = glm::translate(dmat4(), lasfile->boxMin); + dmat4 view = renderer->views[0].view; + dmat4 proj = renderer->views[0].proj; + dmat4 worldView = view * world; + dmat4 worldViewProj = proj * view * world; + + mat4 transform = worldViewProj; + mat4 fWorld = world; + + memcpy( + ssFilesBuffer->data_u8 + 256 * lasfile->fileIndex + 0, + glm::value_ptr(transform), + 64); + + if(Debug::updateFrustum){ + memcpy( + ssFilesBuffer->data_u8 + 256 * lasfile->fileIndex + 64, + glm::value_ptr(transform), + 64); + } + + memcpy( + ssFilesBuffer->data_u8 + 256 * lasfile->fileIndex + 128, + glm::value_ptr(fWorld), + 64); + + } + + glNamedBufferSubData(ssFiles.handle, 0, 256 * las->files.size(), ssFilesBuffer->data); + } + if(Debug::enableShaderDebugValue){ DebugData data; data.enabled = true; @@ -177,12 +211,14 @@ struct ComputeLoopLas : public Method{ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 42, las->ssXyzMed.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 43, las->ssXyzLow.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 44, las->ssColors.handle); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 45, ssFiles.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 50, ssBoundingBoxes.handle); glBindImageTexture(0, fbo->colorAttachments[0]->handle, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8UI); - int numBatches = ceil(double(las->numPointsLoaded) / double(POINTS_PER_WORKGROUP)); + // int numBatches = ceil(double(las->numPointsLoaded) / double(POINTS_PER_WORKGROUP)); + int numBatches = las->numBatchesLoaded; glDispatchCompute(numBatches, 1, 1); @@ -196,6 +232,7 @@ struct ComputeLoopLas : public Method{ glUseProgram(csResolve->program); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssFramebuffer.handle); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 30, ssDebug.handle); glBindBufferBase(GL_UNIFORM_BUFFER, 31, uniformBuffer.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 44, las->ssColors.handle); diff --git a/modules/compute_loop_las/render.cs b/modules/compute_loop_las/render.cs index e696441..0a86773 100644 --- a/modules/compute_loop_las/render.cs +++ b/modules/compute_loop_las/render.cs @@ -30,7 +30,7 @@ struct Batch{ int numPoints; int firstPoint; - int padding2; + int fileIndex; int padding3; int padding4; int padding5; @@ -39,6 +39,15 @@ struct Batch{ int padding8; }; +// metadata for each file +// each file has its own worldViewProj matrix +struct File{ + mat4 transform; + mat4 transformFrustum; + mat4 world; + mat4 padding; +}; + struct BoundingBox{ vec4 position; // 16 0 vec4 size; // 16 16 @@ -59,6 +68,7 @@ struct Point{ layout (std430, binding = 42) buffer abc_4 { uint32_t ssXyz_8b[]; }; layout (std430, binding = 43) buffer abc_5 { uint32_t ssXyz_4b[]; }; layout (std430, binding = 44) buffer abc_6 { uint32_t ssRGBA[]; }; +layout (std430, binding = 45) buffer abc_7 { File ssFiles[]; }; layout (std430, binding = 30) buffer abc_1 { uint32_t value; @@ -70,11 +80,6 @@ struct Point{ uint32_t numPointsVisible; } debug; -// layout (std430, binding = 45) buffer data_lod { -// int numPoints; -// uint32_t ssLOD[]; -// } lod; - layout (std430, binding = 50) buffer data_bb { uint count; uint instanceCount; @@ -87,7 +92,7 @@ struct Point{ } boundingBoxes; layout(std140, binding = 31) uniform UniformData{ - mat4 world; + mat4 pad0; //mat4 world; mat4 view; mat4 proj; mat4 transform; @@ -109,64 +114,6 @@ struct Point{ 0x001c19d7 }; -// void renderLOD(Batch batch){ -// int loopSize = int(ceil(float(batch.lod_numPoints) / 128.0)); - -// for(int i = 0; i < loopSize; i++){ - -// uint localIndex = i * gl_WorkGroupSize.x + gl_LocalInvocationID.x; -// int lodPointIndex = int(batch.lod_offset + localIndex); - -// if(localIndex < batch.lod_numPoints) -// { - -// uint32_t encoded = lod.ssLOD[16 + lodPointIndex]; -// // set leftmost bit to flag as lod point -// uint32_t pointIndex = lodPointIndex | 0x80000000; - -// vec3 wgMin = vec3(batch.min_x, batch.min_y, batch.min_z); -// vec3 wgMax = vec3(batch.max_x, batch.max_y, batch.max_z); -// vec3 boxSize = wgMax - wgMin; - -// uint32_t X = (encoded >> 0) & MASK_10BIT; -// uint32_t Y = (encoded >> 10) & MASK_10BIT; -// uint32_t Z = (encoded >> 20) & MASK_10BIT; - -// float x = float(X) * (boxSize.x / STEPS_10BIT) + wgMin.x; -// float y = float(Y) * (boxSize.y / STEPS_10BIT) + wgMin.y; -// float z = float(Z) * (boxSize.z / STEPS_10BIT) + wgMin.z; - -// vec3 point = vec3(x, y, z); - -// // now project to screen -// vec4 pos = vec4(point, 1.0); -// pos = uniforms.transform * pos; -// pos.xyz = pos.xyz / pos.w; - -// bool isInsideFrustum = true; -// if(pos.w <= 0.0 || pos.x < -1.0 || pos.x > 1.0 || pos.y < -1.0 || pos.y > 1.0){ -// isInsideFrustum = false; -// } - -// if(isInsideFrustum){ -// vec2 imgPos = (pos.xy * 0.5 + 0.5) * uniforms.imageSize; -// ivec2 pixelCoords = ivec2(imgPos); -// int pixelID = pixelCoords.x + pixelCoords.y * uniforms.imageSize.x; - -// uint32_t depth = floatBitsToInt(pos.w); -// uint64_t newPoint = (uint64_t(depth) << 32UL) | pointIndex; - -// uint64_t oldPoint = ssFramebuffer[pixelID]; -// if(newPoint < oldPoint){ -// atomicMin(ssFramebuffer[pixelID], newPoint); -// } -// } -// } -// } - -// return; -// } - struct Plane{ vec3 normal; float constant; @@ -195,22 +142,40 @@ Plane createPlane(float x, float y, float z, float w){ return plane; } -Plane[6] frustumPlanes(){ +Plane[6] frustumPlanes(mat4 worldViewProj){ + + float m_0 = worldViewProj[0][0]; + float m_1 = worldViewProj[0][1]; + float m_2 = worldViewProj[0][2]; + float m_3 = worldViewProj[0][3]; + float m_4 = worldViewProj[1][0]; + float m_5 = worldViewProj[1][1]; + float m_6 = worldViewProj[1][2]; + float m_7 = worldViewProj[1][3]; + float m_8 = worldViewProj[2][0]; + float m_9 = worldViewProj[2][1]; + float m_10 = worldViewProj[2][2]; + float m_11 = worldViewProj[2][3]; + float m_12 = worldViewProj[3][0]; + float m_13 = worldViewProj[3][1]; + float m_14 = worldViewProj[3][2]; + float m_15 = worldViewProj[3][3]; + Plane planes[6] = { - createPlane(t( 3) - t(0), t( 7) - t(4), t(11) - t( 8), t(15) - t(12)), - createPlane(t( 3) + t(0), t( 7) + t(4), t(11) + t( 8), t(15) + t(12)), - createPlane(t( 3) + t(1), t( 7) + t(5), t(11) + t( 9), t(15) + t(13)), - createPlane(t( 3) - t(1), t( 7) - t(5), t(11) - t( 9), t(15) - t(13)), - createPlane(t( 3) - t(2), t( 7) - t(6), t(11) - t(10), t(15) - t(14)), - createPlane(t( 3) + t(2), t( 7) + t(6), t(11) + t(10), t(15) + t(14)), + createPlane(m_3 - m_0, m_7 - m_4, m_11 - m_8, m_15 - m_12), + createPlane(m_3 + m_0, m_7 + m_4, m_11 + m_8, m_15 + m_12), + createPlane(m_3 + m_1, m_7 + m_5, m_11 + m_9, m_15 + m_13), + createPlane(m_3 - m_1, m_7 - m_5, m_11 - m_9, m_15 - m_13), + createPlane(m_3 - m_2, m_7 - m_6, m_11 - m_10, m_15 - m_14), + createPlane(m_3 + m_2, m_7 + m_6, m_11 + m_10, m_15 + m_14), }; return planes; } -bool intersectsFrustum(vec3 wgMin, vec3 wgMax){ +bool intersectsFrustum(mat4 worldViewProj, vec3 wgMin, vec3 wgMax){ - Plane[] planes = frustumPlanes(); + Plane[] planes = frustumPlanes(worldViewProj); for(int i = 0; i < 6; i++){ @@ -231,12 +196,12 @@ bool intersectsFrustum(vec3 wgMin, vec3 wgMax){ return true; } -int getPrecisionLevel(vec3 wgMin, vec3 wgMax){ +int getPrecisionLevel(vec3 wgMin, vec3 wgMax, mat4 world){ vec3 wgCenter = (wgMin + wgMax) / 2.0; float wgRadius = distance(wgMin, wgMax); - vec4 viewCenter = uniforms.view * uniforms.world * vec4(wgCenter, 1.0); + vec4 viewCenter = uniforms.view * world * vec4(wgCenter, 1.0); vec4 viewEdge = viewCenter + vec4(wgRadius, 0.0, 0.0, 0.0); vec4 projCenter = uniforms.proj * viewCenter; @@ -269,6 +234,7 @@ void main(){ uint batchIndex = gl_WorkGroupID.x; Batch batch = ssBatches[batchIndex]; + File file = ssFiles[batch.fileIndex]; uint wgFirstPoint = batch.firstPoint; @@ -280,14 +246,12 @@ void main(){ vec3 wgMax = vec3(batch.max_x, batch.max_y, batch.max_z); vec3 boxSize = wgMax - wgMin; - // debug.numPointsProcessed = uint(boxSize.x); - // FRUSTUM CULLING - if((uniforms.enableFrustumCulling != 0) && !intersectsFrustum(wgMin, wgMax)){ + if((uniforms.enableFrustumCulling != 0) && !intersectsFrustum(file.transformFrustum, wgMin, wgMax)){ return; } - int level = getPrecisionLevel(wgMin, wgMax); + int level = getPrecisionLevel(wgMin, wgMax, file.world); // POPULATE BOUNDING BOX BUFFER, if enabled if((uniforms.showBoundingBox != 0) && gl_LocalInvocationID.x == 0){ @@ -297,8 +261,11 @@ void main(){ boundingBoxes.first = 0; boundingBoxes.baseInstance = 0; - vec3 wgPos = (wgMin + wgMax) / 2.0; - vec3 wgSize = wgMax - wgMin; + vec3 worldMin = (file.world * vec4(wgMin, 1.0)).xyz; + vec3 worldMax = (file.world * vec4(wgMax, 1.0)).xyz; + + vec3 wgPos = (worldMin + worldMax) / 2.0; + vec3 wgSize = worldMax - worldMin; uint color = 0x0000FF00; if(level > 0){ @@ -313,16 +280,8 @@ void main(){ box.color = color; boundingBoxes.ssBoxes[boxIndex] = box; - - // debug.numPointsRendered = 100000; } - // if(level > 3 && false){ - // renderLOD(batch); - - // return; - // } - if(debug.enabled && gl_LocalInvocationID.x == 0){ atomicAdd(debug.numNodesRendered, 1); } @@ -354,7 +313,7 @@ void main(){ #endif - if(localIndex > batch.numPoints){ + if(localIndex >= batch.numPoints){ return; } @@ -362,16 +321,6 @@ void main(){ atomicAdd(debug.numPointsProcessed, 1); } - // if(localIndex > 3406){ - // continue; - // } - - // if(localIndex != 3406){ - // continue; - // } - - - vec3 point; // - to reduce memory bandwidth, we load lower precision coordinates for smaller screen-size bounding boxes @@ -449,7 +398,7 @@ void main(){ // now project to screen vec4 pos = vec4(point, 1.0); - pos = uniforms.transform * pos; + pos = file.transform * pos; pos.xyz = pos.xyz / pos.w; bool isInsideFrustum = !(pos.w <= 0.0 || pos.x < -1.0 || pos.x > 1.0 || pos.y < -1.0 || pos.y > 1.0); diff --git a/modules/compute_loop_las2/compute_loop_las2.h b/modules/compute_loop_las2/compute_loop_las2.h index 4682343..5a5063d 100644 --- a/modules/compute_loop_las2/compute_loop_las2.h +++ b/modules/compute_loop_las2/compute_loop_las2.h @@ -12,6 +12,7 @@ #include #include "nlohmann/json.hpp" #include +#include #include "unsuck.hpp" @@ -31,6 +32,8 @@ using namespace std; using namespace std::chrono_literals; using nlohmann::json; +using glm::ivec2; + struct ComputeLoopLas2 : public Method{ struct UniformData{ @@ -65,8 +68,10 @@ struct ComputeLoopLas2 : public Method{ GLBuffer ssFramebuffer; GLBuffer ssDebug; GLBuffer ssBoundingBoxes; + GLBuffer ssFiles; GLBuffer uniformBuffer; UniformData uniformData; + shared_ptr ssFilesBuffer; shared_ptr las = nullptr; @@ -94,25 +99,21 @@ struct ComputeLoopLas2 : public Method{ this->renderer = renderer; + ssFilesBuffer = make_shared(10'000 * 128); + ssDebug = renderer->createBuffer(256); ssBoundingBoxes = renderer->createBuffer(48 * 1'000'000); + ssFiles = renderer->createBuffer(ssFilesBuffer->size); uniformBuffer = renderer->createUniformBuffer(512); GLuint zero = 0; glClearNamedBufferData(ssDebug.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); glClearNamedBufferData(ssBoundingBoxes.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); + glClearNamedBufferData(ssFiles.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); } void update(Renderer* renderer){ - if(Runtime::resource != (Resource*)las.get()){ - - if(Runtime::resource != nullptr){ - Runtime::resource->unload(renderer); - } - - } - } void render(Renderer* renderer) { @@ -126,9 +127,8 @@ struct ComputeLoopLas2 : public Method{ } auto fbo = renderer->views[0].framebuffer; - - // Update Uniform Buffer - { + + { // Update Uniform Buffer mat4 world; mat4 view = renderer->views[0].view; mat4 proj = renderer->views[0].proj; @@ -153,13 +153,49 @@ struct ComputeLoopLas2 : public Method{ glNamedBufferSubData(uniformBuffer.handle, 0, sizeof(UniformData), &uniformData); } + { // update file buffer + + for(int i = 0; i < las->files.size(); i++){ + auto lasfile = las->files[i]; + + dmat4 world = glm::translate(dmat4(), lasfile->boxMin); + dmat4 view = renderer->views[0].view; + dmat4 proj = renderer->views[0].proj; + dmat4 worldView = view * world; + dmat4 worldViewProj = proj * view * world; + + mat4 transform = worldViewProj; + mat4 fWorld = world; + + memcpy( + ssFilesBuffer->data_u8 + 256 * lasfile->fileIndex + 0, + glm::value_ptr(transform), + 64); + + if(Debug::updateFrustum){ + memcpy( + ssFilesBuffer->data_u8 + 256 * lasfile->fileIndex + 64, + glm::value_ptr(transform), + 64); + } + + memcpy( + ssFilesBuffer->data_u8 + 256 * lasfile->fileIndex + 128, + glm::value_ptr(fWorld), + 64); + + } + + glNamedBufferSubData(ssFiles.handle, 0, 256 * las->files.size(), ssFilesBuffer->data); + } + if(Debug::enableShaderDebugValue){ DebugData data; data.enabled = true; glNamedBufferSubData(ssDebug.handle, 0, sizeof(DebugData), &data); } - + // RENDER if(csRender->program != -1){ GLTimerQueries::timestamp("draw-start"); @@ -177,6 +213,7 @@ struct ComputeLoopLas2 : public Method{ glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 42, las->ssXyzMed.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 43, las->ssXyzLow.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 44, las->ssColors.handle); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 45, ssFiles.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 50, ssBoundingBoxes.handle); @@ -237,7 +274,6 @@ struct ComputeLoopLas2 : public Method{ renderer->drawBoundingBoxes(camera.get(), ssBoundingBoxes); } - { // CLEAR glMemoryBarrier(GL_ALL_BARRIER_BITS); diff --git a/modules/compute_loop_las2/render.cs b/modules/compute_loop_las2/render.cs index 9609681..7dabff5 100644 --- a/modules/compute_loop_las2/render.cs +++ b/modules/compute_loop_las2/render.cs @@ -30,7 +30,7 @@ struct Batch{ int numPoints; int firstPoint; - int padding2; + int fileIndex; int padding3; int padding4; int padding5; @@ -39,6 +39,15 @@ struct Batch{ int padding8; }; +// metadata for each file +// each file has its own worldViewProj matrix +struct File{ + mat4 transform; + mat4 transformFrustum; + mat4 world; + mat4 padding; +}; + struct BoundingBox{ vec4 position; // 16 0 vec4 size; // 16 16 @@ -55,10 +64,11 @@ struct Point{ layout (std430, binding = 1) buffer abc_0 { uint64_t ssFramebuffer[]; }; layout (std430, binding = 40) buffer abc_2 { Batch ssBatches[]; }; -layout (std430, binding = 41) buffer abc_3 { uvec4 ssXyz_12b[]; }; -layout (std430, binding = 42) buffer abc_4 { uvec4 ssXyz_8b[]; }; -layout (std430, binding = 43) buffer abc_5 { uvec4 ssXyz_4b[]; }; +layout (std430, binding = 41) buffer abc_3 { uvec4 ssXyz_12b[]; }; +layout (std430, binding = 42) buffer abc_4 { uvec4 ssXyz_8b[]; }; +layout (std430, binding = 43) buffer abc_5 { uvec4 ssXyz_4b[]; }; layout (std430, binding = 44) buffer abc_6 { uint32_t ssRGBA[]; }; +layout (std430, binding = 45) buffer abc_7 { File ssFiles[]; }; layout (std430, binding = 30) buffer abc_1 { uint32_t value; @@ -82,7 +92,7 @@ struct Point{ } boundingBoxes; layout(std140, binding = 31) uniform UniformData{ - mat4 world; + mat4 pad0; //mat4 world; mat4 view; mat4 proj; mat4 transform; @@ -92,6 +102,8 @@ struct Point{ int showBoundingBox; int numPoints; ivec2 imageSize; + bool colorizeChunks; + bool colorizeOverdraw; } uniforms; uint SPECTRAL[5] = { @@ -130,22 +142,40 @@ Plane createPlane(float x, float y, float z, float w){ return plane; } -Plane[6] frustumPlanes(){ +Plane[6] frustumPlanes(mat4 worldViewProj){ + + float m_0 = worldViewProj[0][0]; + float m_1 = worldViewProj[0][1]; + float m_2 = worldViewProj[0][2]; + float m_3 = worldViewProj[0][3]; + float m_4 = worldViewProj[1][0]; + float m_5 = worldViewProj[1][1]; + float m_6 = worldViewProj[1][2]; + float m_7 = worldViewProj[1][3]; + float m_8 = worldViewProj[2][0]; + float m_9 = worldViewProj[2][1]; + float m_10 = worldViewProj[2][2]; + float m_11 = worldViewProj[2][3]; + float m_12 = worldViewProj[3][0]; + float m_13 = worldViewProj[3][1]; + float m_14 = worldViewProj[3][2]; + float m_15 = worldViewProj[3][3]; + Plane planes[6] = { - createPlane(t( 3) - t(0), t( 7) - t(4), t(11) - t( 8), t(15) - t(12)), - createPlane(t( 3) + t(0), t( 7) + t(4), t(11) + t( 8), t(15) + t(12)), - createPlane(t( 3) + t(1), t( 7) + t(5), t(11) + t( 9), t(15) + t(13)), - createPlane(t( 3) - t(1), t( 7) - t(5), t(11) - t( 9), t(15) - t(13)), - createPlane(t( 3) - t(2), t( 7) - t(6), t(11) - t(10), t(15) - t(14)), - createPlane(t( 3) + t(2), t( 7) + t(6), t(11) + t(10), t(15) + t(14)), + createPlane(m_3 - m_0, m_7 - m_4, m_11 - m_8, m_15 - m_12), + createPlane(m_3 + m_0, m_7 + m_4, m_11 + m_8, m_15 + m_12), + createPlane(m_3 + m_1, m_7 + m_5, m_11 + m_9, m_15 + m_13), + createPlane(m_3 - m_1, m_7 - m_5, m_11 - m_9, m_15 - m_13), + createPlane(m_3 - m_2, m_7 - m_6, m_11 - m_10, m_15 - m_14), + createPlane(m_3 + m_2, m_7 + m_6, m_11 + m_10, m_15 + m_14), }; return planes; } -bool intersectsFrustum(vec3 wgMin, vec3 wgMax){ +bool intersectsFrustum(mat4 worldViewProj, vec3 wgMin, vec3 wgMax){ - Plane[] planes = frustumPlanes(); + Plane[] planes = frustumPlanes(worldViewProj); for(int i = 0; i < 6; i++){ @@ -166,12 +196,12 @@ bool intersectsFrustum(vec3 wgMin, vec3 wgMax){ return true; } -int getPrecisionLevel(vec3 wgMin, vec3 wgMax){ +int getPrecisionLevel(vec3 wgMin, vec3 wgMax, mat4 world){ vec3 wgCenter = (wgMin + wgMax) / 2.0; float wgRadius = distance(wgMin, wgMax); - vec4 viewCenter = uniforms.view * uniforms.world * vec4(wgCenter, 1.0); + vec4 viewCenter = uniforms.view * world * vec4(wgCenter, 1.0); vec4 viewEdge = viewCenter + vec4(wgRadius, 0.0, 0.0, 0.0); vec4 projCenter = uniforms.proj * viewCenter; @@ -200,10 +230,10 @@ int getPrecisionLevel(vec3 wgMin, vec3 wgMax){ return level; } -void rasterize(vec3 point, uint index) { +void rasterize(vec3 point, uint index, mat4 transform) { vec4 pos = vec4(point, 1.0f); - pos = uniforms.transform * pos; + pos = transform * pos; pos.xy = pos.xy / pos.w; vec2 imgPos = (pos.xy * 0.5f + 0.5f) * uniforms.imageSize; @@ -230,6 +260,7 @@ void main(){ uint batchIndex = gl_WorkGroupID.x; Batch batch = ssBatches[batchIndex]; + File file = ssFiles[batch.fileIndex]; uint wgFirstPoint = batch.firstPoint; @@ -242,11 +273,11 @@ void main(){ vec3 boxSize = wgMax - wgMin; // FRUSTUM CULLING - if((uniforms.enableFrustumCulling != 0) && !intersectsFrustum(wgMin, wgMax)){ + if((uniforms.enableFrustumCulling != 0) && !intersectsFrustum(file.transformFrustum, wgMin, wgMax)){ return; } - int level = getPrecisionLevel(wgMin, wgMax); + int level = getPrecisionLevel(wgMin, wgMax, file.world); // POPULATE BOUNDING BOX BUFFER, if enabled if((uniforms.showBoundingBox != 0) && gl_LocalInvocationID.x == 0){ @@ -256,8 +287,11 @@ void main(){ boundingBoxes.first = 0; boundingBoxes.baseInstance = 0; - vec3 wgPos = (wgMin + wgMax) / 2.0; - vec3 wgSize = wgMax - wgMin; + vec3 worldMin = (file.world * vec4(wgMin, 1.0)).xyz; + vec3 worldMax = (file.world * vec4(wgMax, 1.0)).xyz; + + vec3 wgPos = (worldMin + worldMax) / 2.0; + vec3 wgSize = worldMax - worldMin; uint color = 0x0000FF00; if(level > 0){ @@ -279,6 +313,8 @@ void main(){ } int loopSize = uniforms.pointsPerThread; + + // level = 4; if(level == 0){ uint base = wgFirstPoint / 4 + gl_LocalInvocationID.x; @@ -301,6 +337,11 @@ void main(){ for (int j = 0; j < 4; j++){ uint index = 4 * (base + i * gl_WorkGroupSize.x) + j; + uint localIndex = index - wgFirstPoint; + if(localIndex > batch.numPoints){ + return; + } + uint32_t b4 = encodedw_4b[j]; uint32_t b8 = encodedw_8b[j]; uint32_t b12 = encodedw_12b[j]; @@ -331,7 +372,7 @@ void main(){ } // now rasterize to screen - rasterize(point, index); + rasterize(point, index, file.transform); } } }else if(level == 1){ @@ -352,6 +393,11 @@ void main(){ for (int j = 0; j < 4; j++){ uint index = 4 * (base + i * gl_WorkGroupSize.x) + j; + uint localIndex = index - wgFirstPoint; + if(localIndex > batch.numPoints){ + return; + } + uint32_t b4 = encodedw_4b[j]; uint32_t b8 = encodedw_8b[j]; @@ -377,7 +423,7 @@ void main(){ } // now rasterize to screen - rasterize(point, index); + rasterize(point, index, file.transform); } } @@ -395,7 +441,13 @@ void main(){ for (int j = 0; j < 4; j++){ uint index = 4 * (base + i * gl_WorkGroupSize.x) + j; + uint32_t encoded = encodedw[j]; + + uint localIndex = index - wgFirstPoint; + if(localIndex > batch.numPoints){ + return; + } uint32_t X = (encoded >> 0) & MASK_10BIT; uint32_t Y = (encoded >> 10) & MASK_10BIT; @@ -411,7 +463,7 @@ void main(){ } // now rasterize to screen - rasterize(point, index); + rasterize(point, index, file.transform); } } } diff --git a/modules/compute_loop_las_hqs/color.cs b/modules/compute_loop_las_hqs/color.cs index 39d3366..ef71854 100644 --- a/modules/compute_loop_las_hqs/color.cs +++ b/modules/compute_loop_las_hqs/color.cs @@ -34,7 +34,7 @@ struct Batch{ int numPoints; int firstPoint; - int padding2; + int fileIndex; int padding3; int padding4; int padding5; @@ -43,6 +43,15 @@ struct Batch{ int padding8; }; +// metadata for each file +// each file has its own worldViewProj matrix +struct File{ + mat4 transform; + mat4 transformFrustum; + mat4 world; + mat4 padding; +}; + struct BoundingBox{ vec4 position; // 16 0 vec4 size; // 16 16 @@ -64,6 +73,7 @@ struct Point{ layout (std430, binding = 42) buffer abc_4 { uint32_t ssXyz_8b[]; }; layout (std430, binding = 43) buffer abc_5 { uint32_t ssXyz_4b[]; }; layout (std430, binding = 44) buffer abc_6 { uint32_t ssRGBA[]; }; +layout (std430, binding = 45) buffer abc_7 { File ssFiles[]; }; layout (std430, binding = 30) buffer abc_1 { uint32_t value; @@ -86,12 +96,12 @@ struct Point{ uint baseInstance; uint pad0; uint pad1; uint pad2; uint pad3; uint pad4; uint pad5; uint pad6; uint pad7; - // 48 + // offset: 48 BoundingBox ssBoxes[]; } boundingBoxes; layout(std140, binding = 31) uniform UniformData{ - mat4 world; + mat4 pad0; //mat4 world; mat4 view; mat4 proj; mat4 transform; @@ -141,22 +151,40 @@ Plane createPlane(float x, float y, float z, float w){ return plane; } -Plane[6] frustumPlanes(){ +Plane[6] frustumPlanes(mat4 worldViewProj){ + + float m_0 = worldViewProj[0][0]; + float m_1 = worldViewProj[0][1]; + float m_2 = worldViewProj[0][2]; + float m_3 = worldViewProj[0][3]; + float m_4 = worldViewProj[1][0]; + float m_5 = worldViewProj[1][1]; + float m_6 = worldViewProj[1][2]; + float m_7 = worldViewProj[1][3]; + float m_8 = worldViewProj[2][0]; + float m_9 = worldViewProj[2][1]; + float m_10 = worldViewProj[2][2]; + float m_11 = worldViewProj[2][3]; + float m_12 = worldViewProj[3][0]; + float m_13 = worldViewProj[3][1]; + float m_14 = worldViewProj[3][2]; + float m_15 = worldViewProj[3][3]; + Plane planes[6] = { - createPlane(t( 3) - t(0), t( 7) - t(4), t(11) - t( 8), t(15) - t(12)), - createPlane(t( 3) + t(0), t( 7) + t(4), t(11) + t( 8), t(15) + t(12)), - createPlane(t( 3) + t(1), t( 7) + t(5), t(11) + t( 9), t(15) + t(13)), - createPlane(t( 3) - t(1), t( 7) - t(5), t(11) - t( 9), t(15) - t(13)), - createPlane(t( 3) - t(2), t( 7) - t(6), t(11) - t(10), t(15) - t(14)), - createPlane(t( 3) + t(2), t( 7) + t(6), t(11) + t(10), t(15) + t(14)), + createPlane(m_3 - m_0, m_7 - m_4, m_11 - m_8, m_15 - m_12), + createPlane(m_3 + m_0, m_7 + m_4, m_11 + m_8, m_15 + m_12), + createPlane(m_3 + m_1, m_7 + m_5, m_11 + m_9, m_15 + m_13), + createPlane(m_3 - m_1, m_7 - m_5, m_11 - m_9, m_15 - m_13), + createPlane(m_3 - m_2, m_7 - m_6, m_11 - m_10, m_15 - m_14), + createPlane(m_3 + m_2, m_7 + m_6, m_11 + m_10, m_15 + m_14), }; return planes; } -bool intersectsFrustum(vec3 wgMin, vec3 wgMax){ +bool intersectsFrustum(mat4 worldViewProj, vec3 wgMin, vec3 wgMax){ - Plane[] planes = frustumPlanes(); + Plane[] planes = frustumPlanes(worldViewProj); for(int i = 0; i < 6; i++){ @@ -177,12 +205,12 @@ bool intersectsFrustum(vec3 wgMin, vec3 wgMax){ return true; } -int getPrecisionLevel(vec3 wgMin, vec3 wgMax){ +int getPrecisionLevel(vec3 wgMin, vec3 wgMax, mat4 world){ vec3 wgCenter = (wgMin + wgMax) / 2.0; float wgRadius = distance(wgMin, wgMax); - vec4 viewCenter = uniforms.view * uniforms.world * vec4(wgCenter, 1.0); + vec4 viewCenter = uniforms.view * world * vec4(wgCenter, 1.0); vec4 viewEdge = viewCenter + vec4(wgRadius, 0.0, 0.0, 0.0); vec4 projCenter = uniforms.proj * viewCenter; @@ -191,17 +219,21 @@ int getPrecisionLevel(vec3 wgMin, vec3 wgMax){ projCenter.xy = projCenter.xy / projCenter.w; projEdge.xy = projEdge.xy / projEdge.w; - float w = distance(projCenter.xy, projEdge.xy); + vec2 screenCenter = uniforms.imageSize.xy * (projCenter.xy + 1.0) / 2.0; + vec2 screenEdge = uniforms.imageSize.xy * (projEdge.xy + 1.0) / 2.0; + float pixelSize = distance(screenEdge, screenCenter); int level = 0; - if(w < 0.01){ + if(pixelSize < 100){ level = 4; - }else if(w < 0.02){ + }else if(pixelSize < 200){ level = 3; - }else if(w < 0.05){ + }else if(pixelSize < 500){ level = 2; - }else if(w < 0.1){ + }else if(pixelSize < 10000){ level = 1; + }else{ + level = 0; } return level; @@ -211,6 +243,7 @@ void main(){ uint batchIndex = gl_WorkGroupID.x; Batch batch = ssBatches[batchIndex]; + File file = ssFiles[batch.fileIndex]; uint wgFirstPoint = batch.firstPoint; @@ -223,13 +256,11 @@ void main(){ vec3 boxSize = wgMax - wgMin; // FRUSTUM CULLING - if((uniforms.enableFrustumCulling != 0) && !intersectsFrustum(wgMin, wgMax)){ + if((uniforms.enableFrustumCulling != 0) && !intersectsFrustum(file.transformFrustum, wgMin, wgMax)){ return; } - int level = getPrecisionLevel(wgMin, wgMax); - - // level = 0; + int level = getPrecisionLevel(wgMin, wgMax, file.world); // POPULATE BOUNDING BOX BUFFER, if enabled if((uniforms.showBoundingBox != 0) && gl_LocalInvocationID.x == 0){ @@ -239,8 +270,11 @@ void main(){ boundingBoxes.first = 0; boundingBoxes.baseInstance = 0; - vec3 wgPos = (wgMin + wgMax) / 2.0; - vec3 wgSize = wgMax - wgMin; + vec3 worldMin = (file.world * vec4(wgMin, 1.0)).xyz; + vec3 worldMax = (file.world * vec4(wgMax, 1.0)).xyz; + + vec3 wgPos = (worldMin + worldMax) / 2.0; + vec3 wgSize = worldMax - worldMin; uint color = 0x0000FF00; if(level > 0){ @@ -260,13 +294,14 @@ void main(){ if(debug.enabled && gl_LocalInvocationID.x == 0){ atomicAdd(debug.color_numNodesRendered, 1); } - + int loopSize = uniforms.pointsPerThread; for(int i = 0; i < loopSize; i++){ - uint index = wgFirstPoint + i * gl_WorkGroupSize.x + gl_LocalInvocationID.x; + uint localIndex = i * gl_WorkGroupSize.x + gl_LocalInvocationID.x; + uint index = wgFirstPoint + localIndex; - if(index > uniforms.numPoints){ + if(localIndex > batch.numPoints){ return; } @@ -351,7 +386,7 @@ void main(){ // now project to screen vec4 pos = vec4(point, 1.0); - pos = uniforms.transform * pos; + pos = file.transform * pos; pos.xyz = pos.xyz / pos.w; bool isInsideFrustum = !(pos.w <= 0.0 || pos.x < -1.0 || pos.x > 1.0 || pos.y < -1.0 || pos.y > 1.0); diff --git a/modules/compute_loop_las_hqs/compute_loop_las_hqs.h b/modules/compute_loop_las_hqs/compute_loop_las_hqs.h index c42d108..e097a9d 100644 --- a/modules/compute_loop_las_hqs/compute_loop_las_hqs.h +++ b/modules/compute_loop_las_hqs/compute_loop_las_hqs.h @@ -12,6 +12,7 @@ #include #include "nlohmann/json.hpp" #include +#include #include "unsuck.hpp" @@ -31,6 +32,8 @@ using namespace std; using namespace std::chrono_literals; using nlohmann::json; +using glm::ivec2; + struct ComputeLoopLasHqs : public Method{ struct UniformData{ @@ -71,8 +74,10 @@ struct ComputeLoopLasHqs : public Method{ GLBuffer ssRGBA; GLBuffer ssDebug; GLBuffer ssBoundingBoxes; + GLBuffer ssFiles; GLBuffer uniformBuffer; UniformData uniformData; + shared_ptr ssFilesBuffer; shared_ptr las = nullptr; @@ -95,27 +100,23 @@ averages overlapping points ssDepth = renderer->createBuffer(4 * 2048 * 2048); ssRGBA = renderer->createBuffer(16 * 2048 * 2048); + this->renderer = renderer; + + ssFilesBuffer = make_shared(10'000 * 128); + ssDebug = renderer->createBuffer(256); ssBoundingBoxes = renderer->createBuffer(48 * 1'000'000); + ssFiles = renderer->createBuffer(ssFilesBuffer->size); uniformBuffer = renderer->createUniformBuffer(512); GLuint zero = 0; glClearNamedBufferData(ssDebug.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); glClearNamedBufferData(ssBoundingBoxes.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); - - this->renderer = renderer; + glClearNamedBufferData(ssFiles.handle, GL_R32UI, GL_RED, GL_UNSIGNED_INT, &zero); } void update(Renderer* renderer){ - // if(Runtime::resource != (Resource*)las.get()){ - - // if(Runtime::resource != nullptr){ - // Runtime::resource->unload(renderer); - // } - - // } - } void render(Renderer* renderer) { @@ -130,8 +131,7 @@ averages overlapping points auto fbo = renderer->views[0].framebuffer; - // Update Uniform Buffer - { + { // Update Uniform Buffer mat4 world; mat4 view = renderer->views[0].view; mat4 proj = renderer->views[0].proj; @@ -156,6 +156,42 @@ averages overlapping points glNamedBufferSubData(uniformBuffer.handle, 0, sizeof(UniformData), &uniformData); } + { // update file buffer + + for(int i = 0; i < las->files.size(); i++){ + auto lasfile = las->files[i]; + + dmat4 world = glm::translate(dmat4(), lasfile->boxMin); + dmat4 view = renderer->views[0].view; + dmat4 proj = renderer->views[0].proj; + dmat4 worldView = view * world; + dmat4 worldViewProj = proj * view * world; + + mat4 transform = worldViewProj; + mat4 fWorld = world; + + memcpy( + ssFilesBuffer->data_u8 + 256 * lasfile->fileIndex + 0, + glm::value_ptr(transform), + 64); + + if(Debug::updateFrustum){ + memcpy( + ssFilesBuffer->data_u8 + 256 * lasfile->fileIndex + 64, + glm::value_ptr(transform), + 64); + } + + memcpy( + ssFilesBuffer->data_u8 + 256 * lasfile->fileIndex + 128, + glm::value_ptr(fWorld), + 64); + + } + + glNamedBufferSubData(ssFiles.handle, 0, 256 * las->files.size(), ssFilesBuffer->data); + } + if(Debug::enableShaderDebugValue){ DebugData data; data.enabled = true; @@ -179,6 +215,7 @@ averages overlapping points glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 42, las->ssXyzMed.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 43, las->ssXyzLow.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 44, las->ssColors.handle); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 45, ssFiles.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 50, ssBoundingBoxes.handle); @@ -207,7 +244,8 @@ averages overlapping points glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 42, las->ssXyzMed.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 43, las->ssXyzLow.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 44, las->ssColors.handle); - + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 45, ssFiles.handle); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 50, ssBoundingBoxes.handle); glBindImageTexture(0, fbo->colorAttachments[0]->handle, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8UI); @@ -220,8 +258,7 @@ averages overlapping points } // RESOLVE - if(csResolve->program != -1){ - + if(csResolve->program != -1){ GLTimerQueries::timestamp("resolve-start"); glUseProgram(csResolve->program); @@ -230,7 +267,6 @@ averages overlapping points glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, ssRGBA.handle); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 30, ssDebug.handle); glBindBufferBase(GL_UNIFORM_BUFFER, 31, uniformBuffer.handle); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 44, las->ssColors.handle); glBindImageTexture(0, fbo->colorAttachments[0]->handle, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8UI); @@ -239,7 +275,6 @@ averages overlapping points int groups_y = ceil(float(fbo->height) / 16.0f); glDispatchCompute(groups_x, groups_y, 1); - glMemoryBarrier(GL_ALL_BARRIER_BITS); GLTimerQueries::timestamp("resolve-end"); @@ -252,8 +287,6 @@ averages overlapping points DebugData data; glGetNamedBufferSubData(ssDebug.handle, 0, sizeof(DebugData), &data); - // Debug::getInstance()->values["debug value"] = formatNumber(data.value); - auto dbg = Debug::getInstance(); dbg->pushFrameStat("[depth] #nodes processed" , formatNumber(data.depth_numNodesProcessed)); diff --git a/modules/compute_loop_las_hqs/depth.cs b/modules/compute_loop_las_hqs/depth.cs index 694a249..deb7828 100644 --- a/modules/compute_loop_las_hqs/depth.cs +++ b/modules/compute_loop_las_hqs/depth.cs @@ -23,7 +23,6 @@ #define Infinity (1.0 / 0.0) - struct Batch{ int state; float min_x; @@ -35,7 +34,7 @@ struct Batch{ int numPoints; int firstPoint; - int padding2; + int fileIndex; int padding3; int padding4; int padding5; @@ -44,6 +43,14 @@ struct Batch{ int padding8; }; +// metadata for each file +// each file has its own worldViewProj matrix +struct File{ + mat4 transform; + mat4 transformFrustum; + mat4 world; + mat4 padding; +}; struct BoundingBox{ vec4 position; // 16 0 @@ -66,6 +73,7 @@ struct Point{ layout (std430, binding = 42) buffer abc_4 { uint32_t ssXyz_8b[]; }; layout (std430, binding = 43) buffer abc_5 { uint32_t ssXyz_4b[]; }; layout (std430, binding = 44) buffer abc_6 { uint32_t ssRGBA[]; }; +layout (std430, binding = 45) buffer abc_7 { File ssFiles[]; }; layout (std430, binding = 30) buffer abc_1 { uint32_t value; @@ -88,12 +96,12 @@ struct Point{ uint baseInstance; uint pad0; uint pad1; uint pad2; uint pad3; uint pad4; uint pad5; uint pad6; uint pad7; - // 48 + // offset: 48 BoundingBox ssBoxes[]; } boundingBoxes; layout(std140, binding = 31) uniform UniformData{ - mat4 world; + mat4 pad0; //mat4 world; mat4 view; mat4 proj; mat4 transform; @@ -115,7 +123,6 @@ struct Point{ 0x001c19d7 }; - struct Plane{ vec3 normal; float constant; @@ -144,22 +151,40 @@ Plane createPlane(float x, float y, float z, float w){ return plane; } -Plane[6] frustumPlanes(){ +Plane[6] frustumPlanes(mat4 worldViewProj){ + + float m_0 = worldViewProj[0][0]; + float m_1 = worldViewProj[0][1]; + float m_2 = worldViewProj[0][2]; + float m_3 = worldViewProj[0][3]; + float m_4 = worldViewProj[1][0]; + float m_5 = worldViewProj[1][1]; + float m_6 = worldViewProj[1][2]; + float m_7 = worldViewProj[1][3]; + float m_8 = worldViewProj[2][0]; + float m_9 = worldViewProj[2][1]; + float m_10 = worldViewProj[2][2]; + float m_11 = worldViewProj[2][3]; + float m_12 = worldViewProj[3][0]; + float m_13 = worldViewProj[3][1]; + float m_14 = worldViewProj[3][2]; + float m_15 = worldViewProj[3][3]; + Plane planes[6] = { - createPlane(t( 3) - t(0), t( 7) - t(4), t(11) - t( 8), t(15) - t(12)), - createPlane(t( 3) + t(0), t( 7) + t(4), t(11) + t( 8), t(15) + t(12)), - createPlane(t( 3) + t(1), t( 7) + t(5), t(11) + t( 9), t(15) + t(13)), - createPlane(t( 3) - t(1), t( 7) - t(5), t(11) - t( 9), t(15) - t(13)), - createPlane(t( 3) - t(2), t( 7) - t(6), t(11) - t(10), t(15) - t(14)), - createPlane(t( 3) + t(2), t( 7) + t(6), t(11) + t(10), t(15) + t(14)), + createPlane(m_3 - m_0, m_7 - m_4, m_11 - m_8, m_15 - m_12), + createPlane(m_3 + m_0, m_7 + m_4, m_11 + m_8, m_15 + m_12), + createPlane(m_3 + m_1, m_7 + m_5, m_11 + m_9, m_15 + m_13), + createPlane(m_3 - m_1, m_7 - m_5, m_11 - m_9, m_15 - m_13), + createPlane(m_3 - m_2, m_7 - m_6, m_11 - m_10, m_15 - m_14), + createPlane(m_3 + m_2, m_7 + m_6, m_11 + m_10, m_15 + m_14), }; return planes; } -bool intersectsFrustum(vec3 wgMin, vec3 wgMax){ +bool intersectsFrustum(mat4 worldViewProj, vec3 wgMin, vec3 wgMax){ - Plane[] planes = frustumPlanes(); + Plane[] planes = frustumPlanes(worldViewProj); for(int i = 0; i < 6; i++){ @@ -180,12 +205,12 @@ bool intersectsFrustum(vec3 wgMin, vec3 wgMax){ return true; } -int getPrecisionLevel(vec3 wgMin, vec3 wgMax){ +int getPrecisionLevel(vec3 wgMin, vec3 wgMax, mat4 world){ vec3 wgCenter = (wgMin + wgMax) / 2.0; float wgRadius = distance(wgMin, wgMax); - vec4 viewCenter = uniforms.view * uniforms.world * vec4(wgCenter, 1.0); + vec4 viewCenter = uniforms.view * world * vec4(wgCenter, 1.0); vec4 viewEdge = viewCenter + vec4(wgRadius, 0.0, 0.0, 0.0); vec4 projCenter = uniforms.proj * viewCenter; @@ -194,17 +219,21 @@ int getPrecisionLevel(vec3 wgMin, vec3 wgMax){ projCenter.xy = projCenter.xy / projCenter.w; projEdge.xy = projEdge.xy / projEdge.w; - float w = distance(projCenter.xy, projEdge.xy); + vec2 screenCenter = uniforms.imageSize.xy * (projCenter.xy + 1.0) / 2.0; + vec2 screenEdge = uniforms.imageSize.xy * (projEdge.xy + 1.0) / 2.0; + float pixelSize = distance(screenEdge, screenCenter); int level = 0; - if(w < 0.01){ + if(pixelSize < 100){ level = 4; - }else if(w < 0.02){ + }else if(pixelSize < 200){ level = 3; - }else if(w < 0.05){ + }else if(pixelSize < 500){ level = 2; - }else if(w < 0.1){ + }else if(pixelSize < 10000){ level = 1; + }else{ + level = 0; } return level; @@ -214,6 +243,7 @@ void main(){ uint batchIndex = gl_WorkGroupID.x; Batch batch = ssBatches[batchIndex]; + File file = ssFiles[batch.fileIndex]; uint wgFirstPoint = batch.firstPoint; @@ -226,12 +256,12 @@ void main(){ vec3 boxSize = wgMax - wgMin; // FRUSTUM CULLING - if((uniforms.enableFrustumCulling != 0) && !intersectsFrustum(wgMin, wgMax)){ + if((uniforms.enableFrustumCulling != 0) && !intersectsFrustum(file.transformFrustum, wgMin, wgMax)){ return; } - int level = getPrecisionLevel(wgMin, wgMax); - // level = 0; + int level = getPrecisionLevel(wgMin, wgMax, file.world); + if(debug.enabled && gl_LocalInvocationID.x == 0){ atomicAdd(debug.depth_numNodesRendered, 1); @@ -240,9 +270,10 @@ void main(){ int loopSize = uniforms.pointsPerThread; for(int i = 0; i < loopSize; i++){ - uint index = wgFirstPoint + i * gl_WorkGroupSize.x + gl_LocalInvocationID.x; + uint localIndex = i * gl_WorkGroupSize.x + gl_LocalInvocationID.x; + uint index = wgFirstPoint + localIndex; - if(index > uniforms.numPoints){ + if(localIndex > batch.numPoints){ return; } @@ -250,7 +281,7 @@ void main(){ atomicAdd(debug.depth_numPointsProcessed, 1); } - vec3 point; + vec3 point; // - to reduce memory bandwidth, we load lower precision coordinates for smaller screen-size bounding boxes // - coordinates are stored in up to three 4 byte values @@ -327,7 +358,7 @@ void main(){ // now project to screen vec4 pos = vec4(point, 1.0); - pos = uniforms.transform * pos; + pos = file.transform * pos; pos.xyz = pos.xyz / pos.w; if(uniforms.colorizeOverdraw){ diff --git a/modules/compute_loop_las_hqs_vr/compute_loop_las_hqs_vr.h b/modules/compute_loop_las_hqs_vr/compute_loop_las_hqs_vr.h index 428f419..c966420 100644 --- a/modules/compute_loop_las_hqs_vr/compute_loop_las_hqs_vr.h +++ b/modules/compute_loop_las_hqs_vr/compute_loop_las_hqs_vr.h @@ -26,6 +26,7 @@ #include "GLTimerQueries.h" #include "Method.h" #include "compute/ComputeLasLoader.h" +#include "Runtime.h" using namespace std; using namespace std::chrono_literals; diff --git a/src/Renderer.cpp b/src/Renderer.cpp index 1971f13..02d2739 100644 --- a/src/Renderer.cpp +++ b/src/Renderer.cpp @@ -126,19 +126,20 @@ void Renderer::init(){ GLFWmonitor** monitors = glfwGetMonitors(&numMonitors); cout << "" << endl; - // if (numMonitors > 1) { - // const GLFWvidmode * modeLeft = glfwGetVideoMode(monitors[0]); - // const GLFWvidmode * modeRight = glfwGetVideoMode(monitors[1]); + //if (numMonitors > 1) { + // const GLFWvidmode * modeLeft = glfwGetVideoMode(monitors[0]); + // const GLFWvidmode * modeRight = glfwGetVideoMode(monitors[1]); - // window = glfwCreateWindow(modeRight->width, modeRight->height - 300, "Simple example", nullptr, nullptr); + // window = glfwCreateWindow(modeRight->width, modeRight->height - 300, "Simple example", nullptr, nullptr); - // if (!window) { - // glfwTerminate(); - // exit(EXIT_FAILURE); - // } + // if (!window) { + // glfwTerminate(); + // exit(EXIT_FAILURE); + // } - // glfwSetWindowPos(window, modeLeft->width, 0); - // } else + // glfwSetWindowPos(window, modeLeft->width, 0); + //} + //else { const GLFWvidmode * mode = glfwGetVideoMode(monitors[0]); @@ -362,7 +363,7 @@ void Renderer::loop(function update, function render){ auto windowSize_infos = ImVec2(490, 240); auto windowSize_perf = ImVec2(490, 340); - auto windowSize_datasets = ImVec2(490, 160); + auto windowSize_datasets = ImVec2(490, 260); auto windowSize_debug = ImVec2(490, 200); auto windowSize_state = ImVec2(370, 320); @@ -630,8 +631,6 @@ the fast software-rasterization of point clouds.)ER01"); Make sure you have about 2GB + 16 byte per point of GPU memory. (e.g. about 18GB GPU memory for 1 billion points) - Maximum of 1 billion points. -- All data sets transformed to origin. - (can't load multiple tiles relative to same origin) )ER01"); ImGui::Text("URL: https://github.com/m-schuetz/compute_rasterizer"); @@ -748,7 +747,7 @@ the fast software-rasterization of point clouds.)ER01"); int numItems = lasfiles == nullptr ? 0 : lasfiles->files.size(); ImGui::Text("Point Clouds:"); - if (ImGui::BeginListBox("##listbox 3", ImVec2(-FLT_MIN, (4) * ImGui::GetTextLineHeightWithSpacing()))){ + if (ImGui::BeginListBox("##listbox 3", ImVec2(-FLT_MIN, (11) * ImGui::GetTextLineHeightWithSpacing()))){ for (int n = 0; n < numItems; n++){ const bool is_selected = (item_current_idx == n); diff --git a/src/main.cpp b/src/main.cpp index 5c01ce6..82008d0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -239,23 +239,35 @@ int main(){ Runtime::lasLoaderSparse = lasLoaderSparse; renderer->onFileDrop([lasLoaderSparse, renderer](vector files){ - //cout << "files: " << files.size() << endl; + + vector lasfiles; for(auto file : files){ - if(iEndsWith(file, "las")){ - auto lasfile = lasLoaderSparse->add(file); + if(iEndsWith(file, "las") || iEndsWith(file, "laz")){ + lasfiles.push_back(file); + } + } - // zoom to point cloud - auto size = lasfile->boxMax - lasfile->boxMin; - auto position = size / 2.0; - auto radius = glm::length(size) / 1.5; + lasLoaderSparse->add(lasfiles, [renderer](vector> lasfiles){ - renderer->controls->yaw = 0.53; - renderer->controls->pitch = -0.68; - renderer->controls->radius = radius; - renderer->controls->target = position; + dvec3 boxMin = {Infinity, Infinity, Infinity}; + dvec3 boxMax = {-Infinity, -Infinity, -Infinity}; + + for(auto lasfile : lasfiles){ + boxMin = glm::min(boxMin, lasfile->boxMin); + boxMax = glm::max(boxMax, lasfile->boxMax); } - } + + // zoom to point cloud + auto size = boxMax - boxMin; + auto position = (boxMax + boxMin) / 2.0; + auto radius = glm::length(size) / 1.5; + + renderer->controls->yaw = 0.53; + renderer->controls->pitch = -0.68; + renderer->controls->radius = radius; + renderer->controls->target = position; + }); glfwFocusWindow(renderer->window); }); @@ -351,7 +363,7 @@ int main(){ if(lasfile->isDoubleClicked){ auto size = lasfile->boxMax - lasfile->boxMin; - auto position = size / 2.0; + auto position = (lasfile->boxMax + lasfile->boxMin) / 2.0; auto radius = glm::length(size) / 1.5; renderer->controls->yaw = 0.53; @@ -448,7 +460,7 @@ int main(){ for(auto lasfile : Runtime::lasLoaderSparse->files){ dvec3 size = lasfile->boxMax - lasfile->boxMin; - dvec3 position = size / 2.0; + dvec3 position = (lasfile->boxMax + lasfile->boxMin) / 2.0; if(lasfile->isHovered){ renderer->drawBoundingBox(position, size, {255, 255, 0});