diff -Nru lerc-3.0+ds/CHANGELOG.md lerc-4.0.0+ds/CHANGELOG.md --- lerc-3.0+ds/CHANGELOG.md 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/CHANGELOG.md 2022-07-15 18:25:29.000000000 +0000 @@ -7,6 +7,34 @@ ## [Unreleased][unreleased] +## [4.0.0](https://github.com/Esri/lerc/releases/tag/v4.0.0) - 2022-07-15 + +### Milestones reached +- Incremented Lerc version to 4.0 as the new, wasm based JavaScript decoder may not work on older browsers such as IE. +- Incremented Lerc version to 3.1. All existing 3.0 functions work same as before. +- Incremented Lerc codec version to v2.6. +- This LERC API and all language decoders (C++, C, C#, Python, JavaScript) and encoders (C++, C, Python) are now in sync with ESRI ArcGIS Pro 3.0. + +### Added + +* New, additional LERC API functions *_4D(). Main reason for this upgrade is to support special cases of void or invalid data not supported yet. For 3D or 4D data with an array of values per pixel, and some of such values valid and others invalid, the new functions allow to pass one noData value per band to represent such "mixed cases". There is no change to the valid / invalid byte masks that mark each pixel as valid or invalid. There can be one mask per band, one mask for all bands, or no mask. For more details see the [Readme.md](https://github.com/Esri/lerc/blob/master/README.md), [Lerc_c_api.h](https://github.com/Esri/lerc/blob/master/src/LercLib/include/Lerc_c_api.h), and [Lerc_ByteStream_Specification.pdf](https://github.com/Esri/lerc/blob/master/doc/Lerc_ByteStream_Specification.pdf), in increasing level of detail. [Main.cpp](https://github.com/Esri/lerc/blob/master/src/LercTest/main.cpp) has some sample calls. + +* New lossless compression for data types float and double. + +* Support for large integers > 32 bit up to 53 bit. Pass them as double. + +* Renamed nDim to nDepth, the size of the array per pixel. + +* New #define ENCODE_VERIFY in [Defines.h](https://github.com/Esri/lerc/blob/master/src/LercLib/Defines.h). If enabled, Lerc, as an added last step in encode, will decode the compressed blob again and compare it against the input data. + +* Using [Emscripten](https://emscripten.org/), we compiled the Lerc C++ code into web assembly, resulting in a [new JS Lerc decoder](https://github.com/Esri/lerc/tree/master/OtherLanguages/js/dist). From now on, updates to the Lerc C++ code will be converted to JS automatically. + +* Added a new function to the LERC API called 'lerc_getDataRanges(...)'. It allows fast access to the data ranges in a compressed Lerc blob without having to decode it. It returns 2 double arrays with the minimum and maximum values per band and depth. + +### Fixed + +* The older Lerc codec v2.2 (used around 2016) required 3 extra padding bytes at the end of the Lerc blob. If those padding bytes are missing, Lerc decode could fail. With this fix, decode works on such v2.2 Lerc blobs with missing padding bytes. + ## [3.0](https://github.com/Esri/lerc/releases/tag/v3.0) - 2021-07-30 diff -Nru lerc-3.0+ds/CMakeLists.txt lerc-4.0.0+ds/CMakeLists.txt --- lerc-3.0+ds/CMakeLists.txt 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/CMakeLists.txt 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,9 @@ -cmake_minimum_required (VERSION 3.11) -project (Lerc) +cmake_minimum_required(VERSION 3.12) + +project(Lerc + DESCRIPTION "Limited Error Raster Compression" + HOMEPAGE_URL "https://github.com/Esri/lerc" + VERSION 4.0.0) # Keep in sync with Lerc_c_api.h include(GNUInstallDirs) @@ -22,7 +26,14 @@ PUBLIC_HEADER "src/LercLib/include/Lerc_types.h;src/LercLib/include/Lerc_c_api.h") if(BUILD_SHARED_LIBS) - set_target_properties(Lerc PROPERTIES DEFINE_SYMBOL LERC_EXPORTS) + set_target_properties(Lerc + PROPERTIES + SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} + DEFINE_SYMBOL LERC_EXPORTS) +else() + set_target_properties(Lerc + PROPERTIES + COMPILE_DEFINITIONS LERC_STATIC) endif() install( @@ -32,3 +43,8 @@ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) + +# Configure and install pkgconfig file +configure_file(Lerc.pc.in ${CMAKE_CURRENT_BINARY_DIR}/Lerc.pc @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/Lerc.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff -Nru lerc-3.0+ds/debian/changelog lerc-4.0.0+ds/debian/changelog --- lerc-3.0+ds/debian/changelog 2022-07-14 23:41:19.000000000 +0000 +++ lerc-4.0.0+ds/debian/changelog 2023-11-07 16:54:53.000000000 +0000 @@ -1,9 +1,78 @@ -lerc (3.0+ds-1~18.04.sav0) bionic; urgency=medium +lerc (4.0.0+ds-3~18.04.sav0) bionic; urgency=medium * Backport to Bionic + * Partial revert of "Switch to dh-sequence-*." for backport: + - debian/control: Drop dh-sequence-{numpy3,pkgkde-symbolshelper} BDs + - debian/rules: Add --with python3,numpy3,pkgkde_symbolshelper to dh call * debian/control: Set debhelper-compat (= 11) BD + * debian/liblerc4.symbols: Update symbols from build logs - -- Rob Savoury Thu, 14 Jul 2022 16:41:19 -0700 + -- Rob Savoury Tue, 07 Nov 2023 08:54:53 -0800 + +lerc (4.0.0+ds-3) unstable; urgency=medium + + [ Antonio Valentino ] + * debian/rules: + - remove -c0 override for dpkg-gensymbols on Ubuntu (Closes: #1013146). + See also https://salsa.debian.org/debian-gis-team/lerc/-/merge_requests/1. + * debian/control: + - update package description. + - build depend on architecture-is-little-endian. + * Switch to autopkgtest-pkg-pybuild and remove the no + longer needed d/tests folder. + * Update dates in d/copyright. + + [ Bas Couwenberg ] + * Enable numpy3 dh helper. + * Enable Salsa CI. + * Bump Standards-Version to 4.6.2, no changes. + * Bump debhelper compat to 13. + * Switch to dh-sequence-*. + + -- Antonio Valentino Sat, 09 Sep 2023 08:20:46 +0000 + +lerc (4.0.0+ds-2) unstable; urgency=medium + + [ Bas Couwenberg ] + * Update library package name. + + -- Antonio Valentino Sat, 15 Oct 2022 08:54:09 +0000 + +lerc (4.0.0+ds-1) unstable; urgency=medium + + * Move from experimental to unstable. + + -- Antonio Valentino Sun, 31 Jul 2022 07:29:34 +0000 + +lerc (4.0.0+ds-1~exp2) experimental; urgency=medium + + * Update symbol file for all architectures. + + -- Antonio Valentino Sat, 30 Jul 2022 09:49:42 +0000 + +lerc (4.0.0+ds-1~exp1) experimental; urgency=medium + + [ Bas Couwenberg ] + * Bump Standards-Version to 4.6.1, no changes. + * Use supported python3 versions in autopkgtest. + * Use #PACKAGE# substitution in symbols file. + * Include pkg-config file in liblerc-dev. + + [ Antonio Valentino ] + * New upstream release. + * debian/copyright + - exclude pre-built binaries and unused language bindings + located in OtherLanguages + - update copyright date. + * debian/tests: + - add test dependency on python3-all. + * debian/patches: + - drop 0001-Specify-the-soversion.patch and + 0002-Use-system-libLerc.patch: applied upstream. + * Update the package name to match soname. + * Update symbol file. + + -- Antonio Valentino Sat, 23 Jul 2022 07:34:08 +0000 lerc (3.0+ds-1) unstable; urgency=medium diff -Nru lerc-3.0+ds/debian/control lerc-4.0.0+ds/debian/control --- lerc-3.0+ds/debian/control 2022-07-14 21:56:22.000000000 +0000 +++ lerc-4.0.0+ds/debian/control 2023-11-07 16:54:53.000000000 +0000 @@ -3,25 +3,21 @@ Uploaders: Antonio Valentino Section: libs Priority: optional -Testsuite: autopkgtest-pkg-python -Build-Depends: cmake, +Testsuite: autopkgtest-pkg-pybuild +Build-Depends: architecture-is-little-endian, + cmake, debhelper-compat (= 11), dh-python, + dh-sequence-python3, pkg-kde-tools, python3-all, python3-numpy, python3-setuptools -Standards-Version: 4.6.0 +Standards-Version: 4.6.2 Vcs-Browser: https://salsa.debian.org/debian-gis-team/lerc Vcs-Git: https://salsa.debian.org/debian-gis-team/lerc.git Homepage: https://github.com/Esri/lerc Rules-Requires-Root: no - -Package: liblerc3 -Architecture: any -Multi-Arch: same -Depends: ${shlibs:Depends}, - ${misc:Depends} Description: Limited Error Raster Compression library LERC is an open-source image or raster format which supports rapid encoding and decoding for any pixel @@ -30,35 +26,32 @@ precision of the original input image is preserved (within user defined error bounds). +Package: liblerc4 +Architecture: any +Multi-Arch: same +Depends: ${shlibs:Depends}, + ${misc:Depends} +Description: ${source:Synopsis} + ${source:Extended-Description} + Package: liblerc-dev Architecture: any Multi-Arch: same Section: libdevel -Depends: liblerc3 (= ${binary:Version}), +Depends: liblerc4 (= ${binary:Version}), ${misc:Depends} -Description: Limited Error Raster Compression library (Development files) - LERC is an open-source image or raster format which - supports rapid encoding and decoding for any pixel - type (not just RGB or Byte). Users set the maximum - compression error per pixel while encoding, so the - precision of the original input image is preserved - (within user defined error bounds). +Description: ${source:Synopsis} (Development files) + ${source:Extended-Description} . This is a development package of LERC. Package: python3-lerc Architecture: all Section: python -Depends: liblerc3, - python3-numpy, +Depends: liblerc4, ${python3:Depends}, ${misc:Depends} -Description: Python 3 bindings to the Limited Error Raster Compression library - LERC is an open-source image or raster format which - supports rapid encoding and decoding for any pixel - type (not just RGB or Byte). Users set the maximum - compression error per pixel while encoding, so the - precision of the original input image is preserved - (within user defined error bounds). +Description: Python 3 bindings to the ${source:Synopsis} + ${source:Extended-Description} . This is a Python package of LERC. diff -Nru lerc-3.0+ds/debian/copyright lerc-4.0.0+ds/debian/copyright --- lerc-3.0+ds/debian/copyright 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/copyright 2023-09-09 08:20:46.000000000 +0000 @@ -6,13 +6,15 @@ Files-Excluded: bin build doc + OtherLanguages/js + OtherLanguages/CSharp Files: * -Copyright: 2015-2021, Esri +Copyright: 2015-2022, Esri License: Apache-2.0 Files: debian/* -Copyright: 2021, Antonnio Valentino +Copyright: 2021-2023, Antonnio Valentino License: Apache-2.0 License: Apache-2.0 @@ -30,4 +32,3 @@ . On Debian systems, the complete text of the Apache version 2.0 license can be found in "/usr/share/common-licenses/Apache-2.0". - diff -Nru lerc-3.0+ds/debian/.gitlab-ci.yml lerc-4.0.0+ds/debian/.gitlab-ci.yml --- lerc-3.0+ds/debian/.gitlab-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/debian/.gitlab-ci.yml 2023-09-09 08:20:46.000000000 +0000 @@ -0,0 +1,6 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml + +variables: + SALSA_CI_ENABLE_BUILD_PACKAGE_TWICE: 1 diff -Nru lerc-3.0+ds/debian/liblerc3.docs lerc-4.0.0+ds/debian/liblerc3.docs --- lerc-3.0+ds/debian/liblerc3.docs 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/liblerc3.docs 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -NOTICE diff -Nru lerc-3.0+ds/debian/liblerc3.install lerc-4.0.0+ds/debian/liblerc3.install --- lerc-3.0+ds/debian/liblerc3.install 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/liblerc3.install 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -usr/lib/*/*.so.* diff -Nru lerc-3.0+ds/debian/liblerc3.symbols lerc-4.0.0+ds/debian/liblerc3.symbols --- lerc-3.0+ds/debian/liblerc3.symbols 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/liblerc3.symbols 1970-01-01 00:00:00.000000000 +0000 @@ -1,386 +0,0 @@ -# SymbolsHelper-Confirmed: 3.0 amd64 armel armhf hurd-i386 i386 ia64 m68k mips64el mipsel ppc64el riscv64 sh4 x32 -libLerc.so.3 liblerc3 #MINVER# -* Build-Depends-Package: liblerc-dev - _ZN6LercNS10BitStuffer21numTailBytesNotNeededEji@Base 3.0 - _ZN6LercNS10BitStuffer8readUIntEPPhRji@Base 3.0 - _ZN6LercNS10BitStufferD0Ev@Base 3.0 - _ZN6LercNS10BitStufferD1Ev@Base 3.0 - _ZN6LercNS10BitStufferD2Ev@Base 3.0 - _ZN6LercNS11BitStuffer223BitStuff_Before_Lerc2v3EPPhRKSt6vectorIjSaIjEEi@Base 3.0 - _ZN6LercNS11BitStuffer224ComputeNumBytesNeededLutERKSt6vectorISt4pairIjjESaIS3_EERb@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS11BitStuffer225BitUnStuff_Before_Lerc2v3EPPKhRjRSt6vectorIjSaIjEEji@Base 3.0 - _ZN6LercNS11BitStuffer225BitUnStuff_Before_Lerc2v3EPPKhRmRSt6vectorIjSaIjEEji@Base 3.0 - _ZN6LercNS11BitStuffer2D0Ev@Base 3.0 - _ZN6LercNS11BitStuffer2D1Ev@Base 3.0 - _ZN6LercNS11BitStuffer2D2Ev@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS3RLE10decompressEPKhjPPhRj@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS3RLE10decompressEPKhjPhj@Base 3.0 - _ZN6LercNS3RLE10decompressEPKhmPPhRm@Base 3.0 - _ZN6LercNS3RLE10decompressEPKhmPhm@Base 3.0 - _ZN6LercNS3RLE10writeCountEsPPhS2_@Base 3.0 - _ZN6LercNS3RLE9readCountEPPKh@Base 3.0 - _ZN6LercNS3RLED0Ev@Base 3.0 - _ZN6LercNS3RLED1Ev@Base 3.0 - _ZN6LercNS3RLED2Ev@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc11CheckForNaNIdEENS_7ErrCodeEPKT_iiiPKh@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc11CheckForNaNIfEENS_7ErrCodeEPKT_iiiPKh@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIaEENS_7ErrCodeEPT_PKhjiiiiiPh@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIdEENS_7ErrCodeEPT_PKhjiiiiiPh@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIfEENS_7ErrCodeEPT_PKhjiiiiiPh@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIhEENS_7ErrCodeEPT_PKhjiiiiiPh@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIiEENS_7ErrCodeEPT_PKhjiiiiiPh@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIjEENS_7ErrCodeEPT_PKhjiiiiiPh@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIsEENS_7ErrCodeEPT_PKhjiiiiiPh@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplItEENS_7ErrCodeEPT_PKhjiiiiiPh@Base 3.0 - _ZN6LercNS4Lerc11GetLercInfoEPKhjRNS0_8LercInfoE@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS4Lerc11MasksDifferEPKhS2_j@Base 3.0 - _ZN6LercNS4Lerc11MasksDifferEPKhS2_m@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIaEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIdEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIfEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIhEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIiEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIjEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIsEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalItEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS4Lerc15ConvertToDoubleEPKvNS0_8DataTypeEjPd@Base 3.0 - _ZN6LercNS4Lerc15ConvertToDoubleEPKvNS0_8DataTypeEmPd@Base 3.0 - _ZN6LercNS4Lerc21ComputeCompressedSizeEPKviNS0_8DataTypeEiiiiiPKhdRj@Base 3.0 - _ZN6LercNS4Lerc6DecodeEPKhjiPhiiiiNS0_8DataTypeEPv@Base 3.0 - _ZN6LercNS4Lerc6EncodeEPKviNS0_8DataTypeEiiiiiPKhdPhjRj@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS4Lerc6ResizeIhEEbRSt6vectorIT_SaIS3_EEj@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc6ResizeIhEEbRSt6vectorIT_SaIS3_EEm@Base 3.0 - _ZN6LercNS4Lerc7ConvertEPKhiiRNS_7BitMaskE@Base 3.0 - _ZN6LercNS4Lerc7ConvertERKNS_7BitMaskEPh@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc7ConvertIaEEbRKNS_9CntZImageEPT_Phb@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc7ConvertIdEEbRKNS_9CntZImageEPT_Phb@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc7ConvertIfEEbRKNS_9CntZImageEPT_Phb@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc7ConvertIhEEbRKNS_9CntZImageEPT_Phb@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc7ConvertIiEEbRKNS_9CntZImageEPT_Phb@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc7ConvertIjEEbRKNS_9CntZImageEPT_Phb@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc7ConvertIsEEbRKNS_9CntZImageEPT_Phb@Base 3.0 - (optional=templinst)_ZN6LercNS4Lerc7ConvertItEEbRKNS_9CntZImageEPT_Phb@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc210ReadHeaderEPPKhRjRNS0_10HeaderInfoE@Base 3.0 - _ZN6LercNS5Lerc210ReadHeaderEPPKhRmRNS0_10HeaderInfoE@Base 3.0 - _ZN6LercNS5Lerc211WriteHeaderEPPhRKNS0_10HeaderInfoE@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc213GetHeaderInfoEPKhjRNS0_10HeaderInfoERb@Base 3.0 - _ZN6LercNS5Lerc213GetHeaderInfoEPKhmRNS0_10HeaderInfoERb@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc214ReduceDataTypeIdEEiT_NS0_8DataTypeERS3_@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc214ReduceDataTypeIfEEiT_NS0_8DataTypeERS3_@Base 3.0 - _ZN6LercNS5Lerc214SortQuantArrayERKSt6vectorIjSaIjEERS1_ISt4pairIjjESaIS7_EE@Base 3.0 - _ZN6LercNS5Lerc215PruneCandidatesERSt6vectorIdSaIdEES4_RS1_IiSaIiEEd@Base 3.0 - _ZN6LercNS5Lerc222SetEncoderToOldVersionEi@Base 3.0 - _ZN6LercNS5Lerc225ComputeChecksumFletcher32EPKhi@Base 3.0 - _ZN6LercNS5Lerc228ComputeNumBytesHeaderToWriteERKNS0_10HeaderInfoE@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIaEEjPKT_db@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIdEEjPKT_db@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIfEEjPKT_db@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIhEEjPKT_db@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIiEEjPKT_db@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIjEEjPKT_db@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIsEEjPKT_db@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteItEEjPKT_db@Base 3.0 - _ZN6LercNS5Lerc23SetEiiiPKh@Base 3.0 - _ZN6LercNS5Lerc24InitEv@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIaEEbPPKhRjPT_Ph@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26DecodeIaEEbPPKhRmPT_Ph@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIdEEbPPKhRjPT_Ph@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26DecodeIdEEbPPKhRmPT_Ph@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIfEEbPPKhRjPT_Ph@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26DecodeIfEEbPPKhRmPT_Ph@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIhEEbPPKhRjPT_Ph@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26DecodeIhEEbPPKhRmPT_Ph@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIiEEbPPKhRjPT_Ph@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26DecodeIiEEbPPKhRmPT_Ph@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIjEEbPPKhRjPT_Ph@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26DecodeIjEEbPPKhRmPT_Ph@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIsEEbPPKhRjPT_Ph@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26DecodeIsEEbPPKhRmPT_Ph@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeItEEbPPKhRjPT_Ph@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26DecodeItEEbPPKhRmPT_Ph@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26EncodeIaEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26EncodeIdEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26EncodeIfEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26EncodeIhEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26EncodeIiEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26EncodeIjEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26EncodeIsEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZN6LercNS5Lerc26EncodeItEEbPKT_PPh@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc28ReadMaskEPPKhRj@Base 3.0 - _ZN6LercNS5Lerc28ReadMaskEPPKhRm@Base 3.0 - _ZN6LercNS5Lerc2C1EiiiPKh@Base 3.0 - _ZN6LercNS5Lerc2C1Ev@Base 3.0 - _ZN6LercNS5Lerc2C2EiiiPKh@Base 3.0 - _ZN6LercNS5Lerc2C2Ev@Base 3.0 - _ZN6LercNS5Lerc2D0Ev@Base 3.0 - _ZN6LercNS5Lerc2D1Ev@Base 3.0 - _ZN6LercNS5Lerc2D2Ev@Base 3.0 - (optional=templinst)_ZN6LercNS6TImageINS_4CntZEE5clearEv@Base 3.0 - (optional=templinst)_ZN6LercNS6TImageINS_4CntZEEaSERKS2_@Base 3.0 - _ZN6LercNS7BitMask5ClearEv@Base 3.0 - _ZN6LercNS7BitMask7SetSizeEii@Base 3.0 - _ZN6LercNS7BitMaskC1ERKS0_@Base 3.0 - _ZN6LercNS7BitMaskC2ERKS0_@Base 3.0 - _ZN6LercNS7BitMaskD0Ev@Base 3.0 - _ZN6LercNS7BitMaskD1Ev@Base 3.0 - _ZN6LercNS7BitMaskD2Ev@Base 3.0 - _ZN6LercNS7BitMaskaSERKS0_@Base 3.0 - _ZN6LercNS7Huffman12ComputeCodesERKSt6vectorIiSaIiEE@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS7Huffman13ReadCodeTableEPPKhRji@Base 3.0 - _ZN6LercNS7Huffman13ReadCodeTableEPPKhRmi@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS7Huffman15BitUnStuffCodesEPPKhRjii@Base 3.0 - _ZN6LercNS7Huffman15BitUnStuffCodesEPPKhRmii@Base 3.0 - _ZN6LercNS7Huffman18BuildTreeFromCodesERi@Base 3.0 - _ZN6LercNS7Huffman23ConvertCodesToCanonicalEv@Base 3.0 - _ZN6LercNS7Huffman4Node8FreeTreeERi@Base 3.0 - _ZN6LercNS7Huffman5ClearEv@Base 3.0 - _ZN6LercNS7Huffman8SetCodesERKSt6vectorISt4pairItjESaIS3_EE@Base 3.0 - _ZN6LercNS7Huffman9ClearTreeEv@Base 3.0 - _ZN6LercNS7HuffmanD1Ev@Base 3.0 - _ZN6LercNS7HuffmanD2Ev@Base 3.0 - _ZN6LercNS9CntZImage11numBytesFltEf@Base 3.0 - _ZN6LercNS9CntZImage11readCntTileEPPhiiii@Base 3.0 - _ZN6LercNS9CntZImage11resizeFill0Eii@Base 3.0 - _ZN6LercNS9CntZImage33computeNumBytesNeededToReadHeaderEb@Base 3.0 - _ZN6LercNS9CntZImage4readEPPhdbb@Base 3.0 - _ZN6LercNS9CntZImage7readFltEPPhRfi@Base 3.0 - _ZN6LercNS9CntZImage9readTilesEbdiifPh@Base 3.0 - _ZN6LercNS9CntZImage9readZTileEPPhiiiidf@Base 3.0 - _ZN6LercNS9CntZImageC1Ev@Base 3.0 - _ZN6LercNS9CntZImageC2Ev@Base 3.0 - _ZN6LercNS9CntZImageD0Ev@Base 3.0 - _ZN6LercNS9CntZImageD1Ev@Base 3.0 - _ZN6LercNS9CntZImageD2Ev@Base 3.0 - _ZNK6LercNS10BitStuffer4readEPPhRSt6vectorIjSaIjEE@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNK6LercNS11BitStuffer210BitUnStuffEPPKhRjRSt6vectorIjSaIjEEji@Base 3.0 - _ZNK6LercNS11BitStuffer210BitUnStuffEPPKhRmRSt6vectorIjSaIjEEji@Base 3.0 - _ZNK6LercNS11BitStuffer212EncodeSimpleEPPhRKSt6vectorIjSaIjEEi@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNK6LercNS11BitStuffer26DecodeEPPKhRjRSt6vectorIjSaIjEEji@Base 3.0 - _ZNK6LercNS11BitStuffer26DecodeEPPKhRmRSt6vectorIjSaIjEEmi@Base 3.0 - _ZNK6LercNS11BitStuffer28BitStuffEPPhRKSt6vectorIjSaIjEEi@Base 3.0 - _ZNK6LercNS11BitStuffer29EncodeLutEPPhRKSt6vectorISt4pairIjjESaIS5_EEi@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNK6LercNS3RLE18computeNumBytesRLEEPKhj@Base 3.0 - _ZNK6LercNS3RLE18computeNumBytesRLEEPKhm@Base 3.0 - (arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNK6LercNS3RLE8compressEPKhjPPhRjb@Base 3.0 - _ZNK6LercNS3RLE8compressEPKhmPPhRmb@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIaEEbPKT_PPhRi@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIdEEbPKT_PPhRi@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIfEEbPKT_PPhRi@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIhEEbPKT_PPhRi@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIiEEbPKT_PPhRi@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIjEEbPKT_PPhRi@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIsEEbPKT_PPhRi@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesItEEbPKT_PPhRi@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileIaEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileIhEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileIiEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileIjEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileIsEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileItEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIaEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213DecodeHuffmanIaEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIdEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213DecodeHuffmanIdEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIfEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213DecodeHuffmanIfEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIhEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213DecodeHuffmanIhEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIiEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213DecodeHuffmanIiEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIjEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213DecodeHuffmanIjEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIsEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213DecodeHuffmanIsEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanItEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213DecodeHuffmanItEEbPPKhRmPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIaEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIdEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIfEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIhEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIiEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIjEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIsEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanItEEbPKT_PPh@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIaEEbPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIdEEbPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIfEEbPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIhEEbPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIiEEbPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIjEEbPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIsEEbPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageItEEbPT_@Base 3.0 - _ZNK6LercNS5Lerc216DoChecksOnEncodeEPhS1_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIaEEbPKT_Rd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIdEEbPKT_Rd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIfEEbPKT_Rd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIhEEbPKT_Rd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIiEEbPKT_Rd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIjEEbPKT_Rd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIsEEbPKT_Rd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorItEEbPKT_Rd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIaEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIdEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIfEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIhEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIiEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIjEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIsEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesItEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIaEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIdEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIfEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIhEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIiEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIjEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIsEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesItEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIaEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIdEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIfEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIhEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIiEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIjEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIsEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanItEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIaEEbPKT_dRd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIdEEbPKT_dRd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIfEEbPKT_dRd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIhEEbPKT_dRd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIiEEbPKT_dRd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIjEEbPKT_dRd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIsEEbPKT_dRd@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionItEEbPKT_dRd@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIaEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc28ReadTileIaEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIdEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc28ReadTileIdEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIfEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc28ReadTileIfEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIhEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc28ReadTileIhEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIiEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc28ReadTileIiEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIjEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc28ReadTileIjEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIsEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc28ReadTileIsEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileItEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc28ReadTileItEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIaEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29ReadTilesIaEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIdEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29ReadTilesIdEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIfEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29ReadTilesIfEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIhEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29ReadTilesIhEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIiEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29ReadTilesIiEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIjEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29ReadTilesIjEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIsEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29ReadTilesIsEEbPPKhRmPT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesItEEbPPKhRjPT_@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29ReadTilesItEEbPPKhRmPT_@Base 3.0 - _ZNK6LercNS5Lerc29WriteMaskEPPh@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIaEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIdEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIfEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIhEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIiEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIjEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIsEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 - (optional=templinst)_ZNK6LercNS5Lerc29WriteTileItEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 - _ZNK6LercNS7BitMask11SetAllValidEv@Base 3.0 - _ZNK6LercNS7BitMask13SetAllInvalidEv@Base 3.0 - _ZNK6LercNS7BitMask14CountValidBitsEv@Base 3.0 - _ZNK6LercNS7Huffman13BitStuffCodesEPPhii@Base 3.0 - (arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS7Huffman14DecodeOneValueEPPKjRjRiiS5_@Base 3.0 - _ZNK6LercNS7Huffman14WriteCodeTableEPPhi@Base 3.0 - _ZNK6LercNS7Huffman21ComputeCompressedSizeERKSt6vectorIiSaIiEERiRd@Base 3.0 - _ZNK6LercNS7Huffman24ComputeNumBytesCodeTableERi@Base 3.0 - _ZNK6LercNS7Huffman4Node9TreeToLUTEtjRSt6vectorISt4pairItjESaIS4_EE@Base 3.0 - _ZNK6LercNS7Huffman8GetRangeERiS1_S1_@Base 3.0 - _ZNK6LercNS9CntZImage13getTypeStringB5cxx11Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseISt4pairIjjESaIS1_EED1Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseISt4pairIjjESaIS1_EED2Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseISt4pairItjESaIS1_EED1Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseISt4pairItjESaIS1_EED2Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIaSaIaEED1Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIaSaIaEED2Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIdSaIdEED1Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIdSaIdEED2Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIfSaIfEED1Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIfSaIfEED2Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIhSaIhEED1Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIhSaIhEED2Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIiSaIiEED1Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIiSaIiEED2Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIjSaIjEED1Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIjSaIjEED2Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIsSaIsEED1Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseIsSaIsEED2Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseItSaItEED1Ev@Base 3.0 - (optional=templinst)_ZNSt12_Vector_baseItSaItEED2Ev@Base 3.0 - (optional=templinst|arch=!amd64 !sh4 !x32)_ZNSt14priority_queueIN6LercNS7Huffman4NodeESt6vectorIS2_SaIS2_EESt4lessIS2_EE3popEv@Base 3.0 - (optional=templinst)_ZNSt6vectorIN6LercNS7Huffman4NodeESaIS2_EE17_M_realloc_insertIJS2_EEEvN9__gnu_cxx17__normal_iteratorIPS2_S4_EEDpOT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorISt4pairIjjESaIS1_EE17_M_default_appendEj@Base 3.0 - (optional=templinst)_ZNSt6vectorISt4pairIjjESaIS1_EE17_M_default_appendEm@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorISt4pairIssESaIS1_EE14_M_fill_assignEjRKS1_@Base 3.0 - (optional=templinst)_ZNSt6vectorISt4pairIssESaIS1_EE14_M_fill_assignEmRKS1_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorISt4pairItjESaIS1_EE17_M_default_appendEj@Base 3.0 - (optional=templinst)_ZNSt6vectorISt4pairItjESaIS1_EE17_M_default_appendEm@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNSt6vectorIaSaIaEE17_M_default_appendEj@Base 3.0 - (optional=templinst)_ZNSt6vectorIaSaIaEE17_M_default_appendEm@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIdSaIdEE17_M_default_appendEj@Base 3.0 - (optional=templinst)_ZNSt6vectorIdSaIdEE17_M_default_appendEm@Base 3.0 - (optional=templinst)_ZNSt6vectorIdSaIdEE17_M_realloc_insertIJRKdEEEvN9__gnu_cxx17__normal_iteratorIPdS1_EEDpOT_@Base 3.0 - (optional=templinst)_ZNSt6vectorIdSaIdEE8_M_eraseEN9__gnu_cxx17__normal_iteratorIPdS1_EE@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIfSaIfEE17_M_default_appendEj@Base 3.0 - (optional=templinst)_ZNSt6vectorIfSaIfEE17_M_default_appendEm@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIhSaIhEE17_M_default_appendEj@Base 3.0 - (optional=templinst)_ZNSt6vectorIhSaIhEE17_M_default_appendEm@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNSt6vectorIiSaIiEE17_M_default_appendEj@Base 3.0 - (optional=templinst)_ZNSt6vectorIiSaIiEE17_M_default_appendEm@Base 3.0 - (optional=templinst)_ZNSt6vectorIiSaIiEE17_M_realloc_insertIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_@Base 3.0 - (optional=templinst)_ZNSt6vectorIiSaIiEE17_M_realloc_insertIJiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIjSaIjEE14_M_fill_assignEjRKj@Base 3.0 - (optional=templinst)_ZNSt6vectorIjSaIjEE14_M_fill_assignEmRKj@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIjSaIjEE14_M_fill_insertEN9__gnu_cxx17__normal_iteratorIPjS1_EEjRKj@Base 3.0 - (optional=templinst)_ZNSt6vectorIjSaIjEE14_M_fill_insertEN9__gnu_cxx17__normal_iteratorIPjS1_EEmRKj@Base 3.0 - (optional=templinst)_ZNSt6vectorIjSaIjEE14_M_insert_rvalEN9__gnu_cxx17__normal_iteratorIPKjS1_EEOj@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIjSaIjEE17_M_default_appendEj@Base 3.0 - (optional=templinst)_ZNSt6vectorIjSaIjEE17_M_default_appendEm@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNSt6vectorIsSaIsEE17_M_default_appendEj@Base 3.0 - (optional=templinst)_ZNSt6vectorIsSaIsEE17_M_default_appendEm@Base 3.0 - (optional=templinst|arch=!amd64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNSt6vectorItSaItEE17_M_default_appendEj@Base 3.0 - (optional=templinst)_ZNSt6vectorItSaItEE17_M_default_appendEm@Base 3.0 - _ZTIN6LercNS10BitStufferE@Base 3.0 - _ZTIN6LercNS11BitStuffer2E@Base 3.0 - _ZTIN6LercNS3RLEE@Base 3.0 - _ZTIN6LercNS5ImageE@Base 3.0 - _ZTIN6LercNS5Lerc2E@Base 3.0 - _ZTIN6LercNS6TImageINS_4CntZEEE@Base 3.0 - _ZTIN6LercNS7BitMaskE@Base 3.0 - _ZTIN6LercNS9CntZImageE@Base 3.0 - _ZTSN6LercNS10BitStufferE@Base 3.0 - _ZTSN6LercNS11BitStuffer2E@Base 3.0 - _ZTSN6LercNS3RLEE@Base 3.0 - _ZTSN6LercNS5ImageE@Base 3.0 - _ZTSN6LercNS5Lerc2E@Base 3.0 - _ZTSN6LercNS6TImageINS_4CntZEEE@Base 3.0 - _ZTSN6LercNS7BitMaskE@Base 3.0 - _ZTSN6LercNS9CntZImageE@Base 3.0 - _ZTVN6LercNS10BitStufferE@Base 3.0 - _ZTVN6LercNS11BitStuffer2E@Base 3.0 - _ZTVN6LercNS3RLEE@Base 3.0 - _ZTVN6LercNS5Lerc2E@Base 3.0 - _ZTVN6LercNS7BitMaskE@Base 3.0 - _ZTVN6LercNS9CntZImageE@Base 3.0 - lerc_computeCompressedSize@Base 3.0 - lerc_computeCompressedSizeForVersion@Base 3.0 - lerc_decode@Base 3.0 - lerc_decodeToDouble@Base 3.0 - lerc_encode@Base 3.0 - lerc_encodeForVersion@Base 3.0 - lerc_getBlobInfo@Base 3.0 diff -Nru lerc-3.0+ds/debian/liblerc4.docs lerc-4.0.0+ds/debian/liblerc4.docs --- lerc-3.0+ds/debian/liblerc4.docs 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/debian/liblerc4.docs 2023-09-09 08:20:46.000000000 +0000 @@ -0,0 +1 @@ +NOTICE diff -Nru lerc-3.0+ds/debian/liblerc4.install lerc-4.0.0+ds/debian/liblerc4.install --- lerc-3.0+ds/debian/liblerc4.install 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/debian/liblerc4.install 2023-09-09 08:20:46.000000000 +0000 @@ -0,0 +1 @@ +usr/lib/*/*.so.* diff -Nru lerc-3.0+ds/debian/liblerc4.symbols lerc-4.0.0+ds/debian/liblerc4.symbols --- lerc-3.0+ds/debian/liblerc4.symbols 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/debian/liblerc4.symbols 2023-11-07 16:54:53.000000000 +0000 @@ -0,0 +1,655 @@ +# SymbolsHelper-Confirmed: 4.0.0 amd64 arm64 armel armhf hurd-i386 i386 ia64 mipsel riscv64 sh4 +libLerc.so.4 #PACKAGE# #MINVER# +* Build-Depends-Package: liblerc-dev + (arch=!amd64 !arm64 !ia64 !riscv64)_Z12getBestLevelPKhji@Base 4.0.0 + _Z12getBestLevelPKhmi@Base 4.0.0 + _Z13ADD32_BIT_FLTRKjS0_@Base 4.0.0 + _Z13ADD64_BIT_DBLRKmS0_@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z13ADD64_BIT_DBLRKyS0_@Base 4.0.0 + _Z13SUB32_BIT_FLTRKjS0_@Base 4.0.0 + _Z13SUB64_BIT_DBLRKmS0_@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z13SUB64_BIT_DBLRKyS0_@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z13getBestLevel2PKhji@Base 4.0.0 + _Z13getBestLevel2PKhmi@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z14decodePackBitsPKhjjPPh@Base 4.0.0 + _Z14decodePackBitsPKhmmPPh@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z14encodePackBitsPKhjPPh@Base 4.0.0 + _Z14encodePackBitsPKhmPPh@Base 4.0.0 + _Z14moveBits2Frontj@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z15getPackBitsSizePKhjPl@Base 4.0.0 + _Z15getPackBitsSizePKhmPl@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z15restoreSequencePhjib@Base 4.0.0 + _Z15restoreSequencePhmib@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z16restoreByteOrderRSt6vectorISt4pairIiPcESaIS2_EEjjN6LercNS13PredictorTypeENS6_8UnitTypeEPPh@Base 4.0.0 + _Z16restoreByteOrderRSt6vectorISt4pairIiPcESaIS2_EEmmN6LercNS13PredictorTypeENS6_8UnitTypeEPPh@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z17restoreCrossBytesRSt6vectorISt4pairIiPcESaIS2_EEjjjN6LercNS13PredictorTypeENS6_8UnitTypeEPPh@Base 4.0.0 + _Z17restoreCrossBytesRSt6vectorISt4pairIiPcESaIS2_EEmmmN6LercNS13PredictorTypeENS6_8UnitTypeEPPh@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z18setDerivativeFloatPjjii@Base 4.0.0 + _Z18setDerivativeFloatPjmii@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z19ComputeHuffmanCodesPKhjRiRSt6vectorISt4pairItjESaIS4_EE@Base 4.0.0 + _Z19ComputeHuffmanCodesPKhmRiRSt6vectorISt4pairItjESaIS4_EE@Base 4.0.0 + _Z19setDerivativeDoublePmmii@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z19setDerivativeDoublePyjii@Base 4.0.0 + _Z19undo_moveBits2Frontj@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z22ComputeHistoForHuffmanPKhjRSt6vectorIiSaIiEE@Base 4.0.0 + _Z22ComputeHistoForHuffmanPKhmRSt6vectorIiSaIiEE@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z22restoreCrossBytesFloatiPjjj@Base 4.0.0 + _Z22restoreCrossBytesFloatiPjmm@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z22setRowsDerivativeFloatPjjjii@Base 4.0.0 + _Z22setRowsDerivativeFloatPjmmii@Base 4.0.0 + _Z23restoreCrossBytesDoubleiPmmm@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z23restoreCrossBytesDoubleiPyjj@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z23setCrossDerivativeFloatPjjjii@Base 4.0.0 + _Z23setCrossDerivativeFloatPjmmii@Base 4.0.0 + _Z23setRowsDerivativeDoublePmmmii@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z23setRowsDerivativeDoublePyjjii@Base 4.0.0 + _Z24setCrossDerivativeDoublePmmmii@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z24setCrossDerivativeDoublePyjjii@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z25restoreBlockSequenceFloatiPjjj@Base 4.0.0 + _Z25restoreBlockSequenceFloatiPjmm@Base 4.0.0 + _Z26restoreBlockSequenceDoubleiPmmm@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_Z26restoreBlockSequenceDoubleiPyjj@Base 4.0.0 + _Z7_assertb@Base 4.0.0 + _ZN6LercNS10BitStuffer21numTailBytesNotNeededEji@Base 3.0 + _ZN6LercNS10BitStuffer8readUIntEPPKhRji@Base 4.0.0 + _ZN6LercNS10BitStufferD0Ev@Base 3.0 + _ZN6LercNS10BitStufferD1Ev@Base 3.0 + _ZN6LercNS10BitStufferD2Ev@Base 3.0 + _ZN6LercNS11BitStuffer223BitStuff_Before_Lerc2v3EPPhRKSt6vectorIjSaIjEEi@Base 3.0 + _ZN6LercNS11BitStuffer224ComputeNumBytesNeededLutERKSt6vectorISt4pairIjjESaIS3_EERb@Base 3.0 + _ZN6LercNS11BitStuffer2D0Ev@Base 3.0 + _ZN6LercNS11BitStuffer2D1Ev@Base 3.0 + _ZN6LercNS11BitStuffer2D2Ev@Base 3.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS15fpl_Compression14extract_bufferEPKcjjPPc@Base 4.0.0 + _ZN6LercNS15fpl_Compression14extract_bufferEPKcmmPPc@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS15fpl_Compression14getEntropySizeEPKhj@Base 4.0.0 + _ZN6LercNS15fpl_Compression14getEntropySizeEPKhm@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS15fpl_Compression15compress_bufferEPKcjPPcb@Base 4.0.0 + _ZN6LercNS15fpl_Compression15compress_bufferEPKcmPPcb@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS15fpl_EsriHuffman13DecodeHuffmanEPKhjRjPPh@Base 4.0.0 + _ZN6LercNS15fpl_EsriHuffman13DecodeHuffmanEPKhmRmPPh@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS15fpl_EsriHuffman13EncodeHuffmanEPKcjPPhb@Base 4.0.0 + _ZN6LercNS15fpl_EsriHuffman13EncodeHuffmanEPKcmPPhb@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS15fpl_EsriHuffman17getCompressedSizeEPKcj@Base 4.0.0 + _ZN6LercNS15fpl_EsriHuffman17getCompressedSizeEPKcm@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS21LosslessFPCompression16DecodeHuffmanFltEPPKhRjPvbiii@Base 4.0.0 + _ZN6LercNS21LosslessFPCompression16DecodeHuffmanFltEPPKhRmPvbiii@Base 4.0.0 + _ZN6LercNS21LosslessFPCompression16EncodeHuffmanFltEPPh@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS21LosslessFPCompression21DecodeHuffmanFltSliceEPPKhRjPvbii@Base 4.0.0 + _ZN6LercNS21LosslessFPCompression21DecodeHuffmanFltSliceEPPKhRmPvbii@Base 4.0.0 + _ZN6LercNS21LosslessFPCompression22ComputeHuffmanCodesFltEPKvbiii@Base 4.0.0 + _ZN6LercNS21LosslessFPCompression27ComputeHuffmanCodesFltSliceEPKvbii@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS21LosslessFPCompression31selectInitialLinearOrCrossDeltaENS_8UnitTypeEPviiRiRbbPj@Base 4.0.0 + _ZN6LercNS21LosslessFPCompression31selectInitialLinearOrCrossDeltaENS_8UnitTypeEPviiRiRbbPm@Base 4.0.0 + _ZN6LercNS21LosslessFPCompressionD1Ev@Base 4.0.0 + _ZN6LercNS21LosslessFPCompressionD2Ev@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS3RLE10decompressEPKhjPPhRj@Base 3.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS3RLE10decompressEPKhjPhj@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZN6LercNS3RLE10decompressEPKhmPPhRm@Base 4.0.0 + (arch=amd64 arm64 ia64 riscv64)_ZN6LercNS3RLE10decompressEPKhmPhm@Base 4.0.0 + _ZN6LercNS3RLE10writeCountEsPPhS2_@Base 3.0 + _ZN6LercNS3RLE9readCountEPPKh@Base 3.0 + _ZN6LercNS3RLED0Ev@Base 3.0 + _ZN6LercNS3RLED1Ev@Base 3.0 + _ZN6LercNS3RLED2Ev@Base 3.0 + (optional=templinst)_ZN6LercNS4Lerc11CheckForNaNIdEENS_7ErrCodeEPKT_iiiPKh@Base 3.0 + (optional=templinst)_ZN6LercNS4Lerc11CheckForNaNIfEENS_7ErrCodeEPKT_iiiPKh@Base 3.0 + (optional)_ZN6LercNS4Lerc11CheckForNaNIiEENS_7ErrCodeEPKT_iiiPKh@Base 4.0.0 + (optional)_ZN6LercNS4Lerc11CheckForNaNIjEENS_7ErrCodeEPKT_iiiPKh@Base 4.0.0 + (optional)_ZN6LercNS4Lerc11CheckForNaNIsEENS_7ErrCodeEPKT_iiiPKh@Base 4.0.0 + (optional)_ZN6LercNS4Lerc11CheckForNaNItEENS_7ErrCodeEPKT_iiiPKh@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIaEENS_7ErrCodeEPT_PKhjiiiiiPhS7_Pd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIdEENS_7ErrCodeEPT_PKhjiiiiiPhS7_Pd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIfEENS_7ErrCodeEPT_PKhjiiiiiPhS7_Pd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIhEENS_7ErrCodeEPT_PKhjiiiiiPhS7_Pd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIiEENS_7ErrCodeEPT_PKhjiiiiiPhS7_Pd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIjEENS_7ErrCodeEPT_PKhjiiiiiPhS7_Pd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplIsEENS_7ErrCodeEPT_PKhjiiiiiPhS7_Pd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc11DecodeTemplItEENS_7ErrCodeEPT_PKhjiiiiiPhS7_Pd@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS4Lerc11GetLercInfoEPKhjRNS0_8LercInfoEPdS5_j@Base 4.0.0 + _ZN6LercNS4Lerc11GetLercInfoEPKhjRNS0_8LercInfoEPdS5_m@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS4Lerc11MasksDifferEPKhS2_j@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZN6LercNS4Lerc11MasksDifferEPKhS2_m@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc12FilterNoDataIaEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc12FilterNoDataIdEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc12FilterNoDataIfEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc12FilterNoDataIhEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc12FilterNoDataIiEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc12FilterNoDataIjEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc12FilterNoDataIsEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc12FilterNoDataItEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_@Base 4.0.0 + (optional)_ZN6LercNS4Lerc12GetTypeRangeIaEEbT_RSt4pairIddE@Base 4.0.0 + (optional)_ZN6LercNS4Lerc12GetTypeRangeIiEEbT_RSt4pairIddE@Base 4.0.0 + (optional)_ZN6LercNS4Lerc12GetTypeRangeIsEEbT_RSt4pairIddE@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIaEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_S7_PKd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIdEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_S7_PKd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIfEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_S7_PKd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIhEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_S7_PKd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIiEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_S7_PKd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIjEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_S7_PKd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalIsEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_S7_PKd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc14EncodeInternalItEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_S7_PKd@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS4Lerc15ConvertToDoubleEPKvNS0_8DataTypeEjPd@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZN6LercNS4Lerc15ConvertToDoubleEPKvNS0_8DataTypeEmPd@Base 4.0.0 + (optional)_ZN6LercNS4Lerc16ReplaceNaNValuesIdEEbRSt6vectorIT_SaIS3_EERS2_IhSaIhEEiii@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc17EncodeInternal_v5IaEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc17EncodeInternal_v5IdEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc17EncodeInternal_v5IfEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc17EncodeInternal_v5IhEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc17EncodeInternal_v5IiEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc17EncodeInternal_v5IjEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc17EncodeInternal_v5IsEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc17EncodeInternal_v5ItEENS_7ErrCodeEPKT_iiiiiiPKhdRjPhjS8_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc18FilterNoDataAndNaNIaEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_SC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc18FilterNoDataAndNaNIdEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_SC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc18FilterNoDataAndNaNIfEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_SC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc18FilterNoDataAndNaNIhEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_SC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc18FilterNoDataAndNaNIiEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_SC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc18FilterNoDataAndNaNIjEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_SC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc18FilterNoDataAndNaNIsEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_SC_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc18FilterNoDataAndNaNItEENS_7ErrCodeERSt6vectorIT_SaIS4_EERS3_IhSaIhEEiiiRdbSB_RbSC_SC_@Base 4.0.0 + _ZN6LercNS4Lerc21ComputeCompressedSizeEPKviNS0_8DataTypeEiiiiiPKhdRjS5_PKd@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc26FindNewNoDataBelowValidMinIaEEbddbdRT_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc26FindNewNoDataBelowValidMinIdEEbddbdRT_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc26FindNewNoDataBelowValidMinIfEEbddbdRT_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc26FindNewNoDataBelowValidMinIhEEbddbdRT_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc26FindNewNoDataBelowValidMinIiEEbddbdRT_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc26FindNewNoDataBelowValidMinIjEEbddbdRT_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc26FindNewNoDataBelowValidMinIsEEbddbdRT_@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc26FindNewNoDataBelowValidMinItEEbddbdRT_@Base 4.0.0 + _ZN6LercNS4Lerc6DecodeEPKhjiPhiiiiNS0_8DataTypeEPvS3_Pd@Base 4.0.0 + _ZN6LercNS4Lerc6EncodeEPKviNS0_8DataTypeEiiiiiPKhdPhjRjS5_PKd@Base 4.0.0 + (optional=templinst|arch=armel armhf i386)_ZN6LercNS4Lerc6ResizeIaEEbRSt6vectorIT_SaIS3_EEj@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc6ResizeIaEEbRSt6vectorIT_SaIS3_EEm@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS4Lerc6ResizeIdEEbRSt6vectorIT_SaIS3_EEj@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc6ResizeIdEEbRSt6vectorIT_SaIS3_EEm@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS4Lerc6ResizeIfEEbRSt6vectorIT_SaIS3_EEj@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc6ResizeIfEEbRSt6vectorIT_SaIS3_EEm@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS4Lerc6ResizeIhEEbRSt6vectorIT_SaIS3_EEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZN6LercNS4Lerc6ResizeIhEEbRSt6vectorIT_SaIS3_EEm@Base 3.0 + (optional=templinst|arch=armel armhf i386)_ZN6LercNS4Lerc6ResizeIiEEbRSt6vectorIT_SaIS3_EEj@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc6ResizeIiEEbRSt6vectorIT_SaIS3_EEm@Base 4.0.0 + (optional=templinst|arch=armel armhf i386)_ZN6LercNS4Lerc6ResizeIjEEbRSt6vectorIT_SaIS3_EEj@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc6ResizeIjEEbRSt6vectorIT_SaIS3_EEm@Base 4.0.0 + (optional=templinst|arch=armel armhf i386)_ZN6LercNS4Lerc6ResizeIsEEbRSt6vectorIT_SaIS3_EEj@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc6ResizeIsEEbRSt6vectorIT_SaIS3_EEm@Base 4.0.0 + (optional=templinst|arch=armel armhf i386)_ZN6LercNS4Lerc6ResizeItEEbRSt6vectorIT_SaIS3_EEj@Base 4.0.0 + (optional=templinst)_ZN6LercNS4Lerc6ResizeItEEbRSt6vectorIT_SaIS3_EEm@Base 4.0.0 + _ZN6LercNS4Lerc7ConvertEPKhiiRNS_7BitMaskE@Base 3.0 + _ZN6LercNS4Lerc7ConvertERKNS_7BitMaskEPh@Base 3.0 + (optional=templinst)_ZN6LercNS4Lerc7ConvertIaEEbRKNS_9CntZImageEPT_Phb@Base 3.0 + (optional=templinst)_ZN6LercNS4Lerc7ConvertIdEEbRKNS_9CntZImageEPT_Phb@Base 3.0 + (optional=templinst)_ZN6LercNS4Lerc7ConvertIfEEbRKNS_9CntZImageEPT_Phb@Base 3.0 + (optional=templinst)_ZN6LercNS4Lerc7ConvertIhEEbRKNS_9CntZImageEPT_Phb@Base 3.0 + (optional=templinst)_ZN6LercNS4Lerc7ConvertIiEEbRKNS_9CntZImageEPT_Phb@Base 3.0 + (optional=templinst)_ZN6LercNS4Lerc7ConvertIjEEbRKNS_9CntZImageEPT_Phb@Base 3.0 + (optional=templinst)_ZN6LercNS4Lerc7ConvertIsEEbRKNS_9CntZImageEPT_Phb@Base 3.0 + (optional=templinst)_ZN6LercNS4Lerc7ConvertItEEbRKNS_9CntZImageEPT_Phb@Base 3.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS4Lerc9GetRangesEPKhjiRKNS_5Lerc210HeaderInfoEPdS7_j@Base 4.0.0 + _ZN6LercNS4Lerc9GetRangesEPKhjiRKNS_5Lerc210HeaderInfoEPdS7_m@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc210ReadHeaderEPPKhRjRNS0_10HeaderInfoE@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc210ReadHeaderEPPKhRmRNS0_10HeaderInfoE@Base 4.0.0 + (optional)_ZN6LercNS5Lerc211GetDataTypeIdEENS0_8DataTypeET_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc211GetDataTypeIfEENS0_8DataTypeET_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc211GetDataTypeIiEENS0_8DataTypeET_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc211GetDataTypeIjEENS0_8DataTypeET_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc211GetDataTypeItEENS0_8DataTypeET_@Base 4.0.0 + _ZN6LercNS5Lerc211SetIsAllIntEb@Base 4.0.0 + _ZN6LercNS5Lerc211WriteHeaderEPPhRKNS0_10HeaderInfoE@Base 3.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc213GetHeaderInfoEPKhjRNS0_10HeaderInfoERb@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc213GetHeaderInfoEPKhmRNS0_10HeaderInfoERb@Base 4.0.0 + (optional=templinst)_ZN6LercNS5Lerc214ReduceDataTypeIdEEiT_NS0_8DataTypeERS3_@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc214ReduceDataTypeIfEEiT_NS0_8DataTypeERS3_@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc214ReduceDataTypeIjEEiT_NS0_8DataTypeERS3_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc214ReduceDataTypeIsEEiT_NS0_8DataTypeERS3_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc214ReduceDataTypeItEEiT_NS0_8DataTypeERS3_@Base 4.0.0 + _ZN6LercNS5Lerc214SortQuantArrayERKSt6vectorIjSaIjEERS1_ISt4pairIjjESaIS7_EE@Base 3.0 + (optional)_ZN6LercNS5Lerc215GetDataTypeUsedENS0_8DataTypeEi@Base 4.0.0 + _ZN6LercNS5Lerc215PruneCandidatesERSt6vectorIdSaIdEES4_RS1_IiSaIiEEd@Base 3.0 + _ZN6LercNS5Lerc215SetNoDataValuesEbdd@Base 4.0.0 + (optional|arch-bits=32)_ZN6LercNS5Lerc216ReadMinMaxRangesIaEEbPPKhRjPKT_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc216ReadMinMaxRangesIaEEbPPKhRmPKT_@Base 4.0.0 + (optional|arch-bits=32)_ZN6LercNS5Lerc216ReadMinMaxRangesIdEEbPPKhRjPKT_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc216ReadMinMaxRangesIdEEbPPKhRmPKT_@Base 4.0.0 + (optional|arch-bits=32)_ZN6LercNS5Lerc216ReadMinMaxRangesIfEEbPPKhRjPKT_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc216ReadMinMaxRangesIfEEbPPKhRmPKT_@Base 4.0.0 + (optional|arch-bits=32)_ZN6LercNS5Lerc216ReadMinMaxRangesIhEEbPPKhRjPKT_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc216ReadMinMaxRangesIhEEbPPKhRmPKT_@Base 4.0.0 + (optional|arch-bits=32)_ZN6LercNS5Lerc216ReadMinMaxRangesIiEEbPPKhRjPKT_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc216ReadMinMaxRangesIiEEbPPKhRmPKT_@Base 4.0.0 + (optional|arch-bits=32)_ZN6LercNS5Lerc216ReadMinMaxRangesIjEEbPPKhRjPKT_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc216ReadMinMaxRangesIjEEbPPKhRmPKT_@Base 4.0.0 + (optional|arch-bits=32)_ZN6LercNS5Lerc216ReadMinMaxRangesIsEEbPPKhRjPKT_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc216ReadMinMaxRangesIsEEbPPKhRmPKT_@Base 4.0.0 + (optional|arch-bits=32)_ZN6LercNS5Lerc216ReadMinMaxRangesItEEbPPKhRjPKT_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc216ReadMinMaxRangesItEEbPPKhRmPKT_@Base 4.0.0 + (optional)_ZN6LercNS5Lerc220ReadVariableDataTypeEPPKhNS0_8DataTypeE@Base 4.0.0 + _ZN6LercNS5Lerc221SetNumBlobsMoreToComeEi@Base 4.0.0 + _ZN6LercNS5Lerc222SetEncoderToOldVersionEi@Base 3.0 + _ZN6LercNS5Lerc225ComputeChecksumFletcher32EPKhi@Base 3.0 + _ZN6LercNS5Lerc228ComputeNumBytesHeaderToWriteERKNS0_10HeaderInfoE@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIaEEjPKT_db@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIdEEjPKT_db@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIfEEjPKT_db@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIhEEjPKT_db@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIiEEjPKT_db@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIjEEjPKT_db@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteIsEEjPKT_db@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc228ComputeNumBytesNeededToWriteItEEjPKT_db@Base 3.0 + _ZN6LercNS5Lerc23SetEiiiPKh@Base 3.0 + _ZN6LercNS5Lerc24InitEv@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIaEEbPPKhRjPT_Ph@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc26DecodeIaEEbPPKhRmPT_Ph@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIdEEbPPKhRjPT_Ph@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc26DecodeIdEEbPPKhRmPT_Ph@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIfEEbPPKhRjPT_Ph@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc26DecodeIfEEbPPKhRmPT_Ph@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIhEEbPPKhRjPT_Ph@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc26DecodeIhEEbPPKhRmPT_Ph@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIiEEbPPKhRjPT_Ph@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc26DecodeIiEEbPPKhRmPT_Ph@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIjEEbPPKhRjPT_Ph@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc26DecodeIjEEbPPKhRmPT_Ph@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeIsEEbPPKhRjPT_Ph@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc26DecodeIsEEbPPKhRmPT_Ph@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc26DecodeItEEbPPKhRjPT_Ph@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc26DecodeItEEbPPKhRmPT_Ph@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc26EncodeIaEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc26EncodeIdEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc26EncodeIfEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc26EncodeIhEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc26EncodeIiEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc26EncodeIjEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc26EncodeIsEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZN6LercNS5Lerc26EncodeItEEbPKT_PPh@Base 3.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS5Lerc28ReadMaskEPPKhRj@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZN6LercNS5Lerc28ReadMaskEPPKhRm@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS5Lerc29GetRangesEPKhjPdS3_@Base 4.0.0 + _ZN6LercNS5Lerc29GetRangesEPKhmPdS3_@Base 4.0.0 + _ZN6LercNS5Lerc2C1EiiiPKh@Base 3.0 + _ZN6LercNS5Lerc2C1Ev@Base 3.0 + _ZN6LercNS5Lerc2C2EiiiPKh@Base 3.0 + _ZN6LercNS5Lerc2C2Ev@Base 3.0 + _ZN6LercNS5Lerc2D0Ev@Base 3.0 + _ZN6LercNS5Lerc2D1Ev@Base 3.0 + _ZN6LercNS5Lerc2D2Ev@Base 3.0 + (optional=templinst)_ZN6LercNS6TImageINS_4CntZEE5clearEv@Base 3.0 + (optional)_ZN6LercNS6TImageINS_4CntZEE6resizeEii@Base 4.0.0 + (optional=templinst)_ZN6LercNS6TImageINS_4CntZEEaSERKS2_@Base 3.0 + _ZN6LercNS7BitMask5ClearEv@Base 3.0 + _ZN6LercNS7BitMask7SetSizeEii@Base 3.0 + _ZN6LercNS7BitMaskC1ERKS0_@Base 3.0 + _ZN6LercNS7BitMaskC2ERKS0_@Base 3.0 + _ZN6LercNS7BitMaskD0Ev@Base 3.0 + _ZN6LercNS7BitMaskD1Ev@Base 3.0 + _ZN6LercNS7BitMaskD2Ev@Base 3.0 + _ZN6LercNS7BitMaskaSERKS0_@Base 3.0 + _ZN6LercNS7Huffman12ComputeCodesERKSt6vectorIiSaIiEE@Base 3.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS7Huffman13ReadCodeTableEPPKhRji@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZN6LercNS7Huffman13ReadCodeTableEPPKhRmi@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZN6LercNS7Huffman15BitUnStuffCodesEPPKhRjii@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZN6LercNS7Huffman15BitUnStuffCodesEPPKhRmii@Base 4.0.0 + _ZN6LercNS7Huffman18BuildTreeFromCodesERi@Base 3.0 + _ZN6LercNS7Huffman23ConvertCodesToCanonicalEv@Base 3.0 + (optional=templinst)_ZN6LercNS7Huffman4Node8FreeTreeERi@Base 3.0 + _ZN6LercNS7Huffman5ClearEv@Base 3.0 + _ZN6LercNS7Huffman8SetCodesERKSt6vectorISt4pairItjESaIS3_EE@Base 3.0 + _ZN6LercNS7Huffman9ClearTreeEv@Base 3.0 + _ZN6LercNS7HuffmanD1Ev@Base 3.0 + _ZN6LercNS7HuffmanD2Ev@Base 3.0 + _ZN6LercNS9CntZImage11numBytesFltEf@Base 3.0 + _ZN6LercNS9CntZImage11readCntTileEPPKhiiii@Base 4.0.0 + _ZN6LercNS9CntZImage11resizeFill0Eii@Base 3.0 + _ZN6LercNS9CntZImage33computeNumBytesNeededToReadHeaderEb@Base 3.0 + _ZN6LercNS9CntZImage4readEPPKhdbb@Base 4.0.0 + _ZN6LercNS9CntZImage7readFltEPPKhRfi@Base 4.0.0 + _ZN6LercNS9CntZImage9readTilesEbdiifPKh@Base 4.0.0 + _ZN6LercNS9CntZImage9readZTileEPPKhiiiidf@Base 4.0.0 + _ZN6LercNS9CntZImageC1Ev@Base 3.0 + _ZN6LercNS9CntZImageC2Ev@Base 3.0 + _ZN6LercNS9CntZImageD0Ev@Base 3.0 + _ZN6LercNS9CntZImageD1Ev@Base 3.0 + _ZN6LercNS9CntZImageD2Ev@Base 3.0 + _ZN6LercNS9Predictor11getIntDeltaENS_13PredictorTypeE@Base 4.0.0 + _ZN6LercNS9Predictor15getMaxByteDeltaENS_13PredictorTypeE@Base 4.0.0 + _ZN6LercNS9Predictor17fromDeltaAndCrossEib@Base 4.0.0 + _ZN6LercNS9Predictor7getCodeENS_13PredictorTypeE@Base 4.0.0 + _ZN6LercNS9Predictor7getTypeEc@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS9UnitTypes13setDerivativeENS_8UnitTypeEPvjii@Base 4.0.0 + _ZN6LercNS9UnitTypes13setDerivativeENS_8UnitTypeEPvmii@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS9UnitTypes16doFloatTransformEPjj@Base 4.0.0 + _ZN6LercNS9UnitTypes16doFloatTransformEPjm@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS9UnitTypes17restoreCrossBytesEiPvjjNS_8UnitTypeE@Base 4.0.0 + _ZN6LercNS9UnitTypes17restoreCrossBytesEiPvmmNS_8UnitTypeE@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS9UnitTypes17setRowsDerivativeENS_8UnitTypeEPvjjii@Base 4.0.0 + _ZN6LercNS9UnitTypes17setRowsDerivativeENS_8UnitTypeEPvmmii@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS9UnitTypes18setBlockDerivativeENS_8UnitTypeEPvjjii@Base 4.0.0 + _ZN6LercNS9UnitTypes18setBlockDerivativeENS_8UnitTypeEPvmmii@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS9UnitTypes18setCrossDerivativeENS_8UnitTypeEPvjjii@Base 4.0.0 + _ZN6LercNS9UnitTypes18setCrossDerivativeENS_8UnitTypeEPvmmii@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS9UnitTypes18undoFloatTransformEPjj@Base 4.0.0 + _ZN6LercNS9UnitTypes18undoFloatTransformEPjm@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZN6LercNS9UnitTypes20restoreBlockSequenceEiPvjjNS_8UnitTypeE@Base 4.0.0 + _ZN6LercNS9UnitTypes20restoreBlockSequenceEiPvmmNS_8UnitTypeE@Base 4.0.0 + _ZN6LercNS9UnitTypes4sizeENS_8UnitTypeE@Base 4.0.0 + _ZN6LercNS9UnitTypes4typeEib@Base 4.0.0 + _ZN6LercNS9UnitTypes8unitCodeENS_8UnitTypeE@Base 4.0.0 + _ZNK6LercNS10BitStuffer4readEPPKhRSt6vectorIjSaIjEE@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNK6LercNS11BitStuffer210BitUnStuffEPPKhRjRSt6vectorIjSaIjEEji@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS11BitStuffer210BitUnStuffEPPKhRmRSt6vectorIjSaIjEEji@Base 4.0.0 + _ZNK6LercNS11BitStuffer212EncodeSimpleEPPhRKSt6vectorIjSaIjEEi@Base 3.0 + (arch=!amd64 !arm64 !ia64 !riscv64)_ZNK6LercNS11BitStuffer225BitUnStuff_Before_Lerc2v3EPPKhRjRSt6vectorIjSaIjEEji@Base 4.0.0 + _ZNK6LercNS11BitStuffer225BitUnStuff_Before_Lerc2v3EPPKhRmRSt6vectorIjSaIjEEji@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNK6LercNS11BitStuffer26DecodeEPPKhRjRSt6vectorIjSaIjEEji@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS11BitStuffer26DecodeEPPKhRmRSt6vectorIjSaIjEEmi@Base 4.0.0 + _ZNK6LercNS11BitStuffer28BitStuffEPPhRKSt6vectorIjSaIjEEi@Base 3.0 + _ZNK6LercNS11BitStuffer29EncodeLutEPPhRKSt6vectorISt4pairIjjESaIS5_EEi@Base 3.0 + _ZNK6LercNS21LosslessFPCompression16compressedLengthEv@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNK6LercNS3RLE18computeNumBytesRLEEPKhj@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS3RLE18computeNumBytesRLEEPKhm@Base 4.0.0 + (arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNK6LercNS3RLE8compressEPKhjPPhRjb@Base 3.0 + (arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS3RLE8compressEPKhmPPhRmb@Base 4.0.0 + (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIaEEbPKT_PPhRi@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIdEEbPKT_PPhRi@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIfEEbPKT_PPhRi@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIhEEbPKT_PPhRi@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIiEEbPKT_PPhRi@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIjEEbPKT_PPhRi@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesIsEEbPKT_PPhRi@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc210WriteTilesItEEbPKT_PPhRi@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileIaEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileIhEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileIiEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileIsEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc212NumBytesTileItEEiiT_S2_NS0_8DataTypeEbRNS0_15BlockEncodeModeERKSt6vectorISt4pairIjjESaIS8_EE@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIaEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIaEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIdEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIdEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIfEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIfEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIhEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIhEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIiEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIiEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIjEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIjEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIsEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanIsEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanItEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc213DecodeHuffmanItEEbPPKhRmPT_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIaEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIdEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIfEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIhEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIiEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIjEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanIsEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc213EncodeHuffmanItEEbPKT_PPh@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIaEEbPT_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIdEEbPT_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIfEEbPT_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIhEEbPT_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIiEEbPT_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIjEEbPT_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageIsEEbPT_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc214FillConstImageItEEbPT_@Base 3.0 + _ZNK6LercNS5Lerc216DoChecksOnEncodeEPhS1_@Base 3.0 + (optional)_ZNK6LercNS5Lerc217CheckMinMaxRangesERb@Base 4.0.0 + (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIaEEbPKT_Rd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIdEEbPKT_Rd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIfEEbPKT_Rd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIhEEbPKT_Rd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIiEEbPKT_Rd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIjEEbPKT_Rd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorIsEEbPKT_Rd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc217TryRaiseMaxZErrorItEEbPKT_Rd@Base 3.0 + (optional)_ZNK6LercNS5Lerc217WriteMinMaxRangesIaEEbPKT_PPh@Base 4.0.0 + (optional)_ZNK6LercNS5Lerc217WriteMinMaxRangesIdEEbPKT_PPh@Base 4.0.0 + (optional)_ZNK6LercNS5Lerc217WriteMinMaxRangesIfEEbPKT_PPh@Base 4.0.0 + (optional)_ZNK6LercNS5Lerc217WriteMinMaxRangesIhEEbPKT_PPh@Base 4.0.0 + (optional)_ZNK6LercNS5Lerc217WriteMinMaxRangesIiEEbPKT_PPh@Base 4.0.0 + (optional)_ZNK6LercNS5Lerc217WriteMinMaxRangesIjEEbPKT_PPh@Base 4.0.0 + (optional)_ZNK6LercNS5Lerc217WriteMinMaxRangesIsEEbPKT_PPh@Base 4.0.0 + (optional)_ZNK6LercNS5Lerc217WriteMinMaxRangesItEEbPKT_PPh@Base 4.0.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIaEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIdEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIfEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIhEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIiEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIjEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesIsEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeHuffmanCodesItEEvPKT_RiRNS0_15ImageEncodeModeERSt6vectorISt4pairItjESaISA_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIaEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIdEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIfEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIhEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIiEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIjEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesIsEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc219ComputeMinMaxRangesItEEbPKT_RSt6vectorIdSaIdEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIaEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIdEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIfEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIhEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIiEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIjEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanIsEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222ComputeHistoForHuffmanItEEvPKT_RSt6vectorIiSaIiEES8_@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIaEEbPKT_dRd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIdEEbPKT_dRd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIfEEbPKT_dRd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIhEEbPKT_dRd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIiEEbPKT_dRd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIjEEbPKT_dRd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionIsEEbPKT_dRd@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc222TryBitPlaneCompressionItEEbPKT_dRd@Base 3.0 + (optional)_ZNK6LercNS5Lerc28QuantizeIiEEvPKT_iS2_RSt6vectorIjSaIjEE@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIaEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc28ReadTileIaEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIdEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc28ReadTileIdEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIfEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc28ReadTileIfEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIhEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc28ReadTileIhEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIiEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc28ReadTileIiEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIjEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc28ReadTileIjEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileIsEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc28ReadTileIsEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc28ReadTileItEEbPPKhRjPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc28ReadTileItEEbPPKhRmPT_iiiiiRSt6vectorIjSaIjEE@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIaEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc29ReadTilesIaEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIdEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc29ReadTilesIdEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIfEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc29ReadTilesIfEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIhEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc29ReadTilesIhEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIiEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc29ReadTilesIiEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIjEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc29ReadTilesIjEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesIsEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc29ReadTilesIsEEbPPKhRmPT_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS5Lerc29ReadTilesItEEbPPKhRjPT_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNK6LercNS5Lerc29ReadTilesItEEbPPKhRmPT_@Base 3.0 + _ZNK6LercNS5Lerc29WriteMaskEPPh@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIaEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIdEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIfEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIhEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIiEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIjEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc29WriteTileIsEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 + (optional=templinst)_ZNK6LercNS5Lerc29WriteTileItEEbPKT_iPPhRiiS2_S2_NS0_8DataTypeEbRKSt6vectorIjSaIjEENS0_15BlockEncodeModeERKS9_ISt4pairIjjESaISG_EE@Base 3.0 + _ZNK6LercNS7BitMask11SetAllValidEv@Base 3.0 + _ZNK6LercNS7BitMask13SetAllInvalidEv@Base 3.0 + _ZNK6LercNS7BitMask14CountValidBitsEv@Base 3.0 + _ZNK6LercNS7Huffman13BitStuffCodesEPPhii@Base 3.0 + (arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNK6LercNS7Huffman14DecodeOneValueEPPKjRjRiiS5_@Base 3.0 + (optional)_ZNK6LercNS7Huffman14DecodeOneValueEPPKjRmRiiS5_@Base 4.0.0 + _ZNK6LercNS7Huffman14WriteCodeTableEPPhi@Base 3.0 + _ZNK6LercNS7Huffman21ComputeCompressedSizeERKSt6vectorIiSaIiEERiRd@Base 3.0 + _ZNK6LercNS7Huffman24ComputeNumBytesCodeTableERi@Base 3.0 + (optional|arch-bits=32)_ZNK6LercNS7Huffman29DecodeOneValue_NoOverrunCheckEPPKjRjRiiS5_@Base 4.0.0 + (optional)_ZNK6LercNS7Huffman29DecodeOneValue_NoOverrunCheckEPPKjRmRiiS5_@Base 4.0.0 + _ZNK6LercNS7Huffman4Node9TreeToLUTEtjRSt6vectorISt4pairItjESaIS4_EE@Base 3.0 + _ZNK6LercNS7Huffman8GetRangeERiS1_S1_@Base 3.0 + _ZNK6LercNS9CntZImage13getTypeStringB5cxx11Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseISt4pairIjjESaIS1_EED1Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseISt4pairIjjESaIS1_EED2Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseISt4pairItjESaIS1_EED1Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseISt4pairItjESaIS1_EED2Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIaSaIaEED1Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIaSaIaEED2Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIdSaIdEED1Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIdSaIdEED2Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIfSaIfEED1Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIfSaIfEED2Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIhSaIhEED1Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIhSaIhEED2Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIiSaIiEED1Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIiSaIiEED2Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIjSaIjEED1Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIjSaIjEED2Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIsSaIsEED1Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseIsSaIsEED2Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseItSaItEED1Ev@Base 3.0 + (optional=templinst)_ZNSt12_Vector_baseItSaItEED2Ev@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !i386 !sh4 !x32)_ZNSt14priority_queueIN6LercNS7Huffman4NodeESt6vectorIS2_SaIS2_EESt4lessIS2_EE3popEv@Base 3.0 + (optional=templinst)_ZNSt6vectorI9TestBlockSaIS0_EE17_M_realloc_insertIJRKS0_EEEvN9__gnu_cxx17__normal_iteratorIPS0_S2_EEDpOT_@Base 4.0.0 + (optional=templinst)_ZNSt6vectorIN6LercNS7Huffman4NodeESaIS2_EE17_M_realloc_insertIJS2_EEEvN9__gnu_cxx17__normal_iteratorIPS2_S4_EEDpOT_@Base 3.0 + (optional=templinst)_ZNSt6vectorIPN6LercNS21LosslessFPCompression14outBlockBufferESaIS3_EE17_M_realloc_insertIJRKS3_EEEvN9__gnu_cxx17__normal_iteratorIPS3_S5_EEDpOT_@Base 4.0.0 + (optional=templinst)_ZNSt6vectorISt4pairIiPcESaIS2_EE17_M_realloc_insertIJS2_EEEvN9__gnu_cxx17__normal_iteratorIPS2_S4_EEDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZNSt6vectorISt4pairIjiESaIS1_EE17_M_realloc_insertIJS1_EEEvN9__gnu_cxx17__normal_iteratorIPS1_S3_EEDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorISt4pairIjjESaIS1_EE17_M_default_appendEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorISt4pairIjjESaIS1_EE17_M_default_appendEm@Base 3.0 + (optional=templinst)_ZNSt6vectorISt4pairImiESaIS1_EE17_M_realloc_insertIJS1_EEEvN9__gnu_cxx17__normal_iteratorIPS1_S3_EEDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !i386 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorISt4pairIssESaIS1_EE14_M_fill_assignEjRKS1_@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorISt4pairIssESaIS1_EE14_M_fill_assignEmRKS1_@Base 3.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorISt4pairItjESaIS1_EE17_M_default_appendEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorISt4pairItjESaIS1_EE17_M_default_appendEm@Base 3.0 + (optional|arch-bits=32)_ZNSt6vectorISt4pairItjESaIS1_EE6resizeEj@Base 4.0.0 + (optional=templinst)_ZNSt6vectorISt4pairItjESaIS1_EE6resizeEm@Base 4.0.0 + (optional)_ZNSt6vectorISt4pairItjESaIS1_EEaSERKS3_@Base 4.0.0 + (optional)_ZNSt6vectorIaSaIaEE12emplace_backIJaEEERaDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNSt6vectorIaSaIaEE17_M_default_appendEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorIaSaIaEE17_M_default_appendEm@Base 3.0 + (optional=templinst)_ZNSt6vectorIaSaIaEE17_M_realloc_insertIJaEEEvN9__gnu_cxx17__normal_iteratorIPaS1_EEDpOT_@Base 4.0.0 + (optional)_ZNSt6vectorIdSaIdEE12emplace_backIJdEEERdDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIdSaIdEE17_M_default_appendEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorIdSaIdEE17_M_default_appendEm@Base 3.0 + (optional=templinst)_ZNSt6vectorIdSaIdEE17_M_realloc_insertIJRKdEEEvN9__gnu_cxx17__normal_iteratorIPdS1_EEDpOT_@Base 3.0 + (optional=templinst)_ZNSt6vectorIdSaIdEE17_M_realloc_insertIJdEEEvN9__gnu_cxx17__normal_iteratorIPdS1_EEDpOT_@Base 4.0.0 + (optional=templinst|arch=!hurd-i386 !mipsel)_ZNSt6vectorIdSaIdEE8_M_eraseEN9__gnu_cxx17__normal_iteratorIPdS1_EE@Base 3.0 + (optional)_ZNSt6vectorIfSaIfEE12emplace_backIJfEEERfDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIfSaIfEE17_M_default_appendEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorIfSaIfEE17_M_default_appendEm@Base 3.0 + (optional=templinst)_ZNSt6vectorIfSaIfEE17_M_realloc_insertIJfEEEvN9__gnu_cxx17__normal_iteratorIPfS1_EEDpOT_@Base 4.0.0 + (optional)_ZNSt6vectorIhSaIhEE12emplace_backIJhEEERhDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIhSaIhEE17_M_default_appendEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorIhSaIhEE17_M_default_appendEm@Base 3.0 + (optional=templinst)_ZNSt6vectorIhSaIhEE17_M_realloc_insertIJRKhEEEvN9__gnu_cxx17__normal_iteratorIPhS1_EEDpOT_@Base 4.0.0 + (optional=templinst)_ZNSt6vectorIhSaIhEE17_M_realloc_insertIJhEEEvN9__gnu_cxx17__normal_iteratorIPhS1_EEDpOT_@Base 4.0.0 + (optional)_ZNSt6vectorIhSaIhEEaSERKS1_@Base 4.0.0 + (optional)_ZNSt6vectorIiSaIiEE12emplace_backIJiEEERiDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNSt6vectorIiSaIiEE17_M_default_appendEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorIiSaIiEE17_M_default_appendEm@Base 3.0 + (optional=templinst)_ZNSt6vectorIiSaIiEE17_M_realloc_insertIJRKiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_@Base 3.0 + (optional=templinst)_ZNSt6vectorIiSaIiEE17_M_realloc_insertIJiEEEvN9__gnu_cxx17__normal_iteratorIPiS1_EEDpOT_@Base 3.0 + (optional)_ZNSt6vectorIjSaIjEE12emplace_backIJjEEERjDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !i386 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIjSaIjEE14_M_fill_assignEjRKj@Base 3.0 + (optional=templinst|arch=arm64 ia64 riscv64)_ZNSt6vectorIjSaIjEE14_M_fill_assignEmRKj@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIjSaIjEE14_M_fill_insertEN9__gnu_cxx17__normal_iteratorIPjS1_EEjRKj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorIjSaIjEE14_M_fill_insertEN9__gnu_cxx17__normal_iteratorIPjS1_EEmRKj@Base 3.0 + (optional=templinst)_ZNSt6vectorIjSaIjEE14_M_insert_rvalEN9__gnu_cxx17__normal_iteratorIPKjS1_EEOj@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !mips64el !ppc64el !riscv64)_ZNSt6vectorIjSaIjEE17_M_default_appendEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorIjSaIjEE17_M_default_appendEm@Base 3.0 + (optional=templinst)_ZNSt6vectorIjSaIjEE17_M_realloc_insertIJjEEEvN9__gnu_cxx17__normal_iteratorIPjS1_EEDpOT_@Base 4.0.0 + (optional)_ZNSt6vectorIsSaIsEE12emplace_backIJsEEERsDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNSt6vectorIsSaIsEE17_M_default_appendEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorIsSaIsEE17_M_default_appendEm@Base 3.0 + (optional=templinst)_ZNSt6vectorIsSaIsEE17_M_realloc_insertIJsEEEvN9__gnu_cxx17__normal_iteratorIPsS1_EEDpOT_@Base 4.0.0 + (optional)_ZNSt6vectorItSaItEE12emplace_backIJtEEERtDpOT_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !m68k !mips64el !ppc64el !riscv64)_ZNSt6vectorItSaItEE17_M_default_appendEj@Base 3.0 + (optional=templinst|arch=amd64 arm64 ia64 riscv64)_ZNSt6vectorItSaItEE17_M_default_appendEm@Base 3.0 + (optional=templinst)_ZNSt6vectorItSaItEE17_M_realloc_insertIJtEEEvN9__gnu_cxx17__normal_iteratorIPtS1_EEDpOT_@Base 4.0.0 + (optional|arch-bits=32)_ZSt11__push_heapIN9__gnu_cxx17__normal_iteratorIPN6LercNS7Huffman4NodeESt6vectorIS4_SaIS4_EEEEiS4_NS0_5__ops14_Iter_comp_valISt4lessIS4_EEEEvT_T0_SG_T1_RT2_@Base 4.0.0 + (optional|arch-bits=32)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPaSt6vectorIaSaIaEEEEiaNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPaSt6vectorIaSaIaEEEElaNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional|arch-bits=32)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPdSt6vectorIdSaIdEEEEidNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPdSt6vectorIdSaIdEEEEldNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional|arch-bits=32)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPfSt6vectorIfSaIfEEEEifNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPfSt6vectorIfSaIfEEEElfNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional|arch-bits=32)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPhSt6vectorIhSaIhEEEEihNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPhSt6vectorIhSaIhEEEElhNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional|arch-bits=32)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEiiNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEliNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional|arch-bits=32)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPjSt6vectorIjSaIjEEEEijNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPjSt6vectorIjSaIjEEEEljNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional|arch-bits=32)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPsSt6vectorIsSaIsEEEEisNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPsSt6vectorIsSaIsEEEElsNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional|arch-bits=32)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPtSt6vectorItSaItEEEEitNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional)_ZSt13__adjust_heapIN9__gnu_cxx17__normal_iteratorIPtSt6vectorItSaItEEEEltNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_T0_SD_T1_T2_@Base 4.0.0 + (optional)_ZSt16__insertion_sortIN9__gnu_cxx17__normal_iteratorIPaSt6vectorIaSaIaEEEENS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_@Base 4.0.0 + (optional)_ZSt16__insertion_sortIN9__gnu_cxx17__normal_iteratorIPdSt6vectorIdSaIdEEEENS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_@Base 4.0.0 + (optional)_ZSt16__insertion_sortIN9__gnu_cxx17__normal_iteratorIPfSt6vectorIfSaIfEEEENS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_@Base 4.0.0 + (optional)_ZSt16__insertion_sortIN9__gnu_cxx17__normal_iteratorIPhSt6vectorIhSaIhEEEENS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_@Base 4.0.0 + (optional)_ZSt16__insertion_sortIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEENS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_@Base 4.0.0 + (optional)_ZSt16__insertion_sortIN9__gnu_cxx17__normal_iteratorIPjSt6vectorIjSaIjEEEENS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_@Base 4.0.0 + (optional)_ZSt16__insertion_sortIN9__gnu_cxx17__normal_iteratorIPsSt6vectorIsSaIsEEEENS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_@Base 4.0.0 + (optional)_ZSt16__insertion_sortIN9__gnu_cxx17__normal_iteratorIPtSt6vectorItSaItEEEENS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPaSt6vectorIaSaIaEEEEiNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPaSt6vectorIaSaIaEEEElNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPdSt6vectorIdSaIdEEEEiNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPdSt6vectorIdSaIdEEEElNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPfSt6vectorIfSaIfEEEEiNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPfSt6vectorIfSaIfEEEElNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPhSt6vectorIhSaIhEEEEiNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPhSt6vectorIhSaIhEEEElNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEEiNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEElNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPjSt6vectorIjSaIjEEEEiNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPjSt6vectorIjSaIjEEEElNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPsSt6vectorIsSaIsEEEEiNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPsSt6vectorIsSaIsEEEElNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst|arch=!amd64 !arm64 !ia64 !riscv64)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPtSt6vectorItSaItEEEEiNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + (optional=templinst)_ZSt16__introsort_loopIN9__gnu_cxx17__normal_iteratorIPtSt6vectorItSaItEEEElNS0_5__ops15_Iter_comp_iterISt7greaterIdEEEEvT_SC_T0_T1_@Base 4.0.0 + _ZTIN6LercNS10BitStufferE@Base 3.0 + _ZTIN6LercNS11BitStuffer2E@Base 3.0 + _ZTIN6LercNS3RLEE@Base 3.0 + _ZTIN6LercNS5ImageE@Base 3.0 + _ZTIN6LercNS5Lerc2E@Base 3.0 + _ZTIN6LercNS6TImageINS_4CntZEEE@Base 3.0 + _ZTIN6LercNS7BitMaskE@Base 3.0 + _ZTIN6LercNS9CntZImageE@Base 3.0 + _ZTSN6LercNS10BitStufferE@Base 3.0 + _ZTSN6LercNS11BitStuffer2E@Base 3.0 + _ZTSN6LercNS3RLEE@Base 3.0 + _ZTSN6LercNS5ImageE@Base 3.0 + _ZTSN6LercNS5Lerc2E@Base 3.0 + _ZTSN6LercNS6TImageINS_4CntZEEE@Base 3.0 + _ZTSN6LercNS7BitMaskE@Base 3.0 + _ZTSN6LercNS9CntZImageE@Base 3.0 + _ZTVN6LercNS10BitStufferE@Base 3.0 + _ZTVN6LercNS11BitStuffer2E@Base 3.0 + _ZTVN6LercNS3RLEE@Base 3.0 + _ZTVN6LercNS5Lerc2E@Base 3.0 + _ZTVN6LercNS7BitMaskE@Base 3.0 + _ZTVN6LercNS9CntZImageE@Base 3.0 + lerc_computeCompressedSize@Base 3.0 + lerc_computeCompressedSizeForVersion@Base 3.0 + lerc_computeCompressedSize_4D@Base 4.0.0 + lerc_decode@Base 3.0 + lerc_decodeToDouble@Base 3.0 + lerc_decodeToDouble_4D@Base 4.0.0 + lerc_decode_4D@Base 4.0.0 + lerc_encode@Base 3.0 + lerc_encodeForVersion@Base 3.0 + lerc_encode_4D@Base 4.0.0 + lerc_getBlobInfo@Base 3.0 + lerc_getDataRanges@Base 4.0.0 diff -Nru lerc-3.0+ds/debian/liblerc-dev.install lerc-4.0.0+ds/debian/liblerc-dev.install --- lerc-3.0+ds/debian/liblerc-dev.install 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/liblerc-dev.install 2023-09-09 08:20:46.000000000 +0000 @@ -1,3 +1,4 @@ usr/include usr/lib/*/*.a usr/lib/*/*.so +usr/lib/*/pkgconfig diff -Nru lerc-3.0+ds/debian/liblerc-dev.links lerc-4.0.0+ds/debian/liblerc-dev.links --- lerc-3.0+ds/debian/liblerc-dev.links 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/liblerc-dev.links 2023-09-09 08:20:46.000000000 +0000 @@ -1 +1 @@ -usr/share/doc/liblerc3/NOTICE usr/share/doc/liblerc-dev/NOTICE +usr/share/doc/liblerc4/NOTICE usr/share/doc/liblerc-dev/NOTICE diff -Nru lerc-3.0+ds/debian/patches/0001-Specify-the-soversion.patch lerc-4.0.0+ds/debian/patches/0001-Specify-the-soversion.patch --- lerc-3.0+ds/debian/patches/0001-Specify-the-soversion.patch 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/patches/0001-Specify-the-soversion.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -From: Antonio Valentino -Date: Sun, 31 Oct 2021 10:28:09 +0000 -Subject: Specify the soversion - -Forwarded: https://github.com/Esri/lerc/pull/188 -Applied-Upstream: https://github.com/Esri/lerc/commit/c0699c9b2a2bd213ca2db81ce5b382c908fe4891 ---- - CMakeLists.txt | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index eff3d95..ef243c9 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -19,6 +19,7 @@ add_library(Lerc ${SOURCES}) - - set_target_properties(Lerc - PROPERTIES -+ SOVERSION 3 - PUBLIC_HEADER "src/LercLib/include/Lerc_types.h;src/LercLib/include/Lerc_c_api.h") - - if(BUILD_SHARED_LIBS) diff -Nru lerc-3.0+ds/debian/patches/0002-Use-system-libLerc.patch lerc-4.0.0+ds/debian/patches/0002-Use-system-libLerc.patch --- lerc-3.0+ds/debian/patches/0002-Use-system-libLerc.patch 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/patches/0002-Use-system-libLerc.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -From: Antonio Valentino -Date: Mon, 1 Nov 2021 11:26:49 +0100 -Subject: Use system libLerc - -Forwarded: https://github.com/Esri/lerc/pull/190 ---- - OtherLanguages/Python/lerc/_lerc.py | 29 +++++++++++++++++++++-------- - 1 file changed, 21 insertions(+), 8 deletions(-) - -diff --git a/OtherLanguages/Python/lerc/_lerc.py b/OtherLanguages/Python/lerc/_lerc.py -index 46ce06b..d8052f4 100644 ---- a/OtherLanguages/Python/lerc/_lerc.py -+++ b/OtherLanguages/Python/lerc/_lerc.py -@@ -40,14 +40,27 @@ import ctypes as ct - from timeit import default_timer as timer - import platform - import os --dir_path = os.path.dirname(os.path.realpath(__file__)) -- --if platform.system() == "Windows": -- lercDll = ct.CDLL (os.path.join(dir_path, 'Lerc.dll')) --if platform.system() == "Linux": -- lercDll = ct.CDLL (os.path.join(dir_path, 'Lerc.so')) --if platform.system() == "Darwin": -- lercDll = ct.CDLL (os.path.join(dir_path, 'Lerc.dylib')) -+ -+def _get_lib(): -+ dir_path = os.path.dirname(os.path.realpath(__file__)) -+ -+ if platform.system() == "Windows": -+ lib = os.path.join(dir_path, 'Lerc.dll') -+ elif platform.system() == "Linux": -+ lib = os.path.join(dir_path, 'Lerc.so') -+ elif platform.system() == "Darwin": -+ lib = os.path.join(dir_path, 'Lerc.dylib') -+ else: -+ lib = None -+ -+ if not lib or not os.path.exists(lib): -+ import ctypes.util -+ lib = ctypes.util.find_library('Lerc') -+ -+ return lib -+ -+lercDll = ct.CDLL (_get_lib()) -+del _get_lib - - #------------------------------------------------------------------------------- - diff -Nru lerc-3.0+ds/debian/patches/series lerc-4.0.0+ds/debian/patches/series --- lerc-3.0+ds/debian/patches/series 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -0001-Specify-the-soversion.patch -0002-Use-system-libLerc.patch diff -Nru lerc-3.0+ds/debian/python3-lerc.links lerc-4.0.0+ds/debian/python3-lerc.links --- lerc-3.0+ds/debian/python3-lerc.links 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/python3-lerc.links 2023-09-09 08:20:46.000000000 +0000 @@ -1 +1 @@ -usr/share/doc/liblerc3/NOTICE usr/share/doc/python3-lerc/NOTICE +usr/share/doc/liblerc4/NOTICE usr/share/doc/python3-lerc/NOTICE diff -Nru lerc-3.0+ds/debian/rules lerc-4.0.0+ds/debian/rules --- lerc-3.0+ds/debian/rules 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/rules 2023-11-07 16:04:23.000000000 +0000 @@ -11,7 +11,7 @@ export PYBUILD_NAME=lerc %: - dh $@ --with python3,pkgkde_symbolshelper --buildsystem=cmake + dh $@ --with python3,numpy3,pkgkde_symbolshelper --buildsystem=cmake override_dh_auto_clean: dh_auto_clean @@ -38,5 +38,13 @@ dh_auto_install --builddirectory build-static dh_auto_install +# Ubuntu wants the build to fail when symbols disappear override_dh_makeshlibs: +ifneq ($(shell dpkg-vendor --query vendor),Ubuntu) dh_makeshlibs -- -v$(UPSTREAM_VERSION) -c0 +else + dh_makeshlibs -- -v$(UPSTREAM_VERSION) +endif + +override_dh_numpy3: + dh_numpy3 -p python3-lerc diff -Nru lerc-3.0+ds/debian/tests/control lerc-4.0.0+ds/debian/tests/control --- lerc-3.0+ds/debian/tests/control 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/tests/control 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -Tests: python3 -Depends: liblerc3, python3-lerc diff -Nru lerc-3.0+ds/debian/tests/python3 lerc-4.0.0+ds/debian/tests/python3 --- lerc-3.0+ds/debian/tests/python3 2021-12-08 10:58:37.000000000 +0000 +++ lerc-4.0.0+ds/debian/tests/python3 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -#!/bin/sh -set -efu - -PYS=${PYS:-"$(py3versions -r 2>/dev/null)"} -TESTPKG=${TESTPKG:-lerc} - -cd "$AUTOPKGTEST_TMP" - -for py in $PYS; do - echo "=== $py ===" - $py -c "import lerc; assert lerc.test() == 0" -done diff -Nru lerc-3.0+ds/.gitignore lerc-4.0.0+ds/.gitignore --- lerc-3.0+ds/.gitignore 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/.gitignore 2022-07-15 18:25:29.000000000 +0000 @@ -22,13 +22,13 @@ .svn build/windows/MS_VS2019/.vs/ build/conda/output/ - -cmake -OtherLanguages/Python/lerc/Lerc.dll -OtherLanguages/Python/lerc/Lerc.lib -OtherLanguages/Python/lerc/Lerc.dylib -OtherLanguages/Python/lerc/Lerc.so +cmake + +OtherLanguages/Python/lerc/*.dll +OtherLanguages/Python/lerc/*.lib +OtherLanguages/Python/lerc/*.dylib +OtherLanguages/Python/lerc/*.so* /OtherLanguages/Python/build/ /OtherLanguages/Python/dist/ /OtherLanguages/Python/*.egg* diff -Nru lerc-3.0+ds/HOWTO-RELEASE.md lerc-4.0.0+ds/HOWTO-RELEASE.md --- lerc-3.0+ds/HOWTO-RELEASE.md 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/HOWTO-RELEASE.md 2022-07-15 18:25:29.000000000 +0000 @@ -1,20 +1,23 @@ ## Release procedure -Update version numbers in src/LercLib/include/Lerc_c_api.h +Update version numbers in src/LercLib/include/Lerc_c_api.h and CMakeLists.txt ### npm release -- Config build environment. For 3.0 release: Node.js v14.17, npm 6.14.13 +- Config build environment. For 4.0 release: Node.js v16 LTS, npm 8.11 - Update the following files in OtherLanguages/js - Update version numbers in package.json - Update CHANGELOG.md - - If applicable, update the copyright year and usage in README.md and README.hbs + - Update copyright year in Gruntfile.js, README.md and README.hbs + - If applicable, update usage in README.md and README.hbs - Build and publish to npm ``` cd OtherLanguages/js -npm install && npm run lint && npm run build +npm install && npm run build +cd dist +npm pack --dry-run (check file list and version) npm login -npm pack (only needed to review tar file locally) +npm pack (stop here and review tar file locally) npm publish ``` diff -Nru lerc-3.0+ds/Lerc.pc.in lerc-4.0.0+ds/Lerc.pc.in --- lerc-3.0+ds/Lerc.pc.in 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/Lerc.pc.in 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,12 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ +libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ + +Name: @PROJECT_NAME@ +Description: @PROJECT_DESCRIPTION@ +URL: @PROJECT_HOMEPAGE_URL@ +Version: @PROJECT_VERSION@ +Cflags: -I${includedir} +Cflags.private: -DLERC_STATIC +Libs: -L${libdir} -lLerc diff -Nru lerc-3.0+ds/NOTICE lerc-4.0.0+ds/NOTICE --- lerc-3.0+ds/NOTICE 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/NOTICE 2022-07-15 18:25:29.000000000 +0000 @@ -1,6 +1,6 @@ LERC -Copyright 2015-2018 Esri +Copyright 2015-2022 Esri This software embodiment is an implementation of diff -Nru lerc-3.0+ds/OtherLanguages/CSharp/LercDecode.cs lerc-4.0.0+ds/OtherLanguages/CSharp/LercDecode.cs --- lerc-3.0+ds/OtherLanguages/CSharp/LercDecode.cs 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/CSharp/LercDecode.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,252 +0,0 @@ -/* -Copyright 2016 Esri - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -A local copy of the license and additional notices are located with the -source distribution at: - -http://github.com/Esri/lerc/ - -Contributors: Thomas Maurer, Wenxue Ju -*/ - -using System; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; -using System.IO; - -namespace Lerc2017 -{ - class LercDecode - { - const string lercDll = "Lerc.dll"; - - // from Lerc_c_api.h : - // - // typedef unsigned int lerc_status; - // - // // Call this function to get info about the compressed Lerc blob. Optional. - // // Info returned in infoArray is { version, dataType, nDim, nCols, nRows, nBands, nValidPixels, blobSize, nMasks }, see Lerc_types.h . - // // Info returned in dataRangeArray is { zMin, zMax, maxZErrorUsed }, see Lerc_types.h . - // // If more than 1 band the data range [zMin, zMax] is over all bands. - // - // lerc_status lerc_getBlobInfo(const unsigned char* pLercBlob, unsigned int blobSize, - // unsigned int* infoArray, double* dataRangeArray, int infoArraySize, int dataRangeArraySize); - - [DllImport(lercDll)] - public static extern UInt32 lerc_getBlobInfo(byte[] pLercBlob, UInt32 blobSize, UInt32[] infoArray, double[] dataRangeArray, int infoArraySize, int dataRangeArraySize); - - public enum DataType { dt_char, dt_uchar, dt_short, dt_ushort, dt_int, dt_uint, dt_float, dt_double } - - // Lerc decode functions for all Lerc compressed data types - - // from Lerc_c_api.h : - // - // // Decode the compressed Lerc blob into a raw data array. - // // The data array must have been allocated to size (nDim * nCols * nRows * nBands * sizeof(dataType)). - // // The valid bytes array, if not 0, must have been allocated to size (nCols * nRows * nMasks). - // - // lerc_status lerc_decode( - // const unsigned char* pLercBlob, // Lerc blob to decode - // unsigned int blobSize, // blob size in bytes - // int nMasks, // 0, 1, or nBands; return as many masks in the next array - // unsigned char* pValidBytes, // gets filled if not null ptr, even if all valid - // int nDim, // number of values per pixel - // int nCols, int nRows, int nBands, // number of columns, rows, bands - // unsigned int dataType, // data type of outgoing array - // void* pData); // outgoing data array - - [DllImport(lercDll)] - public static extern UInt32 lerc_decode(byte[] pLercBlob, UInt32 blobSize, int nMasks, byte[] pValidBytes, int nDim, int nCols, int nRows, int nBands, int dataType, sbyte[] pData); - [DllImport(lercDll)] - public static extern UInt32 lerc_decode(byte[] pLercBlob, UInt32 blobSize, int nMasks, byte[] pValidBytes, int nDim, int nCols, int nRows, int nBands, int dataType, byte[] pData); - [DllImport(lercDll)] - public static extern UInt32 lerc_decode(byte[] pLercBlob, UInt32 blobSize, int nMasks, byte[] pValidBytes, int nDim, int nCols, int nRows, int nBands, int dataType, short[] pData); - [DllImport(lercDll)] - public static extern UInt32 lerc_decode(byte[] pLercBlob, UInt32 blobSize, int nMasks, byte[] pValidBytes, int nDim, int nCols, int nRows, int nBands, int dataType, ushort[] pData); - [DllImport(lercDll)] - public static extern UInt32 lerc_decode(byte[] pLercBlob, UInt32 blobSize, int nMasks, byte[] pValidBytes, int nDim, int nCols, int nRows, int nBands, int dataType, Int32[] pData); - [DllImport(lercDll)] - public static extern UInt32 lerc_decode(byte[] pLercBlob, UInt32 blobSize, int nMasks, byte[] pValidBytes, int nDim, int nCols, int nRows, int nBands, int dataType, UInt32[] pData); - [DllImport(lercDll)] - public static extern UInt32 lerc_decode(byte[] pLercBlob, UInt32 blobSize, int nMasks, byte[] pValidBytes, int nDim, int nCols, int nRows, int nBands, int dataType, float[] pData); - [DllImport(lercDll)] - public static extern UInt32 lerc_decode(byte[] pLercBlob, UInt32 blobSize, int nMasks, byte[] pValidBytes, int nDim, int nCols, int nRows, int nBands, int dataType, double[] pData); - - // if you are lazy, don't want to deal with generic / templated code, and don't care about wasting memory: - // this function decodes the pixel values into a tile of data type double, independent of the compressed data type. - - [DllImport(lercDll)] - public static extern UInt32 lerc_decodeToDouble(byte[] pLercBlob, UInt32 blobSize, int nMasks, byte[] pValidBytes, int nDim, int nCols, int nRows, int nBands, double[] pData); - } - - class GenericPixelLoop - { - public static void GetMinMax(T[] pData, int nMasks, byte[] pValidBytes, int nDim, int nCols, int nRows, int nBands) - { - double zMin = 1e40; - double zMax = -zMin; - - // access the pixels; here, get the data range over all bands - for (int iBand = 0; iBand < nBands; iBand++) - { - int p0 = 0; - if (nMasks > 1) - p0 = nCols * nRows * iBand; - - int k0 = nCols * nRows * iBand; - for (int k = 0, i = 0; i < nRows; i++) - for (int j = 0; j < nCols; j++, k++) - if (0 == nMasks || 1 == pValidBytes[p0 + k]) // pixel is valid - { - for (int m = 0; m < nDim; m++) - { - double z = Convert.ToDouble(pData[(k0 + k) * nDim + m]); - zMin = Math.Min(zMin, z); - zMax = Math.Max(zMax, z); - } - } - } - - Console.WriteLine("[zMin, zMax] = [{0}, {1}]", zMin, zMax); - } - } - - class Program - { - static void Main(string[] args) - { - //byte[] pLercBlob = File.ReadAllBytes(@"california_400_400_1_float.lerc2"); - //byte[] pLercBlob = File.ReadAllBytes(@"bluemarble_256_256_3_byte.lerc2"); - byte[] pLercBlob = File.ReadAllBytes(@"lerc_level_0.lerc2"); - - String[] infoLabels = { "version", "data type", "nDim", "nCols", "nRows", "nBands", "num valid pixels", "blob size", "nMasks" }; - String[] dataRangeLabels = { "zMin", "zMax", "maxZErrorUsed" }; - - int infoArrSize = infoLabels.Count(); - int dataRangeArrSize = dataRangeLabels.Count(); - - UInt32[] infoArr = new UInt32[infoArrSize]; - double[] dataRangeArr = new double[dataRangeArrSize]; - - UInt32 hr = LercDecode.lerc_getBlobInfo(pLercBlob, (UInt32)pLercBlob.Length, infoArr, dataRangeArr, infoArrSize, dataRangeArrSize); - if (hr > 0) - { - Console.WriteLine("function lerc_getBlobInfo(...) failed with error code {0}.", hr); - return; - } - - Console.WriteLine("Lerc blob info:"); - for (int i = 0; i < infoArrSize; i++) - Console.WriteLine("{0} = {1}", infoLabels[i], infoArr[i]); - for (int i = 0; i < dataRangeArrSize; i++) - Console.WriteLine("{0} = {1}", dataRangeLabels[i], dataRangeArr[i]); - - int lercVersion = (int)infoArr[0]; - int dataType = (int)infoArr[1]; - int nDim = (int)infoArr[2]; - int nCols = (int)infoArr[3]; - int nRows = (int)infoArr[4]; - int nBands = (int)infoArr[5]; - int nMasks = (int)infoArr[8]; - - Console.WriteLine("[zMin, zMax] = [{0}, {1}]", dataRangeArr[0], dataRangeArr[1]); - - byte[] pValidBytes = new byte[nCols * nRows * nMasks]; - uint nValues = (uint)(nDim * nCols * nRows * nBands); - - Stopwatch sw = new Stopwatch(); - sw.Start(); - - switch ((LercDecode.DataType)dataType) - { - case LercDecode.DataType.dt_char: - { - sbyte[] pData = new sbyte[nValues]; - hr = LercDecode.lerc_decode(pLercBlob, (UInt32)pLercBlob.Length, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dataType, pData); - if (hr == 0) - GenericPixelLoop.GetMinMax(pData, nMasks, pValidBytes, nDim, nCols, nRows, nBands); - break; - } - case LercDecode.DataType.dt_uchar: - { - byte[] pData = new byte[nValues]; - hr = LercDecode.lerc_decode(pLercBlob, (UInt32)pLercBlob.Length, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dataType, pData); - if (hr == 0) - GenericPixelLoop.GetMinMax(pData, nMasks, pValidBytes, nDim, nCols, nRows, nBands); - break; - } - case LercDecode.DataType.dt_short: - { - short[] pData = new short[nValues]; - hr = LercDecode.lerc_decode(pLercBlob, (UInt32)pLercBlob.Length, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dataType, pData); - if (hr == 0) - GenericPixelLoop.GetMinMax(pData, nMasks, pValidBytes, nDim, nCols, nRows, nBands); - break; - } - case LercDecode.DataType.dt_ushort: - { - ushort[] pData = new ushort[nValues]; - hr = LercDecode.lerc_decode(pLercBlob, (UInt32)pLercBlob.Length, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dataType, pData); - if (hr == 0) - GenericPixelLoop.GetMinMax(pData, nMasks, pValidBytes, nDim, nCols, nRows, nBands); - break; - } - case LercDecode.DataType.dt_int: - { - Int32[] pData = new Int32[nValues]; - hr = LercDecode.lerc_decode(pLercBlob, (UInt32)pLercBlob.Length, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dataType, pData); - if (hr == 0) - GenericPixelLoop.GetMinMax(pData, nMasks, pValidBytes, nDim, nCols, nRows, nBands); - break; - } - case LercDecode.DataType.dt_uint: - { - UInt32[] pData = new UInt32[nValues]; - hr = LercDecode.lerc_decode(pLercBlob, (UInt32)pLercBlob.Length, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dataType, pData); - if (hr == 0) - GenericPixelLoop.GetMinMax(pData, nMasks, pValidBytes, nDim, nCols, nRows, nBands); - break; - } - case LercDecode.DataType.dt_float: - { - float[] pData = new float[nValues]; - hr = LercDecode.lerc_decode(pLercBlob, (UInt32)pLercBlob.Length, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dataType, pData); - if (hr == 0) - GenericPixelLoop.GetMinMax(pData, nMasks, pValidBytes, nDim, nCols, nRows, nBands); - break; - } - case LercDecode.DataType.dt_double: - { - double[] pData = new double[nValues]; - hr = LercDecode.lerc_decode(pLercBlob, (UInt32)pLercBlob.Length, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dataType, pData); - if (hr == 0) - GenericPixelLoop.GetMinMax(pData, nMasks, pValidBytes, nDim, nCols, nRows, nBands); - break; - } - } - - if (hr > 0) - { - Console.WriteLine("function lerc_decode(...) failed with error code {0}.", hr); - return; - } - - sw.Stop(); - Console.WriteLine("total time for Lerc decode and C# pixel loop = {0} ms", sw.ElapsedMilliseconds); - //Console.ReadKey(); - } - } -} diff -Nru lerc-3.0+ds/OtherLanguages/js/CHANGELOG.md lerc-4.0.0+ds/OtherLanguages/js/CHANGELOG.md --- lerc-3.0+ds/OtherLanguages/js/CHANGELOG.md 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/js/CHANGELOG.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](http://keepachangelog.com/) -and this project adheres to [Semantic Versioning](http://semver.org/). - -## [Unreleased][HEAD] - -## [3.0.0] - 2021-07-30 - -The decoder is in sync with ArcMap 10.8.1 and ArcGIS Pro 2.8. LERC encoded binary blobs from any previous version of ArcMap or ArcGIS Pro can also be read / decoded. - -### Added -* Added an option to return decoded n-dim blob using pixel-interleaved layout - -### Changed -* Upgrade Lerc codec to new version Lerc 2.5. - -## [2.0.0] - 2018-11-06 - -The decoder is in sync with ArcMap 10.7 and ArcGIS Pro 2.3. LERC encoded binary blobs from any previous version of ArcMap or ArcGIS Pro can also be read / decoded. - -### Added -* Extend from one value per pixel to nDim values per pixel. - -### Changed -* Upgrade Lerc codec to new version Lerc 2.4. - -## 1.0.1 - 2017-02-18 - -### Fixed - -* resolved a Huffman code table parsing issue [#31](https://github.com/Esri/lerc/pull/31) - -## 1.0 - 2016-11-30 - -This LERC API JavaScript decoder is in sync with ArcMap 10.5 and ArcGIS Pro 1.4. LERC encoded binary blobs from any previous version of ArcMap or ArcGIS Pro can be read / decoded as well. - -### What will trigger a major version change - -- A change to this LERC API that is not backwards compatible and requires users to update / change their code in order to use an upgraded .dll or .so file. -- A change to the LERC bitstream that is not backwards compatible and requires users to upgrade their LERC encoder and / or decoder. - -[2.0.0]: https://github.com/Esri/lerc/compare/v1.0.1...v2.0 "v2.0" -[HEAD]: https://github.com/Esri/lerc/compare/v2.0...HEAD "Unreleased Changes" diff -Nru lerc-3.0+ds/OtherLanguages/js/.gitignore lerc-4.0.0+ds/OtherLanguages/js/.gitignore --- lerc-3.0+ds/OtherLanguages/js/.gitignore 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/js/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -node_modules - -# built library -LercDecode.min.js - -npm-debug.log diff -Nru lerc-3.0+ds/OtherLanguages/js/index.htm lerc-4.0.0+ds/OtherLanguages/js/index.htm --- lerc-3.0+ds/OtherLanguages/js/index.htm 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/js/index.htm 1970-01-01 00:00:00.000000000 +0000 @@ -1,204 +0,0 @@ - - - - - - - - - - -
Image Header Info
-
Hover over to see current pixel value
- - -
- -
- - - diff -Nru lerc-3.0+ds/OtherLanguages/js/LercDecode.js lerc-4.0.0+ds/OtherLanguages/js/LercDecode.js --- lerc-3.0+ds/OtherLanguages/js/LercDecode.js 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/js/LercDecode.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,2324 +0,0 @@ -/* jshint forin: false, bitwise: false */ -/* -Copyright 2015-2021 Esri - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -A copy of the license and additional notices are located with the -source distribution at: - -http://github.com/Esri/lerc/ - -Contributors: Johannes Schmid, (LERC v1) - Chayanika Khatua, (LERC v1) - Wenxue Ju (LERC v1, v2.x) -*/ - -/* Copyright 2015-2021 Esri. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 @preserve */ - -/** - * a module for decoding LERC blobs - * @module Lerc - */ -(function() { - //this decoder supports all lerc versions, each version has its own class (LercDecode and Lerc2Decode). - //the exported module handles format variation autoamtically. - - //the original LercDecode for Version 1 - var LercDecode = (function() { - - // Note: currently, this module only has an implementation for decoding LERC data, not encoding. The name of - // the class was chosen to be future proof. - - var CntZImage = {}; - - CntZImage.defaultNoDataValue = -3.4027999387901484e+38; // smallest Float32 value - - /** - * Decode a LERC byte stream and return an object containing the pixel data and some required and optional - * information about it, such as the image's width and height. - * - * @param {ArrayBuffer} input The LERC input byte stream - * @param {object} [options] Decoding options, containing any of the following properties: - * @config {number} [inputOffset = 0] - * Skip the first inputOffset bytes of the input byte stream. A valid LERC file is expected at that position. - * @config {Uint8Array} [encodedMask = null] - * If specified, the decoder will not read mask information from the input and use the specified encoded - * mask data instead. Mask header/data must not be present in the LERC byte stream in this case. - * @config {number} [noDataValue = LercCode.defaultNoDataValue] - * Pixel value to use for masked pixels. - * @config {ArrayBufferView|Array} [pixelType = Float32Array] - * The desired type of the pixelData array in the return value. Note that it is the caller's responsibility to - * provide an appropriate noDataValue if the default pixelType is overridden. - * @config {boolean} [returnMask = false] - * If true, the return value will contain a maskData property of type Uint8Array which has one element per - * pixel, the value of which is 1 or 0 depending on whether that pixel's data is present or masked. If the - * input LERC data does not contain a mask, maskData will not be returned. - * @config {boolean} [returnEncodedMask = false] - * If true, the return value will contain a encodedMaskData property, which can be passed into encode() as - * encodedMask. - * @config {boolean} [returnFileInfo = false] - * If true, the return value will have a fileInfo property that contains metadata obtained from the - * LERC headers and the decoding process. - * @config {boolean} [computeUsedBitDepths = false] - * If true, the fileInfo property in the return value will contain the set of all block bit depths - * encountered during decoding. Will only have an effect if returnFileInfo option is true. - * @returns {{width, height, pixelData, minValue, maxValue, noDataValue, maskData, encodedMaskData, fileInfo}} - */ - CntZImage.decode = function(input, options) { - options = options || {}; - - var skipMask = options.encodedMaskData || (options.encodedMaskData === null); - var parsedData = parse(input, options.inputOffset || 0, skipMask); - - var noDataValue = (options.noDataValue !== null) ? options.noDataValue : CntZImage.defaultNoDataValue; - - var uncompressedData = uncompressPixelValues(parsedData, options.pixelType || Float32Array, - options.encodedMaskData, noDataValue, options.returnMask); - - var result = { - width: parsedData.width, - height: parsedData.height, - pixelData: uncompressedData.resultPixels, - minValue: uncompressedData.minValue, - maxValue: parsedData.pixels.maxValue, - noDataValue: noDataValue - }; - - if (uncompressedData.resultMask) { - result.maskData = uncompressedData.resultMask; - } - - if (options.returnEncodedMask && parsedData.mask) { - result.encodedMaskData = parsedData.mask.bitset ? parsedData.mask.bitset : null; - } - - if (options.returnFileInfo) { - result.fileInfo = formatFileInfo(parsedData); - if (options.computeUsedBitDepths) { - result.fileInfo.bitDepths = computeUsedBitDepths(parsedData); - } - } - - return result; - }; - - var uncompressPixelValues = function(data, TypedArrayClass, maskBitset, noDataValue, storeDecodedMask) { - var blockIdx = 0; - var numX = data.pixels.numBlocksX; - var numY = data.pixels.numBlocksY; - var blockWidth = Math.floor(data.width / numX); - var blockHeight = Math.floor(data.height / numY); - var scale = 2 * data.maxZError; - var minValue = Number.MAX_VALUE, currentValue; - maskBitset = maskBitset || ((data.mask) ? data.mask.bitset : null); - - var resultPixels, resultMask; - resultPixels = new TypedArrayClass(data.width * data.height); - if (storeDecodedMask && maskBitset) { - resultMask = new Uint8Array(data.width * data.height); - } - var blockDataBuffer = new Float32Array(blockWidth * blockHeight); - - var xx, yy; - for (var y = 0; y <= numY; y++) { - var thisBlockHeight = (y !== numY) ? blockHeight : (data.height % numY); - if (thisBlockHeight === 0) { - continue; - } - for (var x = 0; x <= numX; x++) { - var thisBlockWidth = (x !== numX) ? blockWidth : (data.width % numX); - if (thisBlockWidth === 0) { - continue; - } - - var outPtr = y * data.width * blockHeight + x * blockWidth; - var outStride = data.width - thisBlockWidth; - - var block = data.pixels.blocks[blockIdx]; - - var blockData, blockPtr, constValue; - if (block.encoding < 2) { - // block is either uncompressed or bit-stuffed (encodings 0 and 1) - if (block.encoding === 0) { - // block is uncompressed - blockData = block.rawData; - } else { - // block is bit-stuffed - unstuff(block.stuffedData, block.bitsPerPixel, block.numValidPixels, block.offset, scale, blockDataBuffer, data.pixels.maxValue); - blockData = blockDataBuffer; - } - blockPtr = 0; - } - else if (block.encoding === 2) { - // block is all 0 - constValue = 0; - } - else { - // block has constant value (encoding === 3) - constValue = block.offset; - } - - var maskByte; - if (maskBitset) { - for (yy = 0; yy < thisBlockHeight; yy++) { - if (outPtr & 7) { - // - maskByte = maskBitset[outPtr >> 3]; - maskByte <<= outPtr & 7; - } - for (xx = 0; xx < thisBlockWidth; xx++) { - if (!(outPtr & 7)) { - // read next byte from mask - maskByte = maskBitset[outPtr >> 3]; - } - if (maskByte & 128) { - // pixel data present - if (resultMask) { - resultMask[outPtr] = 1; - } - currentValue = (block.encoding < 2) ? blockData[blockPtr++] : constValue; - minValue = minValue > currentValue ? currentValue : minValue; - resultPixels[outPtr++] = currentValue; - } else { - // pixel data not present - if (resultMask) { - resultMask[outPtr] = 0; - } - resultPixels[outPtr++] = noDataValue; - } - maskByte <<= 1; - } - outPtr += outStride; - } - } else { - // mask not present, simply copy block over - if (block.encoding < 2) { - // duplicating this code block for performance reasons - // blockData case: - for (yy = 0; yy < thisBlockHeight; yy++) { - for (xx = 0; xx < thisBlockWidth; xx++) { - currentValue = blockData[blockPtr++]; - minValue = minValue > currentValue ? currentValue : minValue; - resultPixels[outPtr++] = currentValue; - } - outPtr += outStride; - } - } - else { - // constValue case: - minValue = minValue > constValue ? constValue : minValue; - for (yy = 0; yy < thisBlockHeight; yy++) { - for (xx = 0; xx < thisBlockWidth; xx++) { - resultPixels[outPtr++] = constValue; - } - outPtr += outStride; - } - } - } - if ((block.encoding === 1) && (blockPtr !== block.numValidPixels)) { - throw "Block and Mask do not match"; - } - blockIdx++; - } - } - - return { - resultPixels: resultPixels, - resultMask: resultMask, - minValue: minValue - }; - }; - - var formatFileInfo = function(data) { - return { - "fileIdentifierString": data.fileIdentifierString, - "fileVersion": data.fileVersion, - "imageType": data.imageType, - "height": data.height, - "width": data.width, - "maxZError": data.maxZError, - "eofOffset": data.eofOffset, - "mask": data.mask ? { - "numBlocksX": data.mask.numBlocksX, - "numBlocksY": data.mask.numBlocksY, - "numBytes": data.mask.numBytes, - "maxValue": data.mask.maxValue - } : null, - "pixels": { - "numBlocksX": data.pixels.numBlocksX, - "numBlocksY": data.pixels.numBlocksY, - "numBytes": data.pixels.numBytes, - "maxValue": data.pixels.maxValue, - "noDataValue": data.noDataValue - } - }; - }; - - var computeUsedBitDepths = function(data) { - var numBlocks = data.pixels.numBlocksX * data.pixels.numBlocksY; - var bitDepths = {}; - for (var i = 0; i < numBlocks; i++) { - var block = data.pixels.blocks[i]; - if (block.encoding === 0) { - bitDepths.float32 = true; - } else if (block.encoding === 1) { - bitDepths[block.bitsPerPixel] = true; - } else { - bitDepths[0] = true; - } - } - - return Object.keys(bitDepths); - }; - - var parse = function(input, fp, skipMask) { - var data = {}; - - // File header - var fileIdView = new Uint8Array(input, fp, 10); - data.fileIdentifierString = String.fromCharCode.apply(null, fileIdView); - if (data.fileIdentifierString.trim() !== "CntZImage") { - throw "Unexpected file identifier string: " + data.fileIdentifierString; - } - fp += 10; - var view = new DataView(input, fp, 24); - data.fileVersion = view.getInt32(0, true); - data.imageType = view.getInt32(4, true); - data.height = view.getUint32(8, true); - data.width = view.getUint32(12, true); - data.maxZError = view.getFloat64(16, true); - fp += 24; - - // Mask Header - if (!skipMask) { - view = new DataView(input, fp, 16); - data.mask = {}; - data.mask.numBlocksY = view.getUint32(0, true); - data.mask.numBlocksX = view.getUint32(4, true); - data.mask.numBytes = view.getUint32(8, true); - data.mask.maxValue = view.getFloat32(12, true); - fp += 16; - - // Mask Data - if (data.mask.numBytes > 0) { - var bitset = new Uint8Array(Math.ceil(data.width * data.height / 8)); - view = new DataView(input, fp, data.mask.numBytes); - var cnt = view.getInt16(0, true); - var ip = 2, op = 0; - do { - if (cnt > 0) { - while (cnt--) { bitset[op++] = view.getUint8(ip++); } - } else { - var val = view.getUint8(ip++); - cnt = -cnt; - while (cnt--) { bitset[op++] = val; } - } - cnt = view.getInt16(ip, true); - ip += 2; - } while (ip < data.mask.numBytes); - if ((cnt !== -32768) || (op < bitset.length)) { - throw "Unexpected end of mask RLE encoding"; - } - data.mask.bitset = bitset; - fp += data.mask.numBytes; - } - else if ((data.mask.numBytes | data.mask.numBlocksY | data.mask.maxValue) === 0) { // Special case, all nodata - data.mask.bitset = new Uint8Array(Math.ceil(data.width * data.height / 8)); - } - } - - // Pixel Header - view = new DataView(input, fp, 16); - data.pixels = {}; - data.pixels.numBlocksY = view.getUint32(0, true); - data.pixels.numBlocksX = view.getUint32(4, true); - data.pixels.numBytes = view.getUint32(8, true); - data.pixels.maxValue = view.getFloat32(12, true); - fp += 16; - - var numBlocksX = data.pixels.numBlocksX; - var numBlocksY = data.pixels.numBlocksY; - // the number of blocks specified in the header does not take into account the blocks at the end of - // each row/column with a special width/height that make the image complete in case the width is not - // evenly divisible by the number of blocks. - var actualNumBlocksX = numBlocksX + ((data.width % numBlocksX) > 0 ? 1 : 0); - var actualNumBlocksY = numBlocksY + ((data.height % numBlocksY) > 0 ? 1 : 0); - data.pixels.blocks = new Array(actualNumBlocksX * actualNumBlocksY); - var blockI = 0; - for (var blockY = 0; blockY < actualNumBlocksY; blockY++) { - for (var blockX = 0; blockX < actualNumBlocksX; blockX++) { - - // Block - var size = 0; - var bytesLeft = input.byteLength - fp; - view = new DataView(input, fp, Math.min(10, bytesLeft)); - var block = {}; - data.pixels.blocks[blockI++] = block; - var headerByte = view.getUint8(0); size++; - block.encoding = headerByte & 63; - if (block.encoding > 3) { - throw "Invalid block encoding (" + block.encoding + ")"; - } - if (block.encoding === 2) { - fp++; - continue; - } - if ((headerByte !== 0) && (headerByte !== 2)) { - headerByte >>= 6; - block.offsetType = headerByte; - if (headerByte === 2) { - block.offset = view.getInt8(1); size++; - } else if (headerByte === 1) { - block.offset = view.getInt16(1, true); size += 2; - } else if (headerByte === 0) { - block.offset = view.getFloat32(1, true); size += 4; - } else { - throw "Invalid block offset type"; - } - - if (block.encoding === 1) { - headerByte = view.getUint8(size); size++; - block.bitsPerPixel = headerByte & 63; - headerByte >>= 6; - block.numValidPixelsType = headerByte; - if (headerByte === 2) { - block.numValidPixels = view.getUint8(size); size++; - } else if (headerByte === 1) { - block.numValidPixels = view.getUint16(size, true); size += 2; - } else if (headerByte === 0) { - block.numValidPixels = view.getUint32(size, true); size += 4; - } else { - throw "Invalid valid pixel count type"; - } - } - } - fp += size; - - if (block.encoding === 3) { - continue; - } - - var arrayBuf, store8; - if (block.encoding === 0) { - var numPixels = (data.pixels.numBytes - 1) / 4; - if (numPixels !== Math.floor(numPixels)) { - throw "uncompressed block has invalid length"; - } - arrayBuf = new ArrayBuffer(numPixels * 4); - store8 = new Uint8Array(arrayBuf); - store8.set(new Uint8Array(input, fp, numPixels * 4)); - var rawData = new Float32Array(arrayBuf); - block.rawData = rawData; - fp += numPixels * 4; - } else if (block.encoding === 1) { - var dataBytes = Math.ceil(block.numValidPixels * block.bitsPerPixel / 8); - var dataWords = Math.ceil(dataBytes / 4); - arrayBuf = new ArrayBuffer(dataWords * 4); - store8 = new Uint8Array(arrayBuf); - store8.set(new Uint8Array(input, fp, dataBytes)); - block.stuffedData = new Uint32Array(arrayBuf); - fp += dataBytes; - } - } - } - data.eofOffset = fp; - return data; - }; - - var unstuff = function(src, bitsPerPixel, numPixels, offset, scale, dest, maxValue) { - var bitMask = (1 << bitsPerPixel) - 1; - var i = 0, o; - var bitsLeft = 0; - var n, buffer; - var nmax = Math.ceil((maxValue - offset) / scale); - // get rid of trailing bytes that are already part of next block - var numInvalidTailBytes = src.length * 4 - Math.ceil(bitsPerPixel * numPixels / 8); - src[src.length - 1] <<= 8 * numInvalidTailBytes; - - for (o = 0; o < numPixels; o++) { - if (bitsLeft === 0) { - buffer = src[i++]; - bitsLeft = 32; - } - if (bitsLeft >= bitsPerPixel) { - n = (buffer >>> (bitsLeft - bitsPerPixel)) & bitMask; - bitsLeft -= bitsPerPixel; - } else { - var missingBits = (bitsPerPixel - bitsLeft); - n = ((buffer & bitMask) << missingBits) & bitMask; - buffer = src[i++]; - bitsLeft = 32 - missingBits; - n += (buffer >>> bitsLeft); - } - //pixel values may exceed max due to quantization - dest[o] = n < nmax ? offset + n * scale : maxValue; - } - return dest; - }; - - return CntZImage; - })(); - - //version 2. Supports 2.1, 2.2, 2.3 - var Lerc2Decode = (function() { - "use strict"; - // Note: currently, this module only has an implementation for decoding LERC data, not encoding. The name of - // the class was chosen to be future proof, following LercDecode. - - /***************************************** - * private static class bitsutffer used by Lerc2Decode - *******************************************/ - var BitStuffer = { - //methods ending with 2 are for the new byte order used by Lerc2.3 and above. - //originalUnstuff is used to unpack Huffman code table. code is duplicated to unstuffx for performance reasons. - unstuff: function(src, dest, bitsPerPixel, numPixels, lutArr, offset, scale, maxValue) { - var bitMask = (1 << bitsPerPixel) - 1; - var i = 0, o; - var bitsLeft = 0; - var n, buffer, missingBits, nmax; - - // get rid of trailing bytes that are already part of next block - var numInvalidTailBytes = src.length * 4 - Math.ceil(bitsPerPixel * numPixels / 8); - src[src.length - 1] <<= 8 * numInvalidTailBytes; - if (lutArr) { - for (o = 0; o < numPixels; o++) { - if (bitsLeft === 0) { - buffer = src[i++]; - bitsLeft = 32; - } - if (bitsLeft >= bitsPerPixel) { - n = (buffer >>> (bitsLeft - bitsPerPixel)) & bitMask; - bitsLeft -= bitsPerPixel; - } - else { - missingBits = (bitsPerPixel - bitsLeft); - n = ((buffer & bitMask) << missingBits) & bitMask; - buffer = src[i++]; - bitsLeft = 32 - missingBits; - n += (buffer >>> bitsLeft); - } - dest[o] = lutArr[n];//offset + lutArr[n] * scale; - } - } - else { - nmax = Math.ceil((maxValue - offset) / scale); - for (o = 0; o < numPixels; o++) { - if (bitsLeft === 0) { - buffer = src[i++]; - bitsLeft = 32; - } - if (bitsLeft >= bitsPerPixel) { - n = (buffer >>> (bitsLeft - bitsPerPixel)) & bitMask; - bitsLeft -= bitsPerPixel; - } - else { - missingBits = (bitsPerPixel - bitsLeft); - n = ((buffer & bitMask) << missingBits) & bitMask; - buffer = src[i++]; - bitsLeft = 32 - missingBits; - n += (buffer >>> bitsLeft); - } - //pixel values may exceed max due to quantization - dest[o] = n < nmax ? offset + n * scale : maxValue; - } - } - }, - - unstuffLUT: function(src, bitsPerPixel, numPixels, offset, scale, maxValue) { - var bitMask = (1 << bitsPerPixel) - 1; - var i = 0, o = 0, missingBits = 0, bitsLeft = 0, n = 0; - var buffer; - var dest = []; - - // get rid of trailing bytes that are already part of next block - var numInvalidTailBytes = src.length * 4 - Math.ceil(bitsPerPixel * numPixels / 8); - src[src.length - 1] <<= 8 * numInvalidTailBytes; - - var nmax = Math.ceil((maxValue - offset) / scale); - for (o = 0; o < numPixels; o++) { - if (bitsLeft === 0) { - buffer = src[i++]; - bitsLeft = 32; - } - if (bitsLeft >= bitsPerPixel) { - n = (buffer >>> (bitsLeft - bitsPerPixel)) & bitMask; - bitsLeft -= bitsPerPixel; - } else { - missingBits = (bitsPerPixel - bitsLeft); - n = ((buffer & bitMask) << missingBits) & bitMask; - buffer = src[i++]; - bitsLeft = 32 - missingBits; - n += (buffer >>> bitsLeft); - } - //dest.push(n); - dest[o] = n < nmax ? offset + n * scale : maxValue; - } - dest.unshift(offset);//1st one - return dest; - }, - - unstuff2: function(src, dest, bitsPerPixel, numPixels, lutArr, offset, scale, maxValue) { - var bitMask = (1 << bitsPerPixel) - 1; - var i = 0, o; - var bitsLeft = 0, bitPos = 0; - var n, buffer, missingBits; - if (lutArr) { - for (o = 0; o < numPixels; o++) { - if (bitsLeft === 0) { - buffer = src[i++]; - bitsLeft = 32; - bitPos = 0; - } - if (bitsLeft >= bitsPerPixel) { - n = ((buffer >>> bitPos) & bitMask); - bitsLeft -= bitsPerPixel; - bitPos += bitsPerPixel; - } else { - missingBits = (bitsPerPixel - bitsLeft); - n = (buffer >>> bitPos) & bitMask; - buffer = src[i++]; - bitsLeft = 32 - missingBits; - n |= (buffer & ((1 << missingBits) - 1)) << (bitsPerPixel - missingBits); - bitPos = missingBits; - } - dest[o] = lutArr[n]; - } - } - else { - var nmax = Math.ceil((maxValue - offset) / scale); - for (o = 0; o < numPixels; o++) { - if (bitsLeft === 0) { - buffer = src[i++]; - bitsLeft = 32; - bitPos = 0; - } - if (bitsLeft >= bitsPerPixel) { - //no unsigned left shift - n = ((buffer >>> bitPos) & bitMask); - bitsLeft -= bitsPerPixel; - bitPos += bitsPerPixel; - } else { - missingBits = (bitsPerPixel - bitsLeft); - n = (buffer >>> bitPos) & bitMask;//((buffer & bitMask) << missingBits) & bitMask; - buffer = src[i++]; - bitsLeft = 32 - missingBits; - n |= (buffer & ((1 << missingBits) - 1)) << (bitsPerPixel - missingBits); - bitPos = missingBits; - } - //pixel values may exceed max due to quantization - dest[o] = n < nmax ? offset + n * scale : maxValue; - } - } - return dest; - }, - - unstuffLUT2: function(src, bitsPerPixel, numPixels, offset, scale, maxValue) { - var bitMask = (1 << bitsPerPixel) - 1; - var i = 0, o = 0, missingBits = 0, bitsLeft = 0, n = 0, bitPos = 0; - var buffer; - var dest = []; - var nmax = Math.ceil((maxValue - offset) / scale); - for (o = 0; o < numPixels; o++) { - if (bitsLeft === 0) { - buffer = src[i++]; - bitsLeft = 32; - bitPos = 0; - } - if (bitsLeft >= bitsPerPixel) { - //no unsigned left shift - n = ((buffer >>> bitPos) & bitMask); - bitsLeft -= bitsPerPixel; - bitPos += bitsPerPixel; - } else { - missingBits = (bitsPerPixel - bitsLeft); - n = (buffer >>> bitPos) & bitMask;//((buffer & bitMask) << missingBits) & bitMask; - buffer = src[i++]; - bitsLeft = 32 - missingBits; - n |= (buffer & ((1 << missingBits) - 1)) << (bitsPerPixel - missingBits); - bitPos = missingBits; - } - //dest.push(n); - dest[o] = n < nmax ? offset + n * scale : maxValue; - } - dest.unshift(offset); - return dest; - }, - - originalUnstuff: function(src, dest, bitsPerPixel, numPixels) { - var bitMask = (1 << bitsPerPixel) - 1; - var i = 0, o; - var bitsLeft = 0; - var n, buffer, missingBits; - - // get rid of trailing bytes that are already part of next block - var numInvalidTailBytes = src.length * 4 - Math.ceil(bitsPerPixel * numPixels / 8); - src[src.length - 1] <<= 8 * numInvalidTailBytes; - - for (o = 0; o < numPixels; o++) { - if (bitsLeft === 0) { - buffer = src[i++]; - bitsLeft = 32; - } - if (bitsLeft >= bitsPerPixel) { - n = (buffer >>> (bitsLeft - bitsPerPixel)) & bitMask; - bitsLeft -= bitsPerPixel; - } - else { - missingBits = (bitsPerPixel - bitsLeft); - n = ((buffer & bitMask) << missingBits) & bitMask; - buffer = src[i++]; - bitsLeft = 32 - missingBits; - n += (buffer >>> bitsLeft); - } - dest[o] = n; - } - return dest; - }, - - originalUnstuff2: function(src, dest, bitsPerPixel, numPixels) { - var bitMask = (1 << bitsPerPixel) - 1; - var i = 0, o; - var bitsLeft = 0, bitPos = 0; - var n, buffer, missingBits; - //micro-optimizations - for (o = 0; o < numPixels; o++) { - if (bitsLeft === 0) { - buffer = src[i++]; - bitsLeft = 32; - bitPos = 0; - } - if (bitsLeft >= bitsPerPixel) { - //no unsigned left shift - n = ((buffer >>> bitPos) & bitMask); - bitsLeft -= bitsPerPixel; - bitPos += bitsPerPixel; - } else { - missingBits = (bitsPerPixel - bitsLeft); - n = (buffer >>> bitPos) & bitMask;//((buffer & bitMask) << missingBits) & bitMask; - buffer = src[i++]; - bitsLeft = 32 - missingBits; - n |= (buffer & ((1 << missingBits) - 1)) << (bitsPerPixel - missingBits); - bitPos = missingBits; - } - dest[o] = n; - } - return dest; - } - }; - - /***************************************** - *private static class used by Lerc2Decode - ******************************************/ - var Lerc2Helpers = { - HUFFMAN_LUT_BITS_MAX: 12, //use 2^12 lut, treat it like constant - computeChecksumFletcher32: function(input) { - - var sum1 = 0xffff, sum2 = 0xffff; - var len = input.length; - var words = Math.floor(len / 2); - var i = 0; - while (words) { - var tlen = (words >= 359) ? 359 : words; - words -= tlen; - do { - sum1 += (input[i++] << 8); - sum2 += sum1 += input[i++]; - } while (--tlen); - - sum1 = (sum1 & 0xffff) + (sum1 >>> 16); - sum2 = (sum2 & 0xffff) + (sum2 >>> 16); - } - - // add the straggler byte if it exists - if (len & 1) { - sum2 += sum1 += (input[i] << 8); - } - // second reduction step to reduce sums to 16 bits - sum1 = (sum1 & 0xffff) + (sum1 >>> 16); - sum2 = (sum2 & 0xffff) + (sum2 >>> 16); - - return (sum2 << 16 | sum1) >>> 0; - }, - - readHeaderInfo: function(input, data) { - var ptr = data.ptr; - var fileIdView = new Uint8Array(input, ptr, 6); - var headerInfo = {}; - headerInfo.fileIdentifierString = String.fromCharCode.apply(null, fileIdView); - if (headerInfo.fileIdentifierString.lastIndexOf("Lerc2", 0) !== 0) { - throw "Unexpected file identifier string (expect Lerc2 ): " + headerInfo.fileIdentifierString; - } - ptr += 6; - var view = new DataView(input, ptr, 8); - var fileVersion = view.getInt32(0, true); - headerInfo.fileVersion = fileVersion; - ptr += 4; - if (fileVersion >= 3) { - headerInfo.checksum = view.getUint32(4, true); //nrows - ptr += 4; - } - - //keys start from here - view = new DataView(input, ptr, 12); - headerInfo.height = view.getUint32(0, true); //nrows - headerInfo.width = view.getUint32(4, true); //ncols - ptr += 8; - if (fileVersion >= 4) { - headerInfo.numDims = view.getUint32(8, true); - ptr += 4; - } - else { - headerInfo.numDims = 1; - } - - view = new DataView(input, ptr, 40); - headerInfo.numValidPixel = view.getUint32(0, true); - headerInfo.microBlockSize = view.getInt32(4, true); - headerInfo.blobSize = view.getInt32(8, true); - headerInfo.imageType = view.getInt32(12, true); - - headerInfo.maxZError = view.getFloat64(16, true); - headerInfo.zMin = view.getFloat64(24, true); - headerInfo.zMax = view.getFloat64(32, true); - ptr += 40; - data.headerInfo = headerInfo; - data.ptr = ptr; - - var checksum, keyLength; - if (fileVersion >= 3) { - keyLength = fileVersion >= 4 ? 52 : 48; - checksum = this.computeChecksumFletcher32(new Uint8Array(input, ptr - keyLength, headerInfo.blobSize - 14)); - if (checksum !== headerInfo.checksum) { - throw "Checksum failed."; - } - } - return true; - }, - - checkMinMaxRanges: function(input, data) { - var headerInfo = data.headerInfo; - var OutPixelTypeArray = this.getDataTypeArray(headerInfo.imageType); - var rangeBytes = headerInfo.numDims * this.getDataTypeSize(headerInfo.imageType); - var minValues = this.readSubArray(input, data.ptr, OutPixelTypeArray, rangeBytes); - var maxValues = this.readSubArray(input, data.ptr + rangeBytes, OutPixelTypeArray, rangeBytes); - data.ptr += (2 * rangeBytes); - var i, equal = true; - for (i = 0; i < headerInfo.numDims; i++) { - if (minValues[i] !== maxValues[i]) { - equal = false; - break; - } - } - headerInfo.minValues = minValues; - headerInfo.maxValues = maxValues; - return equal; - }, - - readSubArray: function(input, ptr, OutPixelTypeArray, numBytes) { - var rawData; - if (OutPixelTypeArray === Uint8Array) { - rawData = new Uint8Array(input, ptr, numBytes); - } - else { - var arrayBuf = new ArrayBuffer(numBytes); - var store8 = new Uint8Array(arrayBuf); - store8.set(new Uint8Array(input, ptr, numBytes)); - rawData = new OutPixelTypeArray(arrayBuf); - } - return rawData; - }, - - readMask: function(input, data) { - var ptr = data.ptr; - var headerInfo = data.headerInfo; - var numPixels = headerInfo.width * headerInfo.height; - var numValidPixel = headerInfo.numValidPixel; - - var view = new DataView(input, ptr, 4); - var mask = {}; - mask.numBytes = view.getUint32(0, true); - ptr += 4; - - // Mask Data - if ((0 === numValidPixel || numPixels === numValidPixel) && 0 !== mask.numBytes) { - throw ("invalid mask"); - } - var bitset, resultMask; - if (numValidPixel === 0) { - bitset = new Uint8Array(Math.ceil(numPixels / 8)); - mask.bitset = bitset; - resultMask = new Uint8Array(numPixels); - data.pixels.resultMask = resultMask; - ptr += mask.numBytes; - }// ????? else if (data.mask.numBytes > 0 && data.mask.numBytes< data.numValidPixel) { - else if (mask.numBytes > 0) { - bitset = new Uint8Array(Math.ceil(numPixels / 8)); - view = new DataView(input, ptr, mask.numBytes); - var cnt = view.getInt16(0, true); - var ip = 2, op = 0, val = 0; - do { - if (cnt > 0) { - while (cnt--) { bitset[op++] = view.getUint8(ip++); } - } else { - val = view.getUint8(ip++); - cnt = -cnt; - while (cnt--) { bitset[op++] = val; } - } - cnt = view.getInt16(ip, true); - ip += 2; - } while (ip < mask.numBytes); - if ((cnt !== -32768) || (op < bitset.length)) { - throw "Unexpected end of mask RLE encoding"; - } - - resultMask = new Uint8Array(numPixels); - var mb = 0, k = 0; - - for (k = 0; k < numPixels; k++) { - if (k & 7) { - mb = bitset[k >> 3]; - mb <<= k & 7; - } - else { - mb = bitset[k >> 3]; - } - if (mb & 128) { - resultMask[k] = 1; - } - } - data.pixels.resultMask = resultMask; - - mask.bitset = bitset; - ptr += mask.numBytes; - } - data.ptr = ptr; - data.mask = mask; - return true; - }, - - readDataOneSweep: function(input, data, OutPixelTypeArray, useBSQForOutputDim) { - var ptr = data.ptr; - var headerInfo = data.headerInfo; - var numDims = headerInfo.numDims; - var numPixels = headerInfo.width * headerInfo.height; - var imageType = headerInfo.imageType; - var numBytes = headerInfo.numValidPixel * Lerc2Helpers.getDataTypeSize(imageType) * numDims; - //data.pixels.numBytes = numBytes; - var rawData; - var mask = data.pixels.resultMask; - if (OutPixelTypeArray === Uint8Array) { - rawData = new Uint8Array(input, ptr, numBytes); - } - else { - var arrayBuf = new ArrayBuffer(numBytes); - var store8 = new Uint8Array(arrayBuf); - store8.set(new Uint8Array(input, ptr, numBytes)); - rawData = new OutPixelTypeArray(arrayBuf); - } - if (rawData.length === numPixels * numDims) { - if (useBSQForOutputDim) { - data.pixels.resultPixels = Lerc2Helpers.swapDimensionOrder(rawData, numPixels, numDims, OutPixelTypeArray, true); - } - else { - data.pixels.resultPixels = rawData; - } - } - else //mask - { - data.pixels.resultPixels = new OutPixelTypeArray(numPixels * numDims); - var z = 0, k = 0, i = 0, nStart = 0; - if (numDims > 1) { - if (useBSQForOutputDim) { - for (k = 0; k < numPixels; k++) { - if (mask[k]) { - nStart = k; - for (i = 0; i < numDims; i++, nStart+=numPixels) { - data.pixels.resultPixels[nStart] = rawData[z++]; - } - } - } - } - else { - for (k = 0; k < numPixels; k++) { - if (mask[k]) { - nStart = k * numDims; - for (i = 0; i < numDims; i++) { - data.pixels.resultPixels[nStart + i] = rawData[z++]; - } - } - } - } - } - else { - for (k = 0; k < numPixels; k++) { - if (mask[k]) { - data.pixels.resultPixels[k] = rawData[z++]; - } - } - } - } - ptr += numBytes; - data.ptr = ptr; //return data; - return true; - }, - - readHuffmanTree: function(input, data) { - var BITS_MAX = this.HUFFMAN_LUT_BITS_MAX; //8 is slow for the large test image - //var size_max = 1 << BITS_MAX; - /* ************************ - * reading code table - *************************/ - var view = new DataView(input, data.ptr, 16); - data.ptr += 16; - var version = view.getInt32(0, true); - if (version < 2) { - throw "unsupported Huffman version"; - } - var size = view.getInt32(4, true); - var i0 = view.getInt32(8, true); - var i1 = view.getInt32(12, true); - if (i0 >= i1) { - return false; - } - var blockDataBuffer = new Uint32Array(i1 - i0); - Lerc2Helpers.decodeBits(input, data, blockDataBuffer); - var codeTable = []; //size - var i, j, k, len; - - for (i = i0; i < i1; i++) { - j = i - (i < size ? 0 : size);//wrap around - codeTable[j] = { first: blockDataBuffer[i - i0], second: null }; - } - - var dataBytes = input.byteLength - data.ptr; - var dataWords = Math.ceil(dataBytes / 4); - var arrayBuf = new ArrayBuffer(dataWords * 4); - var store8 = new Uint8Array(arrayBuf); - store8.set(new Uint8Array(input, data.ptr, dataBytes)); - var stuffedData = new Uint32Array(arrayBuf); //must start from x*4 - var bitPos = 0, word, srcPtr = 0; - word = stuffedData[0]; - for (i = i0; i < i1; i++) { - j = i - (i < size ? 0 : size);//wrap around - len = codeTable[j].first; - if (len > 0) { - codeTable[j].second = (word << bitPos) >>> (32 - len); - - if (32 - bitPos >= len) { - bitPos += len; - if (bitPos === 32) { - bitPos = 0; - srcPtr++; - word = stuffedData[srcPtr]; - } - } - else { - bitPos += len - 32; - srcPtr++; - word = stuffedData[srcPtr]; - codeTable[j].second |= word >>> (32 - bitPos); - } - } - } - - //finished reading code table - - /* ************************ - * building lut - *************************/ - var numBitsLUT = 0, numBitsLUTQick = 0; - var tree = new TreeNode(); - for (i = 0; i < codeTable.length; i++) { - if (codeTable[i] !== undefined) { - numBitsLUT = Math.max(numBitsLUT, codeTable[i].first); - } - } - if (numBitsLUT >= BITS_MAX) { - numBitsLUTQick = BITS_MAX; - } - else { - numBitsLUTQick = numBitsLUT; - } - // for debugging purpose - // if (numBitsLUT >= 30) { - // console.log("WARning, large NUM LUT BITS IS " + numBitsLUT); - // } - var decodeLut = [], entry, code, numEntries, jj, currentBit, node; - for (i = i0; i < i1; i++) { - j = i - (i < size ? 0 : size);//wrap around - len = codeTable[j].first; - if (len > 0) { - entry = [len, j]; - if (len <= numBitsLUTQick) { - code = codeTable[j].second << (numBitsLUTQick - len); - numEntries = 1 << (numBitsLUTQick - len); - for (k = 0; k < numEntries; k++) { - decodeLut[code | k] = entry; - } - } - else { - //build tree - code = codeTable[j].second; - node = tree; - for (jj = len - 1; jj >= 0; jj--) { - currentBit = code >>> jj & 1; //no left shift as length could be 30,31 - if (currentBit) { - if (!node.right) { - node.right = new TreeNode(); - } - node = node.right; - } - else { - if (!node.left) { - node.left = new TreeNode(); - } - node = node.left; - } - if (jj === 0 && !node.val) { - node.val = entry[1]; - } - } - } - } - } - return { - decodeLut: decodeLut, - numBitsLUTQick: numBitsLUTQick, - numBitsLUT: numBitsLUT, - tree: tree, - stuffedData: stuffedData, - srcPtr: srcPtr, - bitPos: bitPos - }; - }, - - readHuffman: function(input, data, OutPixelTypeArray, useBSQForOutputDim) { - var headerInfo = data.headerInfo; - var numDims = headerInfo.numDims; - var height = data.headerInfo.height; - var width = data.headerInfo.width; - var numPixels = width * height; - //var size_max = 1 << BITS_MAX; - /* ************************ - * reading huffman structure info - *************************/ - var huffmanInfo = this.readHuffmanTree(input, data); - var decodeLut = huffmanInfo.decodeLut; - var tree = huffmanInfo.tree; - //stuffedData includes huffman headers - var stuffedData = huffmanInfo.stuffedData; - var srcPtr = huffmanInfo.srcPtr; - var bitPos = huffmanInfo.bitPos; - var numBitsLUTQick = huffmanInfo.numBitsLUTQick; - var numBitsLUT = huffmanInfo.numBitsLUT; - var offset = data.headerInfo.imageType === 0 ? 128 : 0; - /************************* - * decode - ***************************/ - var node, val, delta, mask = data.pixels.resultMask, valTmp, valTmpQuick, currentBit; - var i, j, k, ii; - var prevVal = 0; - if (bitPos > 0) { - srcPtr++; - bitPos = 0; - } - var word = stuffedData[srcPtr]; - var deltaEncode = data.encodeMode === 1; - var resultPixelsAllDim = new OutPixelTypeArray(numPixels * numDims); - var resultPixels = resultPixelsAllDim; - var iDim; - // TODO: reevaluate the need to keep inlined decoding code as IE support is phasing out - if (numDims < 2 || deltaEncode) { - for (iDim = 0; iDim < numDims; iDim++) { - if (numDims > 1) { - //get the mem block of current dimension - resultPixels = new OutPixelTypeArray(resultPixelsAllDim.buffer, numPixels * iDim, numPixels); - prevVal = 0; - } - if (data.headerInfo.numValidPixel === width * height) { //all valid - for (k = 0, i = 0; i < height; i++) { - for (j = 0; j < width; j++, k++) { - val = 0; - valTmp = (word << bitPos) >>> (32 - numBitsLUTQick); - valTmpQuick = valTmp;// >>> deltaBits; - if (32 - bitPos < numBitsLUTQick) { - valTmp |= ((stuffedData[srcPtr + 1]) >>> (64 - bitPos - numBitsLUTQick)); - valTmpQuick = valTmp;// >>> deltaBits; - } - if (decodeLut[valTmpQuick]) // if there, move the correct number of bits and done - { - val = decodeLut[valTmpQuick][1]; - bitPos += decodeLut[valTmpQuick][0]; - } - else { - valTmp = (word << bitPos) >>> (32 - numBitsLUT); - valTmpQuick = valTmp;// >>> deltaBits; - if (32 - bitPos < numBitsLUT) { - valTmp |= ((stuffedData[srcPtr + 1]) >>> (64 - bitPos - numBitsLUT)); - valTmpQuick = valTmp;// >>> deltaBits; - } - node = tree; - for (ii = 0; ii < numBitsLUT; ii++) { - currentBit = valTmp >>> (numBitsLUT - ii - 1) & 1; - node = currentBit ? node.right : node.left; - if (!(node.left || node.right)) { - val = node.val; - bitPos = bitPos + ii + 1; - break; - } - } - } - - if (bitPos >= 32) { - bitPos -= 32; - srcPtr++; - word = stuffedData[srcPtr]; - } - - delta = val - offset; - if (deltaEncode) { - if (j > 0) { - delta += prevVal; // use overflow - } - else if (i > 0) { - delta += resultPixels[k - width]; - } - else { - delta += prevVal; - } - delta &= 0xFF; //overflow - resultPixels[k] = delta;//overflow - prevVal = delta; - } - else { - resultPixels[k] = delta; - } - } - } - } - else { //not all valid, use mask - for (k = 0, i = 0; i < height; i++) { - for (j = 0; j < width; j++, k++) { - if (mask[k]) { - val = 0; - valTmp = (word << bitPos) >>> (32 - numBitsLUTQick); - valTmpQuick = valTmp;// >>> deltaBits; - if (32 - bitPos < numBitsLUTQick) { - valTmp |= ((stuffedData[srcPtr + 1]) >>> (64 - bitPos - numBitsLUTQick)); - valTmpQuick = valTmp;// >>> deltaBits; - } - if (decodeLut[valTmpQuick]) // if there, move the correct number of bits and done - { - val = decodeLut[valTmpQuick][1]; - bitPos += decodeLut[valTmpQuick][0]; - } - else { - valTmp = (word << bitPos) >>> (32 - numBitsLUT); - valTmpQuick = valTmp;// >>> deltaBits; - if (32 - bitPos < numBitsLUT) { - valTmp |= ((stuffedData[srcPtr + 1]) >>> (64 - bitPos - numBitsLUT)); - valTmpQuick = valTmp;// >>> deltaBits; - } - node = tree; - for (ii = 0; ii < numBitsLUT; ii++) { - currentBit = valTmp >>> (numBitsLUT - ii - 1) & 1; - node = currentBit ? node.right : node.left; - if (!(node.left || node.right)) { - val = node.val; - bitPos = bitPos + ii + 1; - break; - } - } - } - - if (bitPos >= 32) { - bitPos -= 32; - srcPtr++; - word = stuffedData[srcPtr]; - } - - delta = val - offset; - if (deltaEncode) { - if (j > 0 && mask[k - 1]) { - delta += prevVal; // use overflow - } - else if (i > 0 && mask[k - width]) { - delta += resultPixels[k - width]; - } - else { - delta += prevVal; - } - - delta &= 0xFF; //overflow - resultPixels[k] = delta;//overflow - prevVal = delta; - } - else { - resultPixels[k] = delta; - } - } - } - } - } - } - } - else { - for (k = 0, i = 0; i < height; i++) { - for (j = 0; j < width; j++) { - k = i * width + j; - if (!mask || mask[k]) { - for (iDim = 0; iDim < numDims; iDim++, k+=numPixels) { - val = 0; - valTmp = (word << bitPos) >>> (32 - numBitsLUTQick); - valTmpQuick = valTmp; - if (32 - bitPos < numBitsLUTQick) { - valTmp |= ((stuffedData[srcPtr + 1]) >>> (64 - bitPos - numBitsLUTQick)); - valTmpQuick = valTmp; - } - if (decodeLut[valTmpQuick]) - { - val = decodeLut[valTmpQuick][1]; - bitPos += decodeLut[valTmpQuick][0]; - } - else { - valTmp = (word << bitPos) >>> (32 - numBitsLUT); - valTmpQuick = valTmp; - if (32 - bitPos < numBitsLUT) { - valTmp |= ((stuffedData[srcPtr + 1]) >>> (64 - bitPos - numBitsLUT)); - valTmpQuick = valTmp; - } - node = tree; - for (ii = 0; ii < numBitsLUT; ii++) { - currentBit = valTmp >>> (numBitsLUT - ii - 1) & 1; - node = currentBit ? node.right : node.left; - if (!(node.left || node.right)) { - val = node.val; - bitPos = bitPos + ii + 1; - break; - } - } - } - - if (bitPos >= 32) { - bitPos -= 32; - srcPtr++; - word = stuffedData[srcPtr]; - } - - delta = val - offset; - resultPixels[k] = delta; - } - } - } - } - } - data.ptr = data.ptr + (srcPtr + 1) * 4 + (bitPos > 0 ? 4 : 0); - data.pixels.resultPixels = resultPixelsAllDim; - //swap for BIP layout - if (numDims > 1 && !useBSQForOutputDim) { - data.pixels.resultPixels = Lerc2Helpers.swapDimensionOrder(resultPixelsAllDim, numPixels, numDims, OutPixelTypeArray); - } - }, - - decodeBits: function(input, data, blockDataBuffer, offset, iDim) { - { - //bitstuff encoding is 3 - var headerInfo = data.headerInfo; - var fileVersion = headerInfo.fileVersion; - //var block = {}; - var blockPtr = 0; - var viewByteLength = ((input.byteLength - data.ptr) >= 5) ? 5 : (input.byteLength - data.ptr); - var view = new DataView(input, data.ptr, viewByteLength); - var headerByte = view.getUint8(0); - blockPtr++; - var bits67 = headerByte >> 6; - var n = (bits67 === 0) ? 4 : 3 - bits67; - var doLut = (headerByte & 32) > 0 ? true : false;//5th bit - var numBits = headerByte & 31; - var numElements = 0; - if (n === 1) { - numElements = view.getUint8(blockPtr); blockPtr++; - } else if (n === 2) { - numElements = view.getUint16(blockPtr, true); blockPtr += 2; - } else if (n === 4) { - numElements = view.getUint32(blockPtr, true); blockPtr += 4; - } else { - throw "Invalid valid pixel count type"; - } - //fix: huffman codes are bit stuffed, but not bound by data's max value, so need to use originalUnstuff - //offset = offset || 0; - var scale = 2 * headerInfo.maxZError; - var stuffedData, arrayBuf, store8, dataBytes, dataWords; - var lutArr, lutData, lutBytes, lutBitsPerElement, bitsPerPixel; - var zMax = headerInfo.numDims > 1 ? headerInfo.maxValues[iDim] : headerInfo.zMax; - if (doLut) { - data.counter.lut++; - lutBytes = view.getUint8(blockPtr); - lutBitsPerElement = numBits; - blockPtr++; - dataBytes = Math.ceil((lutBytes - 1) * numBits / 8); - dataWords = Math.ceil(dataBytes / 4); - arrayBuf = new ArrayBuffer(dataWords * 4); - store8 = new Uint8Array(arrayBuf); - - data.ptr += blockPtr; - store8.set(new Uint8Array(input, data.ptr, dataBytes)); - - lutData = new Uint32Array(arrayBuf); - data.ptr += dataBytes; - - bitsPerPixel = 0; - while ((lutBytes - 1) >>> bitsPerPixel) { - bitsPerPixel++; - } - dataBytes = Math.ceil(numElements * bitsPerPixel / 8); - dataWords = Math.ceil(dataBytes / 4); - arrayBuf = new ArrayBuffer(dataWords * 4); - store8 = new Uint8Array(arrayBuf); - store8.set(new Uint8Array(input, data.ptr, dataBytes)); - stuffedData = new Uint32Array(arrayBuf); - data.ptr += dataBytes; - if (fileVersion >= 3) { - lutArr = BitStuffer.unstuffLUT2(lutData, numBits, lutBytes - 1, offset, scale, zMax); - } - else { - lutArr = BitStuffer.unstuffLUT(lutData, numBits, lutBytes - 1, offset, scale, zMax); - } - //lutArr.unshift(0); - if (fileVersion >= 3) { - //BitStuffer.unstuff2(block, blockDataBuffer, headerInfo.zMax); - BitStuffer.unstuff2(stuffedData, blockDataBuffer, bitsPerPixel, numElements, lutArr); - } - else { - BitStuffer.unstuff(stuffedData, blockDataBuffer, bitsPerPixel, numElements, lutArr); - } - } - else { - //console.debug("bitstuffer"); - data.counter.bitstuffer++; - bitsPerPixel = numBits; - data.ptr += blockPtr; - if (bitsPerPixel > 0) { - dataBytes = Math.ceil(numElements * bitsPerPixel / 8); - dataWords = Math.ceil(dataBytes / 4); - arrayBuf = new ArrayBuffer(dataWords * 4); - store8 = new Uint8Array(arrayBuf); - store8.set(new Uint8Array(input, data.ptr, dataBytes)); - stuffedData = new Uint32Array(arrayBuf); - data.ptr += dataBytes; - if (fileVersion >= 3) { - if (offset == null) { - BitStuffer.originalUnstuff2(stuffedData, blockDataBuffer, bitsPerPixel, numElements); - } - else { - BitStuffer.unstuff2(stuffedData, blockDataBuffer, bitsPerPixel, numElements, false, offset, scale, zMax); - } - } - else { - if (offset == null) { - BitStuffer.originalUnstuff(stuffedData, blockDataBuffer, bitsPerPixel, numElements); - } - else { - BitStuffer.unstuff(stuffedData, blockDataBuffer, bitsPerPixel, numElements, false, offset, scale, zMax); - } - } - } - } - } - - }, - - readTiles: function(input, data, OutPixelTypeArray, useBSQForOutputDim) { - var headerInfo = data.headerInfo; - var width = headerInfo.width; - var height = headerInfo.height; - var numPixels = width * height; - var microBlockSize = headerInfo.microBlockSize; - var imageType = headerInfo.imageType; - var dataTypeSize = Lerc2Helpers.getDataTypeSize(imageType); - var numBlocksX = Math.ceil(width / microBlockSize); - var numBlocksY = Math.ceil(height / microBlockSize); - data.pixels.numBlocksY = numBlocksY; - data.pixels.numBlocksX = numBlocksX; - data.pixels.ptr = 0; - var row = 0, col = 0, blockY = 0, blockX = 0, thisBlockHeight = 0, thisBlockWidth = 0, bytesLeft = 0, headerByte = 0, bits67 = 0, testCode = 0, outPtr = 0, outStride = 0, numBytes = 0, bytesleft = 0, z = 0, blockPtr = 0; - var view, block, arrayBuf, store8, rawData; - var blockEncoding; - var blockDataBuffer = new OutPixelTypeArray(microBlockSize * microBlockSize); - var lastBlockHeight = (height % microBlockSize) || microBlockSize; - var lastBlockWidth = (width % microBlockSize) || microBlockSize; - var offsetType, offset; - var numDims = headerInfo.numDims, iDim; - var mask = data.pixels.resultMask; - var resultPixels = data.pixels.resultPixels; - var fileVersion = headerInfo.fileVersion; - var fileVersionCheckNum = fileVersion >= 5 ? 14 : 15; - var isDiffEncoding; - var zMax = headerInfo.zMax; - //var resultPixelsAllDim = resultPixels; - var resultPixelsPrevDim; - for (blockY = 0; blockY < numBlocksY; blockY++) { - thisBlockHeight = (blockY !== numBlocksY - 1) ? microBlockSize : lastBlockHeight; - for (blockX = 0; blockX < numBlocksX; blockX++) { - //console.debug("y" + blockY + " x" + blockX); - thisBlockWidth = (blockX !== numBlocksX - 1) ? microBlockSize : lastBlockWidth; - - outPtr = blockY * width * microBlockSize + blockX * microBlockSize; - outStride = width - thisBlockWidth; - - for (iDim = 0; iDim < numDims; iDim++) { - if (numDims > 1) { - resultPixelsPrevDim = resultPixels; - outPtr = blockY * width * microBlockSize + blockX * microBlockSize; - resultPixels = new OutPixelTypeArray(data.pixels.resultPixels.buffer, numPixels * iDim * dataTypeSize, numPixels); - zMax = headerInfo.maxValues[iDim]; - } else { - resultPixelsPrevDim = null; - } - bytesLeft = input.byteLength - data.ptr; - view = new DataView(input, data.ptr, Math.min(10, bytesLeft)); - block = {}; - blockPtr = 0; - headerByte = view.getUint8(0); - blockPtr++; - isDiffEncoding = headerInfo.fileVersion >= 5 ? headerByte & 4 : 0; - bits67 = (headerByte >> 6) & 0xFF; - testCode = (headerByte >> 2) & fileVersionCheckNum; // use bits 2345 for integrity check - if (testCode !== (((blockX * microBlockSize) >> 3) & fileVersionCheckNum)) { - throw "integrity issue"; - } - - if (isDiffEncoding && iDim === 0) { - throw "integrity issue"; - } - - blockEncoding = headerByte & 3; - if (blockEncoding > 3) { - data.ptr += blockPtr; - throw "Invalid block encoding (" + blockEncoding + ")"; - } - else if (blockEncoding === 2) { //constant 0 - if (isDiffEncoding) { - if (mask) { - for (row = 0; row < thisBlockHeight; row++) { - for (col = 0; col < thisBlockWidth; col++) { - if (mask[outPtr]) { - resultPixels[outPtr] = resultPixelsPrevDim[outPtr]; - } - outPtr++; - } - } - } - else { - for (row = 0; row < thisBlockHeight; row++) { - for (col = 0; col < thisBlockWidth; col++) { - resultPixels[outPtr] = resultPixelsPrevDim[outPtr]; - outPtr++; - } - } - } - } - data.counter.constant++; - data.ptr += blockPtr; - continue; - } - else if (blockEncoding === 0) { //uncompressed - if (isDiffEncoding) { - // doesn't make sense, should not happen - throw "integrity issue"; - } - data.counter.uncompressed++; - data.ptr += blockPtr; - numBytes = thisBlockHeight * thisBlockWidth * dataTypeSize; - bytesleft = input.byteLength - data.ptr; - numBytes = numBytes < bytesleft ? numBytes : bytesleft; - //bit alignment - arrayBuf = new ArrayBuffer((numBytes % dataTypeSize) === 0 ? numBytes : (numBytes + dataTypeSize - numBytes % dataTypeSize)); - store8 = new Uint8Array(arrayBuf); - store8.set(new Uint8Array(input, data.ptr, numBytes)); - rawData = new OutPixelTypeArray(arrayBuf); - z = 0; - if (mask) { - for (row = 0; row < thisBlockHeight; row++) { - for (col = 0; col < thisBlockWidth; col++) { - if (mask[outPtr]) { - resultPixels[outPtr] = rawData[z++]; - } - outPtr++; - } - outPtr += outStride; - } - } - else {//all valid - for (row = 0; row < thisBlockHeight; row++) { - for (col = 0; col < thisBlockWidth; col++) { - resultPixels[outPtr++] = rawData[z++]; - } - outPtr += outStride; - } - } - data.ptr += z * dataTypeSize; - } - else { //1 or 3 - offsetType = Lerc2Helpers.getDataTypeUsed((isDiffEncoding && imageType < 6) ? 4 : imageType, bits67); - offset = Lerc2Helpers.getOnePixel(block, blockPtr, offsetType, view); - blockPtr += Lerc2Helpers.getDataTypeSize(offsetType); - if (blockEncoding === 3) //constant offset value - { - data.ptr += blockPtr; - data.counter.constantoffset++; - //you can delete the following resultMask case in favor of performance because val is constant and users use nodata mask, otherwise nodatavalue post processing handles it too. - //while the above statement is true, we're not doing it as we want to keep invalid pixel value at 0 rather than arbitrary values - if (mask) { - for (row = 0; row < thisBlockHeight; row++) { - for (col = 0; col < thisBlockWidth; col++) { - if (mask[outPtr]) { - resultPixels[outPtr] = isDiffEncoding ? Math.min(zMax, resultPixelsPrevDim[outPtr] + offset) : offset; - } - outPtr++; - } - outPtr += outStride; - } - } - else { - for (row = 0; row < thisBlockHeight; row++) { - for (col = 0; col < thisBlockWidth; col++) { - resultPixels[outPtr] = isDiffEncoding ? Math.min(zMax, resultPixelsPrevDim[outPtr] + offset) : offset; - outPtr++; - } - outPtr += outStride; - } - } - } - else { //bitstuff encoding is 3 - data.ptr += blockPtr; - //heavy lifting - Lerc2Helpers.decodeBits(input, data, blockDataBuffer, offset, iDim); - blockPtr = 0; - // duplicate code to favor performance, diff encoding is for multidimension only - if (isDiffEncoding) { - if (mask) { - for (row = 0; row < thisBlockHeight; row++) { - for (col = 0; col < thisBlockWidth; col++) { - if (mask[outPtr]) { - resultPixels[outPtr] = blockDataBuffer[blockPtr++] + resultPixelsPrevDim[outPtr]; - } - outPtr++; - } - outPtr += outStride; - } - } - else { - for (row = 0; row < thisBlockHeight; row++) { - for (col = 0; col < thisBlockWidth; col++) { - resultPixels[outPtr] = blockDataBuffer[blockPtr++] + resultPixelsPrevDim[outPtr]; - outPtr++; - } - outPtr += outStride; - } - } - } - else if (mask) { - for (row = 0; row < thisBlockHeight; row++) { - for (col = 0; col < thisBlockWidth; col++) { - if (mask[outPtr]) { - resultPixels[outPtr] = blockDataBuffer[blockPtr++]; - } - outPtr++; - } - outPtr += outStride; - } - } - else { - for (row = 0; row < thisBlockHeight; row++) { - for (col = 0; col < thisBlockWidth; col++) { - resultPixels[outPtr++] = blockDataBuffer[blockPtr++]; - } - outPtr += outStride; - } - } - } - } - } - } - } - //swap for BIP: it's always easier for clients to handle BSQ so we keep existing logic and introduce a swap here to minimze changes - if (numDims > 1 && !useBSQForOutputDim) { - data.pixels.resultPixels = Lerc2Helpers.swapDimensionOrder(data.pixels.resultPixels, numPixels, numDims, OutPixelTypeArray); - } - }, - - /***************** - * private methods (helper methods) - *****************/ - - formatFileInfo: function(data) { - return { - "fileIdentifierString": data.headerInfo.fileIdentifierString, - "fileVersion": data.headerInfo.fileVersion, - "imageType": data.headerInfo.imageType, - "height": data.headerInfo.height, - "width": data.headerInfo.width, - "numValidPixel": data.headerInfo.numValidPixel, - "microBlockSize": data.headerInfo.microBlockSize, - "blobSize": data.headerInfo.blobSize, - "maxZError": data.headerInfo.maxZError, - "pixelType": Lerc2Helpers.getPixelType(data.headerInfo.imageType), - "eofOffset": data.eofOffset, - "mask": data.mask ? { - "numBytes": data.mask.numBytes - } : null, - "pixels": { - "numBlocksX": data.pixels.numBlocksX, - "numBlocksY": data.pixels.numBlocksY, - //"numBytes": data.pixels.numBytes, - "maxValue": data.headerInfo.zMax, - "minValue": data.headerInfo.zMin, - "noDataValue": data.noDataValue - } - }; - }, - - constructConstantSurface: function(data, useBSQForOutputDim) { - var val = data.headerInfo.zMax; - var valMin = data.headerInfo.zMin; - var maxValues = data.headerInfo.maxValues; - var numDims = data.headerInfo.numDims; - var numPixels = data.headerInfo.height * data.headerInfo.width; - var i = 0, k = 0, nStart = 0; - var mask = data.pixels.resultMask; - var resultPixels = data.pixels.resultPixels; - if (mask) { - if (numDims > 1) { - if (useBSQForOutputDim) { - for (i = 0; i < numDims; i++) { - nStart = i * numPixels; - val = maxValues[i]; - for (k = 0; k < numPixels; k++) { - if (mask[k]) { - resultPixels[nStart + k] = val; - } - } - } - } - else { - for (k = 0; k < numPixels; k++) { - if (mask[k]) { - nStart = k * numDims; - for (i = 0; i < numDims; i++) { - resultPixels[nStart + numDims] = maxValues[i]; - } - } - } - } - } - else { - for (k = 0; k < numPixels; k++) { - if (mask[k]) { - resultPixels[k] = val; - } - } - } - } - else { - if (numDims > 1 && valMin !== val) { - if (useBSQForOutputDim) { - for (i = 0; i < numDims; i++) { - nStart = i * numPixels; - val = maxValues[i]; - for (k = 0; k < numPixels; k++) { - resultPixels[nStart + k] = val; - } - } - } - else { - for (k = 0; k < numPixels; k++) { - nStart = k * numDims; - for (i = 0; i < numDims; i++) { - resultPixels[nStart + i] = maxValues[i]; - } - } - } - } - else { - for (k = 0; k < numPixels * numDims; k++) { - resultPixels[k] = val; - } - } - } - return; - }, - - getDataTypeArray: function(t) { - var tp; - switch (t) { - case 0: //char - tp = Int8Array; - break; - case 1: //byte - tp = Uint8Array; - break; - case 2: //short - tp = Int16Array; - break; - case 3: //ushort - tp = Uint16Array; - break; - case 4: - tp = Int32Array; - break; - case 5: - tp = Uint32Array; - break; - case 6: - tp = Float32Array; - break; - case 7: - tp = Float64Array; - break; - default: - tp = Float32Array; - } - return tp; - }, - - getPixelType: function(t) { - var tp; - switch (t) { - case 0: //char - tp = "S8"; - break; - case 1: //byte - tp = "U8"; - break; - case 2: //short - tp = "S16"; - break; - case 3: //ushort - tp = "U16"; - break; - case 4: - tp = "S32"; - break; - case 5: - tp = "U32"; - break; - case 6: - tp = "F32"; - break; - case 7: - tp = "F64"; - break; - default: - tp = "F32"; - } - return tp; - }, - - isValidPixelValue: function(t, val) { - if (val == null) { - return false; - } - var isValid; - switch (t) { - case 0: //char - isValid = val >= -128 && val <= 127; - break; - case 1: //byte (unsigned char) - isValid = val >= 0 && val <= 255; - break; - case 2: //short - isValid = val >= -32768 && val <= 32767; - break; - case 3: //ushort - isValid = val >= 0 && val <= 65536; - break; - case 4: //int 32 - isValid = val >= -2147483648 && val <= 2147483647; - break; - case 5: //uinit 32 - isValid = val >= 0 && val <= 4294967296; - break; - case 6: - isValid = val >= -3.4027999387901484e+38 && val <= 3.4027999387901484e+38; - break; - case 7: - isValid = val >= -1.7976931348623157e+308 && val <= 1.7976931348623157e+308; - break; - default: - isValid = false; - } - return isValid; - }, - - getDataTypeSize: function(t) { - var s = 0; - switch (t) { - case 0: //ubyte - case 1: //byte - s = 1; - break; - case 2: //short - case 3: //ushort - s = 2; - break; - case 4: - case 5: - case 6: - s = 4; - break; - case 7: - s = 8; - break; - default: - s = t; - } - return s; - }, - - getDataTypeUsed: function(dt, tc) { - var t = dt; - switch (dt) { - case 2: //short - case 4: //long - t = dt - tc; - break; - case 3: //ushort - case 5: //ulong - t = dt - 2 * tc; - break; - case 6: //float - if (0 === tc) { - t = dt; - } - else if (1 === tc) { - t = 2; - } - else { - t = 1;//byte - } - break; - case 7: //double - if (0 === tc) { - t = dt; - } - else { - t = dt - 2 * tc + 1; - } - break; - default: - t = dt; - break; - } - return t; - }, - - getOnePixel: function(block, blockPtr, offsetType, view) { - var temp = 0; - switch (offsetType) { - case 0: //char - temp = view.getInt8(blockPtr); - break; - case 1: //byte - temp = view.getUint8(blockPtr); - break; - case 2: - temp = view.getInt16(blockPtr, true); - break; - case 3: - temp = view.getUint16(blockPtr, true); - break; - case 4: - temp = view.getInt32(blockPtr, true); - break; - case 5: - temp = view.getUInt32(blockPtr, true); - break; - case 6: - temp = view.getFloat32(blockPtr, true); - break; - case 7: - temp = view.getFloat64(blockPtr, true); - break; - default: - throw ("the decoder does not understand this pixel type"); - } - return temp; - }, - - swapDimensionOrder: function(pixels, numPixels, numDims, OutPixelTypeArray, inputIsBIP) { - var i = 0, j = 0, iDim = 0, temp = 0, swap = pixels; - if (numDims > 1) { - swap = new OutPixelTypeArray(numPixels * numDims); - if (inputIsBIP) { - for (i=0; i 5) { - throw "unsupported lerc version 2." + fileVersion; - } - - // Mask Header - Lerc2Helpers.readMask(input, data); - if (headerInfo.numValidPixel !== headerInfo.width * headerInfo.height && !data.pixels.resultMask) { - data.pixels.resultMask = options.maskData; - } - - var numPixels = headerInfo.width * headerInfo.height; - data.pixels.resultPixels = new OutPixelTypeArray(numPixels * headerInfo.numDims); - - data.counter = { - onesweep: 0, - uncompressed: 0, - lut: 0, - bitstuffer: 0, - constant: 0, - constantoffset: 0 - }; - var useBSQForOutputDim = !options.returnPixelInterleavedDims; - if (headerInfo.numValidPixel !== 0) { - //not tested - if (headerInfo.zMax === headerInfo.zMin) //constant surface - { - Lerc2Helpers.constructConstantSurface(data, useBSQForOutputDim); - } - else if (fileVersion >= 4 && Lerc2Helpers.checkMinMaxRanges(input, data)) { - Lerc2Helpers.constructConstantSurface(data, useBSQForOutputDim); - } - else { - var view = new DataView(input, data.ptr, 2); - var bReadDataOneSweep = view.getUint8(0); - data.ptr++; - if (bReadDataOneSweep) { - //console.debug("OneSweep"); - Lerc2Helpers.readDataOneSweep(input, data, OutPixelTypeArray, useBSQForOutputDim); - } - else { - //lerc2.1: //bitstuffing + lut - //lerc2.2: //bitstuffing + lut + huffman - //lerc2.3: new bitstuffer - if (fileVersion > 1 && headerInfo.imageType <= 1 && Math.abs(headerInfo.maxZError - 0.5) < 0.00001) { - //this is 2.x plus 8 bit (unsigned and signed) data, possiblity of Huffman - var flagHuffman = view.getUint8(1); - data.ptr++; - data.encodeMode = flagHuffman; - if (flagHuffman > 2 || (fileVersion < 4 && flagHuffman > 1)) { - throw "Invalid Huffman flag " + flagHuffman; - } - if (flagHuffman) {//1 - delta Huffman, 2 - Huffman - //console.log("Huffman"); - Lerc2Helpers.readHuffman(input, data, OutPixelTypeArray, useBSQForOutputDim); - } - else { - //console.log("Tiles"); - Lerc2Helpers.readTiles(input, data, OutPixelTypeArray, useBSQForOutputDim); - } - } - else { //lerc2.x non-8 bit data - //console.log("Tiles"); - Lerc2Helpers.readTiles(input, data, OutPixelTypeArray, useBSQForOutputDim); - } - } - } - } - - data.eofOffset = data.ptr; - var diff; - if (options.inputOffset) { - diff = data.headerInfo.blobSize + options.inputOffset - data.ptr; - if (Math.abs(diff) >= 1) { - //console.debug("incorrect eof: dataptr " + data.ptr + " offset " + options.inputOffset + " blobsize " + data.headerInfo.blobSize + " diff: " + diff); - data.eofOffset = options.inputOffset + data.headerInfo.blobSize; - } - } - else { - diff = data.headerInfo.blobSize - data.ptr; - if (Math.abs(diff) >= 1) { - //console.debug("incorrect first band eof: dataptr " + data.ptr + " blobsize " + data.headerInfo.blobSize + " diff: " + diff); - data.eofOffset = data.headerInfo.blobSize; - } - } - - var result = { - width: headerInfo.width, - height: headerInfo.height, - pixelData: data.pixels.resultPixels, - minValue: headerInfo.zMin, - maxValue: headerInfo.zMax, - validPixelCount: headerInfo.numValidPixel, - dimCount: headerInfo.numDims, - dimStats: { - minValues: headerInfo.minValues, - maxValues: headerInfo.maxValues - }, - maskData: data.pixels.resultMask - //noDataValue: noDataValue - }; - - //we should remove this if there's no existing client - //optional noDataValue processing, it's user's responsiblity - if (data.pixels.resultMask && Lerc2Helpers.isValidPixelValue(headerInfo.imageType, noDataValue)) { - var mask = data.pixels.resultMask; - for (i = 0; i < numPixels; i++) { - if (!mask[i]) { - result.pixelData[i] = noDataValue; - } - } - result.noDataValue = noDataValue; - } - data.noDataValue = noDataValue; - if (options.returnFileInfo) { - result.fileInfo = Lerc2Helpers.formatFileInfo(data); - } - return result; - }, - - getBandCount: function(/*byte array*/ input) { - var count = 0; - var i = 0; - var temp = {}; - temp.ptr = 0; - temp.pixels = {}; - while (i < input.byteLength - 58) { - Lerc2Helpers.readHeaderInfo(input, temp); - i += temp.headerInfo.blobSize; - count++; - temp.ptr = i; - } - return count; - } - }; - - return Lerc2Decode; - })(); - - var isPlatformLittleEndian = (function() { - var a = new ArrayBuffer(4); - var b = new Uint8Array(a); - var c = new Uint32Array(a); - c[0] = 1; - return b[0] === 1; - })(); - - var Lerc = { - /************wrapper**********************************************/ - /** - * A wrapper for decoding both LERC1 and LERC2 byte streams capable of handling multiband pixel blocks for various pixel types. - * - * @alias module:Lerc - * @param {ArrayBuffer} input The LERC input byte stream - * @param {object} [options] The decoding options below are optional. - * @param {number} [options.inputOffset] The number of bytes to skip in the input byte stream. A valid Lerc file is expected at that position. - * @param {string} [options.pixelType] (LERC1 only) Default value is F32. Valid pixel types for input are U8/S8/S16/U16/S32/U32/F32. - * @param {number} [options.noDataValue] (LERC1 only). It is recommended to use the returned mask instead of setting this value. - * @param {boolean} [options.returnPixelInterleavedDims] (nDim LERC2 only) If true, returned dimensions are pixel-interleaved, a.k.a [p1_dim0, p1_dim1, p1_dimn, p2_dim0...], default is [p1_dim0, p2_dim0, ..., p1_dim1, p2_dim1...] - * @returns {{width, height, pixels, pixelType, mask, statistics}} - * @property {number} width Width of decoded image. - * @property {number} height Height of decoded image. - * @property {array} pixels [band1, band2, …] Each band is a typed array of width*height. - * @property {string} pixelType The type of pixels represented in the output. - * @property {mask} mask Typed array with a size of width*height, or null if all pixels are valid. - * @property {array} statistics [statistics_band1, statistics_band2, …] Each element is a statistics object representing min and max values - **/ - decode: function(encodedData, options) { - if (!isPlatformLittleEndian) { - throw "Big endian system is not supported."; - } - options = options || {}; - var inputOffset = options.inputOffset || 0; - var fileIdView = new Uint8Array(encodedData, inputOffset, 10); - var fileIdentifierString = String.fromCharCode.apply(null, fileIdView); - var lerc, majorVersion; - if (fileIdentifierString.trim() === "CntZImage") { - lerc = LercDecode; - majorVersion = 1; - } - else if (fileIdentifierString.substring(0, 5) === "Lerc2") { - lerc = Lerc2Decode; - majorVersion = 2; - } - else { - throw "Unexpected file identifier string: " + fileIdentifierString; - } - - var iPlane = 0, eof = encodedData.byteLength - 10, encodedMaskData, bandMasks = [], bandMask, maskData; - var decodedPixelBlock = { - width: 0, - height: 0, - pixels: [], - pixelType: options.pixelType, - mask: null, - statistics: [] - }; - var uniqueBandMaskCount = 0; - - while (inputOffset < eof) { - var result = lerc.decode(encodedData, { - inputOffset: inputOffset,//for both lerc1 and lerc2 - encodedMaskData: encodedMaskData,//lerc1 only - maskData: maskData,//lerc2 only - returnMask: iPlane === 0 ? true : false,//lerc1 only - returnEncodedMask: iPlane === 0 ? true : false,//lerc1 only - returnFileInfo: true,//for both lerc1 and lerc2 - returnPixelInterleavedDims: options.returnPixelInterleavedDims,//for ndim lerc2 only - pixelType: options.pixelType || null,//lerc1 only - noDataValue: options.noDataValue || null//lerc1 only - }); - - inputOffset = result.fileInfo.eofOffset; - maskData = result.maskData;//lerc2 - if (iPlane === 0) { - encodedMaskData = result.encodedMaskData;//lerc1 - decodedPixelBlock.width = result.width; - decodedPixelBlock.height = result.height; - decodedPixelBlock.dimCount = result.dimCount || 1; - //decodedPixelBlock.dimStats = decodedPixelBlock.dimStats; - decodedPixelBlock.pixelType = result.pixelType || result.fileInfo.pixelType; - decodedPixelBlock.mask = maskData; - } - if (majorVersion > 1) { - if (maskData) { - bandMasks.push(maskData); - } - if (result.fileInfo.mask && result.fileInfo.mask.numBytes > 0) { - uniqueBandMaskCount++; - } - } - - iPlane++; - decodedPixelBlock.pixels.push(result.pixelData); - decodedPixelBlock.statistics.push({ - minValue: result.minValue, - maxValue: result.maxValue, - noDataValue: result.noDataValue, - dimStats: result.dimStats - }); - } - var i, j, numPixels; - if (majorVersion > 1 && uniqueBandMaskCount > 1) { - numPixels = decodedPixelBlock.width * decodedPixelBlock.height; - decodedPixelBlock.bandMasks = bandMasks; - maskData = new Uint8Array(numPixels); - maskData.set(bandMasks[0]); - for (i = 1; i < bandMasks.length; i++) { - bandMask = bandMasks[i]; - for (j = 0; j < numPixels; j++) { - maskData[j] = maskData[j] & bandMask[j]; - } - } - decodedPixelBlock.maskData = maskData; - } - - return decodedPixelBlock; - } - }; - - if (typeof define === "function" && define.amd) {/* jshint ignore:line */ - //amd loaders such as dojo and requireJS - //http://wiki.commonjs.org/wiki/Modules/AsynchronousDefinition - define([], function() { return Lerc; });/* jshint ignore:line */ - } - else if (typeof module !== "undefined" && module.exports) {/* jshint ignore:line */ - //commonJS module 1.0/1.1/1.1.1 systems, such as nodeJS - //http://wiki.commonjs.org/wiki/Modules - module.exports = Lerc;/* jshint ignore:line */ - } - else { - //assign to this, most likely window - this.Lerc = Lerc; - } - -})(); diff -Nru lerc-3.0+ds/OtherLanguages/js/package.json lerc-4.0.0+ds/OtherLanguages/js/package.json --- lerc-3.0+ds/OtherLanguages/js/package.json 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/js/package.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -{ - "name": "lerc", - "browser": "LercDecode.js", - "bugs": { - "url": "https://github.com/esri/lerc/issues" - }, - "description": "Rapid decoding of Lerc compressed raster data for any standard pixel type.", - "version": "3.0.0", - "author": "Esri (http://developers.arcgis.com)", - "contributors": [ - "Johannes Schmid", - "Chayanika Khatua", - "Wenxue Ju" - ], - "devDependencies": { - "gh-release": "^2.2.1", - "jsdoc-to-markdown": "^2.0.1", - "jshint": "^2.13.0", - "uglify-js": "^3.14.0" - }, - "files": [ - "LercDecode.js", - "LercDecode.min.js" - ], - "homepage": "https://github.com/Esri/lerc", - "license": "Apache-2.0", - "main": "LercDecode.js", - "readmeFilename": "README.md", - "repository": { - "type": "git", - "url": "https://github.com/Esri/lerc.git" - }, - "scripts": { - "lint": "jshint LercDecode.js", - "build": "uglifyjs LercDecode.js -o LercDecode.min.js --comments", - "docs": "jsdoc2md --template README.hbs --files LercDecode.js", - "release": "./release.sh" - } -} diff -Nru lerc-3.0+ds/OtherLanguages/js/README.hbs lerc-4.0.0+ds/OtherLanguages/js/README.hbs --- lerc-3.0+ds/OtherLanguages/js/README.hbs 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/js/README.hbs 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -[![npm version][npm-img]][npm-url] - -[npm-img]: https://img.shields.io/npm/v/lerc.svg?style=flat-square -[npm-url]: https://www.npmjs.com/package/lerc - -# Lerc JS - -> Rapid decoding of Lerc compressed raster data for any standard pixel type, not just rgb or byte - -## Browser - -```html - -``` -```js -Lerc.decode(xhrResponse, { - inputOffset: 10, // start from the 10th byte (default is 0) - pixelType: "U8", // only needed for lerc1 (default is F32) - returnPixelInterleavedDims: false // only applicable to n-dim lerc2 blobs (default is false) -}); -``` - -## Node - -```js -npm install lerc && npm install node-fetch -``` -```js -const fetch = require('node-fetch'); -const Lerc = require('lerc'); - -fetch('http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer/tile/0/0/0') - .then(response => response.arrayBuffer()) - .then(body => { - const image = Lerc.decode(body); - image.width // 257 - }); -``` - -## API Reference - -{{>main}} -* * * - -## Licensing - -Copyright © 2017-2021 Esri - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and limitations under the License. - -A local copy of the license and additional notices are located with the source distribution at: - -http://github.com/Esri/lerc/ diff -Nru lerc-3.0+ds/OtherLanguages/js/README.md lerc-4.0.0+ds/OtherLanguages/js/README.md --- lerc-3.0+ds/OtherLanguages/js/README.md 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/js/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -[![npm version][npm-img]][npm-url] - -[npm-img]: https://img.shields.io/npm/v/lerc.svg?style=flat-square -[npm-url]: https://www.npmjs.com/package/lerc - -# Lerc JS - -> Rapid decoding of Lerc compressed raster data for any standard pixel type, not just rgb or byte - -## Browser - -```html - -``` -```js -Lerc.decode(xhrResponse, { - inputOffset: 10, // start from the 10th byte (default is 0) - pixelType: "U8", // only needed for lerc1 (default is F32) - returnPixelInterleavedDims: false // only applicable to n-dim lerc2 blobs (default is false) -}); -``` - -## Node - -```js -npm install lerc && npm install node-fetch -``` -```js -const fetch = require('node-fetch'); -const Lerc = require('lerc'); - -fetch('http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer/tile/0/0/0') - .then(response => response.arrayBuffer()) - .then(body => { - const image = Lerc.decode(body); - image.width // 257 - }); -``` - -## API Reference - - - -## Lerc -a module for decoding LERC blobs - - - -### decode(input, [options]) ⇒ Object ⏏ -A wrapper for decoding both LERC1 and LERC2 byte streams capable of handling multiband pixel blocks for various pixel types. - -**Kind**: Exported function - -| Param | Type | Description | -| --- | --- | --- | -| input | ArrayBuffer | The LERC input byte stream | -| [options] | object | The decoding options below are optional. | -| [options.inputOffset] | number | The number of bytes to skip in the input byte stream. A valid Lerc file is expected at that position. | -| [options.pixelType] | string | (LERC1 only) Default value is F32. Valid pixel types for input are U8/S8/S16/U16/S32/U32/F32. | -| [options.noDataValue] | number | (LERC1 only). It is recommended to use the returned mask instead of setting this value. | - -**Result Object Properties** - -| Name | Type | Description | -| --- | --- | --- | -| width | number | Width of decoded image. | -| height | number | Height of decoded image. | -| pixels | array | [band1, band2, …] Each band is a typed array of width*height. | -| pixelType | string | The type of pixels represented in the output. | -| mask | mask | Typed array with a size of width*height, or null if all pixels are valid. | -| statistics | array | [statistics_band1, statistics_band2, …] Each element is a statistics object representing min and max values | - -* * * - -## Licensing - -Copyright © 2017-2021 Esri - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and limitations under the License. - -A local copy of the license and additional notices are located with the source distribution at: - -http://github.com/Esri/lerc/ \ No newline at end of file diff -Nru lerc-3.0+ds/OtherLanguages/js/release.sh lerc-4.0.0+ds/OtherLanguages/js/release.sh --- lerc-3.0+ds/OtherLanguages/js/release.sh 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/js/release.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -#!/bin/bash - -# config -VERSION=$(node --eval "console.log(require('./package.json').version);") -NAME=$(node --eval "console.log(require('./package.json').name);") - -# no tests (yet) -# npm test || exit 1 - -# checkout temp branch for release -git checkout -b gh-release - -# build files -npm run lint && npm run build - -# force add files -git add LercDecode.min.js -f - -# commit changes with a versioned commit message -git commit -m "build $VERSION" - -# push commit so it exists on GitHub when we run gh-release -git push upstream gh-release - -# run gh-release to create the tag and push release to github -gh-release - -# checkout master and delete release branch locally and on GitHub -git checkout master -git branch -D gh-release -git push upstream :gh-release - -# publish release on NPM -npm publish diff -Nru lerc-3.0+ds/OtherLanguages/Python/lerc/_lerc.py lerc-4.0.0+ds/OtherLanguages/Python/lerc/_lerc.py --- lerc-3.0+ds/OtherLanguages/Python/lerc/_lerc.py 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/Python/lerc/_lerc.py 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ #------------------------------------------------------------------------------- -# Copyright 2016 - 2021 Esri +# Copyright 2016 - 2022 Esri # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,30 +24,120 @@ #------------------------------------------------------------------------------- # -# How to use: +# How to run the test: # # You need 2 files, this file "_lerc.py" and the Lerc dll (for Windows) or the -# Lerc .so file (for Linux). Set the path below so the Lerc dll or .so file -# can be read from here. You don't need a pip install or any installer. Then +# Lerc .so file (for Linux) next to it. Then # # >>> import _lerc # >>> _lerc.test() # #------------------------------------------------------------------------------- +#------------------------------------------------------------------------------- +# +# What's new in Lerc 4.0 +# +# An encoded image tile (2D, 3D, or 4D), of data type byte to double, can have +# any value set to invalid. There are new and hopefully easy to use numpy +# functions to decode from or encode to Lerc. They make use of the numpy +# masked arrays. +# +# (result, npmaArr, nDepth, npmaNoData) = decode_ma(lercBlob) +# +# _ lercBlob is the compressed Lerc blob as a string buffer or byte array as +# read in from disk or passed in memory. +# _ result is 0 for success or an error code for failure. +# _ npmaArr is the masked numpy array with the data and mask of the same shape. +# _ nDepth == nValuesPerPixel. E.g., 3 for RGB, or 2 for complex numbers. +# _ npmaNoData is a 1D masked array of size nBands. It can hold one noData +# value per band. The caller can usually ignore it as npmaArr has all mask +# info. It may be useful if the data needs to be Lerc encoded again. +# +# (result, nBytesWritten, lercBlob) = encode_ma(npmaArr, nDepth, maxZErr, +# nBytesHint, npmaNoData = None) +# +# _ npmaArr is the image tile (2D, 3D, or 4D) to be encoded, as a numpy masked +# array. +# _ nDepth == nValuesPerPixel. E.g., 3 for RGB, or 2 for complex numbers. +# _ maxZErr is the max encoding error allowed per value. 0 means lossless. +# _ nBytesHint can be +# _ 0 - compute num bytes needed for output buffer, but do not encode it (faster than encode) +# _ 1 - do both, compute exact buffer size needed and encode (slower than encode alone) +# _ > 1 - create buffer of that given size and encode, if buffer too small encode will fail. +# _ npmaNoData is a 1D masked array of size nBands. It can hold one noData +# value per band. It can be used as an alternative to masks. It must be +# used for the so called mixed case of valid and invalid values at the same +# pixel, only possible for nDepth > 1. In most cases None can be passed. +# Note Lerc does not take NaN as a noData value here. It is enough to set the +# data values to NaN and not specify a noData value. +# +# As an alternative to the numpy masked array above, there is also the option +# to have data and masked as separate numpy arrays. +# +# (result, npArr, npValidMask, npmaNoData) = decode_4D(lercBlob) +# +# Here, npArr can be of the same shapes as npmaArr above, but it is a regular +# numpy array, not a masked array. The mask is passed separately as a regular +# numpy array of type bool. Note that in contrast to the masked array above, +# True means now valid and False means invalid. The npValidMask can have the +# following shapes: +# _ None, all pixels are valid or are marked invalid using noData value or NaN +# _ 2D or (nRows, nCols), same mask for all bands. +# _ 3D or (nBands, nRows, nCols), one mask per band. + +# The _4D() functions may work well if all pixels are valid, or nDepth == 1, +# and the shape of the mask here matches the shape of the data anyway. +# In such cases the use of a numpy masked array might not be needed or +# considered an overkill. +# +# Similar for encode: +# +# (result, nBytesWritten, lercBlob) = encode_4D(npArr, nDepth, npValidMask, +# maxZErr, nBytesHint, npmaNoData = None) +# +# Note that for all encode functions, you can set values to invalid using a +# mask, or using a noData value, or using NaN (for data types float or double). +# Or any combination which is then merged using AND for valid (same as OR for invalid) +# by the Lerc API. +# +# The decode functions, however, return this info as a mask, wherever possible. +# Only for nDepth > 1 and the mixed case of valid and invalid values at the same +# pixel, a noData value is used internally. NaN is never returned by decode. +# +# The existing Lerc 3.0 encode and decode functions can still be used. +# Only for nDepth > 1 the mixed case cannot be encoded. If the decoder +# should encounter a Lerc blob with such a mixed case, it will fail with +# the error code LercNS::ErrCode::HasNoData == 5. +# +#------------------------------------------------------------------------------- + import numpy as np import ctypes as ct from timeit import default_timer as timer import platform import os -dir_path = os.path.dirname(os.path.realpath(__file__)) -if platform.system() == "Windows": - lercDll = ct.CDLL (os.path.join(dir_path, 'Lerc.dll')) -if platform.system() == "Linux": - lercDll = ct.CDLL (os.path.join(dir_path, 'Lerc.so')) -if platform.system() == "Darwin": - lercDll = ct.CDLL (os.path.join(dir_path, 'Lerc.dylib')) +def _get_lib(): + dir_path = os.path.dirname(os.path.realpath(__file__)) + + if platform.system() == "Windows": + lib = os.path.join(dir_path, 'Lerc.dll') + elif platform.system() == "Linux": + lib = os.path.join(dir_path, 'libLerc.so.4') + elif platform.system() == "Darwin": + lib = os.path.join(dir_path, 'libLerc.dylib') + else: + lib = None + + if not lib or not os.path.exists(lib): + import ctypes.util + lib = ctypes.util.find_library('Lerc') + + return lib + +lercDll = ct.CDLL (_get_lib()) +del _get_lib #------------------------------------------------------------------------------- @@ -71,7 +161,8 @@ #------------------------------------------------------------------------------- # Lerc expects an image of size nRows x nCols. -# Optional, it allows multiple values per pixel, like [RGB, RGB, RGB, ... ]. Or an array of values per pixel. As a 3rd dimension. +# Optional, it allows multiple values per pixel, like [RGB, RGB, RGB, ... ]. +# Or an array of values per pixel. As a 3rd dimension. # Optional, it allows multiple bands. As an outer 3rd or 4th dimension. def getLercShape(npArr, nValuesPerPixel): @@ -96,21 +187,64 @@ #------------------------------------------------------------------------------- +# Lerc version 3.0 +# use only if all pixel values are valid. + def findMaxZError(npArr1, npArr2): npDiff = npArr2 - npArr1 yMin = np.amin(npDiff) yMax = np.amax(npDiff) return max(abs(yMin), abs(yMax)) +# Lerc version 4.0 +# honors byte masks and works with noData value: if decoded output +# has a noData value, the orig input must have the same. + +def findMaxZError_4D(npDataOrig, npDataDec, npValidMaskDec, nBands): + + npDiff = npDataDec - npDataOrig + + if (npValidMaskDec is None): + zMin = np.amin(npDiff) + zMax = np.amax(npDiff) + else: + if not npValidMaskDec.any(): # if all pixel values are void + return 0 + + if nBands == 1 or npValidMaskDec.ndim == 3: # one mask per band + zMin = np.amin(npDiff[npValidMaskDec]) + zMax = np.amax(npDiff[npValidMaskDec]) + elif nBands > 1: # same mask for all bands + zMin = float("inf") + zMax = -zMin + for m in range(nBands): + zMin = min(np.amin(npDiff[m][npValidMaskDec]), zMin) + zMax = max(np.amax(npDiff[m][npValidMaskDec]), zMax) + + return max(abs(zMin), abs(zMax)) + +# Lerc version 4.0, using masked arrays + +def findMaxZError_ma(npmaArrOrig, npmaArrDec): + npDiff = npmaArrDec - npmaArrOrig + yMin = np.amin(npDiff) + yMax = np.amax(npDiff) + return max(abs(yMin), abs(yMax)) + #------------------------------------------------------------------------------- +# Lerc version 3.0 + def findDataRange(npArr, bHasMask, npValidMask, nBands, printInfo = False): start = timer() - if not bHasMask: + if not bHasMask or npValidMask is None: zMin = np.amin(npArr) zMax = np.amax(npArr) else: + if not npValidMask.any(): # if all pixel values are void + return (-1, -1) + if nBands == 1 or npValidMask.ndim == 3: # one mask per band zMin = np.amin(npArr[npValidMask]) zMax = np.amax(npArr[npValidMask]) @@ -126,21 +260,55 @@ print('time findDataRange() = ', (end - start)) return (zMin, zMax) +# Lerc version 4.0, using masked array + +def findDataRange_ma(npmaArr): + if not npmaArr.any(): + return (-1, -1) + zMin = np.amin(npmaArr) + zMax = np.amax(npmaArr) + return (zMin, zMax) + #------------------------------------------------------------------------------- # see include/Lerc_c_api.h lercDll.lerc_computeCompressedSize.restype = ct.c_uint -lercDll.lerc_computeCompressedSize.argtypes = (ct.c_void_p, ct.c_uint, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_char_p, ct.c_double, ct.POINTER(ct.c_uint)) +lercDll.lerc_computeCompressedSize.argtypes = (ct.c_void_p, ct.c_uint, ct.c_int, ct.c_int, ct.c_int, + ct.c_int, ct.c_int, ct.c_char_p, ct.c_double, ct.POINTER(ct.c_uint)) lercDll.lerc_encode.restype = ct.c_uint -lercDll.lerc_encode.argtypes = (ct.c_void_p, ct.c_uint, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_char_p, ct.c_double, ct.c_char_p, ct.c_uint, ct.POINTER(ct.c_uint)) +lercDll.lerc_encode.argtypes = (ct.c_void_p, ct.c_uint, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_int, + ct.c_char_p, ct.c_double, ct.c_char_p, ct.c_uint, ct.POINTER(ct.c_uint)) lercDll.lerc_getBlobInfo.restype = ct.c_uint -lercDll.lerc_getBlobInfo.argtypes = (ct.c_char_p, ct.c_uint, ct.POINTER(ct.c_uint), ct.POINTER(ct.c_double), ct.c_int, ct.c_int) +lercDll.lerc_getBlobInfo.argtypes = (ct.c_char_p, ct.c_uint, ct.POINTER(ct.c_uint), + ct.POINTER(ct.c_double), ct.c_int, ct.c_int) + +lercDll.lerc_getDataRanges.restype = ct.c_uint +lercDll.lerc_getDataRanges.argtypes = (ct.c_char_p, ct.c_uint, ct.c_int, ct.c_int, + ct.POINTER(ct.c_double), ct.POINTER(ct.c_double)) lercDll.lerc_decode.restype = ct.c_uint -lercDll.lerc_decode.argtypes = (ct.c_char_p, ct.c_uint, ct.c_int, ct.c_char_p, ct.c_int, ct.c_int, ct.c_int, ct.c_int, ct.c_uint, ct.c_void_p) +lercDll.lerc_decode.argtypes = (ct.c_char_p, ct.c_uint, ct.c_int, ct.c_char_p, ct.c_int, + ct.c_int, ct.c_int, ct.c_int, ct.c_uint, ct.c_void_p) + +# the new _4D functions + +lercDll.lerc_computeCompressedSize_4D.restype = ct.c_uint +lercDll.lerc_computeCompressedSize_4D.argtypes = (ct.c_void_p, ct.c_uint, ct.c_int, ct.c_int, ct.c_int, + ct.c_int, ct.c_int, ct.c_char_p, ct.c_double, + ct.POINTER(ct.c_uint), ct.c_char_p, ct.POINTER(ct.c_double)) + +lercDll.lerc_encode_4D.restype = ct.c_uint +lercDll.lerc_encode_4D.argtypes = (ct.c_void_p, ct.c_uint, ct.c_int, ct.c_int, ct.c_int, ct.c_int, + ct.c_int, ct.c_char_p, ct.c_double, ct.c_char_p, ct.c_uint, + ct.POINTER(ct.c_uint), ct.c_char_p, ct.POINTER(ct.c_double)) + +lercDll.lerc_decode_4D.restype = ct.c_uint +lercDll.lerc_decode_4D.argtypes = (ct.c_char_p, ct.c_uint, ct.c_int, ct.c_char_p, ct.c_int, + ct.c_int, ct.c_int, ct.c_int, ct.c_uint, + ct.c_void_p, ct.c_char_p, ct.POINTER(ct.c_double)) #------------------------------------------------------------------------------- @@ -149,35 +317,72 @@ # npValidMask can be None (bHasMask == False), 2D byte array, or 3D byte array (bHasMask == True). # if 2D or [nRows, nCols], it is one mask for all bands. 1 means pixel is valid, 0 means invalid. # if 3D or [nBands, nRows, nCols], it is one mask PER band. -# note that an array of values per pixel is either all valid or all invalid. -# the case that such inner array values are partially valid or invalid is not represented by a mask -# yet but a noData value would have to be used. # # nBytesHint can be # 0 - compute num bytes needed for output buffer, but do not encode it (faster than encode) # 1 - do both, compute exact buffer size needed and encode (slower than encode alone) -# > 1 - create buffer of that given size and encode, if buffer too small encode will fail. +# > 1 - create buffer of that given size and encode, if buffer too small encode will fail. +# +# Lerc version 4.0 can also support the mixed case of valid and invalid values at the same pixel. +# As this case is rather special, instead of changing the valid / invalid byte mask representation, +# the concept of a noData value is used. +# For this, pass a 1D masked array of size nBands and data type double. For each band that uses a noData value, +# set the mask value to False, not masked, and the double value to the noData value used. +# Note that Lerc will push the noData value to the valid / invalid byte mask wherever possible. +# On Decode, Lerc only returns the noData values used for those bands where the byte mask cannot represent +# the void values. def encode(npArr, nValuesPerPixel, bHasMask, npValidMask, maxZErr, nBytesHint, printInfo = False): + return _encode_Ext(npArr, nValuesPerPixel, npValidMask, maxZErr, nBytesHint, None, printInfo) + +def encode_4D(npArr, nValuesPerPixel, npValidMask, maxZErr, nBytesHint, npmaNoDataPerBand = None, printInfo = False): + return _encode_Ext(npArr, nValuesPerPixel, npValidMask, maxZErr, nBytesHint, npmaNoDataPerBand, printInfo) + +def _encode_Ext(npArr, nValuesPerPixel, npValidMask, maxZErr, nBytesHint, npmaNoData, printInfo): global lercDll + + fctErr = 'Error in _encode_Ext(): ' dataType = getLercDatatype(npArr.dtype) if dataType == -1: - print('Error in encode(): unsupported numpy data type.') + print(fctErr, 'unsupported numpy data type.') return (-1, 0) (nBands, nRows, nCols) = getLercShape(npArr, nValuesPerPixel) if nBands == 0: - print('Error in encode(): unsupported numpy array shape.') + print(fctErr, 'unsupported numpy array shape.') return (-1, 0) nMasks = 0 - if bHasMask: + if npValidMask is not None: (nMasks, nRows2, nCols2) = getLercShape(npValidMask, 1) if not(nMasks == 0 or nMasks == 1 or nMasks == nBands) or not(nRows2 == nRows and nCols2 == nCols): - print('Error in encode(): unsupported mask array shape.') + print(fctErr, 'unsupported mask array shape.') return (-1, 0) + if (npmaNoData is not None): + if (len(npmaNoData) != nBands): + print(fctErr, 'noData array must be of size nBands or None.') + return (-1, 0) + + noDataArr = np.zeros([nBands], 'd') + npHasNoData = np.zeros([nBands], 'B') + + for m in range(nBands): + if not npmaNoData.mask[m]: + noDataArr[m] = npmaNoData[m] + npHasNoData[m] = 1 + + hasNoData = npHasNoData.tobytes('C') + cpHasNoData = ct.cast(hasNoData, ct.c_char_p) + + tempArr = noDataArr.tobytes('C') + cpNoData = ct.cast(tempArr, ct.POINTER(ct.c_double)) + + else: + cpHasNoData = None + cpNoData = None + if printInfo: print('dataType = ', dataType) print('nBands = ', nBands) @@ -185,11 +390,13 @@ print('nCols = ', nCols) print('nValuesPerPixel = ', nValuesPerPixel) print('nMasks = ', nMasks) + if cpHasNoData: + print('has noData value') byteArr = npArr.tobytes('C') # C order cpData = ct.cast(byteArr, ct.c_void_p) - if bHasMask: + if npValidMask is not None: npValidBytes = npValidMask.astype('B') validArr = npValidBytes.tobytes('C') cpValidArr = ct.cast(validArr, ct.c_char_p) @@ -200,16 +407,17 @@ if nBytesHint == 0 or nBytesHint == 1: start = timer() - result = lercDll.lerc_computeCompressedSize(cpData, dataType, nValuesPerPixel, nCols, nRows, nBands, nMasks, cpValidArr, maxZErr, ptr) + result = lercDll.lerc_computeCompressedSize_4D(cpData, dataType, nValuesPerPixel, nCols, nRows, nBands, + nMasks, cpValidArr, maxZErr, ptr, cpHasNoData, cpNoData) nBytesNeeded = ptr[0] end = timer() if result > 0: - print('Error in encode(): lercDll.lerc_computeCompressedSize() failed with error code = ', result) + print(fctErr, 'lercDll.lerc_computeCompressedSize_4D() failed with error code = ', result) return (result, 0) if printInfo: - print('time lerc_computeCompressedSize() = ', (end - start)) + print('time lerc_computeCompressedSize_4D() = ', (end - start)) else: nBytesNeeded = nBytesHint @@ -217,16 +425,17 @@ outBytes = ct.create_string_buffer(nBytesNeeded) cpOutBuffer = ct.cast(outBytes, ct.c_char_p) start = timer() - result = lercDll.lerc_encode(cpData, dataType, nValuesPerPixel, nCols, nRows, nBands, nMasks, cpValidArr, maxZErr, cpOutBuffer, nBytesNeeded, ptr) + result = lercDll.lerc_encode_4D(cpData, dataType, nValuesPerPixel, nCols, nRows, nBands, nMasks, cpValidArr, + maxZErr, cpOutBuffer, nBytesNeeded, ptr, cpHasNoData, cpNoData) nBytesWritten = ptr[0] end = timer() if result > 0: - print('Error in encode(): lercDll.lerc_encode() failed with error code = ', result) + print(fctErr, 'lercDll.lerc_encode_4D() failed with error code = ', result) return (result, 0) if printInfo: - print('time lerc_encode() = ', (end - start)) + print('time lerc_encode_4D() = ', (end - start)) if nBytesHint == 0: return (result, nBytesNeeded) @@ -235,10 +444,92 @@ #------------------------------------------------------------------------------- +def _has_mixed_case(uv, nValuesPerPixel, iBand): + fctErr = 'In function _has_mixed_case(): ' + if ((uv.len == 1 and not (uv[0] == 0 or uv[0] == nValuesPerPixel)) or + (uv.len == 2 and not (uv[0] == 0 and uv[1] == nValuesPerPixel)) or + (uv.len > 2)): + print(fctErr, 'mixed case detected of valid and invalid values at the same pixel for band ', + iBand, ', please provide a noData value') + return True + return False + +# encode a masked array; +# for nValuesPerPixel > 1 and a mixed case of some values valid and others invalid at the same pixel, +# caller must provide a noData value for the bands with such a mixed case; + +def encode_ma(npmaArr, nValuesPerPixel, maxZErr, nBytesHint, npmaNoDataPerBand = None, printInfo = False): + + fctErr = 'Error in encode_ma(): ' + + if nValuesPerPixel == 1: + return _encode_Ext(npmaArr.data, nValuesPerPixel, np.logical_not(npmaArr.mask), + maxZErr, nBytesHint, npmaNoDataPerBand, printInfo) + + elif nValuesPerPixel > 1: + + npArr = npmaArr.data + + if npmaNoDataPerBand is not None: + # for each band that has noData value, fill all masked values with that noData value + if npmaArr.ndim == 3: # nBands == 1 + if not npmaNoDataPerBand.mask[0]: + npArr = np.ma.filled(npmaArr, npmaNoDataPerBand[0]) + return _encode_Ext(npArr, nValuesPerPixel, None, maxZErr, nBytesHint, npmaNoDataPerBand, printInfo) + + elif npmaArr.ndim == 4: # nBands > 1 + nBands = npmaNoDataPerBand.size + for m in range(nBands): + if not npmaNoDataPerBand.mask[m]: + npArr[m] = np.ma.filled(npmaArr[m], npmaNoDataPerBand[m]) + if not np.any(npmaNoDataPerBand.mask): + return _encode_Ext(npArr, nValuesPerPixel, None, maxZErr, nBytesHint, npmaNoDataPerBand, printInfo) + + # now we have at least one band w/o a noData value, so we must convert the mask and check there is no mixed case + + # compute sum of most inner dimension, + # so that resulting array has one dim less, and values are in [0, nDepth] + + intMask = np.sum(npmaArr.mask, axis = npmaArr.mask.ndim - 1, dtype = int) + + # for each band without a noData value, check there is no other value but 0 or nDepth + # (ensure there is no mixed case) + + if intMask.ndim == 2: # nBands == 1 + if npmaNoDataPerBand.mask[0]: + uv = np.unique(intMask) + if _has_mixed_case(uv, nValuesPerPixel, 0): + return (-1, 0) + + elif intMask.ndim == 3: # nBands > 1 + for m in range(nBands): + if npmaNoDataPerBand.mask[m]: + uv = np.unique(intMask[m]) + if _has_mixed_case(uv, nValuesPerPixel, m): + return (-1, 0) + + # convert this int mask back to boolean + boolMask = intMask.astype(bool) + + return _encode_Ext(npArr, nValuesPerPixel, np.logical_not(boolMask), + maxZErr, nBytesHint, npmaNoDataPerBand, printInfo) + +#------------------------------------------------------------------------------- + def getLercBlobInfo(lercBlob, printInfo = False): + return _getLercBlobInfo_Ext(lercBlob, 0, printInfo) + +def getLercBlobInfo_4D(lercBlob, printInfo = False): + return _getLercBlobInfo_Ext(lercBlob, 1, printInfo) + +def _getLercBlobInfo_Ext(lercBlob, nSupportNoData, printInfo): global lercDll + + fctErr = 'Error in _getLercBlobInfo_Ext(): ' + + info = ['codec version', 'data type', 'nValuesPerPixel', 'nCols', 'nRows', 'nBands', 'nValidPixels', + 'blob size', 'nMasks', 'nDepth', 'nUsesNoDataValue'] - info = ['version', 'data type', 'nValuesPerPixel', 'nCols', 'nRows', 'nBands', 'nValidPixels', 'blob size', 'nMasks'] dataRange = ['zMin', 'zMax', 'maxZErrorUsed'] nBytes = len(lercBlob) @@ -250,27 +541,101 @@ result = lercDll.lerc_getBlobInfo(cpBytes, nBytes, p0, p1, len0, len1) if result > 0: - print('Error in getLercBlobInfo(): lercDLL.lerc_getBlobInfo() failed with error code = ', result) - return (result, 0,0,0,0,0,0,0,0,0,0,0,0) - + print(fctErr, 'lercDLL.lerc_getBlobInfo() failed with error code = ', result) + if nSupportNoData: + return (result, 0,0,0,0,0,0,0,0,0,0,0,0,0) + else: + return (result, 0,0,0,0,0,0,0,0,0,0,0,0) + if printInfo: for i in range(len0): print(info[i], p0[i]) for i in range(len1): print(dataRange[i], p1[i]) - - return (result, p0[0], p0[1], p0[2], p0[3], p0[4], p0[5], p0[6], p0[7], p0[8], p1[0], p1[1], p1[2]) + + nUsesNoDataValue = p0[10] # new key 'nUsesNoDataValue' + + if nUsesNoDataValue and not nSupportNoData: + print(fctErr, 'This Lerc blob uses noData value. Please upgrade to \ + Lerc version 4.0 functions or newer that support this.') + return (5, 0,0,0,0,0,0,0,0,0,0,0,0) # 5 == LercNS::ErrCode::HasNoData + + if not nSupportNoData: # old version, up to Lerc version 3.0 + return (result, + p0[0], p0[1], p0[2], p0[3], p0[4], p0[5], p0[6], p0[7], p0[8], + p1[0], p1[1], p1[2]) + else: # newer version, >= Lerc version 4.0 + return (result, + p0[0], p0[1], p0[2], p0[3], p0[4], p0[5], p0[6], p0[7], p0[8], + p1[0], p1[1], p1[2], + nUsesNoDataValue) # append to the end + +#------------------------------------------------------------------------------- + +# This still works same as before for Lerc blobs encoded using data and valid / invalid byte masks, +# but it will fail if the Lerc blob contains noData value (for mixed case of +# valid and invalid values at the same pixel). +# To be fixed in a future release (needs a codec upgrade). + +def getLercDataRanges(lercBlob, nDepth, nBands, printInfo = False): + global lercDll + + nBytes = len(lercBlob) + len0 = nDepth * nBands; + + cpBytes = ct.cast(lercBlob, ct.c_char_p) + + mins = ct.create_string_buffer(len0 * 8) + maxs = ct.create_string_buffer(len0 * 8) + cpMins = ct.cast(mins, ct.POINTER(ct.c_double)) + cpMaxs = ct.cast(maxs, ct.POINTER(ct.c_double)) + + start = timer() + result = lercDll.lerc_getDataRanges(cpBytes, nBytes, nDepth, nBands, cpMins, cpMaxs) + end = timer() + + if result > 0: + print('Error in getLercDataRanges(): lercDLL.lerc_getDataRanges() failed with error code = ', result) + return (result) + + if printInfo: + print('time lerc_getDataRanges() = ', (end - start)) + print('data ranges per band and depth:') + for i in range(nBands): + for j in range(nDepth): + print('band', i, 'depth', j, ': [', cpMins[i * nDepth + j], ',', cpMaxs[i * nDepth + j], ']') + + npMins = np.frombuffer(mins, 'd') + npMaxs = np.frombuffer(maxs, 'd') + npMins.shape = (nBands, nDepth) + npMaxs.shape = (nBands, nDepth) + + return (result, npMins, npMaxs) #------------------------------------------------------------------------------- def decode(lercBlob, printInfo = False): + return _decode_Ext(lercBlob, 0, printInfo) + +def decode_4D(lercBlob, printInfo = False): + return _decode_Ext(lercBlob, 1, printInfo) + +def _decode_Ext(lercBlob, nSupportNoData, printInfo): global lercDll - (result, version, dataType, nValuesPerPixel, nCols, nRows, nBands, nValidPixels, blobSize, nMasks, zMin, zMax, maxZErrUsed) = getLercBlobInfo(lercBlob, printInfo) + fctErr = 'Error in _decode_Ext(): ' + + (result, version, dataType, nValuesPerPixel, nCols, nRows, nBands, nValidPixels, blobSize, + nMasks, zMin, zMax, maxZErrUsed, nUsesNoData) = getLercBlobInfo_4D(lercBlob, printInfo) if result > 0: - print('Error in decode(): getLercBlobInfo() failed with error code = ', result) + print(fctErr, 'getLercBlobInfo() failed with error code = ', result) return result + if nUsesNoData and not nSupportNoData: + print(fctErr, 'This Lerc blob uses noData value. Please upgrade to \ + Lerc version 4.0 functions or newer that support this.') + return (5, None, None) # 5 == LercNS::ErrCode::HasNoData + # convert Lerc dataType to np data type npDtArr = ['b', 'B', 'h', 'H', 'i', 'I', 'f', 'd'] npDtype = npDtArr[dataType] @@ -300,13 +665,23 @@ validBuf = ct.create_string_buffer(nMasks * nRows * nCols) cpValidArr = ct.cast(validBuf, ct.c_char_p) + # create empty buffer for noData arrays, if needed + cpHasNoDataArr = None + cpNoDataArr = None + if (nUsesNoData): + hasNoDataBuf = ct.create_string_buffer(nBands) + noDataBuf = ct.create_string_buffer(nBands * 8) + cpHasNoDataArr = ct.cast(hasNoDataBuf, ct.c_char_p) + cpNoDataArr = ct.cast(noDataBuf, ct.POINTER(ct.c_double)) + # call decode start = timer() - result = lercDll.lerc_decode(cpBytes, len(lercBlob), nMasks, cpValidArr, nValuesPerPixel, nCols, nRows, nBands, dataType, cpData) + result = lercDll.lerc_decode_4D(cpBytes, len(lercBlob), nMasks, cpValidArr, nValuesPerPixel, + nCols, nRows, nBands, dataType, cpData, cpHasNoDataArr, cpNoDataArr) end = timer() if result > 0: - print('Error in decode(): lercDll.lerc_decode() failed with error code = ', result) + print(fctErr, 'lercDll.lerc_decode() failed with error code = ', result) return result if printInfo: @@ -316,27 +691,109 @@ npArr = np.frombuffer(dataBuf, npDtype) npArr.shape = shape + npValidMask = None if nMasks > 0: npValidBytes = np.frombuffer(validBuf, dtype='B') if nMasks == 1: npValidBytes.shape = (nRows, nCols) else: npValidBytes.shape = (nMasks, nRows, nCols) - npValidMask = (npValidBytes != 0) + + npmaNoData = None + if (nUsesNoData): + npHasNoData = np.frombuffer(hasNoDataBuf, dtype='B') + npHasNoData.shape = (nBands) + npHasNoDataMask = (npHasNoData == 0) + npNoData = np.frombuffer(noDataBuf, 'd') + npNoData.shape = (nBands) + npmaNoData = np.ma.array(npNoData, mask = npHasNoDataMask) + + if not nSupportNoData: # old version, up to Lerc version 3.0 return (result, npArr, npValidMask) - else: - return (result, npArr, None) - + else: # newer version, >= Lerc version 4.0 + return (result, npArr, npValidMask, npmaNoData) + +#------------------------------------------------------------------------------- + +# return data as a masked array; convenient but slower + +def decode_ma(lercBlob, printInfo = False): + + fctErr = 'Error in decode_ma(): ' + + (result, version, dataType, nValuesPerPixel, nCols, nRows, nBands, nValidPixels, + blobSize, nMasks, zMin, zMax, maxZErrUsed, nUsesNoData) = getLercBlobInfo_4D(lercBlob, printInfo) + if result > 0: + print(fctErr, 'getLercBlobInfo() failed with error code = ', result) + return result + + (result, npArr, npValidMask, npmaNoData) = _decode_Ext(lercBlob, 1, printInfo) + if result > 0: + print(fctErr, '_decode_Ext() failed with error code = ', result) + return result + + npmaArr = convert2ma(npArr, npValidMask, nValuesPerPixel, nBands, npmaNoData) + + return (result, npmaArr, nValuesPerPixel, npmaNoData) + +# convert numpy data array, valid / invalid byte mask, and noData values to one masked array + +def convert2ma(npArr, npValidMask, nValuesPerPixel, nBands, npmaNoData): + + if (npmaNoData is None) and (npValidMask is None): + return(np.ma.array(npArr, mask = False)) + + if not npValidMask is None: + + if (nValuesPerPixel > 1): # need to blow up mask from 2D to 3D or 3D to 4D + + npMask3D = npValidMask + for k in range(nValuesPerPixel - 1): + npMask3D = np.dstack((npMask3D, npValidMask)) + + if nBands == 1 or npValidMask.ndim == 3: # one mask per band + npmaArr = np.ma.array(npArr, mask = (npMask3D == False)) + + elif nBands > 1: # use same mask for all bands + npMask4D = np.stack([npMask3D for _ in range(nBands)]) + npmaArr = np.ma.array(npArr, mask = (npMask4D == False)) + + elif (nValuesPerPixel == 1): + if nBands == 1 or npValidMask.ndim == 3: # one mask per band + npmaArr = np.ma.array(npArr, mask = (npValidMask == False)) + + elif nBands > 1: # same mask for all bands + npMask3D = np.stack([npValidMask for _ in range(nBands)]) + npmaArr = np.ma.array(npArr, mask = (npMask3D == False)) + + elif npValidMask is None: + npmaArr = np.ma.array(npArr, mask = False) + + if not npmaNoData is None: + + if nBands == 1: + if not npmaNoData.mask[0]: + npmaArr = np.ma.masked_equal(npmaArr, npmaNoData[0]) + + elif nBands > 1: + for m in range(nBands): + if not npmaNoData.mask[m]: + npmaArr[m] = np.ma.masked_equal(npmaArr[m], npmaNoData[m]) + + return (npmaArr) + #------------------------------------------------------------------------------- #------------------------------------------------------------------------------- def test(): + fctErr = 'Error in test(): ' + # data types supported by Lerc, all little endian byte order # 'b', 'B', 'h', 'H', 'i', 'I', 'f', 'd' - print(' -------- encode test 1 -------- ') + print('\n -------- encode test 1 -------- ') nBands = 1 nRows = 256 @@ -356,34 +813,36 @@ # call with buffer size 0 to only compute compressed size, optional numBytesNeeded = 0 - (result, numBytesNeeded) = encode(npArr, nValuesPerPixel, False, npValidMask, maxZErr, numBytesNeeded, True) + (result, numBytesNeeded) = encode(npArr, nValuesPerPixel, False, npValidMask, + maxZErr, numBytesNeeded, True) if result > 0: - print('Error in test(): error code = ', result) + print(fctErr, 'encode() failed with error code = ', result) return result print('computed compressed size = ', numBytesNeeded) # encode with numBytesNeeded from above or big enough estimate - (result, numBytesWritten, outBuffer) = encode(npArr, nValuesPerPixel, False, npValidMask, maxZErr, numBytesNeeded, True) + (result, numBytesWritten, outBuffer) = encode(npArr, nValuesPerPixel, False, npValidMask, + maxZErr, numBytesNeeded, True) if result > 0: - print('Error in test(): error code = ', result) + print(fctErr, 'encode() failed with error code = ', result) return result print('num bytes written to buffer = ', numBytesWritten) # decode again (result, npArrDec, npValidMaskDec) = decode(outBuffer, True) if result > 0: - print('Error in test(): decode() failed with error code = ', result) + print(fctErr, 'decode() failed with error code = ', result) return result - # evaluate the difference to orig (assuming no mask all valid) - maxZErrFound = findMaxZError(npArr, npArrDec) + # evaluate the difference to orig + maxZErrFound = findMaxZError_4D(npArr, npArrDec, npValidMaskDec, nBands) print('maxZErr found = ', maxZErrFound) # find the range [zMin, zMax] in the numpy array (zMin, zMax) = findDataRange(npArrDec, False, None, nBands, True) print('data range found = ', zMin, zMax) - print(' -------- encode test 2 -------- ') + print('\n -------- encode test 2 -------- ') nBands = 3 nRows = 256 @@ -402,54 +861,175 @@ # encode nBytesBigEnough = npArr.nbytes * 2 - (result, numBytesWritten, outBuffer) = encode(npArr, nValuesPerPixel, True, npValidMask, maxZErr, nBytesBigEnough, True) + (result, numBytesWritten, outBuffer) = encode(npArr, nValuesPerPixel, True, npValidMask, + maxZErr, nBytesBigEnough, True) if result > 0: - print('Error in encode(): error code = ', result) + print(fctErr, 'encode() failed with error code = ', result) return result print('num bytes written to buffer = ', numBytesWritten) # decode again (result, npArrDec, npValidMaskDec) = decode(outBuffer, True) if result > 0: - print('Error in test(): decode() failed with error code = ', result) + print(fctErr, 'decode() failed with error code = ', result) return result - # evaluate the difference to orig (assuming no mask all valid) - maxZErrFound = findMaxZError(npArr, npArrDec) + # evaluate the difference to orig + maxZErrFound = findMaxZError_4D(npArr, npArrDec, npValidMaskDec, nBands) print('maxZErr found = ', maxZErrFound) + + # find the range [zMin, zMax] + (zMin, zMax) = findDataRange(npArrDec, True, npValidMaskDec, nBands, True) + print('data range found = ', zMin, zMax) # save compressed Lerc blob to disk #open('C:/temp/test_1_256_256_3_double.lrc', 'wb').write(outBuffer) + print('\n -------- encode test 3 -------- ') + + # example for the new _4D() and _ma() functions in Lerc version 4.0 + + nBands = 3 + nRows = 512 + nCols = 512 + nValuesPerPixel = 2 # values or array per pixel + + npArr = np.zeros((nBands, nRows, nCols, nValuesPerPixel), 'f', 'C') # data type float, C order + npValidMask = None # same as all pixels valid, but we are going to add a noData value + maxZErr = 0.01 + noDataVal = -9999.0 + cntInvalid = 0 + + # fill it with something + start = timer() + for m in range(nBands): + for i in range(nRows): + for j in range(nCols): + for k in range(nValuesPerPixel): + z = 0.001 * i * j + 5 * m + k + # for all values at the same pixel, will get pushed into the byte mask + if j == i: + z = noDataVal + cntInvalid += 1 + # create mixed case, decoded output will use noData for this one pixel in band 0 + if (m == 0 and i == 5 and j == 7 and k == 0): + z = noDataVal + cntInvalid += 1 + npArr[m][i][j][k] = z + end = timer() + print('time fill test array = ', (end - start)) + + # prepare noData arrays + npNoDataArr = np.zeros((nBands), 'd') # noData value is always type double + npNoDataArr.fill(noDataVal) # noData value can vary between bands + npmaNoData = np.ma.array(npNoDataArr, mask = False) # input has noData values in all 3 bands + + # part A, using _4D() functions: + + # encode using encode_4D() + start = timer() + nBytesBigEnough = npArr.nbytes * 2 + (result, numBytesWritten, outBuffer) = encode_4D(npArr, nValuesPerPixel, npValidMask, maxZErr, + nBytesBigEnough, npmaNoData, False) + end = timer() + if result > 0: + print(fctErr, 'encode_4D() failed with error code = ', result) + return result + print('time encode_4D() = ', (end - start)) + print('num bytes written to buffer = ', numBytesWritten) + + # decode using decode_4D() + start = timer() + (result, npArrDec, npValidMaskDec, npmaNoDataDec) = decode_4D(outBuffer, False) + end = timer() + if result > 0: + print(fctErr, 'decode_4D() failed with error code = ', result) + return result + print('time decode_4D() = ', (end - start)) + + # evaluate the difference to orig + maxZErrFound = findMaxZError_4D(npArr, npArrDec, npValidMaskDec, nBands) + print('maxZErr found = ', maxZErrFound) + + # find the range [zMin, zMax] + npmaArrDec = convert2ma(npArrDec, npValidMaskDec, nValuesPerPixel, nBands, npmaNoDataDec) + (zMin, zMax) = findDataRange_ma(npmaArrDec) + print('data range found = ', zMin, zMax) + + # part B, using _ma() functions or masked arrays: + + npmaArr = np.ma.array(npArr, mask = False) + start = timer() + (result, numBytesWritten2, outBuffer2) = encode_ma(npmaArr, nValuesPerPixel, maxZErr, + nBytesBigEnough, npmaNoData, False) + end = timer() + if result > 0: + print(fctErr, 'encode_ma() failed with error code = ', result) + return result + print('time encode_ma() = ', (end - start)) + print('num bytes written to buffer = ', numBytesWritten2) + print('lerc blob size from encode_4D() = ', numBytesWritten, + ', and from encode_ma() = ', numBytesWritten2) + + #decode using decode_ma() + start = timer() + (result, npmaArrDec, nDepthDec, npmaNoDataDec2) = decode_ma(outBuffer2, False) + end = timer() + if result > 0: + print(fctErr, 'decode_ma() failed with error code = ', result) + return result + print('time decode_ma() = ', (end - start)) + + # find the range [zMin, zMax], again + (zMin, zMax) = findDataRange_ma(npmaArrDec) + print('data range found for ma = ', zMin, zMax) + print('number of invalid values, orig = ', cntInvalid, ', in masked array = ', + np.ma.count_masked(npmaArrDec)) + + if False: - print(' -------- decode tests -------- ') + print('\n -------- decode test on ~100 different Lerc blobs -------- ') folder = 'D:/GitHub/LercOpenSource_v2.5/testData/' - files = ['california_400_400_1_float.lerc2', - 'bluemarble_256_256_3_byte.lerc2', - 'landsat_512_512_6_byte.lerc2', - 'world.lerc1', - 'Different_Masks/lerc_level_0.lerc2'] + listFile = folder + '_list.txt' + with open(listFile, 'r') as f: + lines = f.readlines() + f.close() + + skipFirstLine = True + for line in lines: + if skipFirstLine: + skipFirstLine = False + continue - for n in range(len(files)): - fn = folder + files[n] + fn = folder + line.rstrip() bytesRead = open(fn, 'rb').read() # read the blob header, optional - (result, version, dataType, nValuesPerPixel, nCols, nRows, nBands, nValidPixels, blobSize, nMasks, zMin, zMax, maxZErrUsed) = getLercBlobInfo(bytesRead, False) + (result, codecVersion, dataType, nValuesPerPixel, nCols, nRows, nBands, nValidPixels, + blobSize, nMasks, zMin, zMax, maxZErrUsed, nUsesNoData) = getLercBlobInfo_4D(bytesRead, False) if result > 0: - print('Error in test(): getLercBlobInfo() failed with error code = ', result) + print(fctErr, 'getLercBlobInfo_4D() failed with error code = ', result) return result + # read the data ranges, optional + if nUsesNoData == 0: + (result, npMins, npMaxs) = getLercDataRanges(bytesRead, nValuesPerPixel, nBands, False) + if result > 0: + print(fctErr, 'getLercDataRanges() failed with error code = ', result) + return result + # decode - (result, npArr, npValidMask) = decode(bytesRead, True) + (result, npmaArr, nDepth, npmaNoData) = decode_ma(bytesRead, False) if result > 0: - print('Error in test(): decode() failed with error code = ', result) + print(fctErr, 'decode_ma() failed with error code = ', result) return result - # find the range [zMin, zMax] in the numpy array and compare to the above - (zMin, zMax) = findDataRange(npArr, nMasks > 0, npValidMask, nBands, True) - print('data range found = ', zMin, zMax) - print('------') - + # find the range [zMin, zMax] + (zMin, zMax) = findDataRange_ma(npmaArr) + + print(f'codec {codecVersion:1}, dt {dataType:1}, nDepth {nValuesPerPixel:3}, nCols {nCols:5},', + f'nRows {nRows:5}, nBands {nBands:3}, nMasks {nMasks:3}, maxZErr {maxZErrUsed:.6f},', + f'nUsesNoData {nUsesNoData:3}, zMin {zMin:9.3f}, zMax {zMax:14.3f}, ', line.rstrip()) + return result diff -Nru lerc-3.0+ds/OtherLanguages/Python/lerc/README.md lerc-4.0.0+ds/OtherLanguages/Python/lerc/README.md --- lerc-3.0+ds/OtherLanguages/Python/lerc/README.md 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/Python/lerc/README.md 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,80 @@ +# What's new in Lerc 4.0? + +## Option 1, uses numpy masked array + +An encoded image tile (2D, 3D, or 4D), of data type byte to double, can have +any value set to invalid. There are new and hopefully easy to use numpy +functions to decode from or encode to Lerc. They make use of the numpy +masked array. + +(result, npmaArr, nDepth, npmaNoData) = decode_ma(lercBlob) + +- lercBlob is the compressed Lerc blob as a string buffer or byte array as + read in from disk or passed in memory. +- result is 0 for success or an error code for failure. +- npmaArr is the masked numpy array with the data and mask of the same shape. +- nDepth == nValuesPerPixel. E.g., 3 for RGB, or 2 for complex numbers. +- npmaNoData is a 1D masked array of size nBands. It can hold one noData + value per band. The caller can usually ignore it as npmaArr has all mask + info. It may be useful if the data needs to be Lerc encoded again. + +(result, nBytesWritten, lercBlob) = encode_ma(npmaArr, nDepth, maxZErr, + nBytesHint, npmaNoData = None) + +- npmaArr is the image tile (2D, 3D, or 4D) to be encoded, as a numpy masked array. +- nDepth == nValuesPerPixel. E.g., 3 for RGB, or 2 for complex numbers. +- maxZErr is the max encoding error allowed per value. 0 means lossless. +- nBytesHint can be + - 0 - compute num bytes needed for output buffer, but do not encode it (faster than encode) + - 1 - do both, compute exact buffer size needed and encode (slower than encode alone) + - N - create buffer of size N and encode, if buffer too small encode will fail. + +- npmaNoData is a 1D masked array of size nBands. It can hold one noData + value per band. It can be used as an alternative to masks. It must be + used for the so called mixed case of valid and invalid values at the same + pixel, only possible for nDepth > 1. In most cases None can be passed. + Note Lerc does not take NaN as a noData value here. It is enough to set the + data values to NaN and not specify a noData value. + +## Option 2, uses regular numpy arrays for data and mask + +As an alternative to the numpy masked array above, there is also the option +to have data and masked as separate numpy arrays. + +(result, npArr, npValidMask, npmaNoData) = decode_4D(lercBlob) + +Here, npArr can be of the same shapes as npmaArr above, but it is a regular +numpy array, not a masked array. The mask is passed separately as a regular +numpy array of type bool. Note that in contrast to the masked array above, +True means now valid and False means invalid. The npValidMask can have the +following shapes: +- None, all pixels are valid or are marked invalid using noData value or NaN. +- 2D or (nRows, nCols), same mask for all bands. +- 3D or (nBands, nRows, nCols), one mask per band. + +The _4D() functions may work well if all pixels are valid, or nDepth == 1, +and the shape of the mask here matches the shape of the data anyway. +In such cases the use of a numpy masked array might not be needed or +considered an overkill. + +Similar for encode: + +(result, nBytesWritten, lercBlob) = encode_4D(npArr, nDepth, npValidMask, + maxZErr, nBytesHint, npmaNoData = None) + +## General remarks + +Note that for all encode functions, you can set values to invalid using a +mask, or using a noData value, or using NaN (for data types float or double). +Or any combination which is then merged using AND for valid (same as OR for invalid) +by the Lerc API. + +The decode functions, however, return this info as a mask, wherever possible. +Only for nDepth > 1 and the mixed case of valid and invalid values at the same +pixel, a noData value is used internally. NaN is never returned by decode. + +The existing Lerc 3.0 encode and decode functions can still be used. +Only for nDepth > 1 the mixed case cannot be encoded. If the decoder +should encounter a Lerc blob with such a mixed case, it will fail with +the error code LercNS::ErrCode::HasNoData == 5. + diff -Nru lerc-3.0+ds/OtherLanguages/Python/setup.py lerc-4.0.0+ds/OtherLanguages/Python/setup.py --- lerc-3.0+ds/OtherLanguages/Python/setup.py 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/OtherLanguages/Python/setup.py 2022-07-15 18:25:29.000000000 +0000 @@ -16,7 +16,7 @@ # Using MANIFEST.in doesn't respect relative paths above the package root. # Instead, inspect the location and copy in the binaries if newer. -BINARY_TYPES = ["*.dll", "*.lib", "*.so", "*.dylib"] +BINARY_TYPES = ["*.dll", "*.lib", "*.so*", "*.dylib"] PLATFORMS = ["Linux", "MacOS", "windows"] for platform in PLATFORMS: platform_dir = join("..", "..", "bin", platform) @@ -29,7 +29,7 @@ setuptools.setup( name="lerc", - version="3.0", + version="4.0", author="esri", author_email="python@esri.com", description="Limited Error Raster Compression", diff -Nru lerc-3.0+ds/README.md lerc-4.0.0+ds/README.md --- lerc-3.0+ds/README.md 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/README.md 2022-07-15 18:25:29.000000000 +0000 @@ -13,6 +13,7 @@ `uint lerc_computeCompressedSize(...)` | Computes the buffer size that needs to be allocated so the image can be Lerc compressed into that buffer. The size is accurate to the byte. This function is optional. It is faster than `lerc_encode(...)`. It can also be called to decide whether an image or image tile should be encoded by Lerc or another method. `uint lerc_encode(...)` | Compresses a given image into a pre-allocated buffer. If that buffer is too small, the function fails with the corresponding error code. The function also returns the number of bytes written. `uint lerc_getBlobInfo(...)` | Looks into a given Lerc byte blob and returns an array with all the header info. From this, the image to be decoded can be allocated and constructed. This function is optional. You don't need to call it if you already know the image properties such as tile size and data type. +`uint lerc_getDataRanges(...)` | Looks into a given Lerc byte blob and returns 2 double arrays with the minimum and maximum values per band and depth. This function is optional. It allows fast access to the data ranges without having to decode the pixels. `uint lerc_decode(...)` | Uncompresses a given Lerc byte blob into a pre-allocated image. If the data found in the Lerc byte blob does not fit the specified image properties, the function fails with the corresponding error code. `uint lerc_decodeToDouble(...)` | Uncompresses a given Lerc byte blob into a pre-allocated image of type double independent of the compressed data type. This function was added mainly to be called from other languages such as Python and C#. @@ -20,11 +21,15 @@ See the sample program `src/LercTest/main.cpp` which demonstrates how the above functions are called and used. Also see the two header files in the `src/LercLib/include/` folder and the comments in there. -About multiple bands, or multiple values per pixel. This has changed with Lerc version 2.4. Before, you could either store each band into its own Lerc byte blob which allowed you to access / decode each band individually. Lerc also allowed to stack bands together into one single Lerc byte blob. This could be useful if the bands are always used together anyway. Now, since Lerc version 2.4, you can additionally store multiple values per pixel interleaved, meaning an array of values for pixel 1, next array of values for pixel 2, and so forth. We have added a new parameter "nDim" for this number of values per pixel. +About multiple bands, or multiple values per pixel. This has changed with Lerc version 2.4. Before, you could either store each band into its own Lerc byte blob which allowed you to access / decode each band individually. Lerc also allowed to stack bands together into one single Lerc byte blob. This could be useful if the bands are always used together anyway. Now, since Lerc version 2.4, you can additionally store multiple values per pixel interleaved, meaning an array of values for pixel 1, next array of values for pixel 2, and so forth. We have added a new parameter "nDepth" for this number of values per pixel. -While the above can be used as an "interleave flag" to store multiple raster bands as a 3D array as either [nBands, nRows, nCols] for band interleaved or as [nRows, nCols, nDim] for pixel interleaved, it also allows to do both at the same time and store a 4D array as [nBands, nRows, nCols, nDim]. +While the above can be used as an "interleave flag" to store multiple raster bands as a 3D array as either [nBands, nRows, nCols] for band interleaved or as [nRows, nCols, nDepth] for pixel interleaved, it also allows to do both at the same time and store a 4D array as [nBands, nRows, nCols, nDepth]. -Note that the valid / invalid pixel byte mask is not 4D but limited to [nBands, nRows, nCols]. This mask is per pixel per band. For nDim > 1 or an array of values per pixel, Lerc assumes all values in that array at that pixel are either valid or invalid. If the values in the innermost array per pixel can be partially valid and invalid, use a predefined noData value or NaN. +Note that the valid / invalid pixel byte mask is not 4D but limited to [nBands, nRows, nCols]. This mask is per pixel per band. For nDepth > 1 or an array of values per pixel, up to Lerc version 3.0, Lerc assumed all values in that array at that pixel are either valid or invalid. If the values in the innermost array per pixel can be partially valid and invalid, use a predefined noData value or NaN. + +To better support this special "mixed" case, we have added new Lerc API functions *_4D() in Lerc version 4.0, see [Lerc_c_api.h](https://github.com/Esri/lerc/blob/master/src/LercLib/include/Lerc_c_api.h). These functions allow to pass one noData value per band to the encode_4D() function and can receive it back in the decode_4D() function. This way such data can be compressed with maxZError > 0 or lossy, despite the presence of noData values in the data. Note that Lerc will convert noData values to 0 bytes in the valid / invalid byte mask whenever possible. This also allows now to pass raster data with noData values to the encoder without first creating the valid / invalid byte mask. NoData values can be passed both ways, as noData or as byte mask. Note that on decode Lerc only returns a noData value for the mixed case of valid and invalid values at the same pixel (which can only happen for nDepth > 1). The valid / invalid byte mask remains the preferred way to represent void or noData values. + +Remark about NaN. As Lerc supports both integer and floating point data types, and there is no NaN for integer types, Lerc filters out NaN values and replaces them. Preferred it pushes NaN's into the valid / invalid byte mask. For the mixed case, it replaces NaN by the passed noData value. If there is no noData value, encode will fail. Lerc decode won't return any NaN's. ## When to use @@ -50,11 +55,19 @@ Check out the Lerc decoders and encoders in `OtherLanguages/`. You may need to adjust the paths to input or output data and the dll or .so file. Other than that they should just work. +### Other download sites + +- [Lerc for Python / Conda](https://anaconda.org/Esri/lerc) +- [Lerc for JavaScript / npm](https://www.npmjs.com/package/lerc) + ### How to compile LERC and the C++ test program +For building the Lerc library on any platform using CMake, use `CMakeLists.txt`. +For the most common platforms you can find alternative project files under `build/`. + #### Windows -- Open `build/Windows/MS_VS2019/Lerc.sln` with Microsoft Visual Studio. +- Open `build/Windows/MS_VS2022/Lerc.sln` with Microsoft Visual Studio. - Build and run. #### Linux @@ -93,7 +106,7 @@ the error allowed, the stronger the compression. Compression factors larger than 100x have been reported. -- this Lerc package can read all (legacy) versions of Lerc, such as Lerc1, Lerc2 v1 to v4, and the current Lerc2 v5. It always writes the latest stable version. +- this Lerc package can read all (legacy) codec versions of Lerc, such as Lerc1, Lerc2 v1 to v5, and the current Lerc2 v6. It always writes the latest stable version. The main principle of Lerc and history can be found in [doc/MORE.md](doc/MORE.md) @@ -108,7 +121,7 @@ ## Licensing -Copyright 2015-2019 Esri +Copyright 2015-2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff -Nru lerc-3.0+ds/src/LercLib/BitMask.cpp lerc-4.0.0+ds/src/LercLib/BitMask.cpp --- lerc-3.0+ds/src/LercLib/BitMask.cpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/BitMask.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff -Nru lerc-3.0+ds/src/LercLib/BitMask.h lerc-4.0.0+ds/src/LercLib/BitMask.h --- lerc-3.0+ds/src/LercLib/BitMask.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/BitMask.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff -Nru lerc-3.0+ds/src/LercLib/BitStuffer2.cpp lerc-4.0.0+ds/src/LercLib/BitStuffer2.cpp --- lerc-3.0+ds/src/LercLib/BitStuffer2.cpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/BitStuffer2.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -349,7 +349,7 @@ // -------------------------------------------------------------------------- ; bool BitStuffer2::BitUnStuff_Before_Lerc2v3(const Byte** ppByte, size_t& nBytesRemaining, - vector& dataVec, unsigned int numElements, int numBits) + vector& dataVec, unsigned int numElements, int numBits) const { if (numElements == 0 || numBits >= 32) return false; @@ -360,36 +360,31 @@ size_t numUInts = (size_t)numUIntsLL; size_t numBytes = (size_t)numBytesLL; // could theoretically overflow on 32 bit system - if (numBytes != numBytesLL || nBytesRemaining < numBytes) + const unsigned int ntbnn = NumTailBytesNotNeeded(numElements, numBits); + + if (numBytes != numBytesLL || nBytesRemaining + ntbnn < numBytes) return false; try { dataVec.resize(numElements, 0); // init with 0 + m_tmpBitStuffVec.resize(numUInts); } catch (const std::exception&) { return false; } - unsigned int* arr = (unsigned int*)(*ppByte); - unsigned int* srcPtr = arr; - srcPtr += numUInts - 1; + m_tmpBitStuffVec[numUInts - 1] = 0; // set last uint to 0 - // needed to save the 0-3 bytes not used in the last UInt - unsigned int lastUInt = *srcPtr; - unsigned int numBytesNotNeeded = NumTailBytesNotNeeded(numElements, numBits); + unsigned int nBytesToCopy = (numElements * numBits + 7) / 8; + memcpy(&m_tmpBitStuffVec[0], *ppByte, nBytesToCopy); - for (unsigned int n = numBytesNotNeeded; n; n--) - { - unsigned int val; - memcpy(&val, srcPtr, sizeof(unsigned int)); - val <<= 8; - memcpy(srcPtr, &val, sizeof(unsigned int)); - } + unsigned int* pLastULong = &m_tmpBitStuffVec[numUInts - 1]; + for (unsigned int k = ntbnn; k > 0; k--) + *pLastULong <<= 8; - // do the un-stuffing - srcPtr = arr; + unsigned int* srcPtr = &m_tmpBitStuffVec[0]; unsigned int* dstPtr = &dataVec[0]; int bitPos = 0; @@ -422,11 +417,9 @@ } } - if (numBytesNotNeeded > 0) - memcpy(srcPtr, &lastUInt, sizeof(unsigned int)); // restore the last UInt + *ppByte += nBytesToCopy; + nBytesRemaining -= nBytesToCopy; - *ppByte += numBytes - numBytesNotNeeded; - nBytesRemaining -= numBytes - numBytesNotNeeded; return true; } diff -Nru lerc-3.0+ds/src/LercLib/BitStuffer2.h lerc-4.0.0+ds/src/LercLib/BitStuffer2.h --- lerc-3.0+ds/src/LercLib/BitStuffer2.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/BitStuffer2.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -53,7 +53,7 @@ mutable std::vector m_tmpLutVec, m_tmpIndexVec, m_tmpBitStuffVec; static void BitStuff_Before_Lerc2v3(Byte** ppByte, const std::vector& dataVec, int numBits); - static bool BitUnStuff_Before_Lerc2v3(const Byte** ppByte, size_t& nBytesRemaining, std::vector& dataVec, unsigned int numElements, int numBits); + bool BitUnStuff_Before_Lerc2v3(const Byte** ppByte, size_t& nBytesRemaining, std::vector& dataVec, unsigned int numElements, int numBits) const; void BitStuff(Byte** ppByte, const std::vector& dataVec, int numBits) const; bool BitUnStuff(const Byte** ppByte, size_t& nBytesRemaining, std::vector& dataVec, unsigned int numElements, int numBits) const; diff -Nru lerc-3.0+ds/src/LercLib/Defines.h lerc-4.0.0+ds/src/LercLib/Defines.h --- lerc-3.0+ds/src/LercLib/Defines.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Defines.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ #define USING_NAMESPACE_LERC using namespace LercNS; #define HAVE_LERC1_DECODE +//#define ENCODE_VERIFY NAMESPACE_LERC_START diff -Nru lerc-3.0+ds/src/LercLib/fpl_Compression.cpp lerc-4.0.0+ds/src/LercLib/fpl_Compression.cpp --- lerc-3.0+ds/src/LercLib/fpl_Compression.cpp 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/fpl_Compression.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,109 @@ +/* +Copyright 2021 - 2022 Esri + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +A local copy of the license and additional notices are located with the +source distribution at: + +http://github.com/Esri/lerc/ + +Analytical Raster Compression. +Original coding 2021 Yuriy Yakimenko +*/ + +#include "fpl_Compression.h" +#include "fpl_EsriHuffman.h" +#include +#include +#include + +USING_NAMESPACE_LERC + +static const bool use_esri_huffman = true; +static const bool use_rle = true; + +size_t fpl_Compression::extract_buffer(const char* data, size_t size, size_t uncompressed_size, char** output) +{ + if (use_esri_huffman) + { + size_t expected_size = uncompressed_size; + + bool ret = fpl_EsriHuffman::DecodeHuffman((const unsigned char*)data, size, expected_size, (unsigned char**)output); + + //if (!ret) + // printf("input size %zu, expected out %zu\n", size, uncompressed_size); + + assert(ret); + return uncompressed_size; + } + + assert(0); + return 0; +} + +size_t fpl_Compression::compress_buffer(const char* data, size_t size, char** output, bool fast) +{ + if (use_esri_huffman) + { + if (fast && !output) + { + // return getCompressedSize (data, size); + + return getEntropySize((unsigned char*)data, size); // this is approximate but fastest + } + + assert(size > 0); + + int ret = fpl_EsriHuffman::EncodeHuffman(data, size, (unsigned char**)output, use_rle); + +#ifdef _DEBUG + //if (ret < 0) + // fprintf(stderr, "Huffman failed. Input size: %zd. Code: %d\n", size, ret); +#endif + + return ret; + } + + return 0; +} + +long fpl_Compression::getEntropySize(const unsigned char* ptr, const size_t size) +{ + unsigned long table[256]; + + memset(table, 0, sizeof(table)); + + int total_count = 0; + + for (size_t i = 0; i < size; i += PRIME_MULT) + { + table[ptr[i]]++; + total_count++; + } + + double total_bits = 0; + + for (size_t i = 0; i < 256; i++) + { + if (table[i] == 0) continue; + + double p = (double)total_count / table[i]; + + double bits = log2(p); + + total_bits += (bits * table[i]); + } + + return (long)((total_bits + 7) / 8); +} diff -Nru lerc-3.0+ds/src/LercLib/fpl_Compression.h lerc-4.0.0+ds/src/LercLib/fpl_Compression.h --- lerc-3.0+ds/src/LercLib/fpl_Compression.h 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/fpl_Compression.h 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,46 @@ +/* +Copyright 2021 - 2022 Esri + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +A local copy of the license and additional notices are located with the +source distribution at: + +http://github.com/Esri/lerc/ + +Analytical Raster Compression. +Original coding 2021 Yuriy Yakimenko +*/ + +#ifndef FPL_COMPRESSION_H +#define FPL_COMPRESSION_H + +#include +#include "Defines.h" + +NAMESPACE_LERC_START + +#define PRIME_MULT 7 + +class fpl_Compression +{ +public: + static size_t compress_buffer(const char* data, size_t size, char** output, bool fast = false); + static size_t extract_buffer(const char* data, const size_t size, size_t uncompressed_size, char** output = 0); + +private: + static long getEntropySize(const unsigned char* ptr, const size_t size); +}; + +NAMESPACE_LERC_END +#endif diff -Nru lerc-3.0+ds/src/LercLib/fpl_EsriHuffman.cpp lerc-4.0.0+ds/src/LercLib/fpl_EsriHuffman.cpp --- lerc-3.0+ds/src/LercLib/fpl_EsriHuffman.cpp 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/fpl_EsriHuffman.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,573 @@ +/* +Copyright 2021 - 2022 Esri + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +A local copy of the license and additional notices are located with the +source distribution at: + +http://github.com/Esri/lerc/ + +Analytical Raster Compression. +Original coding 2021 Yuriy Yakimenko +*/ + +#include "fpl_EsriHuffman.h" +#include "Huffman.h" +#include +#include +#include +#include + +USING_NAMESPACE_LERC + +void _assert(bool v); + +bool decodePackBits (const unsigned char *ptr, const size_t size, size_t expected, unsigned char **output) +{ + unsigned char *out = NULL; + + if (*output == NULL) + { + out = (unsigned char *)malloc (expected); // max possible + + *output = out; + } + else + { + out = *output; + } + + size_t curr = 0; + + for (size_t i = 0; i < size; ) + { + // read header byte. + + int b = ptr[i]; + + if (b <= 127) + { + while (b >= 0) { i++; out[curr++] = ptr[i]; b--; } + i++; + continue; + } + else + { + i++; + while (b >= 127) { out[curr++] = ptr[i]; b--; } + i++; + continue; + } + } + + return (curr == expected); +} + +long encodePackBits (const unsigned char *ptr, const size_t size, unsigned char **output) +{ + unsigned char *out = NULL; + + if (*output == NULL) + { + out = (unsigned char *)malloc (size * 2 + 1); // max possible + + *output = out; + } + else + { + out = *output; + } + + int repeat_count = 0; + + int literal_count = 0; + + int curr = 0; + + int literal_len_pos = -1; + + for (size_t i = 0; i <= size; ) + { + int b = (i == size) ? -1 : ptr[i]; + + repeat_count = 0; + + while (i < size - 1 && b == ptr[i + 1] && repeat_count < 128) + { + i++; + repeat_count++; + } + + i++; + + // two cases: + // repeat count = 0: increase literal_len, assign literal_pos if not set. + // repeat count > 0 : + // a) if literal_len > 0, flush literal_len, then write sequence + // b) if literal_len = 0 then just write sequence + + if (repeat_count == 0 && b >= 0) + { + if (literal_len_pos < 0) + { + literal_len_pos = curr; + curr++; + } + + out[curr++] = (unsigned char)b; + + literal_count++; + + if (literal_count == 128) + { + out[literal_len_pos] = (unsigned char)(literal_count - 1); + literal_count = 0; + literal_len_pos = -1; + } + } + else + { + if (literal_count > 0) + { + out[literal_len_pos] = (unsigned char)(literal_count - 1); + literal_len_pos = -1; + literal_count = 0; + } + + if (repeat_count > 0) + { + out[curr++] = (unsigned char)(127 + repeat_count); + out[curr++] = (unsigned char)b; + } + + } + + } + + return curr; +} + + +long getPackBitsSize (const unsigned char *ptr, const size_t size, long * limit) +{ + int repeat_count = 0; + + int literal_count = 0; + + long curr = 0; + + int literal_len_pos = -1; + + long m_limit = *limit ? *limit : (std::numeric_limits::max)(); + + for (size_t i = 0; i <= size; ) + { + int b = (i == size) ? -1 : ptr[i]; + + if (curr > m_limit) + return -1; + + repeat_count = 0; + + while (i < size - 1 && b == ptr[i + 1] && repeat_count < 128) + { + i++; + repeat_count++; + } + + i++; + + // two cases: + // repeat count = 0: increase literal_len, assign literal_pos if not set. + // repeat count > 0 : + // a) if literal_len > 0, flush literal_len, then write sequence + // b) if literal_len = 0 then just write sequence + + if (repeat_count == 0 && b >= 0) + { + if (literal_len_pos < 0) + { + literal_len_pos = curr; + curr++; + } + + curr++; + + literal_count++; + + if (literal_count == 128) + { + literal_count = 0; + literal_len_pos = -1; + } + } + else + { + if (literal_count > 0) + { + literal_len_pos = -1; + literal_count = 0; + } + + if (repeat_count > 0) + { + curr += 2; + } + + } + + } + + return curr; +} + +enum HuffmanErrorCodes { MEMORY_ALLOC_FAIL = -1, HUFF_UNEXPECTED = -2 }; + +enum HuffmanFirstByte { HUFFMAN_NORMAL = 0, HUFFMAN_RLE = 1, HUFFMAN_NO_ENCODING = 2, HUFFMAN_PACKBITS = 3 }; + +bool ComputeHistoForHuffman(const unsigned char* data, size_t len, std::vector& histo) +{ + histo.resize(256); + + memset (&histo[0], 0, histo.size() * sizeof(int)); + + for (size_t i = 0; i < len; i++) + { + unsigned char val = data[i]; + + histo[val]++; + } + + int cnt = 0; + for (size_t i = 0; i < 256; i++) + { + if (histo[i] > 0) + { + cnt++; + if (cnt == 2) break; + } + } + + return (cnt > 1); +} + +void ComputeHuffmanCodes(const unsigned char* data, size_t len, int& numBytes, std::vector >& codes) +{ + std::vector histo; + + if (!ComputeHistoForHuffman(data, len, histo)) + { + numBytes = -1; + return; + } + + int nBytes = 0; + double avgBpp = 0; + Huffman huffman; + + if (!huffman.ComputeCodes(histo) || !huffman.ComputeCompressedSize(histo, nBytes, avgBpp)) + nBytes = 0; + + if (nBytes > 0) + { + codes = huffman.GetCodes() ; + } + + numBytes = nBytes; +} + +int fpl_EsriHuffman::getCompressedSize (const char *input, size_t input_len) +{ + int numBytes = 0; + std::vector > m_huffmanCodes; + + ComputeHuffmanCodes ((const unsigned char *)input, input_len, numBytes, m_huffmanCodes); + + if (numBytes < 0) return 6; + + if (numBytes == 0) return 0; + + if (numBytes > (int)input_len) + return (int)input_len + 1; + + return numBytes + 1; +} + +////////////////////////////////////////////////////////////////////////////////// +// outputs size of encoded buffer. +// input: byte array, its length; +// output buffer, its length. If output len isn't sufficient, returns 0. +////////////////////////////////////////////////////////////////////////////////// + +int fpl_EsriHuffman::EncodeHuffman (const char *input, size_t input_len, unsigned char ** ppByte, bool use_rle) +{ + int numBytes = 0; + std::vector > m_huffmanCodes; + + ComputeHuffmanCodes ((const unsigned char *)input, input_len, numBytes, m_huffmanCodes); + + if (numBytes == -1) + { + // there's only one value in entire block (probably zeroes) + // encode as 6 bytes: + // byte 1 RLE flag + // byte 2 repeating value + // bytes 3-6 repeat count (32-bit unsigned int). + *ppByte = (unsigned char *)calloc (6, 1); + + unsigned char *ptr = *ppByte; + + ptr[0] = HUFFMAN_RLE; // RLE flag + ptr[1] = input[0]; + + _assert(input_len <= 0xffffffff); + + uint32_t len = (uint32_t)input_len; + + memcpy (ptr + 2, &len, 4); + + return 6; + } + + if (numBytes == 0) + { + return HUFF_UNEXPECTED; + } + + if (use_rle) + { + long limit = (std::min) (numBytes, (int)input_len); + + long rle_len = getPackBitsSize ((unsigned char *)input, input_len, &limit); + if (rle_len > 0 && (rle_len < numBytes) && (rle_len < (long)input_len)) + { + *ppByte = (unsigned char *)malloc (rle_len + 1); + unsigned char * originalPtr = *ppByte ; + originalPtr[0] = HUFFMAN_PACKBITS; + + unsigned char *packed = originalPtr + 1; //NULL; + encodePackBits ((unsigned char *)input, input_len, &packed); + + return rle_len + 1; + } + } + + if (numBytes >= (int)input_len) // huffman will take more space than uncompressed. Don't encode. + { + *ppByte = (unsigned char *)malloc (input_len + 1); + unsigned char * originalPtr = *ppByte ; + originalPtr[0] = HUFFMAN_NO_ENCODING; // as is flag + memcpy (originalPtr + 1, input, input_len); + + return (int)input_len + 1; + } + + *ppByte = (unsigned char *)malloc (numBytes + 1); + + if (*ppByte == NULL) + { + return MEMORY_ALLOC_FAIL; + } + + unsigned char * originalPtr = *ppByte ; + + originalPtr[0] = HUFFMAN_NORMAL; // normal "huffman flag" + + *ppByte = originalPtr + 1; + + Huffman huffman; + + if (!huffman.SetCodes(m_huffmanCodes) || !huffman.WriteCodeTable(ppByte, 5)) // header and code table + { + free (originalPtr); + return HUFF_UNEXPECTED; + } + + //unsigned char * afterTablePtr = *ppByte; + + int bitPos = 0; + + unsigned int* arr = (unsigned int*)(*ppByte); + unsigned int* dstPtr = arr; + + int offset = 0; + const unsigned char *data = (const unsigned char *)input; + + for (size_t m = 0; m < input_len; m++) + { + unsigned char val = data[m]; + + // bit stuff the huffman code for this val + int kBin = offset + (int)val; + int len = m_huffmanCodes[kBin].first; + if (len <= 0) + { + free (originalPtr); + return HUFF_UNEXPECTED; + } + + unsigned int code = m_huffmanCodes[kBin].second; + + if (32 - bitPos >= len) + { + if (bitPos == 0) + *dstPtr = 0; + + *dstPtr |= code << (32 - bitPos - len); + bitPos += len; + if (bitPos == 32) + { + bitPos = 0; + dstPtr++; + } + } + else + { + bitPos += len - 32; + *dstPtr++ |= code >> bitPos; + *dstPtr = code << (32 - bitPos); + } + } + + size_t numUInts = dstPtr - arr + (bitPos > 0 ? 1 : 0) + 1; // add one more as the decode LUT can read ahead + *ppByte += numUInts * sizeof(unsigned int); + + int ret = (int)(*ppByte - originalPtr); + + *ppByte = originalPtr; + + return ret; +} + +bool fpl_EsriHuffman::DecodeHuffman(const unsigned char* inBytes, const size_t inCount, size_t& nBytesRemainingInOut, unsigned char** output) +{ + const unsigned char* ppByte = (const unsigned char *)inBytes; + + if (!ppByte) + { + //fprintf (stderr, "bad input: null pointer\n"); + return false; + } + + // check for RLE flag + + if (ppByte[0] == HUFFMAN_RLE) + { + unsigned char s = ppByte[1]; + uint32_t rle_size = 0; + + memcpy (&rle_size, ppByte + 2, 4); + + if (rle_size != nBytesRemainingInOut) + { + assert (0); + } + + unsigned char *data = (unsigned char *)malloc (nBytesRemainingInOut); + *output = data; + + if (data == NULL) + { + //fprintf (stderr, "malloc() failed\n"); + return false; + } + + memset (data, s, nBytesRemainingInOut); + + return true; + } + + if (ppByte[0] == HUFFMAN_NO_ENCODING) // "as is flag" + { + unsigned char *data = (unsigned char *)malloc (nBytesRemainingInOut); + *output = data; + + if (data == NULL) + { + //printf ("malloc 2 fail\n"); + return false; + } + + memcpy (data, ppByte + 1, nBytesRemainingInOut); + + return true; + } + + if (ppByte[0] == HUFFMAN_PACKBITS) + { + unsigned char *unpacked = NULL; + + _assert (true == decodePackBits (ppByte + 1, inCount - 1, nBytesRemainingInOut, &unpacked)); + + *output = unpacked; + + return true; + } + + assert (ppByte[0] == HUFFMAN_NORMAL); // "normal" huffman flag. + + ppByte++; + + size_t expected_output_len = nBytesRemainingInOut; + + Huffman huffman; + if (!huffman.ReadCodeTable(&ppByte, nBytesRemainingInOut, 5)) // header and code table + { + return false; + } + + int numBitsLUT = 0; + if (!huffman.BuildTreeFromCodes(numBitsLUT)) + { + return false; + } + + unsigned char *data = (unsigned char *)malloc (expected_output_len); + *output = data; + + if (!data) + { + return false; + } + + int offset = 0; + + const unsigned int* arr = (const unsigned int*)(ppByte); + const unsigned int* srcPtr = arr; + int bitPos = 0; + size_t nBytesRemaining = nBytesRemainingInOut; + + for (size_t m = 0; m < expected_output_len; m++) + { + int val = 0; + if (nBytesRemaining >= 4 * sizeof(unsigned int)) + { + if (!huffman.DecodeOneValue_NoOverrunCheck(&srcPtr, nBytesRemaining, bitPos, numBitsLUT, val)) + { + return false; + } + } + else + { + if (!huffman.DecodeOneValue(&srcPtr, nBytesRemaining, bitPos, numBitsLUT, val)) + { + return false; + } + } + + data[m] = (unsigned char)(val - offset); + } + + return true; +} diff -Nru lerc-3.0+ds/src/LercLib/fpl_EsriHuffman.h lerc-4.0.0+ds/src/LercLib/fpl_EsriHuffman.h --- lerc-3.0+ds/src/LercLib/fpl_EsriHuffman.h 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/fpl_EsriHuffman.h 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,44 @@ +/* +Copyright 2021 - 2022 Esri + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +A local copy of the license and additional notices are located with the +source distribution at: + +http://github.com/Esri/lerc/ + +Analytical Raster Compression. +Original coding 2021 Yuriy Yakimenko +*/ + +#ifndef FPL_ESRI_HUFFMAN_H +#define FPL_ESRI_HUFFMAN_H + +#include +#include "Defines.h" + +NAMESPACE_LERC_START + +class fpl_EsriHuffman +{ +public: + static int EncodeHuffman(const char* input, size_t input_len, unsigned char** ppByte, bool use_rle); + + static bool DecodeHuffman(const unsigned char* inBytes, const size_t inCount, size_t& nBytesRemainingInOut, unsigned char** output); + + static int getCompressedSize(const char* input, size_t input_len); +}; + +NAMESPACE_LERC_END +#endif diff -Nru lerc-3.0+ds/src/LercLib/fpl_Lerc2Ext.cpp lerc-4.0.0+ds/src/LercLib/fpl_Lerc2Ext.cpp --- lerc-3.0+ds/src/LercLib/fpl_Lerc2Ext.cpp 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/fpl_Lerc2Ext.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,865 @@ +/* +Copyright 2021 - 2022 Esri + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +A local copy of the license and additional notices are located with the +source distribution at: + +http://github.com/Esri/lerc/ + +Analytical Raster Compression. +Original coding 2021 Yuriy Yakimenko +*/ + +#include "fpl_Lerc2Ext.h" +#include "fpl_Compression.h" +#include +#include +#include +#include + +USING_NAMESPACE_LERC + +void _assert(bool v) +{ + if (v == false) + throw "Assertion failed"; +} + +template +size_t getMinIndex(const T* array, size_t size) +{ + T min_value = array[0]; + size_t ret = 0; + + for (size_t i = 1; i < size; i++) + { + if (array[i] < min_value) + { + min_value = array[i]; + ret = i; + } + } + + return ret; +} + +struct TestBlock +{ + long top, height; +}; + +static void generateListOfTestBlocks(const int width, const int height, std::vector & blocks) +{ + size_t size = (size_t)width * height; + + _assert(size > 0); + + const int block_target_size = 8 * 1024; + + double t = (round)((double)size / block_target_size); + + int count = (int)round(sqrt(t + 1)); // Always at least 1. We want count to grow but not linearly as size grows. + // A more conservative scheme would be to use log not sqrt. + + // when block size is too wide (>100K) we may need to consider taking a "narrower part" + + int block_height = block_target_size / width; + + if (block_height < 4) block_height = 4; + + while ((count * block_height > height) && (count > 1)) count--; + + float top_margin = (float)((height - count * block_height) / (2.0 * count)); + + float delta = 2.0f * top_margin + block_height; + + for (int i = 0; i < count; i++) + { + TestBlock tb; + + tb.top = (long)(top_margin + delta * i); + tb.height = block_height; + if (tb.top < 0) tb.top = 0; + if (tb.top + tb.height > height) tb.height = height - tb.top; + + if (tb.height > 0) + blocks.push_back(tb); + } +} + +static void setDerivativePrime(unsigned char* data, size_t size) +{ + int off = (int)size - 1; + + off = PRIME_MULT * (off / PRIME_MULT); + + unsigned char* ptr = data + off; + //for (int i = (int)off; i >= 1; i -= PRIME_MULT) + while (ptr >= data + 1) + { + *ptr -= *(ptr - 1); + ptr -= PRIME_MULT; + } +} + +static void setDerivative(unsigned char* data, size_t size, const int level) +{ + if (level == 0) return; + + for (int l = 1; l <= level; l++) + { + unsigned char* ptr = data + size - 1; + for (int i = (int)size - 1; i >= l; i--) + { + *ptr -= *(ptr - 1); + ptr--; + } + } +} + +unsigned char* restoreSequence(unsigned char* data, size_t size, const int level, bool make_copy) +{ + assert(size > 0); + + unsigned char* copy = NULL; + + if (make_copy) + { + copy = (unsigned char*)malloc(size); + + if (!copy) + { + //perror("Out of memory."); + return NULL; + } + + memcpy(copy, data, size); + } + else + copy = data; + + if (level == 0) + return copy; + + for (int l = level; l > 0; l--) + { + unsigned char* ptr = copy + l; + + for (int i = l; i < (int)size; i++) + { + *ptr += *(ptr - 1); + ptr++; + } + } + + return copy; +} + +static size_t testBlocksSize(std::vector & blocks, const UnitType unit_type, const void* _data, const long raster_width, bool test_first_byte_delta) +{ + size_t ret = 0; + + const uint8_t* data = (const uint8_t*)_data; + + const size_t unit_size = UnitTypes::size(unit_type); + + for (size_t blk = 0; blk < blocks.size(); blk++) + { + TestBlock& tb = blocks[blk]; + + size_t start = unit_size * tb.top * raster_width; + size_t length = tb.height * raster_width; + + // input_len += length * unit_size; //sizeof(uint32_t); + + char* plane_buffer = (char*)malloc(length); + + assert(plane_buffer); + + for (int byte = 0; byte < (int)unit_size; byte++) + { + char* ptr = (char*)(data + start); + + ptr += byte; + + for (size_t i = 0; i < length; i++) + { + plane_buffer[i] = *ptr; + + ptr += unit_size; + } + + size_t plane_encoded = fpl_Compression::compress_buffer((const char*)plane_buffer, length, NULL, true); + + // test with delta 1: + + if (test_first_byte_delta) + { + // setDerivative ((unsigned char *)plane_buffer, length, 1); + + setDerivativePrime((unsigned char*)plane_buffer, length); + + size_t plane_encoded2 = fpl_Compression::compress_buffer((const char*)plane_buffer, length, NULL, true); + + ret += (std::min)(plane_encoded, plane_encoded2); + } + else + { + ret += plane_encoded; + } + } + + free(plane_buffer); + } + + return ret; +} + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// + + +int getBestLevel2(const unsigned char* ptr, size_t size, const int max_delta_order) +{ + std::vector > snippets; + + const unsigned int targetSampleSize = 1024 * 8; + + double t = round((double)size / targetSampleSize); + + int count = (int)round(sqrt(t + 1)); // Always at least 1. We want count to grow but not linearly as size grows. + // A more conservative scheme would be to use log not sqrt. + + while (count * targetSampleSize > size && (count > 0)) count--; + + float top_margin = (float)(((int)size - count * targetSampleSize) / (2.0 * count)); + + float delta = 2.0f * top_margin + targetSampleSize; + + for (int i = 0; i < count; i++) + { + long start = (long)(top_margin + delta * i); + int len = targetSampleSize; + + if (start < 0) + start = 0; + if (start + len > (int)size) + len = (int)size - start; + + assert(len > 0); + + if (len > 0) + snippets.push_back(std::make_pair(start, len)); + } + + unsigned char* copy = (unsigned char*)malloc(size); + + if (!copy) + return 0; // return 0 as fall-back "default" delta. + + memcpy(copy, ptr, size); + + size_t best_comp = 0; + + int ret = 0; + + for (int l = 0; l <= max_delta_order; l++) + { + if (l > 0) + { + for (size_t s = 0; s < snippets.size(); s++) + { + size_t start = snippets[s].first; + int len = snippets[s].second; + + for (int i = (int)start + len - 1; i >= (int)start + l; i--) + { + copy[i] -= copy[i - 1]; + } + } + } + + size_t comp = 0; + + for (size_t i = 0; i < snippets.size(); i++) + { + size_t start = snippets[i].first; + int len = snippets[i].second; + + comp += fpl_Compression::compress_buffer((const char*)copy + start, len, NULL, true); + } + + if (comp < best_comp || l == 0) + { + best_comp = comp; + ret = l; + } + else + { + break; // if deteriorating, stop. + } + } + + free(copy); + + return ret; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +int getBestLevel(const unsigned char* ptr, size_t size, const int max_delta_order) +{ + if (max_delta_order == 0) + return 0; + + return getBestLevel2(ptr, size, max_delta_order); +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +LosslessFPCompression::~LosslessFPCompression() +{ + delete m_data_slice; +} + +void LosslessFPCompression::selectInitialLinearOrCrossDelta(const UnitType unit_type, void* pData, const int iWidth, + const int iHeight, int& initial_delta, bool& use_cross, bool test_first_byte_delta, size_t* stats) +{ + std::vector blocks; + + size_t local_stats[3] = { 0 }; + + if (stats == NULL) + { + stats = local_stats; + } + + generateListOfTestBlocks(iWidth, iHeight, blocks); + + size_t est = 0; + + // unchanged block: + est = testBlocksSize(blocks, unit_type, pData, iWidth, test_first_byte_delta); + + if (stats) { stats[0] += est; } + + // linear part: + + UnitTypes::setBlockDerivative(unit_type, pData, iWidth, iHeight, 1, 1); + + est = testBlocksSize(blocks, unit_type, pData, iWidth, test_first_byte_delta); + + if (stats) { stats[1] += est; } + + UnitTypes::setCrossDerivative(unit_type, pData, iWidth, iHeight, 2, 2); + + est = testBlocksSize(blocks, unit_type, pData, iWidth, test_first_byte_delta); + + if (stats) { stats[2] += est; } + + size_t min_index = getMinIndex(stats, 3); + + if (min_index == 2) + { + use_cross = true; + initial_delta = 2; + } + else if (min_index == 1) + { + use_cross = false; + initial_delta = 1; + } + else + { + use_cross = false; + initial_delta = 0; + } +} + +int LosslessFPCompression::compressedLength() const +{ + int ret = 1; // predictor code + + for (auto b : m_data_slice->m_buffers) + { + ret += b->compressed_size; + ret += 6; // 2 bytes plus compr. size. We can use 1 byte in place of 2 by packing level and byte index. + } + + return ret; +} + +bool LosslessFPCompression::EncodeHuffmanFlt(unsigned char ** ppByte) +{ + memcpy(*ppByte, &(m_data_slice->m_predictor_code), sizeof(m_data_slice->m_predictor_code)); + *ppByte += sizeof(m_data_slice->m_predictor_code); + + for (auto b : m_data_slice->m_buffers) + { + memcpy(*ppByte, &(b->byte_index), sizeof(b->byte_index)); + *ppByte += sizeof(b->byte_index); + memcpy(*ppByte, &(b->best_level), sizeof(b->best_level)); + *ppByte += sizeof(b->best_level); + memcpy(*ppByte, &(b->compressed_size), sizeof(b->compressed_size)); + *ppByte += sizeof(b->compressed_size); + memcpy(*ppByte, b->compressed, b->compressed_size); + *ppByte += b->compressed_size; + } + + for (auto v : m_data_slice->m_buffers) + { + delete v; + } + + m_data_slice->m_buffers.clear(); + + return true; +} + +bool LosslessFPCompression::ComputeHuffmanCodesFlt(const void* input, bool bIsDouble, + int iCols, int iRows, int iDepth) +{ + if (iDepth == 1) + { + if (m_data_slice && !m_data_slice->m_buffers.empty()) + { + // we decided not to write compressed output last time. + // in this case, old compressed content must be removed. + for (auto v : m_data_slice->m_buffers) + { + delete v; + } + + m_data_slice->m_buffers.clear(); + } + return ComputeHuffmanCodesFltSlice (input, bIsDouble, iCols, iRows); + } + else + { + return ComputeHuffmanCodesFltSlice(input, bIsDouble, iDepth, iCols * iRows); + } +} + +bool LosslessFPCompression::ComputeHuffmanCodesFltSlice (const void* pInput, bool bIsDouble, int iCols, int iRows) +{ + // 1. copy input; when input is 32-bit floats, move bits of copied input values. + // 2. copy to temp buffer. + // 3. perform selectInitialLinearOrCrossDelta on temp copy; deallocate temp copy. + // 4. apply best predictor. + // 5. compress individual byte planes while applying additional dynamic delta (bestLevel). + // 6. save compessed byte planes in m_buffers and predictor in m_predictor_code. + // 7. deallocate copied input. + + UnitType unit_type = bIsDouble ? UNIT_TYPE_DOUBLE : UNIT_TYPE_FLOAT; + + int max_byte_delta = MAX_DELTA; // in pactice it will rarely be more than 3. + // I observed values greater than 3 only with doble data. + // see getMaxByteDelta() call below. + + size_t size = (size_t)iCols * iRows; + + size_t unit_size = UnitTypes::size(unit_type); + size_t block_size = size; + + uint8_t* block_values = (uint8_t*)malloc(block_size * unit_size); + + memcpy(block_values, pInput, block_size * unit_size); + + if (unit_type == UNIT_TYPE_FLOAT) + UnitTypes::doFloatTransform((uint32_t*)block_values, size); + + size_t stats1[3] = { 0 }; + + int block_width = iCols, block_height = iRows; + + int dummy_delta = 0; + bool dummy_cross = false; + bool test_first_byte_delta = true; + + uint8_t* copy = (uint8_t*)malloc(block_size * unit_size); + + if (!copy) + { + //perror("Out of memory."); + free(block_values); + return false; + } + + memcpy(copy, block_values, unit_size * block_size); + + selectInitialLinearOrCrossDelta(unit_type, copy, block_width, block_height, dummy_delta, dummy_cross, test_first_byte_delta, stats1); + + free(copy); + + size_t min_index = getMinIndex(stats1, 3); + + PredictorType predictor = PREDICTOR_NONE; + + if (min_index < 2) + { + if (min_index == 1) predictor = PREDICTOR_DELTA1; + } + else + { + if (min_index == 2) predictor = PREDICTOR_ROWS_COLS; + } + + int w = block_width, h = block_height; + + if (predictor == PREDICTOR_ROWS_COLS) + { + if (predictor == PREDICTOR_ROWS_COLS) + UnitTypes::setCrossDerivative(unit_type, block_values, w, h, 2); + } + else + { + int delta = 0; + + if (predictor == PREDICTOR_DELTA1) delta = 1; + + UnitTypes::setBlockDerivative(unit_type, block_values, w, h, delta); + } + + int max_delta = Predictor::getMaxByteDelta(predictor); + + if (max_byte_delta >= 0 && max_byte_delta < max_delta) + max_delta = max_byte_delta; + + unsigned char* block_buff = (unsigned char*)malloc(block_size); // size is the same for all byte planes + + if (!block_buff) + { + //perror("Out of memory."); + free(block_values); + return false; + } + + if (!m_data_slice) + m_data_slice = new compressedDataSlice(); + + for (int byte = 0; byte < (int)unit_size; byte++) + { + size_t block_index = 0; + + size_t block_shift = 0; + + block_index = byte; + + size_t len = (size_t)h * w; + + while (block_shift < len) + { + block_buff[block_shift] = block_values[block_index]; + + block_index += unit_size; + + block_shift++; + } + + int bestLevel = getBestLevel(block_buff, block_size, max_delta); + + setDerivative(block_buff, block_size, bestLevel); + + char* compressed = NULL; + + size_t ret = fpl_Compression::compress_buffer((const char*)block_buff, block_size, &compressed); + + if (ret > UINT32_MAX) // cannot store compressed size in 32 bits. should not happen. + { + free(block_buff); + free(block_values); + + return false; + } + + // Down the road, we also can store compression method code (other than default Huffman) + // in upper top 6 bits, since max predicor value is 2. + + m_data_slice->m_predictor_code = Predictor::getCode(predictor); + + { + outBlockBuffer* ob = new outBlockBuffer; + ob->compressed = compressed; + ob->compressed_size = (uint32_t)ret; + ob->byte_index = (unsigned char)byte; + ob->best_level = (unsigned char)bestLevel; + + m_data_slice->m_buffers.push_back(ob); + } + } + + free(block_buff); + free(block_values); + + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// + +bool restoreCrossBytes(std::vector >& output_buffers, const size_t input_in_bytes, + const size_t cols, const size_t rows, const PredictorType predictor, + //FILE* output, + const UnitType unit_type, uint8_t** output_block) +{ + _assert(predictor == PREDICTOR_NONE || predictor == PREDICTOR_ROWS_COLS); + + size_t unit_size = output_buffers.size(); + + _assert(unit_size == UnitTypes::size(unit_type)); + + const int delta = Predictor::getIntDelta(predictor); + + size_t block_size = cols * rows; + + assert(input_in_bytes == block_size); + + uint8_t* data_buffer = (uint8_t*)malloc(block_size * unit_size); + + if (NULL == data_buffer) + { + //perror("Out of memory."); + return false; + } + + size_t offset = 0; + + for (size_t i = 0; i < block_size; i++) + { + for (size_t byte = 0; byte < unit_size; byte++) + { + int idx = output_buffers[byte].first; + data_buffer[idx + offset] = output_buffers[byte].second[i]; + } + + offset += unit_size; + } + + UnitTypes::restoreCrossBytes(delta, data_buffer, cols, rows, unit_type); + + if (unit_type == UNIT_TYPE_FLOAT) + { + UnitTypes::undoFloatTransform((uint32_t*)data_buffer, block_size); + } + + if (output_block) + { + *output_block = data_buffer; + } + else + { + free(data_buffer); // Coverity warning fix. Currently, execution never gets here. + } + + return true; +} + +bool restoreByteOrder(std::vector >& output_buffers, + const size_t cols, const size_t rows, const PredictorType predictor, //FILE* output, + const UnitType unit_type, uint8_t** output_block) +{ + _assert(predictor == PREDICTOR_NONE || predictor == PREDICTOR_DELTA1); + + size_t unit_size = output_buffers.size(); + + _assert(unit_size == UnitTypes::size(unit_type)); + + const int delta = Predictor::getIntDelta(predictor); + + const size_t block_size = cols * rows; + + uint8_t* data_buffer = (uint8_t*)malloc(block_size * unit_size); + + if (NULL == data_buffer) + { + //perror("Out of memory."); + return false; + } + + size_t offset = 0; + + for (size_t i = 0; i < block_size; i++) + { + for (size_t byte = 0; byte < unit_size; byte++) + { + int idx = output_buffers[byte].first; + data_buffer[idx + offset] = output_buffers[byte].second[i]; + } + + offset += unit_size; + } + + UnitTypes::restoreBlockSequence(delta, data_buffer, cols, rows, unit_type); + + if (unit_type == UNIT_TYPE_FLOAT) + { + UnitTypes::undoFloatTransform((uint32_t*)data_buffer, block_size); + } + + if (output_block) + { + *output_block = data_buffer; + } + else + { + free(data_buffer); // Coverity warning fix. Currently, execution never gets here. + } + + return true; +} + +////////////////////////////////////////////////////////////////////////////////////// + +bool LosslessFPCompression::DecodeHuffmanFlt(const unsigned char** ppByte, size_t& nBytesRemainingInOut, + void* pData, bool bIsDouble, int iWidth, int iHeight, int iDepth) +{ + if (iDepth == 1) + { + return DecodeHuffmanFltSlice (ppByte, nBytesRemainingInOut, pData, bIsDouble, iWidth, iHeight); + } + else + { + return DecodeHuffmanFltSlice(ppByte, nBytesRemainingInOut, pData, bIsDouble, iDepth, iWidth * iHeight); + } +} + +bool LosslessFPCompression::DecodeHuffmanFltSlice (const unsigned char** ppByte, size_t& nBytesRemainingInOut, + void * pData, bool bIsDouble, int iWidth, int iHeight) +{ + unsigned char* ptr = (unsigned char *)(*ppByte); + + UnitType unit_type = bIsDouble ? UNIT_TYPE_DOUBLE : UNIT_TYPE_FLOAT; + + size_t bytes = UnitTypes::size(unit_type); + + size_t expected_size = ((size_t)iWidth * iHeight); + + std::vector >output_buffers; + + unsigned char pred_code = 0; + + memcpy(&pred_code, ptr, sizeof(pred_code)); + + if (pred_code > 2) // down the road, upper 6 bits can contain compression method. + // we support only Huffman in this version for now. + { + return false; + } + + ptr += sizeof(pred_code); + + nBytesRemainingInOut -= sizeof(pred_code); + + for (size_t byte = 0; byte < bytes; byte++) + { + unsigned char byte_index = 0, best_level = 0; + + uint32_t compressed_size = 0; + + if (nBytesRemainingInOut < 6) + return false; + + memcpy(&byte_index, ptr, sizeof(byte_index)); + + if (byte_index >= bytes) + return false; + + ptr += sizeof(byte_index); + nBytesRemainingInOut -= sizeof(byte_index); + + memcpy(&best_level, ptr, sizeof(best_level)); + ptr += sizeof(best_level); + nBytesRemainingInOut -= sizeof(best_level); + + if (best_level > MAX_DELTA) + return false; + + memcpy(&compressed_size, ptr, sizeof(compressed_size)); + ptr += sizeof(compressed_size); + nBytesRemainingInOut -= sizeof(compressed_size); + + if (nBytesRemainingInOut < compressed_size) + return false; + + unsigned char* compressed = (unsigned char *)malloc(compressed_size); + + if (!compressed) + return false; + + memcpy(compressed, ptr, compressed_size); + ptr += compressed_size; + + nBytesRemainingInOut -= compressed_size; + + char* uncompressed = NULL; + + size_t extracted_size = fpl_Compression::extract_buffer((const char *)compressed, compressed_size, expected_size, &uncompressed); + + _assert(expected_size == extracted_size); + + free(compressed); + compressed = NULL; + + int byte_delta = best_level; + + unsigned char* restored = restoreSequence((unsigned char*)uncompressed, extracted_size, byte_delta, false); + + output_buffers.push_back(std::make_pair(byte_index, (char*)restored)); + } + + // nBytesRemainingInOut -= (ptr - *ppByte); => done above. + *ppByte = ptr; + + PredictorType predictor = Predictor::getType(pred_code); + + const bool use_cross_delta = (predictor == PREDICTOR_ROWS_COLS); + + uint8_t* output_block_data = NULL; + + bool ret = (predictor != PREDICTOR_UNKNOWN); + + if (ret) + { + if (use_cross_delta) + { + ret = restoreCrossBytes(output_buffers, expected_size, iWidth, iHeight, predictor, + //NULL, + unit_type, + &output_block_data); + } + else + { + ret = restoreByteOrder(output_buffers, iWidth, iHeight, predictor, + //NULL, + unit_type, + &output_block_data); + } + } + + for (size_t i = 0; i < output_buffers.size(); i++) + { + free(output_buffers[i].second); + } + + output_buffers.clear(); + + if (output_block_data) // ret is already set to false if memory was not allocated. + { + memcpy(pData, output_block_data, bytes * iWidth * iHeight); + + free(output_block_data); + } + + return (ret); +} diff -Nru lerc-3.0+ds/src/LercLib/fpl_Lerc2Ext.h lerc-4.0.0+ds/src/LercLib/fpl_Lerc2Ext.h --- lerc-3.0+ds/src/LercLib/fpl_Lerc2Ext.h 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/fpl_Lerc2Ext.h 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,107 @@ +/* +Copyright 2021 - 2022 Esri + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +A local copy of the license and additional notices are located with the +source distribution at: + +http://github.com/Esri/lerc/ + +Analytical Raster Compression. +Original coding 2021 Yuriy Yakimenko +*/ + +#ifndef FPL_LERC2_EXT_H +#define FPL_LERC2_EXT_H + +#include +#include +#include "fpl_Predictor.h" +#include "fpl_UnitTypes.h" +#include "Defines.h" + +NAMESPACE_LERC_START + +class LosslessFPCompression +{ +private: + + struct outBlockBuffer + { + char* compressed; + uint32_t compressed_size; // assuming that tile single byte-plane compressed size will not exceed 32 bits. + + unsigned char byte_index; // we store byte index in case we decide to parallelize processing + // and byte planes end up not in strict order. + unsigned char best_level; + + outBlockBuffer() + { + compressed = NULL; + compressed_size = 0; + byte_index = 0; + best_level = 0; + } + + ~outBlockBuffer() + { + free(compressed); + } + }; + + struct compressedDataSlice + { + std::vector m_buffers; + unsigned char m_predictor_code; + + compressedDataSlice() + { + m_predictor_code = PREDICTOR_UNKNOWN; + } + + ~compressedDataSlice() + { + for (auto b : m_buffers) + { + delete b; + } + + m_buffers.clear(); + } + }; + + compressedDataSlice * m_data_slice; + + void selectInitialLinearOrCrossDelta(const UnitType type, void* pData, const int iWidth, const int iHeight, int& initial_delta, bool& use_cross, bool test_first_byte_delta, size_t* stats = NULL); + + bool ComputeHuffmanCodesFltSlice (const void* pInput, bool bIsDouble, int iCols, int iRows); + + static bool DecodeHuffmanFltSlice (const unsigned char** ppByte, size_t& nBytesRemainingInOut, void* pData, + bool bIsDouble, int iCols, int iRows); + + +public: + LosslessFPCompression() : m_data_slice (nullptr) { } + ~LosslessFPCompression(); + + bool ComputeHuffmanCodesFlt(const void * pInput, bool nIsDouble, int iCols, int iRows, int iDepth); + int compressedLength() const; + bool EncodeHuffmanFlt(unsigned char ** ppByte) ; + + static bool DecodeHuffmanFlt(const unsigned char** ppByte, size_t& nBytesRemainingInOut, void * pData, + bool bIsDouble, int iCols, int iRows, int iDepth); +}; + +NAMESPACE_LERC_END +#endif diff -Nru lerc-3.0+ds/src/LercLib/fpl_Predictor.cpp lerc-4.0.0+ds/src/LercLib/fpl_Predictor.cpp --- lerc-3.0+ds/src/LercLib/fpl_Predictor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/fpl_Predictor.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,91 @@ +/* +Copyright 2021 - 2022 Esri + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +A local copy of the license and additional notices are located with the +source distribution at: + +http://github.com/Esri/lerc/ + +Analytical Raster Compression. +Original coding 2021 Yuriy Yakimenko +*/ + +#include "fpl_Predictor.h" + +USING_NAMESPACE_LERC + +int Predictor::getMaxByteDelta (const PredictorType p) +{ + return MAX_DELTA - Predictor::getIntDelta (p); +} + +PredictorType Predictor::getType (const char code) +{ + if (code == 0) return PREDICTOR_NONE; + if (code == 1) return PREDICTOR_DELTA1; + if (code == 2) return PREDICTOR_ROWS_COLS; + + return PREDICTOR_UNKNOWN; +} + +unsigned char Predictor::getCode (const PredictorType p) +{ + if (p == PREDICTOR_NONE) return 0; + if (p == PREDICTOR_DELTA1) return 1; + if (p == PREDICTOR_ROWS_COLS) return 2; + + return -1; +} + + +int Predictor::getIntDelta (const PredictorType p) +{ + switch (p) + { + case PREDICTOR_NONE: + return 0; + case PREDICTOR_ROWS_COLS: + return 2; + case PREDICTOR_DELTA1: + return 1; + default: + return 0; + } + + return 0; + +} + +PredictorType Predictor::fromDeltaAndCross (int delta, bool cross) +{ + if (delta < 0) + return PREDICTOR_UNKNOWN; + + else if (delta == 0) + return PREDICTOR_NONE; + + else if (false == cross) + { + if (delta == 1) + return PREDICTOR_DELTA1; + } + else + { + if (delta == 2) + return PREDICTOR_ROWS_COLS; + } + + return PREDICTOR_UNKNOWN; +} diff -Nru lerc-3.0+ds/src/LercLib/fpl_Predictor.h lerc-4.0.0+ds/src/LercLib/fpl_Predictor.h --- lerc-3.0+ds/src/LercLib/fpl_Predictor.h 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/fpl_Predictor.h 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,50 @@ +/* +Copyright 2021 - 2022 Esri + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +A local copy of the license and additional notices are located with the +source distribution at: + +http://github.com/Esri/lerc/ + +Analytical Raster Compression. +Original coding 2021 Yuriy Yakimenko +*/ + +#ifndef FPL_PREDICTOR_H +#define FPL_PREDICTOR_H + +#include "Defines.h" + +NAMESPACE_LERC_START + +#define MAX_DELTA 5 + +enum PredictorType { PREDICTOR_UNKNOWN = -1, PREDICTOR_NONE = 0, PREDICTOR_DELTA1 = 1, PREDICTOR_ROWS_COLS = 2 }; + +struct Predictor +{ + static int getMaxByteDelta (const PredictorType p); + + static PredictorType getType (const char code); + + static unsigned char getCode (const PredictorType p); + + static int getIntDelta (const PredictorType p); + + static PredictorType fromDeltaAndCross (int delta, bool cross); +}; + +NAMESPACE_LERC_END +#endif diff -Nru lerc-3.0+ds/src/LercLib/fpl_UnitTypes.cpp lerc-4.0.0+ds/src/LercLib/fpl_UnitTypes.cpp --- lerc-3.0+ds/src/LercLib/fpl_UnitTypes.cpp 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/fpl_UnitTypes.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,926 @@ +/* +Copyright 2021 - 2022 Esri + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +A local copy of the license and additional notices are located with the +source distribution at: + +http://github.com/Esri/lerc/ + +Analytical Raster Compression. +Original coding 2021 Yuriy Yakimenko +*/ + +#include "fpl_UnitTypes.h" +#include +#include +#include + +USING_NAMESPACE_LERC + +#define FLT_TYPES_ONLY // comment out this define to enable all types + +static const uint32_t FLT_MANT_MASK = 0x007FFFFFU; +static const uint32_t FLT_9BIT_MASK = 0xFF800000U; + +#define FLT_BIT_23 (1 << 22) //0x00400000U + +uint32_t moveBits2Front(const uint32_t a) +{ + uint32_t ret = a & FLT_MANT_MASK; + + uint32_t ae = ((a & FLT_9BIT_MASK) >> 23) & 0xFF; + + uint32_t as = ((a & 0x80000000) >> 31) & 0x01; + + ret |= (ae << 24); + ret |= (as << 23); + + return ret; +} + +uint32_t undo_moveBits2Front(const uint32_t a) +{ + uint32_t ret = a & FLT_MANT_MASK; + + uint32_t ae = ((a & FLT_9BIT_MASK) >> 24) & 0xFF; + + uint32_t as = (a >> 23) & 0x01; + + ret |= (ae << 23); + ret |= (as << 31); + + return ret; +} + +void UnitTypes::doFloatTransform(uint32_t* pData, const size_t iCnt) +{ + for (size_t i = 0; i < iCnt; i++) + { + pData[i] = moveBits2Front(pData[i]); + } +} + +void UnitTypes::undoFloatTransform(uint32_t* pData, const size_t iCnt) +{ + for (size_t i = 0; i < iCnt; i++) + { + pData[i] = undo_moveBits2Front(pData[i]); + } +} + +uint32_t SUB32_BIT_FLT(const uint32_t& a, const uint32_t& b) +{ + uint32_t ret = 0; + + const bool no_exp_delta = false; + + ret = ((a - b) & FLT_MANT_MASK); + + uint32_t ae = ((a & FLT_9BIT_MASK) >> 23) & 0x1FF; + uint32_t be = no_exp_delta ? 0 : ((b & FLT_9BIT_MASK) >> 23) & 0x1FF; + + ret |= (((ae - be) & 0x1FF) << 23); + + return ret; +} + +uint32_t ADD32_BIT_FLT(const uint32_t& a, const uint32_t& b) +{ + uint32_t ret = 0; + + const bool no_exp_delta = false; + + ret = ((a + b) & FLT_MANT_MASK); + + uint32_t ae = ((a & FLT_9BIT_MASK) >> 23) & 0x1FF; + uint32_t be = no_exp_delta ? 0 : ((b & FLT_9BIT_MASK) >> 23) & 0x1FF; + + ret |= (((ae + be) & 0x1FF) << 23); + + return ret; +} + +static const uint64_t DBL_MANT_MASK = 0x000FFFFFFFFFFFFFULL; +static const uint64_t DBL_12BIT_MASK = 0xFFF0000000000000ULL; + + +uint64_t SUB64_BIT_DBL(const uint64_t& a, const uint64_t& b) +{ + uint64_t ret = 0; + + const bool no_exp_delta = false; + + uint64_t am = a & DBL_MANT_MASK; + uint64_t bm = b & DBL_MANT_MASK; + + ret = ((am - bm) & DBL_MANT_MASK); + + uint64_t ae = ((a & DBL_12BIT_MASK) >> 52) & 0xFFF; + uint64_t be = no_exp_delta ? 0 : ((b & DBL_12BIT_MASK) >> 52) & 0xFFF; + + ret |= (((ae - be) & 0xFFF) << 52); + + return ret; +} + +uint64_t ADD64_BIT_DBL(const uint64_t& a, const uint64_t& b) +{ + uint64_t ret = 0; + + const bool no_exp_delta = false; + + uint64_t am = a & DBL_MANT_MASK; + uint64_t bm = b & DBL_MANT_MASK; + + ret = ((am + bm) & DBL_MANT_MASK); + + uint64_t ae = ((a & DBL_12BIT_MASK) >> 52) & 0xFFF; + uint64_t be = no_exp_delta ? 0 : ((b & DBL_12BIT_MASK) >> 52) & 0xFFF; + + ret |= (((ae + be) & 0xFFF) << 52); + + return ret; +} + +size_t UnitTypes::size (const UnitType type) +{ + switch (type) + { +#ifndef FLT_TYPES_ONLY + case UNIT_TYPE_BYTE: + return 1; + case UNIT_TYPE_SHORT: + return 2; + case UNIT_TYPE_LONG: + return 4; + case UNIT_TYPE_64_BIT: + return 8; +#endif + case UNIT_TYPE_FLOAT: + return 4; + case UNIT_TYPE_DOUBLE: + assert(sizeof(double) == 8); + return 8; + default: + assert(0); + return 0; + } +} + +///////////////////////////////////////////////////////// + +UnitType UnitTypes::type (int iBytes, bool bFloatType) +{ + if (iBytes == 1) + return UNIT_TYPE_BYTE; + + else if (iBytes == 2) + return UNIT_TYPE_SHORT; + + else if (iBytes == 4) + { + if (bFloatType) + return UNIT_TYPE_FLOAT; + return UNIT_TYPE_LONG; + } + + else if (iBytes == 8) + { + if (bFloatType) + return UNIT_TYPE_DOUBLE; + + return UNIT_TYPE_64_BIT; + } + + assert (0); + + return UNIT_TYPE_UNKNOWN; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// + +unsigned char UnitTypes::unitCode (const UnitType type) +{ + if (type >= UNIT_TYPE_BYTE && type <= UNIT_TYPE_DOUBLE) + return (unsigned char)type; + + assert (0); + + return 0; +} + +/////////////////////////////////////////////////////////////////////////////////////////////// +void setDerivativeFloat (uint32_t * pData, const size_t count, const int level, const int start_level) +{ + for (int l = start_level; l <= level; l++) + { + for (int i = (int)count - 1; i >= l; i--) + { + pData[i] = SUB32_BIT_FLT (pData[i], pData[i - 1]); + } + } +} + +void setDerivativeDouble (uint64_t * pData, const size_t count, const int nLevel, const int start_level) +{ + for (int l = start_level; l <= nLevel; l++) + { + for (int i = (int)count - 1; i >= l; i--) + { + pData[i] = SUB64_BIT_DBL (pData[i], pData[i - 1]); + } + } +} + +template +void setDerivativeGeneric (T * pData, const size_t count, const int nLevel, const int start_level) +{ + for (int l = start_level; l <= nLevel; l++) + { + for (int i = (int)count - 1; i >= l; i--) + { + pData[i] = pData[i] - pData[i - 1]; + } + } +} + + +void UnitTypes::setDerivative (const UnitType type, void *pData, const size_t nCount, const int nLevel, const int start_level) +{ + assert (nCount > 0); + + if (nLevel == 0) return; + + if (type == UNIT_TYPE_FLOAT ) + { + setDerivativeFloat ((uint32_t *)pData, nCount, nLevel, start_level); + } +#ifndef FLT_TYPES_ONLY + else if (type == UNIT_TYPE_BYTE) + { + setDerivativeGeneric ( (uint8_t *)pData, count, level, start_level); + } + else if (type == UNIT_TYPE_SHORT) + { + setDerivativeGeneric ( (uint16_t *)pData, count, level, start_level); + } + else if (type == UNIT_TYPE_LONG) + { + setDerivativeGeneric ( (uint32_t *)pData, count, level, start_level); + } + else if (type == UNIT_TYPE_64_BIT) + { + setDerivativeGeneric ( (uint64_t *)pData, count, level, start_level); + } +#endif + else if (type == UNIT_TYPE_DOUBLE) + { + // assert (0); // TODO + setDerivativeDouble ((uint64_t *)pData, nCount, nLevel, start_level); + } + else + { + //printf ("Unknown unit type: %d\n", type); + assert (0); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void setRowsDerivativeFloat (uint32_t *pData, const size_t nCols, const size_t nRows, const int nLevel, int phase) +{ + assert (nCols > 0 && nRows > 0); + + assert (nLevel >= 2) ; + + uint32_t * row_data = pData; + + int start_level = 1; + int end_level = nLevel; + + if (phase == 2) start_level = 2; + if (phase == 1) end_level = 1; + + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (int l = start_level; l <= end_level; l++) + { + for (int i = (int)nCols - 1; i >= l; i--) + { + row_data [i] = SUB32_BIT_FLT (row_data[i], row_data[i - 1]); + } + } + + row_data += nCols; + } +} + +void setRowsDerivativeDouble (uint64_t *pData, const size_t nCols, const size_t nRows, const int nLevel, int phase) +{ + assert (nCols > 0 && nRows > 0); + + assert (nLevel >= 2) ; + + uint64_t * row_data = pData; + + int start_level = 1; + int end_level = nLevel; + + if (phase == 2) start_level = 2; + if (phase == 1) end_level = 1; + + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (int l = start_level; l <= end_level; l++) + { + for (int i = (int)nCols - 1; i >= l; i--) + { + row_data [i] = SUB64_BIT_DBL (row_data[i], row_data[i - 1]); + } + } + + row_data += nCols; + } +} + + +template +void setRowsDerivativeGeneric (T * pData, const size_t nCols, const size_t nRows, const int nLevel, int phase) +{ + assert (nCols > 0 && nRows > 0); + + assert (nLevel >= 2) ; + + T * row_data = pData; + + int start_level = 1; + int end_level = nLevel; + + if (phase == 2) start_level = 2; + if (phase == 1) end_level = 1; + + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (int l = start_level; l <= end_level; l++) + { + for (int i = (int)nCols - 1; i >= l; i--) + { + row_data [i] = row_data [i] - row_data [i - 1]; + } + } + + row_data += nCols; + } +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +// phase 0 : start = 1, end = level +// phase 1 : start = 1, end = 1 +// phase 2 : start = 2, end = 2 + +void UnitTypes::setRowsDerivative (const UnitType type, void *pData, const size_t nCols, const size_t nRows, const int nLevel, int phase) +{ + assert (nCols > 0 && nRows > 0); + + assert (nLevel >= 2) ; + + if (type == UNIT_TYPE_FLOAT ) + { + setRowsDerivativeFloat ((uint32_t *)pData, nCols, nRows, nLevel, phase); + } +#ifndef FLT_TYPES_ONLY + else if (type == UNIT_TYPE_BYTE) + { + setRowsDerivativeGeneric ( (uint8_t *)pData, nCols, nRows, nLevel, phase); + } + else if (type == UNIT_TYPE_SHORT) + { + setRowsDerivativeGeneric ( (uint16_t *)pData, nCols, nRows, nLevel, phase); + } + else if (type == UNIT_TYPE_LONG) + { + setRowsDerivativeGeneric ( (uint32_t *)pData, nCols, nRows, nLevel, phase); + } + else if (type == UNIT_TYPE_64_BIT) + { + setRowsDerivativeGeneric ( (uint64_t *)pData, nCols, nRows, nLevel, phase); + } +#endif + else if (type == UNIT_TYPE_DOUBLE) + { + setRowsDerivativeDouble ( (uint64_t *)pData, nCols, nRows, nLevel, phase); + } + else + { + assert (0); + } + +} + +//////////////////////////////////////////////////////////////////////////////////////////////// + +void setCrossDerivativeFloat (uint32_t *pData, const size_t nCols, const size_t nRows, const int nLevel, int phase) +{ + assert (nCols > 0 && nRows > 0); + + assert (nLevel >= 2) ; + + uint32_t * row_data = pData; + + if (phase == 0 || phase == 1) + { + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (int i = (int)nCols - 1; i >= 1; i--) + { + row_data[i] = SUB32_BIT_FLT (row_data[i], row_data[i - 1]); + } + + row_data += nCols; + } + } + + row_data = pData; + + if (phase == 0 || phase == 2) + { + for (size_t iCol = 0; iCol < nCols; iCol++) + { + size_t iShift = (nRows - 1) * nCols; + + for (int i = (int)nRows - 1; i >= 1; i--) + { + row_data[iShift] = SUB32_BIT_FLT (row_data[iShift], row_data[iShift - nCols]); + + iShift -= nCols; + } + + row_data += 1; + } + } +} + +void setCrossDerivativeDouble (uint64_t *pData, const size_t nCols, const size_t nRows, const int nLevel, int phase) +{ + assert (nCols > 0 && nRows > 0); + + assert (nLevel >= 2) ; + + uint64_t * row_data = pData; + + if (phase == 0 || phase == 1) + { + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (int i = (int)nCols - 1; i >= 1; i--) + { + row_data[i] = SUB64_BIT_DBL (row_data[i], row_data[i - 1]); + } + + row_data += nCols; + } + } + + row_data = pData; + + if (phase == 0 || phase == 2) + { + for (size_t iCol = 0; iCol < nCols; iCol++) + { + size_t iShift = (nRows - 1) * nCols; + + for (int i = (int)nRows - 1; i >= 1; i--) + { + row_data[iShift] = SUB64_BIT_DBL (row_data[iShift], row_data[iShift - nCols]); + + iShift -= nCols; + } + + row_data += 1; + } + } +} + + +template +void setCrossDerivativeGeneric (T *pData, const size_t nCols, const size_t nRows, const int nLevel, int phase) +{ + assert (nCols > 0 && nRows > 0); + + assert (nLevel >= 2) ; + + T * row_data = pData; + + if (phase == 0 || phase == 1) + { + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (int i = (int)nCols - 1; i >= 1; i--) + { + row_data [i] = row_data [i] - row_data [i - 1]; + } + + row_data += nCols; + } + } + + row_data = pData; + + if (phase == 0 || phase == 2) + { + for (size_t iCol = 0; iCol < nCols; iCol++) + { + size_t iShift = (nRows - 1) * nCols; + + for (int i = (int)nRows - 1; i >= 1; i--) + { + row_data [iShift] = row_data [iShift] - row_data [iShift - nCols]; + + iShift -= nCols; + } + + row_data += 1; + } + } +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + + +void UnitTypes::setCrossDerivative (const UnitType type, void *pData, const size_t nCols, const size_t nRows, const int nLevel, int phase) +{ + assert (nCols > 0 && nRows > 0); + + assert (nLevel >= 2) ; + + if (type == UNIT_TYPE_FLOAT ) + { + setCrossDerivativeFloat ((uint32_t *)pData, nCols, nRows, nLevel, phase); + } +#ifndef FLT_TYPES_ONLY + else if (type == UNIT_TYPE_BYTE) + { + setCrossDerivativeGeneric ( (uint8_t *)pData, nCols, nRows, nLevel, phase); + } + else if (type == UNIT_TYPE_SHORT) + { + setCrossDerivativeGeneric ( (uint16_t *)pData, nCols, nRows, nLevel, phase); + } + else if (type == UNIT_TYPE_LONG) + { + setCrossDerivativeGeneric ( (uint32_t *)pData, nCols, nRows, nLevel, phase); + } + else if (type == UNIT_TYPE_64_BIT) + { + setCrossDerivativeGeneric ( (uint64_t *)pData, nCols, nRows, nLevel, phase); + } +#endif + + else if (type == UNIT_TYPE_DOUBLE) + { + setCrossDerivativeDouble ((uint64_t *)pData, nCols, nRows, nLevel, phase); + } + else + { + assert (0); + } + +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void UnitTypes::setBlockDerivative (const UnitType type, void *pData, const size_t nCols, const size_t nRows, const int level, const int start_level) +{ + if (level == 0) return; + + int phase = -1; + + if (start_level == 1 && level == 2) + phase = 0; + else if (start_level == 1 && level == 1) + phase = 1; + else if (start_level == 2 && level == 2) + phase = 2; + + assert (phase >= 0); + + setRowsDerivative (type, pData, nCols, nRows, 2, phase); +} + +////////////////////////////////////////////////////////////////////////////// + +void restoreBlockSequenceFloat (const int nDelta, uint32_t *pData, const size_t nCols, const size_t nRows) +{ + uint32_t * row_data = pData; + + if (true) + { + if (nDelta == 2) + { + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (size_t i = 2; i < nCols; i++) + { + row_data [i] = ADD32_BIT_FLT (row_data [i], row_data [i - 1] ); + } + + row_data += nCols; + } + } + + if (nDelta > 0) + { + row_data = pData; + + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (size_t i = 1; i < nCols; i++) + { + row_data [i] = ADD32_BIT_FLT (row_data [i], row_data [i - 1] ); + } + + row_data += nCols; + } + } + } +} + +void restoreBlockSequenceDouble (const int nDelta, uint64_t *pData, const size_t nCols, const size_t nRows) +{ + uint64_t * row_data = pData; + + if (true) + { + if (nDelta == 2) + { + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (size_t i = 2; i < nCols; i++) + { + row_data [i] = ADD64_BIT_DBL (row_data [i], row_data [i - 1] ); + } + + row_data += nCols; + } + } + + if (nDelta > 0) + { + row_data = pData; + + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (size_t i = 1; i < nCols; i++) + { + row_data [i] = ADD64_BIT_DBL (row_data [i], row_data [i - 1] ); + } + + row_data += nCols; + } + } + } +} + + +template +void restoreBlockSequenceGeneric (const int nDelta, void *pData, const size_t nCols, const size_t nRows) +{ + T * row_data = (T *)pData; + + if (true) + { + if (nDelta == 2) + { + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (size_t i = 2; i < nCols; i++) + { + row_data [i] = row_data [i] + row_data [i - 1]; + } + + row_data += nCols; + } + } + + if (nDelta > 0) + { + row_data = (T *)pData; + + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (size_t i = 1; i < nCols; i++) + { + row_data [i] = row_data [i] + row_data [i - 1]; + } + + row_data += nCols; + } + } + } +} + + +void UnitTypes::restoreBlockSequence (const int delta, void *pData, const size_t nCols, const size_t nRows, const UnitType type) +{ + if (delta == 0) return; + + if (type == UNIT_TYPE_FLOAT ) + { + restoreBlockSequenceFloat (delta, (uint32_t *)pData, nCols, nRows); + } +#ifndef FLT_TYPES_ONLY + else if (type == UNIT_TYPE_BYTE) + { + restoreBlockSequenceGeneric (delta, pData, nCols, nRows); + } + else if (type == UNIT_TYPE_SHORT) + { + restoreBlockSequenceGeneric (delta, pData, nCols, nRows); + } + else if (type == UNIT_TYPE_LONG) + { + restoreBlockSequenceGeneric (delta, pData, nCols, nRows); + } + else if (type == UNIT_TYPE_64_BIT) + { + restoreBlockSequenceGeneric (delta, pData, nCols, nRows); + } +#endif + else if (type == UNIT_TYPE_DOUBLE) + { + restoreBlockSequenceDouble (delta, (uint64_t *)pData, nCols, nRows); + } + else + { + assert (0); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void restoreCrossBytesFloat (const int delta, uint32_t *pData, const size_t nCols, const size_t nRows) +{ + uint32_t * row_data = pData; + + if (true) + { + if (delta == 2) + { + for (size_t iCol = 0; iCol < nCols; iCol++) + { + size_t iShift = nCols; + + for (size_t i = 1; i < nRows; i++) + { + row_data [iShift] = ADD32_BIT_FLT (row_data [iShift], row_data [iShift - nCols] ); + + iShift += nCols; + } + + row_data += 1; + } + } + + row_data = pData; + + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (size_t i = 1; i < nCols; i++) + { + row_data [i] = ADD32_BIT_FLT (row_data [i], row_data [i - 1] ); + } + + row_data += nCols; + } + } + +} + +void restoreCrossBytesDouble (const int delta, uint64_t *pData, const size_t nCols, const size_t nRows) +{ + uint64_t * row_data = pData; + + if (true) + { + if (delta == 2) + { + for (size_t col = 0; col < nCols; col++) + { + size_t iShift = nCols; + + for (size_t i = 1; i < nRows; i++) + { + row_data [iShift] = ADD64_BIT_DBL (row_data [iShift], row_data [iShift - nCols] ); + + iShift += nCols; + } + + row_data += 1; + } + } + + row_data = pData; + + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (size_t i = 1; i < nCols; i++) + { + row_data [i] = ADD64_BIT_DBL (row_data [i], row_data [i - 1] ); + } + + row_data += nCols; + } + } +} + + +template +void restoreCrossBytesGeneric (const int nDelta, void *pData, const size_t nCols, const size_t nRows) +{ + T * row_data = (T *)pData; + + if (true) + { + if (nDelta == 2) + { + for (size_t iCol = 0; iCol < nCols; iCol++) + { + size_t iShift = nCols; + + for (size_t i = 1; i < nRows; i++) + { + row_data [iShift] = row_data [iShift] + row_data [iShift - nCols]; + + iShift += nCols; + } + + row_data += 1; + } + } + + row_data = (T *)pData; + + for (size_t iRow = 0; iRow < nRows; iRow++) + { + for (size_t i = 1; i < nCols; i++) + { + row_data [i] = row_data [i] + row_data [i - 1]; + } + + row_data += nCols; + } + } + +} + +void UnitTypes::restoreCrossBytes (const int delta, void *pData, const size_t nCols, const size_t nRows, const UnitType type) +{ + if (delta == 0) return; + + if (type == UNIT_TYPE_FLOAT ) + { + restoreCrossBytesFloat (delta, (uint32_t *)pData, nCols, nRows); + } +#ifndef FLT_TYPES_ONLY + else if (type == UNIT_TYPE_BYTE) + { + restoreCrossBytesGeneric (delta, pData, nCols, nRows); + } + else if (type == UNIT_TYPE_SHORT) + { + restoreCrossBytesGeneric (delta, pData, nCols, nRows); + } + else if (type == UNIT_TYPE_LONG) + { + restoreCrossBytesGeneric (delta, pData, nCols, nRows); + } + else if (type == UNIT_TYPE_64_BIT) + { + restoreCrossBytesGeneric (delta, pData, nCols, nRows); + } +#endif + else if (type == UNIT_TYPE_DOUBLE) + { + restoreCrossBytesDouble (delta, (uint64_t *)pData, nCols, nRows); + } + else + { + assert (0); + } + +} + diff -Nru lerc-3.0+ds/src/LercLib/fpl_UnitTypes.h lerc-4.0.0+ds/src/LercLib/fpl_UnitTypes.h --- lerc-3.0+ds/src/LercLib/fpl_UnitTypes.h 1970-01-01 00:00:00.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/fpl_UnitTypes.h 2022-07-15 18:25:29.000000000 +0000 @@ -0,0 +1,61 @@ +/* +Copyright 2021 - 2022 Esri + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +A local copy of the license and additional notices are located with the +source distribution at: + +http://github.com/Esri/lerc/ + +Analytical Raster Compression. +Original coding 2021 Yuriy Yakimenko +*/ + +#ifndef FPL_UNIT_TYPES_H +#define FPL_UNIT_TYPES_H + +#include +#include +#include "Defines.h" + +NAMESPACE_LERC_START + +enum UnitType { UNIT_TYPE_UNKNOWN = 0, UNIT_TYPE_BYTE = 1, UNIT_TYPE_SHORT = 2, UNIT_TYPE_LONG = 3, UNIT_TYPE_64_BIT = 4, UNIT_TYPE_FLOAT = 5, UNIT_TYPE_DOUBLE = 6 }; + +struct UnitTypes +{ + static size_t size (const UnitType type); + static UnitType type (int iBytes, bool bFloatType); + + static unsigned char unitCode (const UnitType type) ; + + static void setDerivative (const UnitType type, void *pData, const size_t nCount, const int nLevel, const int start_level = 1); + static void setRowsDerivative (const UnitType type, void *pData, const size_t nCols, const size_t nRows, const int nLevel, int phase = 0); + static void setCrossDerivative (const UnitType type, void *pData, const size_t nCols, const size_t nRows, const int nLevel, int phase = 0); + + static void setBlockDerivative (const UnitType type, void *pData, const size_t nCols, const size_t nRows, const int nLevel, const int start_level = 1); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + // RESTORE: + + static void restoreBlockSequence (const int delta, void *pData, const size_t nCols, const size_t nRows, const UnitType unit_type); + static void restoreCrossBytes (const int delta, void *pData, const size_t nCols, const size_t nRows, const UnitType unit_type); + + static void doFloatTransform(uint32_t* pData, const size_t iCnt); + static void undoFloatTransform(uint32_t* pData, const size_t iCnt); +} ; + +NAMESPACE_LERC_END +#endif diff -Nru lerc-3.0+ds/src/LercLib/Huffman.cpp lerc-4.0.0+ds/src/LercLib/Huffman.cpp --- lerc-3.0+ds/src/LercLib/Huffman.cpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Huffman.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff -Nru lerc-3.0+ds/src/LercLib/Huffman.h lerc-4.0.0+ds/src/LercLib/Huffman.h --- lerc-3.0+ds/src/LercLib/Huffman.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Huffman.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff -Nru lerc-3.0+ds/src/LercLib/include/Lerc_c_api.h lerc-4.0.0+ds/src/LercLib/include/Lerc_c_api.h --- lerc-3.0+ds/src/LercLib/include/Lerc_c_api.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/include/Lerc_c_api.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2016 Esri +Copyright 2016 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,13 +24,19 @@ #ifndef LERC_API_INCLUDE_GUARD #define LERC_API_INCLUDE_GUARD +//#define USE_EMSCRIPTEN // to build a wasm Lerc decoder, install emscripten first + +#ifdef USE_EMSCRIPTEN + #include +#endif + #ifdef __cplusplus extern "C" { #endif /* LERC version numbers and related macros added in 3.0.0 */ -#define LERC_VERSION_MAJOR 3 +#define LERC_VERSION_MAJOR 4 #define LERC_VERSION_MINOR 0 #define LERC_VERSION_PATCH 0 @@ -46,7 +52,9 @@ (LERC_VERSION_NUMBER >= LERC_COMPUTE_VERSION(maj,min,patch)) #if defined _WIN32 || defined __CYGWIN__ -# ifdef LERC_EXPORTS +# if defined(LERC_STATIC) +# define LERCDLL_API +# elif defined(LERC_EXPORTS) # define LERCDLL_API __declspec(dllexport) # else # define LERCDLL_API __declspec(dllimport) @@ -59,146 +67,298 @@ //! C-API for LERC library - //! All output buffers must have been allocated by caller. - + + //! Added in version 4.0: + //! + //! - 1) better support 3D and 4D data, allow for lossy encoding even if a noData value is used + //! - 2) better lossless compression for float and double (pass maxZError = 0) + //! - 3) allow to pass integer > 32 bit as double (Lerc detects it is all integer and uses that) + //! - 4) renamed nDim to nDepth (without changing the function signatures) + //! + //! More on 1). In version 3.0, for 2D images, the 2D valid / invalid byte masks represent invalid pixels. + //! For more than 1 band, different masks per band can be used. No change to that. + //! For nDepth > 1, or an array of values per pixel, there is the special case of a mix of valid and invalid values + //! at the same pixel. The 2D mask cannot cover this case. + //! We have added 4 new functions to version 4.0 to cover this case, see below. If you don't encounter this + //! "mixed case", you can continue using the same API functions as in version 3.0. + //! If you should encounter a Lerc blob that has this mix, both the regular lerc_decode() and + //! lerc_getDataRanges() functions will fail with "ErrCode::HasNoData". + //! In that case, you need to call the new lerc_decode_4D() function. + //! + //! More on 2). Better lossless compression for float and double is enabled for all API functions. + //! For 1) and 3) you have to call the new "..._4D()" functions, see further below. + + typedef unsigned int lerc_status; - //! Compute the buffer size in bytes required to hold the compressed input tile. Optional. - //! You can call lerc_encode(...) directly as long as the output buffer is big enough. + //! All output buffers must have been allocated by the caller. + + //! Compute the buffer size in bytes required to hold the compressed input tile. Optional. + //! You can call lerc_encode(...) directly as long as the output buffer is big enough. - //! Order of raw input data is top left corner to lower right corner, row by row. This for each band. + //! Order of raw input data is top left corner to lower right corner, row by row. This for each band. //! Data type is { char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 }, see Lerc_types.h . //! maxZErr is the max compression error per pixel allowed. - //! The image or mask of valid pixels is optional. Null pointer means all pixels are valid. - //! If not all pixels are valid, set invalid pixel bytes to 0, valid pixel bytes to 1. - //! Size of the valid / invalid pixel image is (nCols * nRows * nMasks). - - LERCDLL_API - lerc_status lerc_computeCompressedSize( - const void* pData, // raw image data, row by row, band by band - unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 - int nDim, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) - int nCols, // number of columns - int nRows, // number of rows - int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) - int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands - const unsigned char* pValidBytes, // null ptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) - double maxZErr, // max coding error per pixel, defines the precision - unsigned int* numBytes); // size of outgoing Lerc blob - + //! The image or mask of valid pixels is optional. Nullptr means all pixels are valid. + //! If not all pixels are valid, set invalid pixel bytes to 0, valid pixel bytes to 1. + //! Size of the valid / invalid pixel image is (nCols * nRows * nMasks). + LERCDLL_API + lerc_status lerc_computeCompressedSize( + const void* pData, // raw image data, row by row, band by band + unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nCols, // number of columns + int nRows, // number of rows + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands + const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) + double maxZErr, // max coding error per pixel, defines the precision + unsigned int* numBytes); // size of outgoing Lerc blob + //! Encode the input data into a compressed Lerc blob. LERCDLL_API - lerc_status lerc_encode( - const void* pData, // raw image data, row by row, band by band - unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 - int nDim, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) - int nCols, // number of columns - int nRows, // number of rows - int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) - int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands - const unsigned char* pValidBytes, // null ptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) - double maxZErr, // max coding error per pixel, defines the precision - unsigned char* pOutBuffer, // buffer to write to, function fails if buffer too small - unsigned int outBufferSize, // size of output buffer - unsigned int* nBytesWritten); // number of bytes written to output buffer - - - //! Use the 2 functions below to encode to an older version - - LERCDLL_API - lerc_status lerc_computeCompressedSizeForVersion( - const void* pData, // raw image data, row by row, band by band - int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4 - unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 - int nDim, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) - int nCols, // number of columns - int nRows, // number of rows - int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) - int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands - const unsigned char* pValidBytes, // null ptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) - double maxZErr, // max coding error per pixel, defines the precision - unsigned int* numBytes); // size of outgoing Lerc blob - - LERCDLL_API - lerc_status lerc_encodeForVersion( - const void* pData, // raw image data, row by row, band by band - int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4 - unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 - int nDim, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) - int nCols, // number of columns - int nRows, // number of rows - int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) - int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands - const unsigned char* pValidBytes, // null ptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) - double maxZErr, // max coding error per pixel, defines the precision - unsigned char* pOutBuffer, // buffer to write to, function fails if buffer too small - unsigned int outBufferSize, // size of output buffer - unsigned int* nBytesWritten); // number of bytes written to output buffer + lerc_status lerc_encode( + const void* pData, // raw image data, row by row, band by band + unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nCols, // number of columns + int nRows, // number of rows + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands + const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) + double maxZErr, // max coding error per pixel, defines the precision + unsigned char* pOutBuffer, // buffer to write to, function fails if buffer too small + unsigned int outBufferSize, // size of output buffer + unsigned int* nBytesWritten); // number of bytes written to output buffer - //! Call this to get info about the compressed Lerc blob. Optional. - //! Info returned in infoArray is { version, dataType, nDim, nCols, nRows, nBands, nValidPixels, blobSize, nMasks }, see Lerc_types.h . + //! Use the 2 functions below to encode to an older codec version + + LERCDLL_API + lerc_status lerc_computeCompressedSizeForVersion( + const void* pData, // raw image data, row by row, band by band + int codecVersion, // [2 .. 6] for [v2.2 .. v2.6], or -1 for latest codec v2.6 + unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nCols, // number of columns + int nRows, // number of rows + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands + const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) + double maxZErr, // max coding error per pixel, defines the precision + unsigned int* numBytes); // size of outgoing Lerc blob + + LERCDLL_API + lerc_status lerc_encodeForVersion( + const void* pData, // raw image data, row by row, band by band + int codecVersion, // [2 .. 6] for [v2.2 .. v2.6], or -1 for latest codec v2.6 + unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nCols, // number of columns + int nRows, // number of rows + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands + const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) + double maxZErr, // max coding error per pixel, defines the precision + unsigned char* pOutBuffer, // buffer to write to, function fails if buffer too small + unsigned int outBufferSize, // size of output buffer + unsigned int* nBytesWritten); // number of bytes written to output buffer + + + //! Call this to get info about the compressed Lerc blob. Optional. + //! Info returned in infoArray is + //! { version, dataType, nDepth, nCols, nRows, nBands, nValidPixels, blobSize, nMasks, nDepth, nUsesNoDataValue }, see Lerc_types.h . //! Info returned in dataRangeArray is { zMin, zMax, maxZErrorUsed }, see Lerc_types.h . - //! If nDim > 1 or nBands > 1 the data range [zMin, zMax] is over all values. + //! If nDepth > 1 or nBands > 1 the data range [zMin, zMax] is over all values. - // Remark on function signature. The arrays to be filled may grow in future versions. In order not to break - // existing code, the function fills these arrays only up to their allocated size. + // Remark on function signature. The arrays to be filled may grow in future versions. In order not to break + // existing code, the function fills these arrays only up to their allocated size. - // Remark on param blobSize. Usually it is known, either the file size of the blob written to disk, + // Remark on param blobSize. Usually it is known, either the file size of the blob written to disk, // or the size of the blob transmitted. It should be passed accurately for 2 reasons: // _ function finds out how many single band Lerc blobs are concatenated, if any // _ function checks for truncated file or blob // It is OK to pass blobSize too large as long as there is no other (valid) Lerc blob following next. - // If in doubt, check the code in Lerc::GetLercInfo(...) for the exact logic. + // If in doubt, check the code in Lerc::GetLercInfo(...) for the exact logic. LERCDLL_API - lerc_status lerc_getBlobInfo( - const unsigned char* pLercBlob, // Lerc blob to decode - unsigned int blobSize, // blob size in bytes - unsigned int* infoArray, // info array with all info needed to allocate the outgoing arrays for calling decode - double* dataRangeArray, // quick access to overall data range [zMin, zMax] without having to decode the data - int infoArraySize, // number of elements of infoArray - int dataRangeArraySize); // number of elements of dataRangeArray +#ifdef USE_EMSCRIPTEN + EMSCRIPTEN_KEEPALIVE +#endif + lerc_status lerc_getBlobInfo( + const unsigned char* pLercBlob, // Lerc blob to decode + unsigned int blobSize, // blob size in bytes + unsigned int* infoArray, // info array with all info needed to allocate the outgoing arrays for calling decode + double* dataRangeArray, // quick access to overall data range [zMin, zMax] without having to decode the data + int infoArraySize, // number of elements of infoArray + int dataRangeArraySize); // number of elements of dataRangeArray + + //! Call this to quickly get the data ranges [min, max] per dimension and band without having to decode the pixels. Optional. + //! The 2 output data arrays must have been allocated to the same size (nDepth * nBands). + //! The output data array's layout is an image with nDepth columns and nBands rows. + LERCDLL_API +#ifdef USE_EMSCRIPTEN + EMSCRIPTEN_KEEPALIVE +#endif + lerc_status lerc_getDataRanges( + const unsigned char* pLercBlob, // Lerc blob to decode + unsigned int blobSize, // blob size in bytes + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + double* pMins, // outgoing minima per dimension and band + double* pMaxs); // outgoing maxima per dimension and band //! Decode the compressed Lerc blob into a raw data array. - //! The data array must have been allocated to size (nDim * nCols * nRows * nBands * sizeof(dataType)). - //! The valid pixels array, if not all pixels valid, must have been allocated to size (nCols * nRows * nMasks). + //! The data array must have been allocated to size (nDepth * nCols * nRows * nBands * sizeof(dataType)). + //! The valid pixels array, if not all pixels valid, must have been allocated to size (nCols * nRows * nMasks). LERCDLL_API - lerc_status lerc_decode( - const unsigned char* pLercBlob, // Lerc blob to decode - unsigned int blobSize, // blob size in bytes - int nMasks, // 0, 1, or nBands; return as many masks in the next array - unsigned char* pValidBytes, // gets filled if not null ptr, even if all valid - int nDim, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) - int nCols, // number of columns - int nRows, // number of rows - int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) - unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 - void* pData); // outgoing data array - +#ifdef USE_EMSCRIPTEN + EMSCRIPTEN_KEEPALIVE +#endif + lerc_status lerc_decode( + const unsigned char* pLercBlob, // Lerc blob to decode + unsigned int blobSize, // blob size in bytes + int nMasks, // 0, 1, or nBands; return as many masks in the next array + unsigned char* pValidBytes, // gets filled if not nullptr, even if all valid + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nCols, // number of columns + int nRows, // number of rows + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 + void* pData); // outgoing data array //! Same as above, but decode into double array independent of compressed data type. - //! Wasteful in memory, but convenient if a caller from C# or Python does not want to deal with - //! data type conversion, templating, or casting. - //! Should this api be extended to new data types that don't fit into a double such as int64, - //! then this function will fail for such compressed data types. - - LERCDLL_API - lerc_status lerc_decodeToDouble( - const unsigned char* pLercBlob, // Lerc blob to decode - unsigned int blobSize, // blob size in bytes - int nMasks, // 0, 1, or nBands; return as many masks in the next array - unsigned char* pValidBytes, // gets filled if not null ptr, even if all valid - int nDim, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) - int nCols, // number of columns - int nRows, // number of rows - int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) - double* pData); // outgoing data array + //! Wasteful in memory, but convenient if a caller from Python or C# does not want to deal with + //! data type conversion, templating, or casting. + //! Should this api be extended to new data types that don't fit into a double such as int64, + //! then this function will fail for such compressed data types. + + LERCDLL_API + lerc_status lerc_decodeToDouble( + const unsigned char* pLercBlob, // Lerc blob to decode + unsigned int blobSize, // blob size in bytes + int nMasks, // 0, 1, or nBands; return as many masks in the next array + unsigned char* pValidBytes, // gets filled if not nullptr, even if all valid + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nCols, // number of columns + int nRows, // number of rows + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + double* pData); // outgoing data array + + + //! Added in version 4.0: + //! + //! The 4 functions below are new. The main purpose (and difference to the functions above) is to support, for 3D and 4D data, + //! the special case of a mix of valid and invalid values at the same pixel. + //! + //! Main idea: Lerc has the property that for each 8x8 pixel block the minimum value is always encoded lossless in the block header. + //! To enable lossy encoding in the presence of noData values, the original noData value is mapped below the range of the valid values, + //! if possible. If not possible, it switches to lossless. On decode, that temporary noData value gets mapped back to the original + //! noData value. + //! + //! To minimize the occurence of noData values (and for better compression), Lerc tries to move noData values to the byte mask + //! wherever possible (e.g., all values at some pixel are invalid). So for a given band the noData values may disappear and get + //! all moved to the byte mask. Decode only returns a noData value if it is really used. In that case the caller needs to filter + //! the decoded arrays using both the byte mask returned and the noData value returned. + //! + //! In addition to the noData support, the new functions can also take integer values > 32 bit (but < 53 bit) as a double array, + //! and if all integer, use that for compression. + //! + //! If floating point data contains NaN, Lerc tries to move it to the byte mask or replace it by a passed noData value. + //! Note, if not all NaN values can be moved to the mask (mixed case), and no noData value was passed, Lerc will fail. + //! It would be wrong to invent a noData value on the tile level. + + + //! Encode functions: + //! + //! If you don't use a noData value, are fine with the byte masks, just pass nullptr for the last 2 arguments. + //! + //! If you do have noData values at pixels that are marked as valid pixels by the byte mask, + //! pass 2 arrays of size nBands each, one value per band. + //! In pUsesNoData array, for each band, pass 1 for noData value is used, 0 if not. + //! In noDataValues array, for each band, pass the noData value if there is one. + + LERCDLL_API + lerc_status lerc_computeCompressedSize_4D( + const void* pData, // raw image data, row by row, band by band + unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nCols, // number of columns + int nRows, // number of rows + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands + const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) + double maxZErr, // max coding error per pixel, defines the precision + unsigned int* numBytes, // size of outgoing Lerc blob + const unsigned char* pUsesNoData, // if there are invalid values not marked by the mask, pass an array of size nBands, 1 - uses noData, 0 - not + const double* noDataValues); // same, pass an array of size nBands with noData value per band, or pass nullptr + + LERCDLL_API + lerc_status lerc_encode_4D( + const void* pData, // raw image data, row by row, band by band + unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nCols, // number of columns + int nRows, // number of rows + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + int nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands + const unsigned char* pValidBytes, // nullptr if all pixels are valid; otherwise 1 byte per pixel (1 = valid, 0 = invalid) + double maxZErr, // max coding error per pixel, defines the precision + unsigned char* pOutBuffer, // buffer to write to, function fails if buffer too small + unsigned int outBufferSize, // size of output buffer + unsigned int* nBytesWritten, // number of bytes written to output buffer + const unsigned char* pUsesNoData, // if there are invalid values not marked by the mask, pass an array of size nBands, 1 - uses noData, 0 - not + const double* noDataValues); // same, pass an array of size nBands with noData value per band, or pass nullptr + + + //! Decode functions: + //! + //! Same as for regular decode, first call lerc_getBlobInfo() to get all info needed from the blob header. + //! Check the property (InfoArray::nUsesNoDataValue) to check if there is any noData value used. + //! + //! If not, just pass nullptr for the last 2 arguments. + //! + //! If yes, pass 2 arrays of size nBands each, one value per band. + //! In pUsesNoData array, for each band, 1 means a noData value is used, 0 means not. + //! In noDataValues array, for each band, it has the noData value if there is one. + //! This is the same noData value as passed for encode. + + LERCDLL_API +#ifdef USE_EMSCRIPTEN + EMSCRIPTEN_KEEPALIVE +#endif + lerc_status lerc_decode_4D( + const unsigned char* pLercBlob, // Lerc blob to decode + unsigned int blobSize, // blob size in bytes + int nMasks, // 0, 1, or nBands; return as many masks in the next array + unsigned char* pValidBytes, // gets filled if not nullptr, even if all valid + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nCols, // number of columns + int nRows, // number of rows + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + unsigned int dataType, // char = 0, uchar = 1, short = 2, ushort = 3, int = 4, uint = 5, float = 6, double = 7 + void* pData, // outgoing data array + unsigned char* pUsesNoData, // pass an array of size nBands, 1 - band uses noData, 0 - not + double* noDataValues); // same, pass an array of size nBands to get the noData value per band, if any + + LERCDLL_API + lerc_status lerc_decodeToDouble_4D( + const unsigned char* pLercBlob, // Lerc blob to decode + unsigned int blobSize, // blob size in bytes + int nMasks, // 0, 1, or nBands; return as many masks in the next array + unsigned char* pValidBytes, // gets filled if not nullptr, even if all valid + int nDepth, // number of values per pixel (e.g., 3 for RGB, data is stored as [RGB, RGB, ...]) + int nCols, // number of columns + int nRows, // number of rows + int nBands, // number of bands (e.g., 3 for [RRRR ..., GGGG ..., BBBB ...]) + double* pData, // outgoing data array + unsigned char* pUsesNoData, // pass an array of size nBands, 1 - band uses noData, 0 - not + double* noDataValues); // same, pass an array of size nBands to get the noData value per band, if any #ifdef __cplusplus diff -Nru lerc-3.0+ds/src/LercLib/include/Lerc_types.h lerc-4.0.0+ds/src/LercLib/include/Lerc_types.h --- lerc-3.0+ds/src/LercLib/include/Lerc_types.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/include/Lerc_types.h 2022-07-15 18:25:29.000000000 +0000 @@ -14,7 +14,8 @@ Failed, WrongParam, BufferTooSmall, - NaN + NaN, + HasNoData }; enum class DataType : int @@ -31,22 +32,26 @@ enum class InfoArrOrder : int { - version = 0, + version = 0, // codec version dataType, - nDim, + nDim, // = nDepth (we renamed nDim to nDepth but don't want to break anything) nCols, nRows, nBands, nValidPixels, // for 1st band blobSize, - nMasks // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands + nMasks, // 0 - all valid, 1 - same mask for all bands, nBands - masks can differ between bands + nDepth, // = nDim (we renamed nDim to nDepth but don't want to break anything) + nUsesNoDataValue, // 0 - no noData value used, nBands - noData value used in 1 or more bands (only possible for nDepth > 1) + _last }; enum class DataRangeArrOrder : int { zMin = 0, zMax, - maxZErrUsed + maxZErrUsed, + _last }; } // namespace diff -Nru lerc-3.0+ds/src/LercLib/Lerc1Decode/BitStuffer.cpp lerc-4.0.0+ds/src/LercLib/Lerc1Decode/BitStuffer.cpp --- lerc-3.0+ds/src/LercLib/Lerc1Decode/BitStuffer.cpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc1Decode/BitStuffer.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -29,7 +29,7 @@ // -------------------------------------------------------------------------- ; -bool BitStuffer::read(Byte** ppByte, vector& dataVec) const +bool BitStuffer::read(const Byte** ppByte, vector& dataVec) const { if (!ppByte) return false; @@ -117,9 +117,9 @@ // -------------------------------------------------------------------------- ; // -------------------------------------------------------------------------- ; -bool BitStuffer::readUInt(Byte** ppByte, unsigned int& k, int numBytes) +bool BitStuffer::readUInt(const Byte** ppByte, unsigned int& k, int numBytes) { - Byte* ptr = *ppByte; + const Byte* ptr = *ppByte; if (numBytes == 1) { diff -Nru lerc-3.0+ds/src/LercLib/Lerc1Decode/BitStuffer.h lerc-4.0.0+ds/src/LercLib/Lerc1Decode/BitStuffer.h --- lerc-3.0+ds/src/LercLib/Lerc1Decode/BitStuffer.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc1Decode/BitStuffer.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -39,12 +39,12 @@ BitStuffer() {} virtual ~BitStuffer() {} - bool read(Byte** ppByte, std::vector& dataVec) const; + bool read(const Byte** ppByte, std::vector& dataVec) const; protected: mutable std::vector m_tmpBitStuffVec; - static bool readUInt(Byte** ppByte, unsigned int& k, int numBytes); // numBytes = 1, 2, or 4 + static bool readUInt(const Byte** ppByte, unsigned int& k, int numBytes); // numBytes = 1, 2, or 4 static unsigned int numTailBytesNotNeeded(unsigned int numElem, int numBits); }; diff -Nru lerc-3.0+ds/src/LercLib/Lerc1Decode/CntZImage.cpp lerc-4.0.0+ds/src/LercLib/Lerc1Decode/CntZImage.cpp --- lerc-3.0+ds/src/LercLib/Lerc1Decode/CntZImage.cpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc1Decode/CntZImage.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -69,7 +69,7 @@ // -------------------------------------------------------------------------- ; -bool CntZImage::read(Byte** ppByte, double maxZError, bool onlyHeader, bool onlyZPart) +bool CntZImage::read(const Byte** ppByte, double maxZError, bool onlyHeader, bool onlyZPart) { if (!ppByte || !*ppByte) return false; @@ -85,7 +85,7 @@ int version = 0, type = 0, width = 0, height = 0; double maxZErrorInFile = 0; - Byte* ptr = *ppByte; + const Byte* ptr = *ppByte; memcpy(&version, ptr, sizeof(int)); ptr += sizeof(int); memcpy(&type, ptr, sizeof(int)); ptr += sizeof(int); @@ -128,7 +128,7 @@ int numTilesVert = 0, numTilesHori = 0, numBytes = 0; float maxValInImg = 0; - Byte* ptr = *ppByte; + const Byte* ptr = *ppByte; memcpy(&numTilesVert, ptr, sizeof(int)); ptr += sizeof(int); memcpy(&numTilesHori, ptr, sizeof(int)); ptr += sizeof(int); @@ -136,7 +136,7 @@ memcpy(&maxValInImg, ptr, sizeof(float)); ptr += sizeof(float); *ppByte = ptr; - Byte* bArr = ptr; + const Byte* bArr = ptr; SWAP_4(numTilesVert); SWAP_4(numTilesHori); @@ -186,9 +186,9 @@ // -------------------------------------------------------------------------- ; bool CntZImage::readTiles(bool zPart, double maxZErrorInFile, int numTilesVert, int numTilesHori, - float maxValInImg, Byte* bArr) + float maxValInImg, const Byte* bArr) { - Byte* ptr = bArr; + const Byte* ptr = bArr; for (int iTile = 0; iTile <= numTilesVert; iTile++) { @@ -223,9 +223,9 @@ // -------------------------------------------------------------------------- ; -bool CntZImage::readCntTile(Byte** ppByte, int i0, int i1, int j0, int j1) +bool CntZImage::readCntTile(const Byte** ppByte, int i0, int i1, int j0, int j1) { - Byte* ptr = *ppByte; + const Byte* ptr = *ppByte; int numPixel = (i1 - i0) * (j1 - j0); Byte comprFlag = *ptr++; @@ -308,9 +308,9 @@ // -------------------------------------------------------------------------- ; -bool CntZImage::readZTile(Byte** ppByte, int i0, int i1, int j0, int j1, double maxZErrorInFile, float maxZInImg) +bool CntZImage::readZTile(const Byte** ppByte, int i0, int i1, int j0, int j1, double maxZErrorInFile, float maxZInImg) { - Byte* ptr = *ppByte; + const Byte* ptr = *ppByte; int numPixel = 0; Byte comprFlag = *ptr++; @@ -437,9 +437,9 @@ // -------------------------------------------------------------------------- ; -bool CntZImage::readFlt(Byte** ppByte, float& z, int numBytes) +bool CntZImage::readFlt(const Byte** ppByte, float& z, int numBytes) { - Byte* ptr = *ppByte; + const Byte* ptr = *ppByte; if (numBytes == 1) { diff -Nru lerc-3.0+ds/src/LercLib/Lerc1Decode/CntZImage.h lerc-4.0.0+ds/src/LercLib/Lerc1Decode/CntZImage.h --- lerc-3.0+ds/src/LercLib/Lerc1Decode/CntZImage.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc1Decode/CntZImage.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ static unsigned int computeNumBytesNeededToReadHeader(bool onlyZPart); /// read succeeds only if maxZError on file <= maxZError requested - bool read(Byte** ppByte, double maxZError, bool onlyHeader = false, bool onlyZPart = false); + bool read(const Byte** ppByte, double maxZError, bool onlyHeader = false, bool onlyZPart = false); protected: @@ -65,13 +65,13 @@ float maxZInImg; }; - bool readTiles(bool zPart, double maxZErrorInFile, int numTilesVert, int numTilesHori, float maxValInImg, Byte* bArr); + bool readTiles(bool zPart, double maxZErrorInFile, int numTilesVert, int numTilesHori, float maxValInImg, const Byte* bArr); - bool readCntTile(Byte** ppByte, int i0, int i1, int j0, int j1); - bool readZTile(Byte** ppByte, int i0, int i1, int j0, int j1, double maxZErrorInFile, float maxZInImg); + bool readCntTile(const Byte** ppByte, int i0, int i1, int j0, int j1); + bool readZTile(const Byte** ppByte, int i0, int i1, int j0, int j1, double maxZErrorInFile, float maxZInImg); static int numBytesFlt(float z); // returns 1, 2, or 4 - static bool readFlt(Byte** ppByte, float& z, int numBytes); + static bool readFlt(const Byte** ppByte, float& z, int numBytes); protected: diff -Nru lerc-3.0+ds/src/LercLib/Lerc1Decode/Image.h lerc-4.0.0+ds/src/LercLib/Lerc1Decode/Image.h --- lerc-3.0+ds/src/LercLib/Lerc1Decode/Image.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc1Decode/Image.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff -Nru lerc-3.0+ds/src/LercLib/Lerc1Decode/TImage.hpp lerc-4.0.0+ds/src/LercLib/Lerc1Decode/TImage.hpp --- lerc-3.0+ds/src/LercLib/Lerc1Decode/TImage.hpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc1Decode/TImage.hpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff -Nru lerc-3.0+ds/src/LercLib/Lerc2.cpp lerc-4.0.0+ds/src/LercLib/Lerc2.cpp --- lerc-3.0+ds/src/LercLib/Lerc2.cpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc2.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -40,10 +40,10 @@ // -------------------------------------------------------------------------- ; -Lerc2::Lerc2(int nDim, int nCols, int nRows, const Byte* pMaskBits) +Lerc2::Lerc2(int nDepth, int nCols, int nRows, const Byte* pMaskBits) { Init(); - Set(nDim, nCols, nRows, pMaskBits); + Set(nDepth, nCols, nRows, pMaskBits); } // -------------------------------------------------------------------------- ; @@ -53,7 +53,7 @@ if (version < 2 || version > CurrentVersion()) return false; - if (version < 4 && m_headerInfo.nDim > 1) + if (version < 4 && m_headerInfo.nDepth > 1) return false; m_headerInfo.version = version; @@ -78,9 +78,9 @@ // -------------------------------------------------------------------------- ; -bool Lerc2::Set(int nDim, int nCols, int nRows, const Byte* pMaskBits) +bool Lerc2::Set(int nDepth, int nCols, int nRows, const Byte* pMaskBits) { - if (nDim > 1 && m_headerInfo.version < 4) + if (nDepth > 1 && m_headerInfo.version < 4) return false; if (!m_bitMask.SetSize(nCols, nRows)) @@ -97,7 +97,7 @@ m_bitMask.SetAllValid(); } - m_headerInfo.nDim = nDim; + m_headerInfo.nDepth = nDepth; m_headerInfo.nCols = nCols; m_headerInfo.nRows = nRows; @@ -106,6 +106,44 @@ // -------------------------------------------------------------------------- ; +bool Lerc2::SetNoDataValues(bool bNeedsNoDataVal, double noDataVal, double noDataValOrig) +{ + if (m_headerInfo.version < 6) + return false; + + m_headerInfo.bPassNoDataValues = bNeedsNoDataVal; + m_headerInfo.noDataVal = bNeedsNoDataVal ? noDataVal : 0; + m_headerInfo.noDataValOrig = bNeedsNoDataVal ? noDataValOrig : 0; + + return true; +} + +// -------------------------------------------------------------------------- ; + +bool Lerc2::SetNumBlobsMoreToCome(int nBlobsMore) +{ + if (m_headerInfo.version < 6) + return false; + + m_headerInfo.nBlobsMore = nBlobsMore; + + return true; +} + +// -------------------------------------------------------------------------- ; + +bool Lerc2::SetIsAllInt(bool bIsAllInt) +{ + if (m_headerInfo.version < 6) + return false; + + m_headerInfo.bIsInt = bIsAllInt ? 1 : 0; + + return true; +} + +// -------------------------------------------------------------------------- ; + template unsigned int Lerc2::ComputeNumBytesNeededToWrite(const T* arr, double maxZError, bool encodeMask) { @@ -154,7 +192,7 @@ return 0; double maxZErrorNew = maxZError; - if (TryRaiseMaxZError(arr, maxZErrorNew)) + if (maxZError > 0 && TryRaiseMaxZError(arr, maxZErrorNew)) { //printf("%f --> %f\n", maxZError, maxZErrorNew); maxZError = maxZErrorNew; @@ -184,20 +222,20 @@ if (m_headerInfo.zMin == m_headerInfo.zMax) // image is const return nBytesHeaderMask; - int nDim = m_headerInfo.nDim; + int nDepth = m_headerInfo.nDepth; if (m_headerInfo.version >= 4) { // add the min max ranges behind the mask and before the main data; // so we do not write it if no valid pixel or all same value const - m_headerInfo.blobSize += 2 * nDim * sizeof(T); + m_headerInfo.blobSize += 2 * nDepth * sizeof(T); bool minMaxEqual = false; if (!CheckMinMaxRanges(minMaxEqual)) return 0; if (minMaxEqual) - return m_headerInfo.blobSize; // all nDim bands are const + return m_headerInfo.blobSize; // all nDepth bands are const } // data @@ -208,7 +246,7 @@ int nBytesData = nBytesTiling; int nBytesHuffman = 0; - if (m_headerInfo.TryHuffman()) + if (m_headerInfo.TryHuffmanInt()) { ImageEncodeMode huffmanEncMode; ComputeHuffmanCodes(arr, nBytesHuffman, huffmanEncMode, m_huffmanCodes); // save Huffman codes for later use @@ -221,13 +259,34 @@ else m_huffmanCodes.resize(0); } + else if (m_headerInfo.TryHuffmanFlt()) + { + m_huffmanCodes.resize(0); + + bool rv = m_lfpc.ComputeHuffmanCodesFlt(arr, (m_headerInfo.dt == DT_Double), + m_headerInfo.nCols, m_headerInfo.nRows, m_headerInfo.nDepth); + + if (!rv) // remove this check before next release to fall back to regular Lerc instead of fail + return 0; + + if (rv) + { + nBytesHuffman = m_lfpc.compressedLength(); + + if (nBytesHuffman < nBytesTiling * 0.9) // demand at least 10% better than not Huffman + { + nBytesData = nBytesHuffman; + m_imageEncodeMode = IEM_DeltaDeltaHuffman; + } + } + } m_writeDataOneSweep = false; - int nBytesDataOneSweep = (int)(numValid * nDim * sizeof(T)); + int nBytesDataOneSweep = (int)(numValid * nDepth * sizeof(T)); { // try with double block size to reduce block header overhead, if - if ((nBytesTiling * 8 < numTotal * nDim * 1.5) // resulting bit rate < x (2 bpp) + if (((size_t)nBytesTiling * 8 < (size_t)numTotal * nDepth * 1.5) // resulting bit rate < x (2 bpp) && (nBytesTiling < 4 * nBytesDataOneSweep) // bit stuffing is effective && (nBytesHuffman == 0 || nBytesTiling < 2 * nBytesHuffman) // not much worse than huffman (otherwise huffman wins anyway) && (m_headerInfo.nRows > m_microBlockSize || m_headerInfo.nCols > m_microBlockSize)) @@ -251,7 +310,7 @@ } } - if (m_headerInfo.TryHuffman()) + if (m_headerInfo.TryHuffmanInt() || m_headerInfo.TryHuffmanFlt()) nBytesData += 1; // flag for image encode mode if (nBytesDataOneSweep <= nBytesData) @@ -318,20 +377,39 @@ if (!m_writeDataOneSweep) { - if (m_headerInfo.TryHuffman()) + if (m_headerInfo.TryHuffmanInt() || m_headerInfo.TryHuffmanFlt()) { **ppByte = (Byte)m_imageEncodeMode; // Huffman or tiling encode mode (*ppByte)++; - if (!m_huffmanCodes.empty()) // Huffman, no tiling + if (m_imageEncodeMode != IEM_Tiling) { - if (m_imageEncodeMode != IEM_DeltaHuffman && m_imageEncodeMode != IEM_Huffman) - return false; + if (m_headerInfo.TryHuffmanFlt()) + { + if (!(m_imageEncodeMode == IEM_DeltaHuffman || m_imageEncodeMode == IEM_Huffman || m_imageEncodeMode == IEM_DeltaDeltaHuffman)) + return false; - if (!EncodeHuffman(arr, ppByte)) // data bit stuffed - return false; + if (!m_lfpc.EncodeHuffmanFlt(ppByte)) + return false; + + return DoChecksOnEncode(ptrBlob, *ppByte); + } - return DoChecksOnEncode(ptrBlob, *ppByte); + if (!m_huffmanCodes.empty()) // Huffman, no tiling + { + if (m_headerInfo.TryHuffmanInt()) + { + if (!(m_imageEncodeMode == IEM_DeltaHuffman || m_imageEncodeMode == IEM_Huffman)) + return false; + + if (!EncodeHuffman(arr, ppByte)) // data bit stuffed + return false; + } + else + return false; + + return DoChecksOnEncode(ptrBlob, *ppByte); + } } } @@ -380,6 +458,69 @@ // -------------------------------------------------------------------------- ; +bool Lerc2::GetRanges(const Byte* pByte, size_t nBytesRemaining, double* pMins, double* pMaxs) +{ + if (!pByte || !IsLittleEndianSystem() || m_headerInfo.version < 4 || !pMins || !pMaxs) + return false; + + if (!ReadHeader(&pByte, nBytesRemaining, m_headerInfo)) + return false; + + if (!ReadMask(&pByte, nBytesRemaining)) + return false; + + const int nDepth = m_headerInfo.nDepth; + + if (m_headerInfo.numValidPixel == 0) // image is empty + { + memset(pMins, 0, nDepth * sizeof(double)); // fill with 0 + memset(pMaxs, 0, nDepth * sizeof(double)); + + return true; + } + + if (m_headerInfo.zMin == m_headerInfo.zMax) // image is const + { + double val = m_headerInfo.zMin; + + for (int i = 0; i < nDepth; i++) // fill with const + pMins[i] = pMaxs[i] = val; + + return true; + } + + bool rv = false; + void* ptr = nullptr; + + switch (m_headerInfo.dt) + { + case DT_Char: rv = ReadMinMaxRanges(&pByte, nBytesRemaining, (signed char*) ptr); break; + case DT_Byte: rv = ReadMinMaxRanges(&pByte, nBytesRemaining, (Byte*) ptr); break; + case DT_Short: rv = ReadMinMaxRanges(&pByte, nBytesRemaining, (short*) ptr); break; + case DT_UShort: rv = ReadMinMaxRanges(&pByte, nBytesRemaining, (unsigned short*)ptr); break; + case DT_Int: rv = ReadMinMaxRanges(&pByte, nBytesRemaining, (int*) ptr); break; + case DT_UInt: rv = ReadMinMaxRanges(&pByte, nBytesRemaining, (unsigned int*) ptr); break; + case DT_Float : rv = ReadMinMaxRanges(&pByte, nBytesRemaining, (float*) ptr); break; + case DT_Double: rv = ReadMinMaxRanges(&pByte, nBytesRemaining, (double*) ptr); break; + + default: + return false; + } + + if (!rv) + return false; + + for (int i = 0; i < nDepth; i++) + { + pMins[i] = m_zMinVec[i]; + pMaxs[i] = m_zMaxVec[i]; + } + + return true; +} + +// -------------------------------------------------------------------------- ; + template bool Lerc2::Decode(const Byte** ppByte, size_t& nBytesRemaining, T* arr, Byte* pMaskBits) { @@ -412,7 +553,7 @@ if (pMaskBits) // return proper mask bits even if they were not stored memcpy(pMaskBits, m_bitMask.Bits(), m_bitMask.Size()); - memset(arr, 0, m_headerInfo.nCols * m_headerInfo.nRows * m_headerInfo.nDim * sizeof(T)); + memset(arr, 0, (size_t)m_headerInfo.nCols * m_headerInfo.nRows * m_headerInfo.nDepth * sizeof(T)); if (m_headerInfo.numValidPixel == 0) return true; @@ -452,7 +593,7 @@ if (!readDataOneSweep) { - if (m_headerInfo.TryHuffman()) + if (m_headerInfo.TryHuffmanInt() || m_headerInfo.TryHuffmanFlt()) { if (nBytesRemaining < 1) return false; @@ -461,17 +602,31 @@ (*ppByte)++; nBytesRemaining--; - if (flag > 2 || (m_headerInfo.version < 4 && flag > 1)) + if (flag > 3 + || (flag > 2 && m_headerInfo.version < 6) + || (flag > 1 && m_headerInfo.version < 4)) return false; m_imageEncodeMode = (ImageEncodeMode)flag; - if (m_imageEncodeMode == IEM_DeltaHuffman || m_imageEncodeMode == IEM_Huffman) + if (m_imageEncodeMode != IEM_Tiling) { - if (!DecodeHuffman(ppByte, nBytesRemaining, arr)) + if (m_headerInfo.TryHuffmanInt()) + { + if (m_imageEncodeMode == IEM_DeltaHuffman || (m_headerInfo.version >= 4 && m_imageEncodeMode == IEM_Huffman)) + return DecodeHuffman(ppByte, nBytesRemaining, arr); // done. + else + return false; + } + else if (m_headerInfo.TryHuffmanFlt() && m_imageEncodeMode == IEM_DeltaDeltaHuffman) + { + //return DecodeHuffmanFlt(ppByte, nBytesRemaining, arr); // done. + // return false; // not impl yet + return LosslessFPCompression::DecodeHuffmanFlt(ppByte, nBytesRemaining, arr, + (m_headerInfo.dt == DT_Double), m_headerInfo.nCols, m_headerInfo.nRows, m_headerInfo.nDepth); + } + else return false; - - return true; // done. } } @@ -507,7 +662,9 @@ numBytes += 1 * sizeof(int); numBytes += (hd.version >= 3 ? 1 : 0) * sizeof(unsigned int); numBytes += (hd.version >= 4 ? 7 : 6) * sizeof(int); - numBytes += 3 * sizeof(double); + numBytes += (hd.version >= 6 ? 1 : 0) * sizeof(int); + numBytes += (hd.version >= 6 ? 4 : 0) * sizeof(Byte); + numBytes += (hd.version >= 6 ? 5 : 3) * sizeof(double); return numBytes; } @@ -538,25 +695,36 @@ vector intVec; intVec.push_back(hd.nRows); intVec.push_back(hd.nCols); - - if (hd.version >= 4) - { - intVec.push_back(hd.nDim); - } - + if (hd.version >= 4) intVec.push_back(hd.nDepth); intVec.push_back(hd.numValidPixel); intVec.push_back(hd.microBlockSize); intVec.push_back(hd.blobSize); intVec.push_back((int)hd.dt); + if (hd.version >= 6) intVec.push_back(hd.nBlobsMore); len = intVec.size() * sizeof(int); memcpy(ptr, &intVec[0], len); ptr += len; + if (hd.version >= 6) + { + vector byteVec; + byteVec.push_back(hd.bPassNoDataValues); + byteVec.push_back(hd.bIsInt); + byteVec.push_back(hd.bReserved3); + byteVec.push_back(hd.bReserved4); + + len = byteVec.size(); + memcpy(ptr, &byteVec[0], len); + ptr += len; + } + vector dblVec; dblVec.push_back(hd.maxZError); dblVec.push_back(hd.zMin); dblVec.push_back(hd.zMax); + if (hd.version >= 6) dblVec.push_back(hd.noDataVal); + if (hd.version >= 6) dblVec.push_back(hd.noDataValOrig); len = dblVec.size() * sizeof(double); memcpy(ptr, &dblVec[0], len); @@ -605,9 +773,19 @@ nBytesRemaining -= sizeof(unsigned int); } - int nInts = (hd.version >= 4) ? 7 : 6; + int nInts = 6; + nInts += (hd.version >= 4) ? 1 : 0; + nInts += (hd.version >= 6) ? 1 : 0; + + int nBytes = 0; + nBytes += (hd.version >= 6) ? 4 : 0; + + int nDbls = 3; + nDbls += (hd.version >= 6) ? 2 : 0; + vector intVec(nInts, 0); - vector dblVec(3, 0); + vector byteVec(nBytes, 0); + vector dblVec(nDbls, 0); size_t len = sizeof(int) * intVec.size(); @@ -617,6 +795,17 @@ ptr += len; nBytesRemaining -= len; + if (hd.version >= 6) + { + len = byteVec.size(); + + if (nBytesRemaining < len || !memcpy(&byteVec[0], ptr, len)) + return false; + + ptr += len; + nBytesRemaining -= len; + } + len = sizeof(double) * dblVec.size(); if (nBytesRemaining < len || !memcpy(&dblVec[0], ptr, len)) @@ -628,7 +817,7 @@ int i = 0; hd.nRows = intVec[i++]; hd.nCols = intVec[i++]; - hd.nDim = (hd.version >= 4) ? intVec[i++] : 1; + hd.nDepth = (hd.version >= 4) ? intVec[i++] : 1; hd.numValidPixel = intVec[i++]; hd.microBlockSize = intVec[i++]; hd.blobSize = intVec[i++]; @@ -636,12 +825,22 @@ if (dt < DT_Char || dt > DT_Double) return false; hd.dt = static_cast(dt); + hd.nBlobsMore = (hd.version >= 6) ? intVec[i++] : 0; - hd.maxZError = dblVec[0]; - hd.zMin = dblVec[1]; - hd.zMax = dblVec[2]; + i = 0; + hd.bPassNoDataValues = (hd.version >= 6) ? byteVec[i++] : 0; + hd.bIsInt = (hd.version >= 6) ? byteVec[i++] : 0; + hd.bReserved3 = (hd.version >= 6) ? byteVec[i++] : 0; + hd.bReserved4 = (hd.version >= 6) ? byteVec[i++] : 0; + + i = 0; + hd.maxZError = dblVec[i++]; + hd.zMin = dblVec[i++]; + hd.zMax = dblVec[i++]; + hd.noDataVal = (hd.version >= 6) ? dblVec[i++] : 0; + hd.noDataValOrig = (hd.version >= 6) ? dblVec[i++] : 0; - if (hd.nRows <= 0 || hd.nCols <= 0 || hd.nDim <= 0 || hd.numValidPixel < 0 || hd.microBlockSize <= 0 || hd.blobSize <= 0 + if (hd.nRows <= 0 || hd.nCols <= 0 || hd.nDepth <= 0 || hd.numValidPixel < 0 || hd.microBlockSize <= 0 || hd.blobSize <= 0 || hd.numValidPixel > hd.nRows * hd.nCols) return false; @@ -712,11 +911,8 @@ ptr += sizeof(int); nBytesRemaining -= sizeof(int); - if (numValid == 0 || numValid == w * h) // there should be no mask - { - if (numBytesMask != 0) - return false; - } + if ((numValid == 0 || numValid == w * h) && (numBytesMask != 0)) + return false; if (!m_bitMask.SetSize(w, h)) return false; @@ -801,7 +997,6 @@ return sum2 << 16 | sum1; } - // -------------------------------------------------------------------------- ; // for the theory and math, see @@ -816,17 +1011,17 @@ return false; const HeaderInfo& hd = m_headerInfo; - const int nDim = hd.nDim; + const int nDepth = hd.nDepth; const int maxShift = 8 * GetDataTypeSize(hd.dt); const int minCnt = 5000; if (hd.numValidPixel < minCnt) // not enough data for good stats return false; - std::vector cntDiffVec(nDim * maxShift, 0); + std::vector cntDiffVec(nDepth * maxShift, 0); int cnt = 0; - if (nDim == 1 && hd.numValidPixel == hd.nCols * hd.nRows) // special but common case + if (nDepth == 1 && hd.numValidPixel == hd.nCols * hd.nRows) // special but common case { if (hd.dt == DT_Byte || hd.dt == DT_UShort || hd.dt == DT_UInt) // unsigned int { @@ -858,28 +1053,28 @@ return false; // unsupported data type } - else // general case: nDim > 1 or not all pixel valid + else // general case: nDepth > 1 or not all pixel valid { if (hd.dt == DT_Byte || hd.dt == DT_UShort || hd.dt == DT_UInt) // unsigned int { for (int k = 0, m0 = 0, i = 0; i < hd.nRows; i++) - for (int j = 0; j < hd.nCols; j++, k++, m0 += nDim) + for (int j = 0; j < hd.nCols; j++, k++, m0 += nDepth) if (m_bitMask.IsValid(k)) { if (j < hd.nCols - 1 && m_bitMask.IsValid(k + 1)) // hori { - for (int s0 = 0, iDim = 0; iDim < nDim; iDim++, s0 += maxShift) + for (int s0 = 0, m = 0; m < nDepth; m++, s0 += maxShift) { - unsigned int c = ((unsigned int)data[m0 + iDim]) ^ ((unsigned int)data[m0 + iDim + nDim]); + unsigned int c = ((unsigned int)data[m0 + m]) ^ ((unsigned int)data[m0 + m + nDepth]); AddUIntToCounts(&cntDiffVec[s0], c, maxShift); } cnt++; } if (i < hd.nRows - 1 && m_bitMask.IsValid(k + hd.nCols)) // vert { - for (int s0 = 0, iDim = 0; iDim < nDim; iDim++, s0 += maxShift) + for (int s0 = 0, m = 0; m < nDepth; m++, s0 += maxShift) { - unsigned int c = ((unsigned int)data[m0 + iDim]) ^ ((unsigned int)data[m0 + iDim + nDim * hd.nCols]); + unsigned int c = ((unsigned int)data[m0 + m]) ^ ((unsigned int)data[m0 + m + nDepth * hd.nCols]); AddUIntToCounts(&cntDiffVec[s0], c, maxShift); } cnt++; @@ -889,23 +1084,23 @@ else if (hd.dt == DT_Char || hd.dt == DT_Short || hd.dt == DT_Int) // signed int { for (int k = 0, m0 = 0, i = 0; i < hd.nRows; i++) - for (int j = 0; j < hd.nCols; j++, k++, m0 += nDim) + for (int j = 0; j < hd.nCols; j++, k++, m0 += nDepth) if (m_bitMask.IsValid(k)) { if (j < hd.nCols - 1 && m_bitMask.IsValid(k + 1)) // hori { - for (int s0 = 0, iDim = 0; iDim < nDim; iDim++, s0 += maxShift) + for (int s0 = 0, m = 0; m < nDepth; m++, s0 += maxShift) { - int c = ((int)data[m0 + iDim]) ^ ((int)data[m0 + iDim + nDim]); + int c = ((int)data[m0 + m]) ^ ((int)data[m0 + m + nDepth]); AddIntToCounts(&cntDiffVec[s0], c, maxShift); } cnt++; } if (i < hd.nRows - 1 && m_bitMask.IsValid(k + hd.nCols)) // vert { - for (int s0 = 0, iDim = 0; iDim < nDim; iDim++, s0 += maxShift) + for (int s0 = 0, m = 0; m < nDepth; m++, s0 += maxShift) { - int c = ((int)data[m0 + iDim]) ^ ((int)data[m0 + iDim + nDim * hd.nCols]); + int c = ((int)data[m0 + m]) ^ ((int)data[m0 + m + nDepth * hd.nCols]); AddIntToCounts(&cntDiffVec[s0], c, maxShift); } cnt++; @@ -927,9 +1122,9 @@ //if (printAll) printf("bit plane %2d: ", s); bool bCrit = true; - for (int iDim = 0; iDim < nDim; iDim++) + for (int iDepth = 0; iDepth < nDepth; iDepth++) { - double x = cntDiffVec[iDim * maxShift + s]; + double x = cntDiffVec[iDepth * maxShift + s]; double n = cnt; double m = x / n; //double stdDev = sqrt(x * x / n - m * m) / n; @@ -976,7 +1171,7 @@ return false; const HeaderInfo& hd = m_headerInfo; - const int nDim = hd.nDim; + const int nDepth = hd.nDepth; std::vector roundErr, zErr, zErrCand = { 1, 0.5, 0.1, 0.05, 0.01, 0.005, 0.001, 0.0005, 0.0001 }; std::vector zFac, zFacCand = { 1, 2, 10, 20, 100, 200, 1000, 2000, 10000 }; @@ -992,7 +1187,7 @@ if (zErr.empty()) return false; - if (nDim == 1 && hd.numValidPixel == hd.nCols * hd.nRows) // special but common case + if (nDepth == 1 && hd.numValidPixel == hd.nCols * hd.nRows) // special but common case { for (int i = 0; i < hd.nRows; i++) { @@ -1018,17 +1213,17 @@ } } - else // general case: nDim > 1 or not all pixel valid + else // general case: nDepth > 1 or not all pixel valid { for (int k = 0, m0 = 0, i = 0; i < hd.nRows; i++) { size_t nCand = zErr.size(); - for (int j = 0; j < hd.nCols; j++, k++, m0 += nDim) + for (int j = 0; j < hd.nCols; j++, k++, m0 += nDepth) if (m_bitMask.IsValid(k)) - for (int iDim = 0; iDim < nDim; iDim++) + for (int m = 0; m < nDepth; m++) { - double x = data[m0 + iDim]; + double x = data[m0 + m]; for (size_t n = 0; n < nCand; n++) { @@ -1047,7 +1242,7 @@ } for (size_t n = 0; n < zErr.size(); n++) - if (roundErr[n] / zFac[n] <= maxZError) + if (roundErr[n] / zFac[n] <= maxZError / 2) { maxZError = zErr[n]; return true; @@ -1067,7 +1262,7 @@ return false; for (int n = (int)(nCand - 1); n >= 0; n--) - if (roundErr[n] / zFac[n] > maxZError) + if (roundErr[n] / zFac[n] > maxZError / 2) { roundErr.erase(roundErr.begin() + n); zErr.erase(zErr.begin() + n); @@ -1087,11 +1282,11 @@ Byte* ptr = (*ppByte); const HeaderInfo& hd = m_headerInfo; - int nDim = hd.nDim; - int len = nDim * sizeof(T); + int nDepth = hd.nDepth; + int len = nDepth * sizeof(T); for (int k = 0, m0 = 0, i = 0; i < hd.nRows; i++) - for (int j = 0; j < hd.nCols; j++, k++, m0 += nDim) + for (int j = 0; j < hd.nCols; j++, k++, m0 += nDepth) if (m_bitMask.IsValid(k)) { memcpy(ptr, &data[m0], len); @@ -1112,8 +1307,8 @@ const Byte* ptr = (*ppByte); const HeaderInfo& hd = m_headerInfo; - int nDim = hd.nDim; - int len = nDim * sizeof(T); + int nDepth = hd.nDepth; + int len = nDepth * sizeof(T); size_t nValidPix = (size_t)m_bitMask.CountValidBits(); @@ -1121,7 +1316,7 @@ return false; for (int k = 0, m0 = 0, i = 0; i < hd.nRows; i++) - for (int j = 0; j < hd.nCols; j++, k++, m0 += nDim) + for (int j = 0; j < hd.nCols; j++, k++, m0 += nDepth) if (m_bitMask.IsValid(k)) { memcpy(&data[m0], ptr, len); @@ -1143,62 +1338,62 @@ return false; const HeaderInfo& hd = m_headerInfo; - const int nDim = hd.nDim; + const int nDepth = hd.nDepth; bool bInit = false; - zMinVecA.resize(nDim); - zMaxVecA.resize(nDim); + zMinVecA.resize(nDepth); + zMaxVecA.resize(nDepth); - std::vector zMinVec(nDim, 0), zMaxVec(nDim, 0); + std::vector zMinVec(nDepth, 0), zMaxVec(nDepth, 0); if (hd.numValidPixel == hd.nRows * hd.nCols) // all valid, no mask { bInit = true; - for (int iDim = 0; iDim < nDim; iDim++) - zMinVec[iDim] = zMaxVec[iDim] = data[iDim]; + for (int m = 0; m < nDepth; m++) + zMinVec[m] = zMaxVec[m] = data[m]; for (int m0 = 0, i = 0; i < hd.nRows; i++) - for (int j = 0; j < hd.nCols; j++, m0 += nDim) - for (int iDim = 0; iDim < nDim; iDim++) + for (int j = 0; j < hd.nCols; j++, m0 += nDepth) + for (int m = 0; m < nDepth; m++) { - T val = data[m0 + iDim]; + T val = data[m0 + m]; - if (val < zMinVec[iDim]) - zMinVec[iDim] = val; - else if (val > zMaxVec[iDim]) - zMaxVec[iDim] = val; + if (val < zMinVec[m]) + zMinVec[m] = val; + else if (val > zMaxVec[m]) + zMaxVec[m] = val; } } else { for (int k = 0, m0 = 0, i = 0; i < hd.nRows; i++) - for (int j = 0; j < hd.nCols; j++, k++, m0 += nDim) + for (int j = 0; j < hd.nCols; j++, k++, m0 += nDepth) if (m_bitMask.IsValid(k)) { if (bInit) - for (int iDim = 0; iDim < nDim; iDim++) + for (int m = 0; m < nDepth; m++) { - T val = data[m0 + iDim]; + T val = data[m0 + m]; - if (val < zMinVec[iDim]) - zMinVec[iDim] = val; - else if (val > zMaxVec[iDim]) - zMaxVec[iDim] = val; + if (val < zMinVec[m]) + zMinVec[m] = val; + else if (val > zMaxVec[m]) + zMaxVec[m] = val; } else { bInit = true; - for (int iDim = 0; iDim < nDim; iDim++) - zMinVec[iDim] = zMaxVec[iDim] = data[m0 + iDim]; + for (int m = 0; m < nDepth; m++) + zMinVec[m] = zMaxVec[m] = data[m0 + m]; } } } if (bInit) - for (int iDim = 0; iDim < nDim; iDim++) + for (int m = 0; m < nDepth; m++) { - zMinVecA[iDim] = zMinVec[iDim]; - zMaxVecA[iDim] = zMaxVec[iDim]; + zMinVecA[m] = zMinVec[m]; + zMaxVecA[m] = zMaxVec[m]; } return bInit; @@ -1220,14 +1415,14 @@ const HeaderInfo& hd = m_headerInfo; int mbSize = hd.microBlockSize; - int nDim = hd.nDim; + int nDepth = hd.nDepth; std::vector dataVec(mbSize * mbSize, 0); T* dataBuf = &dataVec[0]; const bool bDtInt = (hd.dt < DT_Float); const bool bIntLossless = bDtInt && (hd.maxZError == 0.5); - const bool bTryDiffEnc = (hd.version >= 5) && (nDim > 1); + const bool bTryDiffEnc = (hd.version >= 5) && (nDepth > 1) && (hd.maxZError > 0); // turn off for flt lossless const bool bCheckForIntOverflow = NeedToCheckForIntOverflow(hd); const bool bCheckForFltRndErr = NeedToCheckForFltRndErr(hd); @@ -1253,27 +1448,27 @@ if (jTile == numTilesHori - 1) tileW = hd.nCols - j0; - for (int iDim = 0; iDim < nDim; iDim++) + for (int iDepth = 0; iDepth < nDepth; iDepth++) { T zMin = 0, zMax = 0; int numValidPixel = 0; bool bQuantizeDone = false; bool tryLut = false; - if (!GetValidDataAndStats(data, i0, i0 + tileH, j0, j0 + tileW, iDim, dataBuf, zMin, zMax, numValidPixel, tryLut)) + if (!GetValidDataAndStats(data, i0, i0 + tileH, j0, j0 + tileW, iDepth, dataBuf, zMin, zMax, numValidPixel, tryLut)) return false; if (numValidPixel == 0 && !(*ppByte)) { - numBytesLerc += nDim; // 1 byte per empty block - break; // iDim loop + numBytesLerc += nDepth; // 1 byte per empty block + break; // iDepth loop } //tryLut = false; // always OFF //tryLut = NeedToQuantize(numValidPixel, zMin, zMax); // always ON // if needed, quantize the data here once - if (((*ppByte && iDim == 0) || tryLut) && NeedToQuantize(numValidPixel, zMin, zMax)) + if (((*ppByte && iDepth == 0) || tryLut) && NeedToQuantize(numValidPixel, zMin, zMax)) { Quantize(dataBuf, numValidPixel, zMin, quantVec); bQuantizeDone = true; @@ -1290,7 +1485,7 @@ double zMinDiff(0), zMaxDiff(0); bool bQuantizeDoneDiff = false, tryLutDiff = false; - if (bTryDiffEnc && iDim > 0 && numValidPixel > 0) + if (bTryDiffEnc && iDepth > 0 && numValidPixel > 0) { bool rv = bDtInt ? ComputeDiffSliceInt(&dataVec[0], &prevDataVec[0], numValidPixel, bCheckForIntOverflow, hd.maxZError, diffDataVecInt, zMinDiffInt, zMaxDiffInt, tryLutDiff) : ComputeDiffSliceFlt(&dataVec[0], &prevDataVec[0], numValidPixel, bCheckForFltRndErr, hd.maxZError, diffDataVecFlt, zMinDiffFlt, zMaxDiffFlt, tryLutDiff); @@ -1319,17 +1514,17 @@ numBytesLerc += std::min(numBytesNeeded, numBytesNeededDiff); - if (bTryDiffEnc && iDim < (nDim - 1) && numValidPixel > 0) + if (bTryDiffEnc && iDepth < (nDepth - 1) && numValidPixel > 0) { - if (iDim == 0) + if (iDepth == 0) prevDataVec.resize(numValidPixel); if (!bIntLossless) // if not int lossless: do lossy quantize back and forth, then copy to buffer { - double zMaxClamp = m_zMaxVec[iDim]; + double zMaxClamp = m_zMaxVec[iDepth]; bool bClampScaleBack = (zMax + 2 * hd.maxZError > zMaxClamp); - if (iDim == 0 || numBytesNeeded <= numBytesNeededDiff) // for regular or abs encode + if (iDepth == 0 || numBytesNeeded <= numBytesNeededDiff) // for regular or abs encode { if (bQuantizeDone || NeedToQuantize(numValidPixel, zMin, zMax)) { @@ -1372,7 +1567,7 @@ int numBytesWritten = 0; bool rv = false; - if (iDim == 0 || numBytesNeeded <= numBytesNeededDiff) + if (iDepth == 0 || numBytesNeeded <= numBytesNeededDiff) { if (!bQuantizeDone && NeedToQuantize(numValidPixel, zMin, zMax)) Quantize(dataBuf, numValidPixel, zMin, quantVec); @@ -1414,7 +1609,7 @@ const HeaderInfo& hd = m_headerInfo; int mbSize = hd.microBlockSize; - int nDim = hd.nDim; + int nDepth = hd.nDepth; if (mbSize > 32) // fail gracefully in case of corrupted blob for old version <= 2 which had no checksum return false; @@ -1436,9 +1631,9 @@ if (jTile == numTilesHori - 1) tileW = hd.nCols - j0; - for (int iDim = 0; iDim < nDim; iDim++) + for (int iDepth = 0; iDepth < nDepth; iDepth++) { - if (!ReadTile(ppByte, nBytesRemaining, data, i0, i0 + tileH, j0, j0 + tileW, iDim, bufferVec)) + if (!ReadTile(ppByte, nBytesRemaining, data, i0, i0 + tileH, j0, j0 + tileW, iDepth, bufferVec)) return false; } } @@ -1450,12 +1645,12 @@ // -------------------------------------------------------------------------- ; template -bool Lerc2::GetValidDataAndStats(const T* data, int i0, int i1, int j0, int j1, int iDim, +bool Lerc2::GetValidDataAndStats(const T* data, int i0, int i1, int j0, int j1, int iDepth, T* dataBuf, T& zMin, T& zMax, int& numValidPixel, bool& tryLut) const { const HeaderInfo& hd = m_headerInfo; - if (!data || i0 < 0 || j0 < 0 || i1 > hd.nRows || j1 > hd.nCols || i0 >= i1 || j0 >= j1 || iDim < 0 || iDim > hd.nDim || !dataBuf) + if (!data || i0 < 0 || j0 < 0 || i1 > hd.nRows || j1 > hd.nCols || i0 >= i1 || j0 >= j1 || iDepth < 0 || iDepth > hd.nDepth || !dataBuf) return false; zMin = zMax = 0; @@ -1463,20 +1658,20 @@ T prevVal = 0; int cnt = 0, cntSameVal = 0; - int nDim = hd.nDim; + int nDepth = hd.nDepth; if (hd.numValidPixel == hd.nCols * hd.nRows) // all valid, no mask { int k0 = i0 * hd.nCols + j0; - int m0 = k0 * nDim + iDim; + int m0 = k0 * nDepth + iDepth; zMin = zMax = data[m0]; // init for (int i = i0; i < i1; i++) { int k = i * hd.nCols + j0; - int m = k * nDim + iDim; + int m = k * nDepth + iDepth; - for (int j = j0; j < j1; j++, m += nDim) + for (int j = j0; j < j1; j++, m += nDepth) { T val = data[m]; dataBuf[cnt] = val; @@ -1499,9 +1694,9 @@ for (int i = i0; i < i1; i++) { int k = i * hd.nCols + j0; - int m = k * nDim + iDim; + int m = k * nDepth + iDepth; - for (int j = j0; j < j1; j++, k++, m += nDim) + for (int j = j0; j < j1; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) { T val = data[m]; @@ -1758,7 +1953,7 @@ // -------------------------------------------------------------------------- ; template -bool Lerc2::ReadTile(const Byte** ppByte, size_t& nBytesRemainingInOut, T* data, int i0, int i1, int j0, int j1, int iDim, +bool Lerc2::ReadTile(const Byte** ppByte, size_t& nBytesRemainingInOut, T* data, int i0, int i1, int j0, int j1, int iDepth, std::vector& bufferVec) const { const Byte* ptr = *ppByte; @@ -1769,7 +1964,7 @@ const HeaderInfo& hd = m_headerInfo; int nCols = hd.nCols; - int nDim = hd.nDim; + int nDepth = hd.nDepth; Byte comprFlag = *ptr++; nBytesRemaining--; @@ -1780,7 +1975,7 @@ if (((comprFlag >> 2) & pattern) != ((j0 >> 3) & pattern)) // use bits 2345 for integrity check return false; - if (bDiffEnc && iDim == 0) + if (bDiffEnc && iDepth == 0) return false; int bits67 = comprFlag >> 6; @@ -1791,9 +1986,9 @@ for (int i = i0; i < i1; i++) { int k = i * nCols + j0; - int m = k * nDim + iDim; + int m = k * nDepth + iDepth; - for (int j = j0; j < j1; j++, k++, m += nDim) + for (int j = j0; j < j1; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) data[m] = bDiffEnc ? data[m - 1] : 0; } @@ -1814,9 +2009,9 @@ for (int i = i0; i < i1; i++) { int k = i * nCols + j0; - int m = k * nDim + iDim; + int m = k * nDepth + iDepth; - for (int j = j0; j < j1; j++, k++, m += nDim) + for (int j = j0; j < j1; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) { if (nBytesRemaining < sizeof(T)) @@ -1844,25 +2039,25 @@ double offset = ReadVariableDataType(&ptr, dtUsed); nBytesRemaining -= n; - double zMax = (hd.version >= 4 && nDim > 1) ? m_zMaxVec[iDim] : hd.zMax; + double zMax = (hd.version >= 4 && nDepth > 1) ? m_zMaxVec[iDepth] : hd.zMax; if (comprFlag == 3) // entire tile is constant zMin (all the valid pixels) { for (int i = i0; i < i1; i++) { int k = i * nCols + j0; - int m = k * nDim + iDim; + int m = k * nDepth + iDepth; if (!bDiffEnc) { T val = (T)offset; - for (int j = j0; j < j1; j++, k++, m += nDim) + for (int j = j0; j < j1; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) data[m] = val; } else { - for (int j = j0; j < j1; j++, k++, m += nDim) + for (int j = j0; j < j1; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) { double z = offset + data[m - 1]; @@ -1885,11 +2080,11 @@ for (int i = i0; i < i1; i++) { int k = i * nCols + j0; - int m = k * nDim + iDim; + int m = k * nDepth + iDepth; if (!bDiffEnc) { - for (int j = j0; j < j1; j++, k++, m += nDim) + for (int j = j0; j < j1; j++, k++, m += nDepth) { double z = offset + *srcPtr++ * invScale; data[m] = (T)std::min(z, zMax); // make sure we stay in the orig range @@ -1897,7 +2092,7 @@ } else { - for (int j = j0; j < j1; j++, k++, m += nDim) + for (int j = j0; j < j1; j++, k++, m += nDepth) { double z = offset + *srcPtr++ * invScale + data[m - 1]; data[m] = (T)std::min(z, zMax); @@ -1912,11 +2107,11 @@ for (int i = i0; i < i1; i++) { int k = i * nCols + j0; - int m = k * nDim + iDim; + int m = k * nDepth + iDepth; if (!bDiffEnc) { - for (int j = j0; j < j1; j++, k++, m += nDim) + for (int j = j0; j < j1; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) { double z = offset + *srcPtr++ * invScale; @@ -1925,7 +2120,7 @@ } else { - for (int j = j0; j < j1; j++, k++, m += nDim) + for (int j = j0; j < j1; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) { double z = offset + *srcPtr++ * invScale + data[m - 1]; @@ -1941,9 +2136,9 @@ for (int i = i0; i < i1; i++) { int k = i * nCols + j0; - int m = k * nDim + iDim; + int m = k * nDepth + iDepth; - for (int j = j0; j < j1; j++, k++, m += nDim) + for (int j = j0; j < j1; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) { if (bufferVecIdx == bufferVec.size()) // fail gracefully in case of corrupted blob for old version <= 2 which had no checksum @@ -2055,15 +2250,15 @@ int offset = (m_headerInfo.dt == DT_Char) ? 128 : 0; int height = m_headerInfo.nRows; int width = m_headerInfo.nCols; - int nDim = m_headerInfo.nDim; + int nDepth = m_headerInfo.nDepth; if (m_headerInfo.numValidPixel == width * height) // all valid { - for (int iDim = 0; iDim < nDim; iDim++) + for (int iDepth = 0; iDepth < nDepth; iDepth++) { T prevVal = 0; - for (int m = iDim, i = 0; i < height; i++) - for (int j = 0; j < width; j++, m += nDim) + for (int m = iDepth, i = 0; i < height; i++) + for (int j = 0; j < width; j++, m += nDepth) { T val = data[m]; T delta = val; @@ -2071,7 +2266,7 @@ if (j > 0) delta -= prevVal; // use overflow else if (i > 0) - delta -= data[m - width * nDim]; + delta -= data[m - width * nDepth]; else delta -= prevVal; @@ -2084,11 +2279,11 @@ } else // not all valid { - for (int iDim = 0; iDim < nDim; iDim++) + for (int iDepth = 0; iDepth < nDepth; iDepth++) { T prevVal = 0; - for (int k = 0, m = iDim, i = 0; i < height; i++) - for (int j = 0; j < width; j++, k++, m += nDim) + for (int k = 0, m = iDepth, i = 0; i < height; i++) + for (int j = 0; j < width; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) { T val = data[m]; @@ -2100,7 +2295,7 @@ } else if (i > 0 && m_bitMask.IsValid(k - width)) { - delta -= data[m - width * nDim]; + delta -= data[m - width * nDepth]; } else delta -= prevVal; @@ -2129,7 +2324,7 @@ int offset = (m_headerInfo.dt == DT_Char) ? 128 : 0; int height = m_headerInfo.nRows; int width = m_headerInfo.nCols; - int nDim = m_headerInfo.nDim; + int nDepth = m_headerInfo.nDepth; unsigned int* arr = (unsigned int*)(*ppByte); unsigned int* dstPtr = arr; @@ -2137,11 +2332,11 @@ if (m_imageEncodeMode == IEM_DeltaHuffman) { - for (int iDim = 0; iDim < nDim; iDim++) + for (int iDepth = 0; iDepth < nDepth; iDepth++) { T prevVal = 0; - for (int k = 0, m = iDim, i = 0; i < height; i++) - for (int j = 0; j < width; j++, k++, m += nDim) + for (int k = 0, m = iDepth, i = 0; i < height; i++) + for (int j = 0; j < width; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) { T val = data[m]; @@ -2153,7 +2348,7 @@ } else if (i > 0 && m_bitMask.IsValid(k - width)) { - delta -= data[m - width * nDim]; + delta -= data[m - width * nDepth]; } else delta -= prevVal; @@ -2194,9 +2389,9 @@ else if (m_imageEncodeMode == IEM_Huffman) { for (int k = 0, m0 = 0, i = 0; i < height; i++) - for (int j = 0; j < width; j++, k++, m0 += nDim) + for (int j = 0; j < width; j++, k++, m0 += nDepth) if (m_bitMask.IsValid(k)) - for (int m = 0; m < nDim; m++) + for (int m = 0; m < nDepth; m++) { T val = data[m0 + m]; @@ -2257,7 +2452,7 @@ int offset = (m_headerInfo.dt == DT_Char) ? 128 : 0; int height = m_headerInfo.nRows; int width = m_headerInfo.nCols; - int nDim = m_headerInfo.nDim; + int nDepth = m_headerInfo.nDepth; const unsigned int* arr = (const unsigned int*)(*ppByte); const unsigned int* srcPtr = arr; @@ -2268,11 +2463,11 @@ { if (m_imageEncodeMode == IEM_DeltaHuffman) { - for (int iDim = 0; iDim < nDim; iDim++) + for (int iDepth = 0; iDepth < nDepth; iDepth++) { T prevVal = 0; - for (int m = iDim, i = 0; i < height; i++) - for (int j = 0; j < width; j++, m += nDim) + for (int m = iDepth, i = 0; i < height; i++) + for (int j = 0; j < width; j++, m += nDepth) { int val = 0; if (nBytesRemaining >= 4 * sizeof(unsigned int)) @@ -2291,7 +2486,7 @@ if (j > 0) delta += prevVal; // use overflow else if (i > 0) - delta += data[m - width * nDim]; + delta += data[m - width * nDepth]; else delta += prevVal; @@ -2304,8 +2499,8 @@ else if (m_imageEncodeMode == IEM_Huffman) { for (int k = 0, m0 = 0, i = 0; i < height; i++) - for (int j = 0; j < width; j++, k++, m0 += nDim) - for (int m = 0; m < nDim; m++) + for (int j = 0; j < width; j++, k++, m0 += nDepth) + for (int m = 0; m < nDepth; m++) { int val = 0; if (nBytesRemaining >= 4 * sizeof(unsigned int)) @@ -2331,11 +2526,11 @@ { if (m_imageEncodeMode == IEM_DeltaHuffman) { - for (int iDim = 0; iDim < nDim; iDim++) + for (int iDepth = 0; iDepth < nDepth; iDepth++) { T prevVal = 0; - for (int k = 0, m = iDim, i = 0; i < height; i++) - for (int j = 0; j < width; j++, k++, m += nDim) + for (int k = 0, m = iDepth, i = 0; i < height; i++) + for (int j = 0; j < width; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) { int val = 0; @@ -2358,7 +2553,7 @@ } else if (i > 0 && m_bitMask.IsValid(k - width)) { - delta += data[m - width * nDim]; + delta += data[m - width * nDepth]; } else delta += prevVal; @@ -2372,9 +2567,9 @@ else if (m_imageEncodeMode == IEM_Huffman) { for (int k = 0, m0 = 0, i = 0; i < height; i++) - for (int j = 0; j < width; j++, k++, m0 += nDim) + for (int j = 0; j < width; j++, k++, m0 += nDepth) if (m_bitMask.IsValid(k)) - for (int m = 0; m < nDim; m++) + for (int m = 0; m < nDepth; m++) { int val = 0; if (nBytesRemaining >= 4 * sizeof(unsigned int)) @@ -2417,20 +2612,20 @@ //printf("write min / max = %f %f\n", m_zMinVec[0], m_zMaxVec[0]); - int nDim = m_headerInfo.nDim; - if (/* nDim < 2 || */ (int)m_zMinVec.size() != nDim || (int)m_zMaxVec.size() != nDim) + int nDepth = m_headerInfo.nDepth; + if (/* nDepth < 2 || */ (int)m_zMinVec.size() != nDepth || (int)m_zMaxVec.size() != nDepth) return false; - std::vector zVec(nDim); - size_t len = nDim * sizeof(T); + std::vector zVec(nDepth); + size_t len = nDepth * sizeof(T); - for (int i = 0; i < nDim; i++) + for (int i = 0; i < nDepth; i++) zVec[i] = (T)m_zMinVec[i]; memcpy(*ppByte, &zVec[0], len); (*ppByte) += len; - for (int i = 0; i < nDim; i++) + for (int i = 0; i < nDepth; i++) zVec[i] = (T)m_zMaxVec[i]; memcpy(*ppByte, &zVec[0], len); @@ -2447,13 +2642,13 @@ if (!ppByte || !(*ppByte)) return false; - int nDim = m_headerInfo.nDim; + int nDepth = m_headerInfo.nDepth; - m_zMinVec.resize(nDim); - m_zMaxVec.resize(nDim); + m_zMinVec.resize(nDepth); + m_zMaxVec.resize(nDepth); - std::vector zVec(nDim); - size_t len = nDim * sizeof(T); + std::vector zVec(nDepth); + size_t len = nDepth * sizeof(T); if (nBytesRemaining < len || !memcpy(&zVec[0], *ppByte, len)) return false; @@ -2461,7 +2656,7 @@ (*ppByte) += len; nBytesRemaining -= len; - for (int i = 0; i < nDim; i++) + for (int i = 0; i < nDepth; i++) m_zMinVec[i] = zVec[i]; if (nBytesRemaining < len || !memcpy(&zVec[0], *ppByte, len)) @@ -2470,7 +2665,7 @@ (*ppByte) += len; nBytesRemaining -= len; - for (int i = 0; i < nDim; i++) + for (int i = 0; i < nDepth; i++) m_zMaxVec[i] = zVec[i]; //printf("read min / max = %f %f\n", m_zMinVec[0], m_zMaxVec[0]); @@ -2489,10 +2684,10 @@ const HeaderInfo& hd = m_headerInfo; int nCols = hd.nCols; int nRows = hd.nRows; - int nDim = hd.nDim; + int nDepth = hd.nDepth; T z0 = (T)hd.zMin; - if (nDim == 1) + if (nDepth == 1) { for (int k = 0, i = 0; i < nRows; i++) for (int j = 0; j < nCols; j++, k++) @@ -2501,20 +2696,20 @@ } else { - std::vector zBufVec(nDim, z0); + std::vector zBufVec(nDepth, z0); if (hd.zMin != hd.zMax) { - if ((int)m_zMinVec.size() != nDim) + if ((int)m_zMinVec.size() != nDepth) return false; - for (int m = 0; m < nDim; m++) + for (int m = 0; m < nDepth; m++) zBufVec[m] = (T)m_zMinVec[m]; } - int len = nDim * sizeof(T); + int len = nDepth * sizeof(T); for (int k = 0, m = 0, i = 0; i < nRows; i++) - for (int j = 0; j < nCols; j++, k++, m += nDim) + for (int j = 0; j < nCols; j++, k++, m += nDepth) if (m_bitMask.IsValid(k)) memcpy(&data[m], &zBufVec[0], len); } diff -Nru lerc-3.0+ds/src/LercLib/Lerc2.h lerc-4.0.0+ds/src/LercLib/Lerc2.h --- lerc-3.0+ds/src/LercLib/Lerc2.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc2.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,10 +26,12 @@ #include #include +#include #include #include #include "BitMask.h" #include "BitStuffer2.h" +#include "fpl_Lerc2Ext.h" NAMESPACE_LERC_START @@ -56,13 +58,13 @@ * * Lerc2 v4 * - * -- allow array per pixel, nDim values per pixel. Such as RGB, complex number, or larger arrays per pixel + * -- allow array per pixel, nDepth values per pixel. Such as RGB, complex number, or larger arrays per pixel * -- extend Huffman coding for 8 bit data types from delta only to trying both delta and orig * -- for integer data types, allow to drop bit planes containing only random noise * * Lerc2 v5 * -- for float data (as it might be lower precision like %.2f), try raise maxZError if possible w/o extra loss - * -- add delta encoding of a block iDim relative to previous block (iDim - 1) + * -- add delta encoding of a block iDepth relative to previous block (iDepth - 1) * */ @@ -70,14 +72,19 @@ { public: Lerc2(); - Lerc2(int nDim, int nCols, int nRows, const Byte* pMaskBits = nullptr); // valid / invalid bits as byte array + Lerc2(int nDepth, int nCols, int nRows, const Byte* pMaskBits = nullptr); // valid / invalid bits as byte array virtual ~Lerc2() {} - static int CurrentVersion() { return 5; } + static int CurrentVersion() { return 6; } bool SetEncoderToOldVersion(int version); // call this to encode compatible to an old decoder - bool Set(int nDim, int nCols, int nRows, const Byte* pMaskBits = nullptr); + bool Set(int nDepth, int nCols, int nRows, const Byte* pMaskBits = nullptr); // set mask and dimensions + + // v6 + bool SetNoDataValues(bool bNeedsNoDataVal, double noDataVal, double noDataValOrig); + bool SetNumBlobsMoreToCome(int nBlobsMore); // for safer decode of multi-band blobs + bool SetIsAllInt(bool bIsAllInt); template unsigned int ComputeNumBytesNeededToWrite(const T* arr, double maxZError, bool encodeMask); @@ -94,32 +101,43 @@ int version; unsigned int checksum; int nRows, - nCols, - nDim, - numValidPixel, - microBlockSize, - blobSize; + nCols, + nDepth, + numValidPixel, + microBlockSize, + blobSize, + nBlobsMore; // for multi-band Lerc blob, how many more blobs or bands appended to this one + + Byte bPassNoDataValues, // 1 - pass noData values to decoder, 0 - don't pass, ignore + bIsInt, // 1 - float or double data is all integer numbers, 0 - not + bReserved3, + bReserved4; DataType dt; - double maxZError, - zMin, // if nDim > 1, this is the overall range - zMax; + double maxZError, + zMin, // if nDepth > 1, this is the overall range + zMax, + noDataVal, // temp noData value used for nDepth > 1 if bit mask cannot cover it + noDataValOrig; // orig noData value to map to in decode void RawInit() { memset(this, 0, sizeof(struct HeaderInfo)); } - bool TryHuffman() const { return version > 1 && (dt == DT_Byte || dt == DT_Char) && maxZError == 0.5; } + bool TryHuffmanInt() const { return (version >= 2) && (dt == DT_Byte || dt == DT_Char) && (maxZError == 0.5); } + bool TryHuffmanFlt() const { return (version >= 6) && (dt == DT_Float || dt == DT_Double) && (maxZError == 0); } }; static bool GetHeaderInfo(const Byte* pByte, size_t nBytesRemaining, struct HeaderInfo& headerInfo, bool& bHasMask); + bool GetRanges(const Byte* pByte, size_t nBytesRemaining, double* pMins, double* pMaxs); + /// dst buffer already allocated; byte ptr is moved like a file pointer template bool Decode(const Byte** ppByte, size_t& nBytesRemaining, T* arr, Byte* pMaskBits = nullptr); // if mask ptr is not 0, mask bits are returned (even if all valid or same as previous) private: - enum ImageEncodeMode { IEM_Tiling = 0, IEM_DeltaHuffman, IEM_Huffman }; + enum ImageEncodeMode { IEM_Tiling = 0, IEM_DeltaHuffman, IEM_Huffman, IEM_DeltaDeltaHuffman }; enum BlockEncodeMode { BEM_RawBinary = 0, BEM_BitStuffSimple, BEM_BitStuffLUT }; int m_microBlockSize, @@ -134,6 +152,8 @@ std::vector m_zMinVec, m_zMaxVec; std::vector > m_huffmanCodes; // <= 256 codes, 1.5 kB + LosslessFPCompression m_lfpc; + private: static std::string FileKey() { return "Lerc2 "; } static bool IsLittleEndianSystem() { int n = 1; return (1 == *((Byte*)&n)) && (4 == sizeof(int)); } @@ -177,7 +197,7 @@ bool ReadTiles(const Byte** ppByte, size_t& nBytesRemaining, T* data) const; template - bool GetValidDataAndStats(const T* data, int i0, int i1, int j0, int j1, int iDim, + bool GetValidDataAndStats(const T* data, int i0, int i1, int j0, int j1, int iDepth, T* dataBuf, T& zMin, T& zMax, int& numValidPixel, bool& tryLut) const; template @@ -216,7 +236,7 @@ const std::vector >& sortedQuantVec) const; template - bool ReadTile(const Byte** ppByte, size_t& nBytesRemaining, T* data, int i0, int i1, int j0, int j1, int iDim, + bool ReadTile(const Byte** ppByte, size_t& nBytesRemaining, T* data, int i0, int i1, int j0, int j1, int iDepth, std::vector& bufferVec) const; template @@ -268,8 +288,7 @@ // -------------------------------------------------------------------------- ; // -------------------------------------------------------------------------- ; -inline -void Lerc2::AddUIntToCounts(int* pCounts, unsigned int val, int nBits) +inline void Lerc2::AddUIntToCounts(int* pCounts, unsigned int val, int nBits) { pCounts[0] += val & 1; for (int i = 1; i < nBits; i++) @@ -278,8 +297,7 @@ // -------------------------------------------------------------------------- ; -inline -void Lerc2::AddIntToCounts(int* pCounts, int val, int nBits) +inline void Lerc2::AddIntToCounts(int* pCounts, int val, int nBits) { pCounts[0] += val & 1; for (int i = 1; i < nBits; i++) @@ -435,12 +453,14 @@ template inline int Lerc2::ReduceDataType(T z, DataType dt, DataType& dtReduced) { - Byte b = (Byte)z; + // clamp to dst range first to guarantee portable code, see https://www.cplusplus.com/doc/tutorial/typecasting/ + + Byte b = (z >= 0 && z <= 255) ? (Byte)z : 0; switch (dt) { case DT_Short: { - signed char c = (signed char)z; + signed char c = (z >= (double)-128 && z <= 127) ? (signed char)z : 0; // avoid signed / unsigned warnings on some compilers int tc = (T)c == z ? 2 : (T)b == z ? 1 : 0; dtReduced = (DataType)(dt - tc); return tc; @@ -453,31 +473,31 @@ } case DT_Int: { - short s = (short)z; - unsigned short us = (unsigned short)z; + short s = (z >= (double)SHRT_MIN && z <= SHRT_MAX) ? (short)z : 0; + unsigned short us = (z >= 0 && z <= USHRT_MAX) ? (unsigned short)z : 0; int tc = (T)b == z ? 3 : (T)s == z ? 2 : (T)us == z ? 1 : 0; dtReduced = (DataType)(dt - tc); return tc; } case DT_UInt: { - unsigned short us = (unsigned short)z; + unsigned short us = (z >= 0 && z <= USHRT_MAX) ? (unsigned short)z : 0; int tc = (T)b == z ? 2 : (T)us == z ? 1 : 0; dtReduced = (DataType)(dt - 2 * tc); return tc; } case DT_Float: { - short s = (short)z; + short s = (z >= (float)SHRT_MIN && z <= SHRT_MAX) ? (short)z : 0; int tc = (T)b == z ? 2 : (T)s == z ? 1 : 0; dtReduced = tc == 0 ? dt : (tc == 1 ? DT_Short : DT_Byte); return tc; } case DT_Double: { - short s = (short)z; - int l = (int)z; - float f = (float)z; + short s = (z >= (double)SHRT_MIN && z <= SHRT_MAX) ? (short)z : 0; + int l = (z >= (double)INT_MIN && z <= (double)INT_MAX) ? (int)z : 0; + float f = (z >= -FLT_MAX && z <= FLT_MAX) ? (float)z : 0; int tc = (T)s == z ? 3 : (T)l == z ? 2 : (T)f == z ? 1 : 0; dtReduced = tc == 0 ? dt : (DataType)(dt - 2 * tc + 1); return tc; @@ -494,7 +514,7 @@ inline Lerc2::DataType Lerc2::ValidateDataType(int dt) { - if( dt >= DT_Char && dt <= DT_Double ) + if (dt >= DT_Char && dt <= DT_Double) return static_cast(dt); return DT_Undefined; } @@ -704,11 +724,11 @@ inline bool Lerc2::CheckMinMaxRanges(bool& minMaxEqual) const { - int nDim = m_headerInfo.nDim; - if ((int)m_zMinVec.size() != nDim || (int)m_zMaxVec.size() != nDim) + int nDepth = m_headerInfo.nDepth; + if ((int)m_zMinVec.size() != nDepth || (int)m_zMaxVec.size() != nDepth) return false; - minMaxEqual = (0 == memcmp(&m_zMinVec[0], &m_zMaxVec[0], nDim * sizeof(m_zMinVec[0]))); + minMaxEqual = (0 == memcmp(&m_zMinVec[0], &m_zMaxVec[0], nDepth * sizeof(m_zMinVec[0]))); return true; } diff -Nru lerc-3.0+ds/src/LercLib/Lerc_c_api_impl.cpp lerc-4.0.0+ds/src/LercLib/Lerc_c_api_impl.cpp --- lerc-3.0+ds/src/LercLib/Lerc_c_api_impl.cpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc_c_api_impl.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2016 Esri +Copyright 2016 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -30,15 +30,16 @@ // -------------------------------------------------------------------------- ; -lerc_status lerc_computeCompressedSize(const void* pData, unsigned int dataType, int nDim, int nCols, int nRows, int nBands, +lerc_status lerc_computeCompressedSize(const void* pData, unsigned int dataType, int nDepth, int nCols, int nRows, int nBands, int nMasks, const unsigned char* pValidBytes, double maxZErr, unsigned int* numBytes) { - return lerc_computeCompressedSizeForVersion(pData, -1, dataType, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytes); + return lerc_computeCompressedSizeForVersion(pData, -1, dataType, nDepth, nCols, nRows, nBands, nMasks, + pValidBytes, maxZErr, numBytes); } // -------------------------------------------------------------------------- ; -lerc_status lerc_computeCompressedSizeForVersion(const void* pData, int version, unsigned int dataType, int nDim, int nCols, int nRows, int nBands, +lerc_status lerc_computeCompressedSizeForVersion(const void* pData, int version, unsigned int dataType, int nDepth, int nCols, int nRows, int nBands, int nMasks, const unsigned char* pValidBytes, double maxZErr, unsigned int* numBytes) { if (!numBytes) @@ -46,28 +47,30 @@ *numBytes = 0; - if (!pData || dataType >= Lerc::DT_Undefined || nDim <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0) + if (!pData || dataType >= Lerc::DT_Undefined || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0) return (lerc_status)ErrCode::WrongParam; if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes)) return (lerc_status)ErrCode::WrongParam; Lerc::DataType dt = (Lerc::DataType)dataType; - return (lerc_status)Lerc::ComputeCompressedSize(pData, version, dt, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, *numBytes); + return (lerc_status)Lerc::ComputeCompressedSize(pData, version, dt, nDepth, nCols, nRows, nBands, nMasks, + pValidBytes, maxZErr, *numBytes, nullptr, nullptr); } // -------------------------------------------------------------------------- ; -lerc_status lerc_encode(const void* pData, unsigned int dataType, int nDim, int nCols, int nRows, int nBands, +lerc_status lerc_encode(const void* pData, unsigned int dataType, int nDepth, int nCols, int nRows, int nBands, int nMasks, const unsigned char* pValidBytes, double maxZErr, unsigned char* pOutBuffer, unsigned int outBufferSize, unsigned int* nBytesWritten) { - return lerc_encodeForVersion(pData, -1, dataType, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pOutBuffer, outBufferSize, nBytesWritten); + return lerc_encodeForVersion(pData, -1, dataType, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, + maxZErr, pOutBuffer, outBufferSize, nBytesWritten); } // -------------------------------------------------------------------------- ; -lerc_status lerc_encodeForVersion(const void* pData, int version, unsigned int dataType, int nDim, int nCols, int nRows, int nBands, +lerc_status lerc_encodeForVersion(const void* pData, int version, unsigned int dataType, int nDepth, int nCols, int nRows, int nBands, int nMasks, const unsigned char* pValidBytes, double maxZErr, unsigned char* pOutBuffer, unsigned int outBufferSize, unsigned int* nBytesWritten) { @@ -76,14 +79,15 @@ *nBytesWritten = 0; - if (!pData || dataType >= Lerc::DT_Undefined || nDim <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0 || !pOutBuffer || !outBufferSize) + if (!pData || dataType >= Lerc::DT_Undefined || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0 || !pOutBuffer || !outBufferSize) return (lerc_status)ErrCode::WrongParam; if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes)) return (lerc_status)ErrCode::WrongParam; Lerc::DataType dt = (Lerc::DataType)dataType; - return (lerc_status)Lerc::Encode(pData, version, dt, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pOutBuffer, outBufferSize, *nBytesWritten); + return (lerc_status)Lerc::Encode(pData, version, dt, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, + maxZErr, pOutBuffer, outBufferSize, *nBytesWritten, nullptr, nullptr); } // -------------------------------------------------------------------------- ; @@ -111,7 +115,7 @@ if (i < ias) infoArray[i++] = (unsigned int)lercInfo.dt; if (i < ias) - infoArray[i++] = (unsigned int)lercInfo.nDim; + infoArray[i++] = (unsigned int)lercInfo.nDepth; if (i < ias) infoArray[i++] = (unsigned int)lercInfo.nCols; if (i < ias) @@ -124,6 +128,10 @@ infoArray[i++] = (unsigned int)lercInfo.blobSize; if (i < ias) infoArray[i++] = (unsigned int)lercInfo.nMasks; + if (i < ias) + infoArray[i++] = (unsigned int)lercInfo.nDepth; + if (i < ias) + infoArray[i++] = (unsigned int)lercInfo.nUsesNoDataValue; } if (dataRangeArray) @@ -133,10 +141,15 @@ if (dras > 0) memset(dataRangeArray, 0, dras * sizeof(dataRangeArray[0])); + // for nDepth > 1, and mix of valid and invalid values at same pixel, better reset min / max to -1 + // than return min or max values that contain noData (to be fixed in next codec version) + + bool bUsesNoData = (lercInfo.nDepth > 1) && (lercInfo.nUsesNoDataValue > 0); + if (i < dras) - dataRangeArray[i++] = lercInfo.zMin; + dataRangeArray[i++] = !bUsesNoData ? lercInfo.zMin : -1; if (i < dras) - dataRangeArray[i++] = lercInfo.zMax; + dataRangeArray[i++] = !bUsesNoData ? lercInfo.zMax : -1; if (i < dras) dataRangeArray[i++] = lercInfo.maxZError; } @@ -146,30 +159,105 @@ // -------------------------------------------------------------------------- ; +lerc_status lerc_getDataRanges(const unsigned char* pLercBlob, unsigned int blobSize, + int nDepth, int nBands, double* pMins, double* pMaxs) +{ + if (!pLercBlob || !blobSize || !pMins || !pMaxs || nDepth <= 0 || nBands <= 0) + return (lerc_status)ErrCode::WrongParam; + + Lerc::LercInfo lercInfo; + ErrCode errCode = Lerc::GetLercInfo(pLercBlob, blobSize, lercInfo, pMins, pMaxs, (size_t)nDepth * (size_t)nBands); + if (errCode != ErrCode::Ok) + return (lerc_status)errCode; + + return (lerc_status)ErrCode::Ok; +} + +// -------------------------------------------------------------------------- ; + lerc_status lerc_decode(const unsigned char* pLercBlob, unsigned int blobSize, int nMasks, - unsigned char* pValidBytes, int nDim, int nCols, int nRows, int nBands, unsigned int dataType, void* pData) + unsigned char* pValidBytes, int nDepth, int nCols, int nRows, int nBands, unsigned int dataType, void* pData) { - if (!pLercBlob || !blobSize || !pData || dataType >= Lerc::DT_Undefined || nDim <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0) + return lerc_decode_4D(pLercBlob, blobSize, nMasks, pValidBytes, + nDepth, nCols, nRows, nBands, dataType, pData, nullptr, nullptr); +} +// -------------------------------------------------------------------------- ; + +lerc_status lerc_decodeToDouble(const unsigned char* pLercBlob, unsigned int blobSize, int nMasks, + unsigned char* pValidBytes, int nDepth, int nCols, int nRows, int nBands, double* pData) +{ + return lerc_decodeToDouble_4D(pLercBlob, blobSize, nMasks, pValidBytes, + nDepth, nCols, nRows, nBands, pData, nullptr, nullptr); +} + +// -------------------------------------------------------------------------- ; +// -------------------------------------------------------------------------- ; + +lerc_status lerc_computeCompressedSize_4D(const void* pData, unsigned int dataType, int nDepth, int nCols, int nRows, int nBands, + int nMasks, const unsigned char* pValidBytes, double maxZErr, unsigned int* numBytes, const unsigned char* pUsesNoData, const double* noDataValues) +{ + if (!numBytes) + return (lerc_status)ErrCode::WrongParam; + + *numBytes = 0; + + if (!pData || dataType >= Lerc::DT_Undefined || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0) return (lerc_status)ErrCode::WrongParam; if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes)) return (lerc_status)ErrCode::WrongParam; Lerc::DataType dt = (Lerc::DataType)dataType; + return (lerc_status)Lerc::ComputeCompressedSize(pData, -1, dt, nDepth, nCols, nRows, nBands, nMasks, + pValidBytes, maxZErr, *numBytes, pUsesNoData, noDataValues); +} - ErrCode errCode = Lerc::Decode(pLercBlob, blobSize, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dt, pData); - if (errCode != ErrCode::Ok) - return (lerc_status)errCode; +// -------------------------------------------------------------------------- ; - return (lerc_status)ErrCode::Ok; +lerc_status lerc_encode_4D(const void* pData, unsigned int dataType, int nDepth, int nCols, int nRows, int nBands, + int nMasks, const unsigned char* pValidBytes, double maxZErr, unsigned char* pOutBuffer, unsigned int outBufferSize, + unsigned int* nBytesWritten, const unsigned char* pUsesNoData, const double* noDataValues) +{ + if (!nBytesWritten) + return (lerc_status)ErrCode::WrongParam; + + *nBytesWritten = 0; + + if (!pData || dataType >= Lerc::DT_Undefined || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0 || !pOutBuffer || !outBufferSize) + return (lerc_status)ErrCode::WrongParam; + + if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes)) + return (lerc_status)ErrCode::WrongParam; + + Lerc::DataType dt = (Lerc::DataType)dataType; + return (lerc_status)Lerc::Encode(pData, -1, dt, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, + maxZErr, pOutBuffer, outBufferSize, *nBytesWritten, pUsesNoData, noDataValues); } // -------------------------------------------------------------------------- ; -lerc_status lerc_decodeToDouble(const unsigned char* pLercBlob, unsigned int blobSize, int nMasks, - unsigned char* pValidBytes, int nDim, int nCols, int nRows, int nBands, double* pData) +lerc_status lerc_decode_4D(const unsigned char* pLercBlob, unsigned int blobSize, int nMasks, + unsigned char* pValidBytes, int nDepth, int nCols, int nRows, int nBands, unsigned int dataType, void* pData, + unsigned char* pUsesNoData, double* noDataValues) +{ + if (!pLercBlob || !blobSize || !pData || dataType >= Lerc::DT_Undefined || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0) + return (lerc_status)ErrCode::WrongParam; + + if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes)) + return (lerc_status)ErrCode::WrongParam; + + Lerc::DataType dt = (Lerc::DataType)dataType; + + return (lerc_status)Lerc::Decode(pLercBlob, blobSize, nMasks, pValidBytes, nDepth, nCols, nRows, nBands, dt, pData, pUsesNoData, noDataValues); +} + +// -------------------------------------------------------------------------- ; + +lerc_status lerc_decodeToDouble_4D(const unsigned char* pLercBlob, unsigned int blobSize, int nMasks, + unsigned char* pValidBytes, int nDepth, int nCols, int nRows, int nBands, double* pData, + unsigned char* pUsesNoData, double* noDataValues) { - if (!pLercBlob || !blobSize || !pData || nDim <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0) + if (!pLercBlob || !blobSize || !pData || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0) return (lerc_status)ErrCode::WrongParam; if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes)) @@ -186,18 +274,24 @@ if (dt == Lerc::DT_Double) { - if ((errCode = Lerc::Decode(pLercBlob, blobSize, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dt, pData)) != ErrCode::Ok) + if ((errCode = Lerc::Decode(pLercBlob, blobSize, nMasks, pValidBytes, + nDepth, nCols, nRows, nBands, dt, pData, pUsesNoData, noDataValues)) != ErrCode::Ok) + { return (lerc_status)errCode; + } } else { // use the buffer passed for in place decode and convert int sizeofDt[] = { 1, 1, 2, 2, 4, 4, 4, 8 }; - size_t nDataValues = nDim * nCols * nRows * nBands; + size_t nDataValues = nDepth * nCols * nRows * nBands; void* ptrDec = (Byte*)pData + nDataValues * (sizeof(double) - sizeofDt[dt]); - if ((errCode = Lerc::Decode(pLercBlob, blobSize, nMasks, pValidBytes, nDim, nCols, nRows, nBands, dt, ptrDec)) != ErrCode::Ok) + if ((errCode = Lerc::Decode(pLercBlob, blobSize, nMasks, pValidBytes, + nDepth, nCols, nRows, nBands, dt, ptrDec, pUsesNoData, noDataValues)) != ErrCode::Ok) + { return (lerc_status)errCode; + } if ((errCode = Lerc::ConvertToDouble(ptrDec, dt, nDataValues, pData)) != ErrCode::Ok) return (lerc_status)errCode; diff -Nru lerc-3.0+ds/src/LercLib/Lerc.cpp lerc-4.0.0+ds/src/LercLib/Lerc.cpp --- lerc-3.0+ds/src/LercLib/Lerc.cpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ #include #ifdef HAVE_LERC1_DECODE -#include "Lerc1Decode/CntZImage.h" + #include "Lerc1Decode/CntZImage.h" #endif using namespace std; @@ -36,49 +36,58 @@ // -------------------------------------------------------------------------- ; -ErrCode Lerc::ComputeCompressedSize(const void* pData, int version, DataType dt, int nDim, int nCols, int nRows, int nBands, - int nMasks, const Byte* pValidBytes, double maxZErr, unsigned int& numBytesNeeded) +ErrCode Lerc::ComputeCompressedSize(const void* pData, int version, DataType dt, int nDepth, int nCols, int nRows, int nBands, + int nMasks, const Byte* pValidBytes, double maxZErr, unsigned int& numBytesNeeded, const unsigned char* pUsesNoData, const double* noDataValues) { +#define LERC_ARG_1 version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytesNeeded, pUsesNoData, noDataValues + switch (dt) { - case DT_Char: return ComputeCompressedSizeTempl((const signed char*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytesNeeded); - case DT_Byte: return ComputeCompressedSizeTempl((const Byte*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytesNeeded); - case DT_Short: return ComputeCompressedSizeTempl((const short*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytesNeeded); - case DT_UShort: return ComputeCompressedSizeTempl((const unsigned short*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytesNeeded); - case DT_Int: return ComputeCompressedSizeTempl((const int*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytesNeeded); - case DT_UInt: return ComputeCompressedSizeTempl((const unsigned int*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytesNeeded); - case DT_Float: return ComputeCompressedSizeTempl((const float*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytesNeeded); - case DT_Double: return ComputeCompressedSizeTempl((const double*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, numBytesNeeded); + case DT_Char: return ComputeCompressedSizeTempl((const signed char*)pData, LERC_ARG_1); + case DT_Byte: return ComputeCompressedSizeTempl((const Byte*)pData, LERC_ARG_1); + case DT_Short: return ComputeCompressedSizeTempl((const short*)pData, LERC_ARG_1); + case DT_UShort: return ComputeCompressedSizeTempl((const unsigned short*)pData, LERC_ARG_1); + case DT_Int: return ComputeCompressedSizeTempl((const int*)pData, LERC_ARG_1); + case DT_UInt: return ComputeCompressedSizeTempl((const unsigned int*)pData, LERC_ARG_1); + case DT_Float: return ComputeCompressedSizeTempl((const float*)pData, LERC_ARG_1); + case DT_Double: return ComputeCompressedSizeTempl((const double*)pData, LERC_ARG_1); default: return ErrCode::WrongParam; } + +#undef LERC_ARG_1 } // -------------------------------------------------------------------------- ; -ErrCode Lerc::Encode(const void* pData, int version, DataType dt, int nDim, int nCols, int nRows, int nBands, - int nMasks, const Byte* pValidBytes, double maxZErr, Byte* pBuffer, unsigned int numBytesBuffer, unsigned int& numBytesWritten) +ErrCode Lerc::Encode(const void* pData, int version, DataType dt, int nDepth, int nCols, int nRows, int nBands, + int nMasks, const Byte* pValidBytes, double maxZErr, Byte* pBuffer, unsigned int numBytesBuffer, + unsigned int& numBytesWritten, const unsigned char* pUsesNoData, const double* noDataValues) { +#define LERC_ARG_2 version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pBuffer, numBytesBuffer, numBytesWritten, pUsesNoData, noDataValues + switch (dt) { - case DT_Char: return EncodeTempl((const signed char*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pBuffer, numBytesBuffer, numBytesWritten); - case DT_Byte: return EncodeTempl((const Byte*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pBuffer, numBytesBuffer, numBytesWritten); - case DT_Short: return EncodeTempl((const short*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pBuffer, numBytesBuffer, numBytesWritten); - case DT_UShort: return EncodeTempl((const unsigned short*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pBuffer, numBytesBuffer, numBytesWritten); - case DT_Int: return EncodeTempl((const int*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pBuffer, numBytesBuffer, numBytesWritten); - case DT_UInt: return EncodeTempl((const unsigned int*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pBuffer, numBytesBuffer, numBytesWritten); - case DT_Float: return EncodeTempl((const float*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pBuffer, numBytesBuffer, numBytesWritten); - case DT_Double: return EncodeTempl((const double*)pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, pBuffer, numBytesBuffer, numBytesWritten); + case DT_Char: return EncodeTempl((const signed char*)pData, LERC_ARG_2); + case DT_Byte: return EncodeTempl((const Byte*)pData, LERC_ARG_2); + case DT_Short: return EncodeTempl((const short*)pData, LERC_ARG_2); + case DT_UShort: return EncodeTempl((const unsigned short*)pData, LERC_ARG_2); + case DT_Int: return EncodeTempl((const int*)pData, LERC_ARG_2); + case DT_UInt: return EncodeTempl((const unsigned int*)pData, LERC_ARG_2); + case DT_Float: return EncodeTempl((const float*)pData, LERC_ARG_2); + case DT_Double: return EncodeTempl((const double*)pData, LERC_ARG_2); default: return ErrCode::WrongParam; } + +#undef LERC_ARG_2 } // -------------------------------------------------------------------------- ; -ErrCode Lerc::GetLercInfo(const Byte* pLercBlob, unsigned int numBytesBlob, struct LercInfo& lercInfo) +ErrCode Lerc::GetLercInfo(const Byte* pLercBlob, unsigned int numBytesBlob, struct LercInfo& lercInfo, double* pMins, double* pMaxs, size_t nElem) { lercInfo.RawInit(); @@ -90,53 +99,78 @@ if (Lerc2::GetHeaderInfo(pLercBlob, numBytesBlob, lerc2Info, bHasMask)) { lercInfo.version = lerc2Info.version; - lercInfo.nDim = lerc2Info.nDim; + lercInfo.nDepth = lerc2Info.nDepth; lercInfo.nCols = lerc2Info.nCols; lercInfo.nRows = lerc2Info.nRows; lercInfo.numValidPixel = lerc2Info.numValidPixel; // for 1st band - lercInfo.nBands = 1; lercInfo.blobSize = lerc2Info.blobSize; lercInfo.dt = (DataType)lerc2Info.dt; lercInfo.zMin = lerc2Info.zMin; lercInfo.zMax = lerc2Info.zMax; lercInfo.maxZError = lerc2Info.maxZError; + lercInfo.nUsesNoDataValue = lerc2Info.bPassNoDataValues ? 1 : 0; + + bool bTryNextBlob = (lerc2Info.version <= 5) || (lerc2Info.nBlobsMore > 0); if (bHasMask || lercInfo.numValidPixel == 0) nMasks = 1; + if (pMins && pMaxs) + { + ErrCode errCode = GetRanges(pLercBlob, numBytesBlob, 0, lerc2Info, pMins, pMaxs, nElem); // band 0 + if (errCode != ErrCode::Ok) + return errCode; + } + + lercInfo.nBands = 1; + if (lercInfo.blobSize > (int)numBytesBlob) // truncated blob, we won't be able to read this band return ErrCode::BufferTooSmall; struct Lerc2::HeaderInfo hdInfo; - while (Lerc2::GetHeaderInfo(pLercBlob + lercInfo.blobSize, numBytesBlob - lercInfo.blobSize, hdInfo, bHasMask)) + while (bTryNextBlob && Lerc2::GetHeaderInfo(pLercBlob + lercInfo.blobSize, numBytesBlob - lercInfo.blobSize, hdInfo, bHasMask)) { - if (hdInfo.nDim != lercInfo.nDim + if (hdInfo.nDepth != lercInfo.nDepth || hdInfo.nCols != lercInfo.nCols || hdInfo.nRows != lercInfo.nRows || (int)hdInfo.dt != (int)lercInfo.dt) - //|| hdInfo.maxZError != lercInfo.maxZError) // with the new bitplane compression, maxZError can vary between bands { return ErrCode::Failed; } + bTryNextBlob = (hdInfo.version <= 5) || (hdInfo.nBlobsMore > 0); + + if (hdInfo.bPassNoDataValues) + lercInfo.nUsesNoDataValue++; + if (bHasMask || hdInfo.numValidPixel != lercInfo.numValidPixel) // support mask per band nMasks = 2; - if (lercInfo.blobSize > std::numeric_limits::max() - hdInfo.blobSize) + if (lercInfo.blobSize > std::numeric_limits::max() - hdInfo.blobSize) // guard against overflow return ErrCode::Failed; - lercInfo.blobSize += hdInfo.blobSize; - - if (lercInfo.blobSize > (int)numBytesBlob) // truncated blob, we won't be able to read this band + if (lercInfo.blobSize + hdInfo.blobSize > (int)numBytesBlob) // truncated blob, we won't be able to read this band return ErrCode::BufferTooSmall; - lercInfo.nBands++; lercInfo.zMin = min(lercInfo.zMin, hdInfo.zMin); lercInfo.zMax = max(lercInfo.zMax, hdInfo.zMax); lercInfo.maxZError = max(lercInfo.maxZError, hdInfo.maxZError); // with the new bitplane compression, maxZError can vary between bands + + if (pMins && pMaxs) + { + ErrCode errCode = GetRanges(pLercBlob + lercInfo.blobSize, numBytesBlob - lercInfo.blobSize, lercInfo.nBands, hdInfo, pMins, pMaxs, nElem); + if (errCode != ErrCode::Ok) + return errCode; + } + + lercInfo.blobSize += hdInfo.blobSize; + lercInfo.nBands++; } lercInfo.nMasks = nMasks > 1 ? lercInfo.nBands : nMasks; + + if (lercInfo.nUsesNoDataValue > 0) + lercInfo.nUsesNoDataValue = lercInfo.nBands; // if there is any noData used in any band, allow for different noData per band return ErrCode::Ok; } @@ -146,7 +180,7 @@ // only if not Lerc2, try legacy Lerc1 unsigned int numBytesHeaderBand0 = CntZImage::computeNumBytesNeededToReadHeader(false); unsigned int numBytesHeaderBand1 = CntZImage::computeNumBytesNeededToReadHeader(true); - Byte* pByte = const_cast(pLercBlob); + const Byte* pByte = pLercBlob; lercInfo.zMin = FLT_MAX; lercInfo.zMax = -FLT_MAX; @@ -160,7 +194,7 @@ if (nBytesRead < nBytesNeeded) return ErrCode::Failed; - Byte* ptr = const_cast(pLercBlob); + const Byte* ptr = pLercBlob; ptr += 10 + 2 * sizeof(int); int height(0), width(0); @@ -172,13 +206,13 @@ if (height > 20000 || width > 20000) // guard against bogus numbers; size limitation for old Lerc1 return ErrCode::Failed; - lercInfo.nDim = 1; + lercInfo.nDepth = 1; lercInfo.nCols = width; lercInfo.nRows = height; lercInfo.dt = Lerc::DT_Float; lercInfo.maxZError = maxZErrorInFile; - Byte* pByte = const_cast(pLercBlob); + pByte = pLercBlob; bool onlyZPart = false; while (lercInfo.blobSize + numBytesHeaderBand1 < numBytesBlob) // means there could be another band @@ -187,8 +221,6 @@ return (lercInfo.nBands > 0) ? ErrCode::Ok : ErrCode::Failed; // no other band, we are done onlyZPart = true; - - lercInfo.nBands++; lercInfo.blobSize = (int)(pByte - pLercBlob); // now that we have decoded it, we can go the extra mile and collect some extra info @@ -212,6 +244,14 @@ lercInfo.zMin = std::min(lercInfo.zMin, (double)zMin); lercInfo.zMax = std::max(lercInfo.zMax, (double)zMax); lercInfo.nMasks = numValidPixels < width * height ? 1 : 0; + + if (pMins && pMaxs) + { + pMins[lercInfo.nBands] = zMin; + pMaxs[lercInfo.nBands] = zMax; + } + + lercInfo.nBands++; } return ErrCode::Ok; @@ -224,22 +264,26 @@ // -------------------------------------------------------------------------- ; ErrCode Lerc::Decode(const Byte* pLercBlob, unsigned int numBytesBlob, int nMasks, Byte* pValidBytes, - int nDim, int nCols, int nRows, int nBands, DataType dt, void* pData) + int nDepth, int nCols, int nRows, int nBands, DataType dt, void* pData, unsigned char* pUsesNoData, double* noDataValues) { +#define LERC_ARG_3 pLercBlob, numBytesBlob, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, pUsesNoData, noDataValues + switch (dt) { - case DT_Char: return DecodeTempl((signed char*)pData, pLercBlob, numBytesBlob, nDim, nCols, nRows, nBands, nMasks, pValidBytes); - case DT_Byte: return DecodeTempl((Byte*)pData, pLercBlob, numBytesBlob, nDim, nCols, nRows, nBands, nMasks, pValidBytes); - case DT_Short: return DecodeTempl((short*)pData, pLercBlob, numBytesBlob, nDim, nCols, nRows, nBands, nMasks, pValidBytes); - case DT_UShort: return DecodeTempl((unsigned short*)pData, pLercBlob, numBytesBlob, nDim, nCols, nRows, nBands, nMasks, pValidBytes); - case DT_Int: return DecodeTempl((int*)pData, pLercBlob, numBytesBlob, nDim, nCols, nRows, nBands, nMasks, pValidBytes); - case DT_UInt: return DecodeTempl((unsigned int*)pData, pLercBlob, numBytesBlob, nDim, nCols, nRows, nBands, nMasks, pValidBytes); - case DT_Float: return DecodeTempl((float*)pData, pLercBlob, numBytesBlob, nDim, nCols, nRows, nBands, nMasks, pValidBytes); - case DT_Double: return DecodeTempl((double*)pData, pLercBlob, numBytesBlob, nDim, nCols, nRows, nBands, nMasks, pValidBytes); + case DT_Char: return DecodeTempl((signed char*)pData, LERC_ARG_3); + case DT_Byte: return DecodeTempl((Byte*)pData, LERC_ARG_3); + case DT_Short: return DecodeTempl((short*)pData, LERC_ARG_3); + case DT_UShort: return DecodeTempl((unsigned short*)pData, LERC_ARG_3); + case DT_Int: return DecodeTempl((int*)pData, LERC_ARG_3); + case DT_UInt: return DecodeTempl((unsigned int*)pData, LERC_ARG_3); + case DT_Float: return DecodeTempl((float*)pData, LERC_ARG_3); + case DT_Double: return DecodeTempl((double*)pData, LERC_ARG_3); default: return ErrCode::WrongParam; } + +#undef LERC_ARG_3 } // -------------------------------------------------------------------------- ; @@ -266,12 +310,13 @@ // -------------------------------------------------------------------------- ; template -ErrCode Lerc::ComputeCompressedSizeTempl(const T* pData, int version, int nDim, int nCols, int nRows, int nBands, - int nMasks, const Byte* pValidBytes, double maxZErr, unsigned int& numBytesNeeded) +ErrCode Lerc::ComputeCompressedSizeTempl(const T* pData, int version, int nDepth, int nCols, int nRows, + int nBands, int nMasks, const Byte* pValidBytes, double maxZErr, unsigned int& numBytesNeeded, + const unsigned char* pUsesNoData, const double* noDataValues) { numBytesNeeded = 0; - if (!pData || nDim <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0) + if (!pData || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0) return ErrCode::WrongParam; if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes)) @@ -279,19 +324,33 @@ unsigned int numBytesWritten = 0; - return EncodeInternal(pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, - numBytesNeeded, nullptr, 0, numBytesWritten); + if (version >= 0 && version <= 5) + { + if (pUsesNoData) + for (int i = 0; i < nBands; i++) + if (pUsesNoData[i]) + return ErrCode::WrongParam; + + return EncodeInternal_v5(pData, version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, + numBytesNeeded, nullptr, 0, numBytesWritten); + } + else + { + return EncodeInternal(pData, version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, + numBytesNeeded, nullptr, 0, numBytesWritten, pUsesNoData, noDataValues); + } } // -------------------------------------------------------------------------- ; template -ErrCode Lerc::EncodeTempl(const T* pData, int version, int nDim, int nCols, int nRows, int nBands, - int nMasks, const Byte* pValidBytes, double maxZErr, Byte* pBuffer, unsigned int numBytesBuffer, unsigned int& numBytesWritten) +ErrCode Lerc::EncodeTempl(const T* pData, int version, int nDepth, int nCols, int nRows, int nBands, + int nMasks, const Byte* pValidBytes, double maxZErr, Byte* pBuffer, unsigned int numBytesBuffer, + unsigned int& numBytesWritten, const unsigned char* pUsesNoData, const double* noDataValues) { numBytesWritten = 0; - if (!pData || nDim <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0 || !pBuffer || !numBytesBuffer) + if (!pData || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || maxZErr < 0 || !pBuffer || !numBytesBuffer) return ErrCode::WrongParam; if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes)) @@ -299,17 +358,31 @@ unsigned int numBytesNeeded = 0; - return EncodeInternal(pData, version, nDim, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, - numBytesNeeded, pBuffer, numBytesBuffer, numBytesWritten); + if (version >= 0 && version <= 5) + { + if (pUsesNoData) + for (int i = 0; i < nBands; i++) + if (pUsesNoData[i]) + return ErrCode::WrongParam; + + return EncodeInternal_v5(pData, version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, + numBytesNeeded, pBuffer, numBytesBuffer, numBytesWritten); + } + else + { + return EncodeInternal(pData, version, nDepth, nCols, nRows, nBands, nMasks, pValidBytes, maxZErr, + numBytesNeeded, pBuffer, numBytesBuffer, numBytesWritten, pUsesNoData, noDataValues); + } } // -------------------------------------------------------------------------- ; template ErrCode Lerc::DecodeTempl(T* pData, const Byte* pLercBlob, unsigned int numBytesBlob, - int nDim, int nCols, int nRows, int nBands, int nMasks, Byte* pValidBytes) + int nDepth, int nCols, int nRows, int nBands, int nMasks, Byte* pValidBytes, + unsigned char* pUsesNoData, double* noDataValues) { - if (!pData || nDim <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || !pLercBlob || !numBytesBlob) + if (!pData || nDepth <= 0 || nCols <= 0 || nRows <= 0 || nBands <= 0 || !pLercBlob || !numBytesBlob) return ErrCode::WrongParam; if (!(nMasks == 0 || nMasks == 1 || nMasks == nBands) || (nMasks > 0 && !pValidBytes)) @@ -322,15 +395,35 @@ if (Lerc2::GetHeaderInfo(pByte, numBytesBlob, hdInfo, bHasMask) && hdInfo.version >= 1) // if Lerc2 { LercInfo lercInfo; - ErrCode errCode = GetLercInfo(pLercBlob, numBytesBlob, lercInfo); // fast for Lerc2 + ErrCode errCode = GetLercInfo(pLercBlob, numBytesBlob, lercInfo); // fast for Lerc2, does most checks if (errCode != ErrCode::Ok) return errCode; - const int nMasksEncoded = lercInfo.nMasks; // 0, 1, or nBands + // caller must provide enough space for the masks that are there (e.g., if they differ between bands) + if (nMasks < lercInfo.nMasks) // 0, 1, or nBands + return ErrCode::WrongParam; - if (nMasks < nMasksEncoded) + // caller cannot ask for more bands than are there + if (nBands > lercInfo.nBands) return ErrCode::WrongParam; + // if Lerc blob has noData values not covered by the mask, caller must get it (make sure caller cannot miss it) + if (lercInfo.nUsesNoDataValue && nDepth > 1) + { + if (!pUsesNoData || !noDataValues) + return ErrCode::HasNoData; + + try + { + memset(pUsesNoData, 0, nBands); + memset(noDataValues, 0, nBands * sizeof(double)); + } + catch (...) + { + return ErrCode::HasNoData; + } + } + size_t nBytesRemaining = numBytesBlob; Lerc2 lerc2; BitMask bitMask; @@ -339,14 +432,14 @@ { if (((size_t)(pByte - pLercBlob) < numBytesBlob) && Lerc2::GetHeaderInfo(pByte, nBytesRemaining, hdInfo, bHasMask)) { - if (hdInfo.nDim != nDim || hdInfo.nCols != nCols || hdInfo.nRows != nRows) + if (hdInfo.nDepth != nDepth || hdInfo.nCols != nCols || hdInfo.nRows != nRows) return ErrCode::Failed; if ((pByte - pLercBlob) + (size_t)hdInfo.blobSize > numBytesBlob) return ErrCode::BufferTooSmall; size_t nPix = (size_t)iBand * nRows * nCols; - T* arr = pData + nPix * nDim; + T* arr = pData + nPix * nDepth; bool bGetMask = iBand < nMasks; @@ -356,6 +449,15 @@ if (!lerc2.Decode(&pByte, nBytesRemaining, arr, bGetMask ? bitMask.Bits() : nullptr)) return ErrCode::Failed; + if (lercInfo.nUsesNoDataValue && nDepth > 1) + { + pUsesNoData[iBand] = hdInfo.bPassNoDataValues ? 1 : 0; + noDataValues[iBand] = hdInfo.noDataValOrig; + + if (hdInfo.bPassNoDataValues && !RemapNoData(arr, bitMask, hdInfo)) + return ErrCode::Failed; + } + if (bGetMask && !Convert(bitMask, pValidBytes + nPix)) return ErrCode::Failed; } @@ -367,7 +469,7 @@ #ifdef HAVE_LERC1_DECODE unsigned int numBytesHeaderBand0 = CntZImage::computeNumBytesNeededToReadHeader(false); unsigned int numBytesHeaderBand1 = CntZImage::computeNumBytesNeededToReadHeader(true); - Byte* pByte1 = const_cast(pLercBlob); + const Byte* pByte1 = pLercBlob; CntZImage zImg; for (int iBand = 0; iBand < nBands; iBand++) @@ -402,7 +504,7 @@ // -------------------------------------------------------------------------- ; template -ErrCode Lerc::EncodeInternal(const T* pData, int version, int nDim, int nCols, int nRows, int nBands, +ErrCode Lerc::EncodeInternal_v5(const T* pData, int version, int nDepth, int nCols, int nRows, int nBands, int nMasks, const Byte* pValidBytes, double maxZErr, unsigned int& numBytesNeeded, Byte* pBuffer, unsigned int numBytesBuffer, unsigned int& numBytesWritten) { @@ -416,7 +518,7 @@ Byte* pDst = pBuffer; const size_t nPix = (size_t)nCols * nRows; - const size_t nElem = nPix * nDim; + const size_t nElem = nPix * nDepth; const Byte* pPrevByteMask = nullptr; vector dataBuffer; @@ -432,7 +534,7 @@ const T* arr = pData + nElem * iBand; const Byte* pByteMask = (nMasks > 0) ? (pValidBytes + ((nMasks > 1) ? nPix * iBand : 0)) : nullptr; - ErrCode errCode = CheckForNaN(arr, nDim, nCols, nRows, pByteMask); + ErrCode errCode = CheckForNaN(arr, nDepth, nCols, nRows, pByteMask); if (errCode != ErrCode::Ok && errCode != ErrCode::NaN) return errCode; @@ -444,7 +546,7 @@ memcpy(&dataBuffer[0], arr, nElem * sizeof(T)); pByteMask ? memcpy(&maskBuffer[0], pByteMask, nPix) : memset(&maskBuffer[0], 1, nPix); - if (!ReplaceNaNValues(dataBuffer, maskBuffer, nDim, nCols, nRows)) + if (!ReplaceNaNValues(dataBuffer, maskBuffer, nDepth, nCols, nRows)) return ErrCode::Failed; if (iBand > 0 && MasksDiffer(&maskBuffer[0], pPrevByteMask, nPix)) @@ -474,7 +576,7 @@ if (pByteMask && !Convert(pByteMask, nCols, nRows, bitMask)) return ErrCode::Failed; - if (!lerc2.Set(nDim, nCols, nRows, pByteMask ? bitMask.Bits() : nullptr)) + if (!lerc2.Set(nDepth, nCols, nRows, pByteMask ? bitMask.Bits() : nullptr)) return ErrCode::Failed; } @@ -492,7 +594,166 @@ if (!lerc2.Encode(arr, &pDst)) return ErrCode::Failed; } - } + } // iBand + + numBytesWritten = (unsigned int)(pDst - pBuffer); + return ErrCode::Ok; +} + +// -------------------------------------------------------------------------- ; + +template +ErrCode Lerc::EncodeInternal(const T* pData, int version, int nDepth, int nCols, int nRows, int nBands, + int nMasks, const Byte* pValidBytes, double maxZErr, unsigned int& numBytesNeeded, + Byte* pBuffer, unsigned int numBytesBuffer, unsigned int& numBytesWritten, + const unsigned char* pUsesNoData, const double* noDataValues) +{ + numBytesNeeded = 0; + numBytesWritten = 0; + + if (version >= 0 && version <= 5) + return ErrCode::WrongParam; + + Lerc2 lerc2; + +#ifdef ENCODE_VERIFY + Lerc2 lerc2Verify; +#endif + + if (version >= 0 && !lerc2.SetEncoderToOldVersion(version)) + return ErrCode::WrongParam; + + if (pUsesNoData && !noDataValues) + for (int i = 0; i < nBands; i++) + if (pUsesNoData[i]) + return ErrCode::WrongParam; + + Byte* pDst = pBuffer; + + const size_t nPix = (size_t)nCols * nRows; + const size_t nElem = nPix * nDepth; + + const Byte* pPrevByteMask = nullptr; + vector dataBuffer; + vector maskBuffer, prevMaskBuffer; + BitMask bitMask; + + // allocate buffer for 1 band + if (!Resize(dataBuffer, nElem) || !Resize(maskBuffer, nPix)) + return ErrCode::Failed; + + bool bIsFltOrDbl = (typeid(T) == typeid(float) || typeid(T) == typeid(double)); + bool bAnyMaskModified = false; + ErrCode errCode = ErrCode::Ok; + + // loop over the bands + for (int iBand = 0; iBand < nBands; iBand++) + { + bool bEncMsk = (iBand == 0); + + // get the data and mask for this band + const T* arrOrig = pData + nElem * iBand; + const Byte* pByteMaskOrig = (nMasks > 0) ? (pValidBytes + ((nMasks > 1) ? nPix * iBand : 0)) : nullptr; + + // copy to buffer so we can modify it + memcpy(&dataBuffer[0], arrOrig, nElem * sizeof(T)); + pByteMaskOrig ? memcpy(&maskBuffer[0], pByteMaskOrig, nPix) : memset(&maskBuffer[0], 1, nPix); + + double maxZErrL = maxZErr; // maxZErrL can get modified in the filter functions below + + bool bPassNoDataValue = (pUsesNoData && (pUsesNoData[iBand] > 0)); + const double noDataOrig = bPassNoDataValue ? noDataValues[iBand] : 0; + + double noDataL = noDataOrig; // noDataL can get modified in the filter functions below + + bool bIsFltDblAllInt = false; // are the flt numbers all integer really? Double can be used for int numbers beyond 32 bit range + bool bModifiedMask = false; // can turn true if NaN or noData found at valid pixels + bool bNeedNoData = false; // can only turn true for nDepth > 1, and mix of valid and invalid values at the same pixel (special case) + errCode = ErrCode::Ok; + + if (bIsFltOrDbl) // if flt type, filter out NaN and / or noData values and update the mask if possible + { + errCode = FilterNoDataAndNaN(dataBuffer, maskBuffer, nDepth, nCols, nRows, maxZErrL, bPassNoDataValue, noDataL, + bModifiedMask, bNeedNoData, bIsFltDblAllInt); + } + else if (bPassNoDataValue) // if int type (no NaN), and no noData value specified, nothing to do + { + errCode = FilterNoData(dataBuffer, maskBuffer, nDepth, nCols, nRows, maxZErrL, bPassNoDataValue, noDataL, bModifiedMask, bNeedNoData); + } + + if (errCode != ErrCode::Ok) + return errCode; + + if (bModifiedMask) + bAnyMaskModified = true; + + bool bCompareMasks = (nMasks > 1) || bAnyMaskModified; + + if (bCompareMasks && (iBand > 0) && MasksDiffer(&maskBuffer[0], pPrevByteMask, nPix)) + bEncMsk = true; + + if (nBands > 1 && iBand < nBands - 1) + { + // keep current mask as new previous band mask + prevMaskBuffer = maskBuffer; + pPrevByteMask = &prevMaskBuffer[0]; + } + + const T* arrL = &dataBuffer[0]; + const Byte* pByteMaskL = &maskBuffer[0]; + + if (bEncMsk) + { + bool bAllValid = !memchr(pByteMaskL, 0, nPix); + + if (!bAllValid && !Convert(pByteMaskL, nCols, nRows, bitMask)) + return ErrCode::Failed; + + if (!lerc2.Set(nDepth, nCols, nRows, !bAllValid ? bitMask.Bits() : nullptr)) + return ErrCode::Failed; + } + + // set other flags + + if (!lerc2.SetNoDataValues(bNeedNoData, noDataL, noDataOrig)) + return ErrCode::Failed; + + if (!lerc2.SetNumBlobsMoreToCome(nBands - 1 - iBand)) + return ErrCode::Failed; + + if (!lerc2.SetIsAllInt(bIsFltDblAllInt)) + return ErrCode::Failed; + + unsigned int nBytes = lerc2.ComputeNumBytesNeededToWrite(arrL, maxZErrL, bEncMsk); + if (nBytes <= 0) + return ErrCode::Failed; + + numBytesNeeded += nBytes; + + if (pBuffer) + { + if ((size_t)(pDst - pBuffer) + nBytes > numBytesBuffer) // check we have enough space left + return ErrCode::BufferTooSmall; + +#ifdef ENCODE_VERIFY + const Byte* pDst0 = pDst; +#endif + + if (!lerc2.Encode(arrL, &pDst)) + return ErrCode::Failed; + +#ifdef ENCODE_VERIFY + size_t blobSize = pDst - pDst0; + + if (!DecodeAndCompareToInput(pDst0, blobSize, maxZErrL, lerc2Verify, arrL, pByteMaskL, + arrOrig, pByteMaskOrig, bPassNoDataValue, noDataOrig, bModifiedMask)) + { + return ErrCode::Failed; + } +#endif + + } + } // iBand numBytesWritten = (unsigned int)(pDst - pBuffer); return ErrCode::Ok; @@ -568,9 +829,9 @@ // -------------------------------------------------------------------------- ; -template ErrCode Lerc::CheckForNaN(const T* arr, int nDim, int nCols, int nRows, const Byte* pByteMask) +template ErrCode Lerc::CheckForNaN(const T* arr, int nDepth, int nCols, int nRows, const Byte* pByteMask) { - if (!arr || nDim <= 0 || nCols <= 0 || nRows <= 0) + if (!arr || nDepth <= 0 || nCols <= 0 || nRows <= 0) return ErrCode::WrongParam; if (typeid(T) != typeid(double) && typeid(T) != typeid(float)) @@ -579,21 +840,21 @@ for (size_t k = 0, i = 0; i < (size_t)nRows; i++) { bool bFoundNaN = false; - const T* rowArr = &(arr[i * nCols * nDim]); + const T* rowArr = &(arr[i * nCols * nDepth]); if (!pByteMask) // all valid { - size_t num = (size_t)nCols * nDim; + size_t num = (size_t)nCols * nDepth; for (size_t m = 0; m < num; m++) if (std::isnan((double)rowArr[m])) bFoundNaN = true; } else // not all valid { - for (size_t n = 0, j = 0; j < (size_t)nCols; j++, k++, n += nDim) + for (size_t n = 0, j = 0; j < (size_t)nCols; j++, k++, n += nDepth) if (pByteMask[k]) { - for (int m = 0; m < nDim; m++) + for (int m = 0; m < nDepth; m++) if (std::isnan((double)rowArr[n + m])) bFoundNaN = true; } @@ -608,32 +869,38 @@ // -------------------------------------------------------------------------- ; -template bool Lerc::ReplaceNaNValues(std::vector& dataBuffer, std::vector& maskBuffer, int nDim, int nCols, int nRows) +template bool Lerc::ReplaceNaNValues(std::vector& dataBuffer, std::vector& maskBuffer, int nDepth, int nCols, int nRows) { - if (nDim <= 0 || nCols <= 0 || nRows <= 0 || dataBuffer.size() != (size_t)nDim * nCols * nRows || maskBuffer.size() != (size_t)nCols * nRows) + if (nDepth <= 0 || nCols <= 0 || nRows <= 0 || dataBuffer.size() != (size_t)nDepth * nCols * nRows || maskBuffer.size() != (size_t)nCols * nRows) return false; - bool bIsFloat = (typeid(T) == typeid(float)); - const T noDataValue = (T)(bIsFloat ? -FLT_MAX : -DBL_MAX); + T noDataValue = 0; + { + #if defined _WIN32 + #pragma warning(disable: 4756) // would trigger build warning in linux + #endif + + noDataValue = (T)((typeid(T) == typeid(float)) ? -FLT_MAX : -DBL_MAX); + } for (size_t k = 0, i = 0; i < (size_t)nRows; i++) { - T* rowArr = &(dataBuffer[i * nCols * nDim]); + T* rowArr = &(dataBuffer[i * nCols * nDepth]); - for (size_t n = 0, j = 0; j < (size_t)nCols; j++, k++, n += nDim) + for (size_t n = 0, j = 0; j < (size_t)nCols; j++, k++, n += nDepth) { if (maskBuffer[k]) { int cntNaN = 0; - for (int m = 0; m < nDim; m++) + for (int m = 0; m < nDepth; m++) if (std::isnan((double)rowArr[n + m])) { cntNaN++; rowArr[n + m] = noDataValue; } - if (cntNaN == nDim) + if (cntNaN == nDepth) maskBuffer[k] = 0; } } @@ -714,4 +981,608 @@ } // -------------------------------------------------------------------------- ; + +ErrCode Lerc::GetRanges(const Byte* pLercBlob, unsigned int numBytesBlob, int iBand, + const struct Lerc2::HeaderInfo& lerc2Info, double* pMins, double* pMaxs, size_t nElem) +{ + const int nDepth = lerc2Info.nDepth; + + if (nDepth <= 0 || iBand < 0 || !pMins || !pMaxs) + return ErrCode::WrongParam; + + if (nElem < ((size_t)iBand + 1) * (size_t)nDepth) + return ErrCode::BufferTooSmall; + + if (nDepth == 1) + { + pMins[iBand] = lerc2Info.zMin; + pMaxs[iBand] = lerc2Info.zMax; + } + else + { + if (lerc2Info.bPassNoDataValues) // for nDepth > 1, and mix of valid and invalid values at same pixel, better fail + return ErrCode::HasNoData; // than return min or max values that contain noData (to be fixed in next codec version) + + // read header, mask, ranges, and copy them out + Lerc2 lerc2; + if (!lerc2.GetRanges(pLercBlob, numBytesBlob, &pMins[iBand * nDepth], &pMaxs[iBand * nDepth])) + return ErrCode::Failed; + } + + return ErrCode::Ok; +} + +// -------------------------------------------------------------------------- ; + +template +bool Lerc::RemapNoData(T* data, const BitMask& bitMask, const struct Lerc2::HeaderInfo& lerc2Info) +{ + int nCols = lerc2Info.nCols; + int nRows = lerc2Info.nRows; + int nDepth = lerc2Info.nDepth; + + if (!data || nCols <= 0 || nRows <= 0 || nDepth <= 0) + return false; + + const T noDataOld = (T)lerc2Info.noDataVal; + const T noDataNew = (T)lerc2Info.noDataValOrig; + + if (noDataNew != noDataOld) + { + bool bUseMask = (bitMask.GetWidth() == nCols) && (bitMask.GetHeight() == nRows); + + for (long k = 0, i = 0; i < nRows; i++) + { + T* rowArr = &(data[i * nCols * nDepth]); + + for (long n = 0, j = 0; j < nCols; j++, k++, n += nDepth) + if (!bUseMask || bitMask.IsValid(k)) + for (long m = 0; m < nDepth; m++) + if (rowArr[n + m] == noDataOld) + rowArr[n + m] = noDataNew; + } + } + + return true; +} + +// -------------------------------------------------------------------------- ; + +template +bool Lerc::DecodeAndCompareToInput(const Byte* pLercBlob, size_t blobSize, double maxZErr, Lerc2& lerc2Verify, + const T* pData, const Byte* pByteMask, const T* pDataOrig, const Byte* pByteMaskOrig, + bool bInputHasNoData, double origNoDataA, bool bModifiedMask) +{ + if (!pLercBlob || !pData || !pDataOrig) + return false; + + const Byte* bytePtr = pLercBlob; + size_t nBytesRemaining = blobSize; + + bool bHasMask(false); + Lerc2::HeaderInfo hd; + if (!Lerc2::GetHeaderInfo(bytePtr, nBytesRemaining, hd, bHasMask)) + return false; + + std::vector arrDec; + try + { + arrDec.assign(hd.nRows * hd.nCols * hd.nDepth, 0); + } + catch (...) + { + return false; + } + + BitMask bitMaskDec; + if (!bitMaskDec.SetSize(hd.nCols, hd.nRows)) + return false; + + bitMaskDec.SetAllInvalid(); + + if (!lerc2Verify.Decode(&bytePtr, nBytesRemaining, &arrDec[0], bitMaskDec.Bits())) + return false; + + // compare decoded bit mask and data array against the input to lerc encode (as after that orig input had the noData value remapped, NaN removed, bit mask altered) + { + bool bHasMaskBug(false); + double maxDelta = 0; + + for (int k = 0, i = 0; i < hd.nRows; i++) + for (int j = 0; j < hd.nCols; j++, k++) + if (bitMaskDec.IsValid(k)) + { + if (pByteMask && !pByteMask[k]) + bHasMaskBug = true; + + for (int n = k * hd.nDepth, m = 0; m < hd.nDepth; m++, n++) + { + double d = fabs((double)arrDec[n] - (double)pData[n]); + if (d > maxDelta) + maxDelta = d; + } + } + else if (!pByteMask || pByteMask[k]) + bHasMaskBug = true; + + if (bHasMaskBug || maxDelta > maxZErr * 1.1) // consider float rounding errors + return false; + } + + if (!bInputHasNoData && !bModifiedMask) // any NaN in the input means either modify the mask or replace it by noData value + return true; + + const bool bIsFltOrDbl = (typeid(T) == typeid(double) || typeid(T) == typeid(float)); + const bool bHaveNoDataVal = (hd.version >= 6 && hd.bPassNoDataValues && hd.nDepth > 1); + + if (bHaveNoDataVal && hd.noDataValOrig != origNoDataA) + return false; + + if (bHaveNoDataVal && hd.noDataVal != hd.noDataValOrig) + { + // remap the noData value from internal to orig + if (!RemapNoData(&arrDec[0], bitMaskDec, hd)) + return false; + } + + // compare the final result to orig bit mask and data array as it was handed to the Compress() function + { + T noDataOrig = (T)origNoDataA; + double maxDelta = 0; + bool bHasBug(false); + + for (int k = 0, i = 0; i < hd.nRows; i++) + for (int j = 0; j < hd.nCols; j++, k++) + if (!pByteMaskOrig || pByteMaskOrig[k]) + { + if (!bitMaskDec.IsValid(k)) // then the orig data values must be noData or NaN + { + for (int n = k * hd.nDepth, m = 0; m < hd.nDepth; m++, n++) + { + T zOrig = pDataOrig[n]; + bool bIsNoData = (bInputHasNoData && zOrig == noDataOrig) || (bIsFltOrDbl && std::isnan((double)zOrig)); + + if (!bIsNoData) + bHasBug = true; + } + } + else // valid pixel + { + for (int n = k * hd.nDepth, m = 0; m < hd.nDepth; m++, n++) + { + T zOrig = pDataOrig[n]; + T z = arrDec[n]; + + if (z == zOrig) // valid value or noData + continue; + + if (bIsFltOrDbl && std::isnan((double)zOrig)) + zOrig = noDataOrig; + + if (bInputHasNoData && (z == noDataOrig || zOrig == noDataOrig) && (z != zOrig)) + bHasBug = true; + + if (!bHaveNoDataVal || z != noDataOrig) // is valid + { + double d = fabs((double)z - (double)zOrig); + if (d > maxDelta) + maxDelta = d; + } + } + } + } + else if (bitMaskDec.IsValid(k)) + bHasBug = true; + + if (bHasBug || maxDelta > maxZErr * 1.1) // consider float rounding errors + return false; + } + + return true; +} + +// -------------------------------------------------------------------------- ; + +template +bool Lerc::GetTypeRange(const T, std::pair& range) +{ + range.first = 0; + + if (typeid(T) == typeid(Byte)) + range.second = UCHAR_MAX; + else if (typeid(T) == typeid(unsigned short)) + range.second = USHRT_MAX; + else if (typeid(T) == typeid(unsigned int) || typeid(T) == typeid(unsigned long)) + range.second = UINT_MAX; + + else if (typeid(T) == typeid(signed char)) + range = std::pair(CHAR_MIN, CHAR_MAX); + else if (typeid(T) == typeid(short)) + range = std::pair(SHRT_MIN, SHRT_MAX); + else if (typeid(T) == typeid(int) || typeid(T) == typeid(long)) + range = std::pair(INT_MIN, INT_MAX); + else + return false; + + return true; +} + +// -------------------------------------------------------------------------- ; + +template +ErrCode Lerc::FilterNoData(std::vector& dataBuffer, std::vector& maskBuffer, int nDepth, int nCols, int nRows, + double& maxZError, bool bPassNoDataValue, double& noDataValue, bool& bModifiedMask, bool& bNeedNoData) +{ + if (nDepth <= 0 || nCols <= 0 || nRows <= 0 || maxZError < 0) + return ErrCode::WrongParam; + + if ((dataBuffer.size() != (size_t)nDepth * nCols * nRows) || (maskBuffer.size() != (size_t)nCols * nRows)) + return ErrCode::Failed; + + bModifiedMask = false; + bNeedNoData = false; + + if (!bPassNoDataValue) // nothing to do + return ErrCode::Ok; + + std::pair typeRange; + if (!GetTypeRange(dataBuffer[0], typeRange)) + return ErrCode::Failed; + + if (noDataValue < typeRange.first || noDataValue > typeRange.second) + return ErrCode::WrongParam; + + T origNoData = (T)noDataValue; + + double minVal = DBL_MAX; + double maxVal = -DBL_MAX; + + // check for noData in valid pixels + for (int k = 0, i = 0; i < nRows; i++) + { + T* rowArr = &(dataBuffer[i * nCols * nDepth]); + + for (int n = 0, j = 0; j < nCols; j++, k++, n += nDepth) + if (maskBuffer[k]) + { + int cntInvalid = 0; + + for (int m = 0; m < nDepth; m++) + { + T z = rowArr[n + m]; + + if (z == origNoData) + cntInvalid++; + else if (z < minVal) + minVal = z; + else if (z > maxVal) + maxVal = z; + } + + if (cntInvalid == nDepth) + { + maskBuffer[k] = 0; + bModifiedMask = true; + } + else if (cntInvalid > 0) // found mix of valid and invalid values at the same pixel + bNeedNoData = true; + } + } + + double maxZErrL = (std::max)(0.5, floor(maxZError)); // same mapping for int types as in Lerc2.cpp + double dist = floor(maxZErrL); + + // check the orig noData value is far enough away from the valid range + if ((origNoData >= minVal - dist) && (origNoData <= maxVal + dist)) + { + maxZError = 0.5; // fall back to int lossless + return ErrCode::Ok; + } + + if (bNeedNoData) + { + double minDist = floor(maxZErrL) + 1; + double remapVal = minVal - minDist; + T newNoData = origNoData; + + if (remapVal >= typeRange.first) + { + newNoData = (T)remapVal; + } + else + { + maxZErrL = 0.5; // repeat with int lossless + remapVal = minVal - 1; + + if (remapVal >= typeRange.first) + { + newNoData = (T)remapVal; + } + else + { + remapVal = maxVal + 1; // try to map closer to valid range from top + + if ((remapVal <= typeRange.second) && (remapVal < origNoData)) + newNoData = (T)remapVal; + } + } + + if (newNoData != origNoData) + { + for (int k = 0, i = 0; i < nRows; i++) + { + T* rowArr = &(dataBuffer[i * nCols * nDepth]); + + for (int n = 0, j = 0; j < nCols; j++, k++, n += nDepth) + if (maskBuffer[k]) + for (int m = 0; m < nDepth; m++) + if (rowArr[n + m] == origNoData) + rowArr[n + m] = newNoData; + } + + noDataValue = newNoData; + } + } + + if (maxZError != maxZErrL) + maxZError = maxZErrL; + + return ErrCode::Ok; +} + +// -------------------------------------------------------------------------- ; + +template +ErrCode Lerc::FilterNoDataAndNaN(std::vector& dataBuffer, std::vector& maskBuffer, int nDepth, int nCols, int nRows, + double& maxZError, bool bPassNoDataValue, double& noDataValue, bool& bModifiedMask, bool& bNeedNoData, bool& bIsFltDblAllInt) +{ + if (nDepth <= 0 || nCols <= 0 || nRows <= 0 || maxZError < 0) + return ErrCode::WrongParam; + + if ((dataBuffer.size() != (size_t)nDepth * nCols * nRows) || (maskBuffer.size() != (size_t)nCols * nRows)) + return ErrCode::Failed; + + if (typeid(T) != typeid(double) && typeid(T) != typeid(float)) // only for float or double + return ErrCode::Failed; + + bModifiedMask = false; + bNeedNoData = false; + bIsFltDblAllInt = false; + + bool bHasNoDataValuesLeft = false; + bool bIsFloat4 = (typeid(T) == typeid(float)); + bool bAllInt = true; + bool bHasNaN = false; + + T origNoData(0); + + if (bPassNoDataValue) + { + if (bIsFloat4 && (noDataValue < -FLT_MAX || noDataValue > FLT_MAX)) + return ErrCode::WrongParam; + + origNoData = (T)noDataValue; + } + else + origNoData = (T)(bIsFloat4 ? -FLT_MAX : -DBL_MAX); + + // only treat int values as int's if within this range, incl orig noData + const double lowIntLimit = (double)(bIsFloat4 ? -((long)1 << 23) : -((int64_t)1 << 53)); + const double highIntLimit = (double)(bIsFloat4 ? ((long)1 << 23) : ((int64_t)1 << 53)); + + double minVal = DBL_MAX; + double maxVal = -DBL_MAX; + long cntValidPixels = 0; + + // check for NaN or noData in valid pixels + for (int k = 0, i = 0; i < nRows; i++) + { + T* rowArr = &(dataBuffer[i * nCols * nDepth]); + + for (int n = 0, j = 0; j < nCols; j++, k++, n += nDepth) + if (maskBuffer[k]) + { + cntValidPixels++; + int cntInvalidValues = 0; + + for (int m = 0; m < nDepth; m++) + { + T& zVal = rowArr[n + m]; + + if (std::isnan((double)zVal)) + { + bHasNaN = true; + cntInvalidValues++; + + if (bPassNoDataValue && nDepth > 1) + zVal = origNoData; // replace NaN + } + else if (bPassNoDataValue && zVal == origNoData) + { + cntInvalidValues++; + } + else + { + if (zVal < minVal) + minVal = zVal; + else if (zVal > maxVal) + maxVal = zVal; + + if (bAllInt && !IsInt(zVal)) + bAllInt = false; + } + } + + if (cntInvalidValues == nDepth) + { + maskBuffer[k] = 0; + bModifiedMask = true; + } + else if (cntInvalidValues > 0) // found mix of valid and invalid values at the same pixel + bHasNoDataValuesLeft = true; + } + } + + bNeedNoData = bHasNoDataValuesLeft; + + if (cntValidPixels == 0) + bAllInt = false; + + if (bHasNaN && nDepth > 1 && bHasNoDataValuesLeft && !bPassNoDataValue) + { + return ErrCode::NaN; // Lerc cannot handle this case, cannot pick a noData value on the tile level + } + + // now NaN's are gone, either moved to the mask or replaced by noData value + + double maxZErrL = maxZError; + + if (bAllInt) + { + bAllInt &= (minVal >= lowIntLimit) && (minVal <= highIntLimit) + && (maxVal >= lowIntLimit) && (maxVal <= highIntLimit); + + if (bHasNoDataValuesLeft) + bAllInt &= IsInt(origNoData) && (origNoData >= lowIntLimit) && (origNoData <= highIntLimit); + + if (bAllInt) + maxZErrL = (std::max)(0.5, floor(maxZError)); // same mapping for int types as in Lerc2.cpp + } + + bIsFltDblAllInt = bAllInt; + + if (maxZErrL == 0) // if flt lossless, we are done + return ErrCode::Ok; + + if (bPassNoDataValue) + { + // check the orig noData value is far enough away from the valid range + double dist = bAllInt ? floor(maxZErrL) : 2 * maxZErrL; + + if ((origNoData >= minVal - dist) && (origNoData <= maxVal + dist)) + { + maxZError = bAllInt ? 0.5 : 0; // fall back to lossless + return ErrCode::Ok; + } + } + + if (bHasNoDataValuesLeft) // try to remap noData to new noData just below the global minimum + { + T remapVal = origNoData; + bool bRemapNoData = FindNewNoDataBelowValidMin(minVal, maxZErrL, bAllInt, lowIntLimit, remapVal); + + + //T remapVal = (T)(minVal - max(4 * maxZErrL, 1.0)); // leave some safety margin + //T lowestVal = (T)(bIsFloat4 ? -FLT_MAX : -DBL_MAX); + //bool bRemapNoData = (remapVal > lowestVal) && (remapVal < (T)(minVal - 2 * maxZErrL)); + + //if (bAllInt) + //{ + // bRemapNoData &= IsInt(remapVal) && (remapVal >= lowIntLimit); // prefer all int over remap + //} + //else if (!bRemapNoData) + //{ + // // if minVal is a big number + // remapVal = (T)((minVal > 0) ? minVal / 2 : minVal * 2); // try bigger gap + // bRemapNoData = (remapVal > lowestVal) && (remapVal < (T)(minVal - 2 * maxZErrL)); + //} + + + if (bRemapNoData) + { + if (remapVal != origNoData) + { + for (int k = 0, i = 0; i < nRows; i++) + { + T* rowArr = &(dataBuffer[i * nCols * nDepth]); + + for (int n = 0, j = 0; j < nCols; j++, k++, n += nDepth) + if (maskBuffer[k]) + for (int m = 0; m < nDepth; m++) + if (rowArr[n + m] == origNoData) + rowArr[n + m] = remapVal; + } + + noDataValue = remapVal; + } + } + else if ((double)origNoData >= minVal) // sufficient distance of origNoData to valid range has been checked above + { + maxZErrL = bAllInt ? 0.5 : 0; // need lossless if noData cannot be mapped below valid range + } + } + + if (maxZError != maxZErrL) + maxZError = maxZErrL; + + return ErrCode::Ok; +} + +// -------------------------------------------------------------------------- ; + +template +bool Lerc::FindNewNoDataBelowValidMin(double minVal, double maxZErr, bool bAllInt, double lowIntLimit, T& newNoDataVal) +{ + if (bAllInt) + { + std::vector noDataCandVec; + + { // collect dist candidates + std::vector distCandVec = { 4 * maxZErr, 1, 10, 100, 1000, 10000 }; // only int candidates + + for (double dist : distCandVec) + noDataCandVec.push_back((T)(minVal - dist)); + + double candForLargeMinVal = (minVal > 0 ? floor(minVal / 2) : minVal * 2); // also int + noDataCandVec.push_back((T)candForLargeMinVal); + } + + // sort them in descending order + std::sort(noDataCandVec.begin(), noDataCandVec.end(), std::greater()); + + // take the first one that satisfies the condition + for (T noDataVal : noDataCandVec) + { + if ((noDataVal > (T)lowIntLimit) && (noDataVal < (T)(minVal - 2 * maxZErr)) && IsInt(noDataVal)) + { + newNoDataVal = noDataVal; + return true; + } + } + } + else + { + std::vector noDataCandVec; + + { // dist candidates + std::vector distCandVec = { 4 * maxZErr, 0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 10000 }; + + for (double dist : distCandVec) + noDataCandVec.push_back((T)(minVal - dist)); + + double candForLargeMinVal = (minVal > 0 ? minVal / 2 : minVal * 2); + noDataCandVec.push_back((T)candForLargeMinVal); + } + + // sort them in descending order + std::sort(noDataCandVec.begin(), noDataCandVec.end(), std::greater()); + + bool bIsFloat4 = (typeid(T) == typeid(float)); + T lowestVal = (T)(bIsFloat4 ? -FLT_MAX : -DBL_MAX); + + // take the first one that satisfies the condition + for (T noDataVal : noDataCandVec) + { + if ((noDataVal > lowestVal) && (noDataVal < (T)(minVal - 2 * maxZErr))) + { + newNoDataVal = noDataVal; + return true; + } + } + } + + return false; +} + +// -------------------------------------------------------------------------- ; diff -Nru lerc-3.0+ds/src/LercLib/Lerc.h lerc-4.0.0+ds/src/LercLib/Lerc.h --- lerc-3.0+ds/src/LercLib/Lerc.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/Lerc.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -52,7 +52,7 @@ // Encode // if more than 1 band, the outgoing Lerc blob has the single band Lerc blobs concatenated; - // or, if you have multiple values per pixel and stored as [RGB, RGB, ... ], then set nDim accordingly (e.g., 3) + // or, if you have multiple values per pixel and stored as [RGB, RGB, ... ], then set nDepth accordingly (e.g., 3) // computes the number of bytes needed to allocate the buffer, accurate to the byte; // does not encode the image data, but uses statistics and formulas to compute the buffer size needed; @@ -62,24 +62,26 @@ static ErrCode ComputeCompressedSize( const void* pData, // raw image data, row by row, band by band - int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4 + int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4, 5 = v2.5 DataType dt, // data type, char to double - int nDim, // number of values per pixel + int nDepth, // number of values per pixel int nCols, // number of cols int nRows, // number of rows int nBands, // number of bands int nMasks, // number of masks (0, 1, or nBands) const Byte* pValidBytes, // masks (size = nMasks * nRows * nCols) double maxZErr, // max coding error per pixel, defines the precision - unsigned int& numBytesNeeded); // size of outgoing Lerc blob + unsigned int& numBytesNeeded, // size of outgoing Lerc blob + const unsigned char* pUsesNoData,// if there are invalid values not marked by the mask, pass an array of size nBands, 1 - uses noData, 0 - not + const double* noDataValues); // same, pass an array of size nBands with noData value per band, or pass nullptr // encodes or compresses the image data into the buffer static ErrCode Encode( const void* pData, // raw image data, row by row, band by band - int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4 + int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4, 5 = v2.5 DataType dt, // data type, char to double - int nDim, // number of values per pixel + int nDepth, // number of values per pixel int nCols, // number of cols int nRows, // number of rows int nBands, // number of bands @@ -88,21 +90,23 @@ double maxZErr, // max coding error per pixel, defines the precision Byte* pBuffer, // buffer to write to, function fails if buffer too small unsigned int numBytesBuffer, // buffer size - unsigned int& numBytesWritten); // num bytes written to buffer - + unsigned int& numBytesWritten, // num bytes written to buffer + const unsigned char* pUsesNoData,// if there are invalid values not marked by the mask, pass an array of size nBands, 1 - uses noData, 0 - not + const double* noDataValues); // same, pass an array of size nBands with noData value per band, or pass nullptr // Decode struct LercInfo { - int version, // Lerc version number (0 for old Lerc1, 1 to 4 for Lerc 2.1 to 2.4) - nDim, // number of values per pixel + int version, // Lerc version number (0 for old Lerc1, 1 to 5 for Lerc 2.1 to 2.5) + nDepth, // number of values per pixel nCols, // number of columns nRows, // number of rows numValidPixel, // number of valid pixels nBands, // number of bands blobSize, // total blob size in bytes - nMasks; // number of masks (0, 1, or nBands) + nMasks, // number of masks (0, 1, or nBands) + nUsesNoDataValue; // 0 - no noData value used, nBands - noData value used in 1 or more bands (only possible for nDepth > 1) DataType dt; // data type (float only for old Lerc1) double zMin, // min pixel value, over all data values zMax, // max pixel value, over all data values @@ -126,8 +130,11 @@ // If in doubt, check the code in Lerc::GetLercInfo(...) for the exact logic. static ErrCode GetLercInfo(const Byte* pLercBlob, // Lerc blob to decode - unsigned int numBytesBlob, // size of Lerc blob in bytes - struct LercInfo& lercInfo); + unsigned int numBytesBlob, // size of Lerc blob in bytes + struct LercInfo& lercInfo, + double* pMins = nullptr, // pass array of size (nDepth * nBands) to get the min values per dimension and band + double* pMaxs = nullptr, // same as pMins, to get the max values + size_t nElem = 0); // (nDepth * nBands), if passed // setup outgoing arrays accordingly, then call Decode() @@ -136,18 +143,19 @@ unsigned int numBytesBlob, // size of Lerc blob in bytes int nMasks, // number of masks (0, 1, or nBands) Byte* pValidBytes, // masks (fails if not big enough to take the masks decoded, fills with 1 if all valid) - int nDim, // number of values per pixel + int nDepth, // number of values per pixel int nCols, // number of cols int nRows, // number of rows int nBands, // number of bands DataType dt, // data type of outgoing array - void* pData); // outgoing data bands - + void* pData, // outgoing data bands + unsigned char* pUsesNoData, // pass an array of size nBands, 1 - band uses noData, 0 - not + double* noDataValues); // same, pass an array of size nBands to get the noData value per band, if any static ErrCode ConvertToDouble( const void* pDataIn, // pixel data of image tile of data type dt (< double) DataType dt, // data type of input data - size_t nDataValues, // total number of data values (nDim * nCols * nRows * nBands) + size_t nDataValues, // total number of data values (nDepth * nCols * nRows * nBands) double* pDataOut); // pixel data converted to double @@ -155,20 +163,22 @@ template static ErrCode ComputeCompressedSizeTempl( const T* pData, // raw image data, row by row, band by - int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4 - int nDim, // number of values per pixel + int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4, 5 = v2.5 + int nDepth, // number of values per pixel int nCols, // number of cols int nRows, // number of rows int nBands, // number of bands int nMasks, // number of masks (0, 1, or nBands) const Byte* pValidBytes, // masks (size = nMasks * nRows * nCols) double maxZErr, // max coding error per pixel, defines the precision - unsigned int& numBytes); // size of outgoing Lerc blob + unsigned int& numBytes, // size of outgoing Lerc blob + const unsigned char* pUsesNoData,// if there are invalid values not marked by the mask, pass an array of size nBands, 1 - uses noData, 0 - not + const double* noDataValues); // same, pass an array of size nBands with noData value per band, or pass nullptr template static ErrCode EncodeTempl( const T* pData, // raw image data, row by row, band by band - int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4 - int nDim, // number of values per pixel + int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4 5 = v2.5 + int nDepth, // number of values per pixel int nCols, // number of cols int nRows, // number of rows int nBands, // number of bands @@ -177,25 +187,29 @@ double maxZErr, // max coding error per pixel, defines the precision Byte* pBuffer, // buffer to write to, function will fail if buffer too small unsigned int numBytesBuffer, // buffer size - unsigned int& numBytesWritten); // num bytes written to buffer + unsigned int& numBytesWritten, // num bytes written to buffer + const unsigned char* pUsesNoData,// if there are invalid values not marked by the mask, pass an array of size nBands, 1 - uses noData, 0 - not + const double* noDataValues); // same, pass an array of size nBands with noData value per band, or pass nullptr template static ErrCode DecodeTempl( T* pData, // outgoing data bands const Byte* pLercBlob, // Lerc blob to decode unsigned int numBytesBlob, // size of Lerc blob in bytes - int nDim, // number of values per pixel + int nDepth, // number of values per pixel int nCols, // number of cols int nRows, // number of rows int nBands, // number of bands int nMasks, // number of masks (0, 1, or nBands) - Byte* pValidBytes); // masks (fails if not big enough to take the masks decoded, fills with 1 if all valid) + Byte* pValidBytes, // masks (fails if not big enough to take the masks decoded, fills with 1 if all valid) + unsigned char* pUsesNoData, // pass an array of size nBands, 1 - band uses noData, 0 - not + double* noDataValues); // same, pass an array of size nBands to get the noData value per band, if any private: - template static ErrCode EncodeInternal( + template static ErrCode EncodeInternal_v5( const T* pData, // raw image data, row by row, band by band - int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4 - int nDim, // number of values per pixel + int version, // 2 = v2.2, 3 = v2.3, 4 = v2.4, 5 = v2.5 + int nDepth, // number of values per pixel int nCols, // number of cols int nRows, // number of rows int nBands, // number of bands @@ -207,19 +221,64 @@ unsigned int numBytesBuffer, // buffer size unsigned int& numBytesWritten); // num bytes written to buffer + template static ErrCode EncodeInternal( + const T* pData, // raw image data, row by row, band by band + int version, // 6 = v2.6 (or -1 for current) + int nDepth, // number of values per pixel + int nCols, // number of cols + int nRows, // number of rows + int nBands, // number of bands + int nMasks, // number of masks (0, 1, or nBands) + const Byte* pValidBytes, // masks (size = nMasks * nRows * nCols) + double maxZErr, // max coding error per pixel, defines the precision + unsigned int& numBytes, // size of outgoing Lerc blob + Byte* pBuffer, // buffer to write to, function will fail if buffer too small + unsigned int numBytesBuffer, // buffer size + unsigned int& numBytesWritten, // num bytes written to buffer + const unsigned char* pUsesNoData,// if there are invalid values not marked by the mask, pass an array of size nBands, 1 - uses noData, 0 - not + const double* noDataValues); // same, pass an array of size nBands with noData value per band, or pass nullptr + #ifdef HAVE_LERC1_DECODE template static bool Convert(const CntZImage& zImg, T* arr, Byte* pByteMask, bool bMustFillMask); #endif template static ErrCode ConvertToDoubleTempl(const T* pDataIn, size_t nDataValues, double* pDataOut); - template static ErrCode CheckForNaN(const T* arr, int nDim, int nCols, int nRows, const Byte* pByteMask); + template static ErrCode CheckForNaN(const T* arr, int nDepth, int nCols, int nRows, const Byte* pByteMask); - template static bool ReplaceNaNValues(std::vector& dataBuffer, std::vector& maskBuffer, int nDim, int nCols, int nRows); + template static bool ReplaceNaNValues(std::vector& dataBuffer, std::vector& maskBuffer, int nDepth, int nCols, int nRows); template static bool Resize(std::vector& buffer, size_t nElem); - bool static Convert(const Byte* pByteMask, int nCols, int nRows, BitMask& bitMask); - bool static Convert(const BitMask& bitMask, Byte* pByteMask); - bool static MasksDiffer(const Byte* p0, const Byte* p1, size_t n); + static bool Convert(const Byte* pByteMask, int nCols, int nRows, BitMask& bitMask); + static bool Convert(const BitMask& bitMask, Byte* pByteMask); + static bool MasksDiffer(const Byte* p0, const Byte* p1, size_t n); + + static ErrCode GetRanges(const Byte* pLercBlob, unsigned int numBytesBlob, int iBand, + const struct Lerc2::HeaderInfo& lerc2Info, double* pMins, double* pMaxs, size_t nElem); + + template + static bool RemapNoData(T* data, const BitMask& bitMask, const struct Lerc2::HeaderInfo& lerc2Info); + + template + static bool DecodeAndCompareToInput(const Byte* pLercBlob, size_t blobSize, double maxZErr, Lerc2& lerc2Verify, + const T* pData, const Byte* pByteMask, const T* pDataOrig, const Byte* pByteMaskOrig, + bool bInputHasNoData, double origNoDataA, bool bModifiedMask); + + template + static bool GetTypeRange(const T, std::pair& range); + + template + inline static bool IsInt(T z) { return(z == (T)floor((double)z + 0.5)); }; + + template + static ErrCode FilterNoData(std::vector& dataBuffer, std::vector& maskBuffer, int nDepth, int nCols, int nRows, + double& maxZError, bool bPassNoDataValue, double& noDataValue, bool& bModifiedMask, bool& bNeedNoData); + + template + static ErrCode FilterNoDataAndNaN(std::vector& dataBuffer, std::vector& maskBuffer, int nDepth, int nCols, int nRows, + double& maxZError, bool bPassNoDataValue, double& noDataValue, bool& bModifiedMask, bool& bNeedNoData, bool& bIsFltDblAllInt); + + template + static bool FindNewNoDataBelowValidMin(double minVal, double maxZErr, bool bAllInt, double lowIntLimit, T& newNoDataVal); }; NAMESPACE_LERC_END diff -Nru lerc-3.0+ds/src/LercLib/RLE.cpp lerc-4.0.0+ds/src/LercLib/RLE.cpp --- lerc-3.0+ds/src/LercLib/RLE.cpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/RLE.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff -Nru lerc-3.0+ds/src/LercLib/RLE.h lerc-4.0.0+ds/src/LercLib/RLE.h --- lerc-3.0+ds/src/LercLib/RLE.h 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercLib/RLE.h 2022-07-15 18:25:29.000000000 +0000 @@ -1,5 +1,5 @@ /* -Copyright 2015 Esri +Copyright 2015 - 2022 Esri Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff -Nru lerc-3.0+ds/src/LercTest/main.cpp lerc-4.0.0+ds/src/LercTest/main.cpp --- lerc-3.0+ds/src/LercTest/main.cpp 2021-07-30 18:51:59.000000000 +0000 +++ lerc-4.0.0+ds/src/LercTest/main.cpp 2022-07-15 18:25:29.000000000 +0000 @@ -1,515 +1,694 @@ #include #include -#include #include #include #include +#include #include +#include #include "../LercLib/include/Lerc_c_api.h" -//#include "../LercLib/include/Lerc_types.h" // see for error codes, data types, etc +#include "../LercLib/include/Lerc_types.h" // see for error codes, data types, etc using namespace std; using namespace std::chrono; //#define TestLegacyData +//#define DumpDecodedTiles +//#define CompareAgainstDumpedTiles typedef unsigned char Byte; // convenience typedef unsigned int uint32; +enum lerc_DataType { dt_char = 0, dt_uchar, dt_short, dt_ushort, dt_int, dt_uint, dt_float, dt_double }; // shorter //----------------------------------------------------------------------------- +// some helper functions +//----------------------------------------------------------------------------- -enum lerc_DataType { dt_char = 0, dt_uchar, dt_short, dt_ushort, dt_int, dt_uint, dt_float, dt_double }; +void BlobInfo_Print(const uint32* infoArr); +bool BlobInfo_Equal(const uint32* infoArr, uint32 nDepth, uint32 nCols, uint32 nRows, uint32 nBands, uint32 dataType); -void BlobInfo_Print(const uint32* infoArr) -{ - const uint32* ia = infoArr; - printf("version = %d, dataType = %d, nDim = %d, nCols = %d, nRows = %d, nBands = %d, nValidPixels = %d, blobSize = %d, nMasks = %d\n", - ia[0], ia[1], ia[2], ia[3], ia[4], ia[5], ia[6], ia[7], ia[8]); -} +#ifdef TestLegacyData +bool ReadListFile(const string& listFn, vector& fnVec); +bool ReadFile(const string& fn, Byte* pBuffer, size_t bufferSize, size_t& nBytesRead); +bool WriteDecodedTile(const string& fn, int nMasks, const Byte* pValidBytes, int nDepth, int w, int h, int nBands, int bpp, const void* pArr); +bool ReadDecodedTile(const string& fn, int& nMasks, Byte* pValidBytes, int& nDepth, int& w, int& h, int& nBands, int& bpp, void* pArr); -bool BlobInfo_Equal(const uint32* infoArr, uint32 nDim, uint32 nCols, uint32 nRows, uint32 nBands, uint32 dataType) -{ - const uint32* ia = infoArr; - return ia[1] == dataType && ia[2] == nDim && ia[3] == nCols && ia[4] == nRows && ia[5] == nBands; -} +bool CompareTwoTiles(int nMasks, const Byte* pValidBytes, int nDepth, int w, int h, int nBands, int bpp, const void* pArr, + int nMasks2, const Byte* pValidBytes2, int nDepth2, int w2, int h2, int nBands2, int bpp2, const void* pArr2, int dt); +#endif //----------------------------------------------------------------------------- +// main +//----------------------------------------------------------------------------- int main(int argc, char* arcv[]) { + const int infoArrSize = (int)LercNS::InfoArrOrder::_last; + const int dataRangeArrSize = (int)LercNS::DataRangeArrOrder::_last; + lerc_status hr(0); + uint32 infoArr[infoArrSize]; + double dataRangeArr[dataRangeArrSize]; + high_resolution_clock::time_point t0, t1; - // Sample 1: float image, 1 band, with some pixels set to invalid / void, maxZError = 0.1 +#ifndef TestLegacyData - int h = 512; - int w = 512; + //--------------------------------------------------------------------------- - float* zImg = new float[w * h]; - memset(zImg, 0, w * h * sizeof(float)); + // Sample 1: float image, 1 band, with some pixels set to invalid / void, maxZError = 0.1 + { + int h = 512; + int w = 512; - Byte* maskByteImg = new Byte[w * h]; - memset(maskByteImg, 0, w * h); + float* zImg = new float[w * h]; + memset(zImg, 0, w * h * sizeof(float)); - for (int k = 0, i = 0; i < h; i++) - { - for (int j = 0; j < w; j++, k++) - { - zImg[k] = sqrt((float)(i * i + j * j)); // smooth surface - zImg[k] += rand() % 20; // add some small amplitude noise + Byte* maskByteImg = new Byte[w * h]; + memset(maskByteImg, 1, w * h); - //if (i == 99 && j == 99) - // zImg[k] = NAN; + for (int k = 0, i = 0; i < h; i++) + for (int j = 0; j < w; j++, k++) + { + zImg[k] = sqrt((float)(i * i + j * j)); // smooth surface + zImg[k] += rand() % 20; // add some small amplitude noise - if (j % 100 == 0 || i % 100 == 0) // set some void points - maskByteImg[k] = 0; - else - maskByteImg[k] = 1; - } - } + //if (i == 99 && j == 99) // to test presence of NaN + // zImg[k] = NAN; + if (j % 100 == 0 || i % 100 == 0) // set some void pixels + maskByteImg[k] = 0; + } - // compress into byte arr + // compress into byte arr - double maxZErrorWanted = 0.1; - double eps = 0.0001; // safety margin (optional), to account for finite floating point accuracy - double maxZError = maxZErrorWanted - eps; + double maxZErrorWanted = 0.1; + double eps = 0.0001; // safety margin (optional), to account for finite floating point accuracy + double maxZError = maxZErrorWanted - eps; + + uint32 numBytesNeeded = 0; + uint32 numBytesWritten = 0; + + // optional, can call encode() directly with large enough blob buffer + + hr = lerc_computeCompressedSize((void*)zImg, // raw image data, row by row, band by band + (uint32)dt_float, 1, w, h, 1, + 1, // nMasks, added in Lerc 3.0 to allow for different masks per band + maskByteImg, // can pass nullptr if all pixels are valid and nMasks = 0 + maxZError, // max coding error per pixel, or precision + &numBytesNeeded); // size of outgoing Lerc blob - uint32 numBytesNeeded = 0; - uint32 numBytesWritten = 0; + if (hr) + cout << "lerc_computeCompressedSize(...) failed" << endl; - hr = lerc_computeCompressedSize((void*)zImg, // raw image data, row by row, band by band - (uint32)dt_float, 1, w, h, 1, - 1, // nMasks, added in Lerc 3.0 to allow for different masks per band - maskByteImg, // can pass nullptr if all pixels are valid and nMasks = 0 - maxZError, // max coding error per pixel, or precision - &numBytesNeeded); // size of outgoing Lerc blob + uint32 numBytesBlob = numBytesNeeded; + Byte* pLercBlob = new Byte[numBytesBlob]; - if (hr) - cout << "lerc_computeCompressedSize(...) failed" << endl; + t0 = high_resolution_clock::now(); - uint32 numBytesBlob = numBytesNeeded; - Byte* pLercBlob = new Byte[numBytesBlob]; + hr = lerc_encode((void*)zImg, // raw image data, row by row, band by band + (uint32)dt_float, 1, w, h, 1, + 1, // nMasks, added in Lerc 3.0 to allow for different masks per band + maskByteImg, // can pass nullptr if all pixels are valid and nMasks = 0 + maxZError, // max coding error per pixel, or precision + pLercBlob, // buffer to write to, function will fail if buffer too small + numBytesBlob, // buffer size + &numBytesWritten); // num bytes written to buffer - high_resolution_clock::time_point t0 = high_resolution_clock::now(); + if (hr) + cout << "lerc_encode(...) failed" << endl; - hr = lerc_encode((void*)zImg, // raw image data, row by row, band by band - (uint32)dt_float, 1, w, h, 1, - 1, // nMasks, added in Lerc 3.0 to allow for different masks per band - maskByteImg, // can pass nullptr if all pixels are valid and nMasks = 0 - maxZError, // max coding error per pixel, or precision - pLercBlob, // buffer to write to, function will fail if buffer too small - numBytesBlob, // buffer size - &numBytesWritten); // num bytes written to buffer + t1 = high_resolution_clock::now(); + auto duration = duration_cast(t1 - t0).count(); - if (hr) - cout << "lerc_encode(...) failed" << endl; + double ratio = w * h * (0.125 + sizeof(float)) / numBytesBlob; + cout << "sample 1 compression ratio = " << ratio << ", encode time = " << duration << " ms" << endl; - high_resolution_clock::time_point t1 = high_resolution_clock::now(); - auto duration = duration_cast(t1 - t0).count(); + // decompress + if (hr = lerc_getBlobInfo(pLercBlob, numBytesBlob, infoArr, dataRangeArr, infoArrSize, dataRangeArrSize)) + cout << "lerc_getBlobInfo(...) failed" << endl; - double ratio = w * h * (0.125 + sizeof(float)) / numBytesBlob; - cout << "sample 1 compression ratio = " << ratio << ", encode time = " << duration << " ms" << endl; + BlobInfo_Print(infoArr); + if (!BlobInfo_Equal(infoArr, 1, w, h, 1, (uint32)dt_float)) + cout << "got wrong lerc info" << endl; - // decompress + // new empty data storage + float* zImg2 = new float[w * h]; + memset(zImg2, 0, w * h * sizeof(float)); - uint32 infoArr[10]; - double dataRangeArr[3]; - hr = lerc_getBlobInfo(pLercBlob, numBytesBlob, infoArr, dataRangeArr, 10, 3); - if (hr) - cout << "lerc_getBlobInfo(...) failed" << endl; + Byte* maskByteImg2 = new Byte[w * h]; + memset(maskByteImg2, 0, w * h); - BlobInfo_Print(infoArr); + t0 = high_resolution_clock::now(); - if (!BlobInfo_Equal(infoArr, 1, w, h, 1, (uint32)dt_float)) - cout << "got wrong lerc info" << endl; + hr = lerc_decode(pLercBlob, numBytesBlob, + 1, // nMasks, added in Lerc 3.0 to allow for different masks per band (get it from infoArr via lerc_getBlobInfo(...)) + maskByteImg2, 1, w, h, 1, (uint32)dt_float, (void*)zImg2); - // new empty data storage - float* zImg3 = new float[w * h]; - memset(zImg3, 0, w * h * sizeof(float)); + if (hr) + cout << "lerc_decode(...) failed" << endl; - Byte* maskByteImg3 = new Byte[w * h]; - memset(maskByteImg3, 0, w * h); + t1 = high_resolution_clock::now(); + duration = duration_cast(t1 - t0).count(); - t0 = high_resolution_clock::now(); + // compare to orig - hr = lerc_decode(pLercBlob, numBytesBlob, - 1, // nMasks, added in Lerc 3.0 to allow for different masks per band (get it from infoArr via lerc_getBlobInfo(...)) - maskByteImg3, 1, w, h, 1, (uint32)dt_float, (void*)zImg3); + double maxDelta = 0; + for (int k = 0, i = 0; i < h; i++) + for (int j = 0; j < w; j++, k++) + { + if (maskByteImg2[k] != maskByteImg[k]) + cout << "Error in main: decoded valid bytes differ from encoded valid bytes" << endl; - if (hr) - cout << "lerc_decode(...) failed" << endl; + if (maskByteImg2[k]) + { + double delta = abs((double)zImg2[k] - (double)zImg[k]); + if (delta > maxDelta) + maxDelta = delta; + } + } - t1 = high_resolution_clock::now(); - duration = duration_cast(t1 - t0).count(); + cout << "max z error per pixel = " << maxDelta << ", decode time = " << duration << " ms" << endl; + cout << endl; + delete[] zImg; + delete[] zImg2; + delete[] maskByteImg; + delete[] maskByteImg2; + delete[] pLercBlob; + } - // compare to orig + //--------------------------------------------------------------------------- - double maxDelta = 0; - for (int k = 0, i = 0; i < h; i++) + // Sample 2: random byte image, nDepth = 3, all pixels valid, maxZError = 0 (lossless) { - for (int j = 0; j < w; j++, k++) - { - if (maskByteImg3[k] != maskByteImg[k]) - cout << "Error in main: decoded valid bytes differ from encoded valid bytes" << endl; + int h = 713; + int w = 257; - if (maskByteImg3[k]) - { - double delta = fabs(zImg3[k] - zImg[k]); - if (delta > maxDelta) - maxDelta = delta; - } - } - } + Byte* byteImg = new Byte[3 * w * h]; + memset(byteImg, 0, 3 * w * h); - cout << "max z error per pixel = " << maxDelta << ", decode time = " << duration << " ms" << endl; - cout << endl; + for (int k = 0, i = 0; i < h; i++) + for (int j = 0; j < w; j++, k++) + for (int m = 0; m < 3; m++) + byteImg[k * 3 + m] = rand() % 30; - delete[] zImg; - delete[] zImg3; - delete[] maskByteImg; - delete[] maskByteImg3; - delete[] pLercBlob; - pLercBlob = 0; + // encode + uint32 numBytesWritten = 0; + uint32 numBytesBlob = (3 * w * h) * 2; // should be enough + Byte* pLercBlob = new Byte[numBytesBlob]; + + t0 = high_resolution_clock::now(); + + hr = lerc_encode((void*)byteImg, // raw image data: nDepth values per pixel, row by row, band by band + (uint32)dt_uchar, 3, w, h, 1, + 0, // nMasks, added in Lerc 3.0 to allow for different masks per band + nullptr, // can pass nullptr if all pixels are valid and nMasks = 0 + 0, // max coding error per pixel + pLercBlob, // buffer to write to, function will fail if buffer too small + numBytesBlob, // buffer size + &numBytesWritten); // num bytes written to buffer - //--------------------------------------------------------------------------- + if (hr) + cout << "lerc_encode(...) failed" << endl; - // Sample 2: random byte image, nDim = 3, all pixels valid, maxZError = 0 (lossless) + t1 = high_resolution_clock::now(); + auto duration = duration_cast(t1 - t0).count(); - h = 713; - w = 257; + numBytesBlob = numBytesWritten; + double ratio = 3 * w * h / (double)numBytesBlob; + cout << "sample 2 compression ratio = " << ratio << ", encode time = " << duration << " ms" << endl; - Byte* byteImg = new Byte[3 * w * h]; - memset(byteImg, 0, 3 * w * h); + // decode - for (int k = 0, i = 0; i < h; i++) - for (int j = 0; j < w; j++, k++) - for (int m = 0; m < 3; m++) - byteImg[k * 3 + m] = rand() % 30; + if (hr = lerc_getBlobInfo(pLercBlob, numBytesBlob, infoArr, dataRangeArr, infoArrSize, dataRangeArrSize)) + cout << "lerc_getBlobInfo(...) failed" << endl; + BlobInfo_Print(infoArr); - // encode + if (!BlobInfo_Equal(infoArr, 3, w, h, 1, (uint32)dt_uchar)) + cout << "got wrong lerc info" << endl; - hr = lerc_computeCompressedSize((void*)byteImg, // raw image data: nDim values per pixel, row by row, band by band - (uint32)dt_uchar, 3, w, h, 1, - 0, // nMasks, added in Lerc 3.0 to allow for different masks per band - nullptr, // can pass nullptr if all pixels are valid and nMasks = 0 - 0, // max coding error per pixel - &numBytesNeeded); // size of outgoing Lerc blob + vector zMinVec(3, 0), zMaxVec(3, 0); + if (hr = lerc_getDataRanges(pLercBlob, numBytesBlob, 3, 1, &zMinVec[0], &zMaxVec[0])) + cout << "lerc_getDataRanges(...) failed" << endl; - if (hr) - cout << "lerc_computeCompressedSize(...) failed" << endl; + // new data storage + Byte* byteImg2 = new Byte[3 * w * h]; + memset(byteImg2, 0, 3 * w * h); - numBytesBlob = numBytesNeeded; - pLercBlob = new Byte[numBytesBlob]; + t0 = high_resolution_clock::now(); - t0 = high_resolution_clock::now(); + if (hr = lerc_decode(pLercBlob, numBytesBlob, 0, nullptr, 3, w, h, 1, (uint32)dt_uchar, (void*)byteImg2)) + cout << "lerc_decode(...) failed" << endl; - hr = lerc_encode((void*)byteImg, // raw image data: nDim values per pixel, row by row, band by band - (uint32)dt_uchar, 3, w, h, 1, - 0, // nMasks, added in Lerc 3.0 to allow for different masks per band - nullptr, // can pass nullptr if all pixels are valid and nMasks = 0 - 0, // max coding error per pixel - pLercBlob, // buffer to write to, function will fail if buffer too small - numBytesBlob, // buffer size - &numBytesWritten); // num bytes written to buffer + t1 = high_resolution_clock::now(); + duration = duration_cast(t1 - t0).count(); - if (hr) - cout << "lerc_encode(...) failed" << endl; + // compare to orig - t1 = high_resolution_clock::now(); - duration = duration_cast(t1 - t0).count(); + int maxDelta = 0; + for (int k = 0, i = 0; i < h; i++) + for (int j = 0; j < w; j++, k++) + for (int m = 0; m < 3; m++) + { + int delta = abs((int)byteImg2[k * 3 + m] - (int)byteImg[k * 3 + m]); + if (delta > maxDelta) + maxDelta = delta; + } - ratio = 3 * w * h / (double)numBytesBlob; - cout << "sample 2 compression ratio = " << ratio << ", encode time = " << duration << " ms" << endl; + cout << "max z error per pixel = " << maxDelta << ", decode time = " << duration << " ms" << endl; + cout << endl; + delete[] byteImg; + delete[] byteImg2; + delete[] pLercBlob; + } - // decode + //--------------------------------------------------------------------------- - hr = lerc_getBlobInfo(pLercBlob, numBytesBlob, infoArr, dataRangeArr, 10, 3); - if (hr) - cout << "lerc_getBlobInfo(...) failed" << endl; + // Sample 3: random float image, nBands = 4, no input mask, throw in some NaN pixels, maxZError = 0 (lossless) + // Lerc will replace the NaN as invalid pixels using byte masks, one mask per band, as needed + { + int h = 128; + int w = 257; - BlobInfo_Print(infoArr); + float* fImg = new float[4 * w * h]; + memset(fImg, 0, 4 * w * h * sizeof(float)); - if (!BlobInfo_Equal(infoArr, 3, w, h, 1, (uint32)dt_uchar)) - cout << "got wrong lerc info" << endl; + for (int iBand = 0; iBand < 4; iBand++) + { + float* arr = &fImg[iBand * w * h]; - // new data storage - Byte* byteImg3 = new Byte[3 * w * h]; - memset(byteImg3, 0, 3 * w * h); + for (int k = 0, i = 0; i < h; i++) + for (int j = 0; j < w; j++, k++) + { + arr[k] = sqrt((float)(i * i + j * j)); // smooth surface + arr[k] += rand() % 20; // add some small amplitude noise - t0 = high_resolution_clock::now(); + if (iBand != 2 && rand() > 0.5 * RAND_MAX) // set some NaN's but not for band 2 + arr[k] = NAN; + } + } - hr = lerc_decode(pLercBlob, numBytesBlob, 0, nullptr, 3, w, h, 1, (uint32)dt_uchar, (void*)byteImg3); - if (hr) - cout << "lerc_decode(...) failed" << endl; + // encode + uint32 numBytesNeeded = 0; + uint32 numBytesWritten = 0; + + // optional, can call encode directly with large enough buffer, + // but can be used to decide which method is best, Lerc or method bla + + hr = lerc_computeCompressedSize((void*)fImg, // raw image data: nDepth values per pixel, row by row, band by band + (uint32)dt_float, 1, w, h, 4, + 0, // nMasks, added in Lerc 3.0 to allow for different masks per band + nullptr, // can pass nullptr if all pixels are valid and nMasks = 0 + 0, // max coding error per pixel + &numBytesNeeded); // size of outgoing Lerc blob - t1 = high_resolution_clock::now(); - duration = duration_cast(t1 - t0).count(); + if (hr) + cout << "lerc_computeCompressedSize(...) failed" << endl; - // compare to orig + uint32 numBytesBlob = numBytesNeeded; + Byte* pLercBlob = new Byte[numBytesBlob]; - maxDelta = 0; - for (int k = 0, i = 0; i < h; i++) - for (int j = 0; j < w; j++, k++) - for (int m = 0; m < 3; m++) - { - double delta = abs(byteImg3[k * 3 + m] - byteImg[k * 3 + m]); - if (delta > maxDelta) - maxDelta = delta; - } + t0 = high_resolution_clock::now(); - cout << "max z error per pixel = " << maxDelta << ", decode time = " << duration << " ms" << endl; - cout << endl; + hr = lerc_encode((void*)fImg, // raw image data: nDepth values per pixel, row by row, band by band + (uint32)dt_float, 1, w, h, 4, + 0, // nMasks, added in Lerc 3.0 to allow for different masks per band + nullptr, // can pass nullptr if all pixels are valid and nMasks = 0 + 0, // max coding error per pixel + pLercBlob, // buffer to write to, function will fail if buffer too small + numBytesBlob, // buffer size + &numBytesWritten); // num bytes written to buffer - delete[] byteImg; - delete[] byteImg3; - delete[] pLercBlob; - pLercBlob = 0; + if (hr) + cout << "lerc_encode(...) failed" << endl; - //--------------------------------------------------------------------------- + t1 = high_resolution_clock::now(); + auto duration = duration_cast(t1 - t0).count(); - // Sample 3: random float image, nBands = 4, no input mask, throw in some NaN pixels, maxZError = 0 (lossless) - // Lerc will replace the NaN as invalid pixels using byte masks, one mask per band, as needed + double ratio = 4 * w * h * sizeof(float) / (double)numBytesBlob; + cout << "sample 3 compression ratio = " << ratio << ", encode time = " << duration << " ms" << endl; - h = 128; - w = 257; + // decode - float* fImg = new float[4 * w * h]; - memset(fImg, 0, 4 * w * h * sizeof(float)); + if (hr = lerc_getBlobInfo(pLercBlob, numBytesBlob, infoArr, dataRangeArr, infoArrSize, dataRangeArrSize)) + cout << "lerc_getBlobInfo(...) failed" << endl; - for (int iBand = 0; iBand < 4; iBand++) - { - float* arr = &fImg[iBand * w * h]; + BlobInfo_Print(infoArr); - for (int k = 0, i = 0; i < h; i++) + if (!BlobInfo_Equal(infoArr, 1, w, h, 4, (uint32)dt_float)) + cout << "got wrong lerc info" << endl; + + // new data storage + float* fImg2 = new float[4 * w * h]; + memset(fImg2, 0, 4 * w * h * sizeof(float)); + + t0 = high_resolution_clock::now(); + + int nMasks = infoArr[(int)LercNS::InfoArrOrder::nMasks]; // get the number of valid / invalid byte masks from infoArr + + Byte* maskByteImg2 = nullptr; + + if (nMasks > 0) // possible values are 0, 1, or nBands { - for (int j = 0; j < w; j++, k++) - { - arr[k] = sqrt((float)(i * i + j * j)); // smooth surface - arr[k] += rand() % 20; // add some small amplitude noise + maskByteImg2 = new Byte[nMasks * w * h]; + memset(maskByteImg2, 0, nMasks * w * h); + } - if (iBand != 2 && rand() > 0.5 * RAND_MAX) // set some NaN's but not for band 2 - arr[k] = NAN; - } + if (hr = lerc_decode(pLercBlob, numBytesBlob, nMasks, maskByteImg2, 1, w, h, 4, (uint32)dt_float, (void*)fImg2)) + cout << "lerc_decode(...) failed" << endl; + + t1 = high_resolution_clock::now(); + duration = duration_cast(t1 - t0).count(); + + // compare to orig + + double maxDelta = 0; + for (int iBand = 0; iBand < 4; iBand++) + { + const float* arr = &fImg[iBand * w * h]; + const float* arr2 = &fImg2[iBand * w * h]; + const Byte* mask = (nMasks == 4) ? &maskByteImg2[iBand * w * h] + : ((nMasks == 1) ? maskByteImg2 : nullptr); + + for (int k = 0, i = 0; i < h; i++) + for (int j = 0; j < w; j++, k++) + if (!mask || mask[k]) + { + double delta = abs((double)arr2[k] - (double)arr[k]); + if (delta > maxDelta || std::isnan(delta)) + maxDelta = delta; + } } + + cout << "max z error per pixel = " << maxDelta << ", decode time = " << duration << " ms" << endl; + cout << endl; + + delete[] fImg; + delete[] fImg2; + delete[] maskByteImg2; + delete[] pLercBlob; } + //--------------------------------------------------------------------------- - // encode + // Sample 4: float image, nBands = 2, nDepth = 2, pass noData value to band 0, maxZError = 0.001, + // produce special mixed case of valid and invalid values at the same pixel; + // so here we need to use a noData value as the 2D byte masks cannot cover this case. - hr = lerc_computeCompressedSize((void*)fImg, // raw image data: nDim values per pixel, row by row, band by band - (uint32)dt_float, 1, w, h, 4, - 0, // nMasks, added in Lerc 3.0 to allow for different masks per band - nullptr, // can pass nullptr if all pixels are valid and nMasks = 0 - 0, // max coding error per pixel - &numBytesNeeded); // size of outgoing Lerc blob + // this example shows how to call the new Lerc API 4.0 functions which enable working with such mixed valid / invalid cases, + // and allow to pass a noData value for that, one per band; + { + int h = 128; + int w = 65; + const int nBands = 2; + const int nDepth = 2; // might be a complex image + int nValues = nDepth * w * h * nBands; + + float noDataVal = FLT_MAX; + double maxZErr = 0.001; + + // here, the 1st band uses a noData value, the 2nd band does not; + // noData value can be passed in addition to a byte mask, it is not one or the other; + Byte bUsesNoDataArr[nBands] = { 1, 0 }; + double noDataArr[nBands] = { noDataVal, 0 }; - if (hr) - cout << "lerc_computeCompressedSize(...) failed" << endl; + float* fImg = new float[nValues]; + memset(fImg, 0, nValues * sizeof(float)); - numBytesBlob = numBytesNeeded; - pLercBlob = new Byte[numBytesBlob]; + for (int iBand = 0; iBand < nBands; iBand++) + { + float* arr = &fImg[nDepth * w * h * iBand]; - t0 = high_resolution_clock::now(); + for (int k = 0, i = 0; i < h; i++) + for (int j = 0; j < w; j++, k++) + { + int m = k * nDepth; + arr[m] = sqrt((float)(i * i + j * j)); // smooth surface + arr[m] += rand() % 20; // add some small amplitude noise - hr = lerc_encode((void*)fImg, // raw image data: nDim values per pixel, row by row, band by band - (uint32)dt_float, 1, w, h, 4, - 0, // nMasks, added in Lerc 3.0 to allow for different masks per band - nullptr, // can pass nullptr if all pixels are valid and nMasks = 0 - 0, // max coding error per pixel - pLercBlob, // buffer to write to, function will fail if buffer too small - numBytesBlob, // buffer size - &numBytesWritten); // num bytes written to buffer + arr[m + 1] = arr[m]; - if (hr) - cout << "lerc_encode(...) failed" << endl; + if (iBand == 0 && rand() > 0.5 * RAND_MAX) // set some values to noData for band 0 + arr[m] = noDataVal; + } + } - t1 = high_resolution_clock::now(); - duration = duration_cast(t1 - t0).count(); + // encode + uint32 numBytesNeeded = 0; + uint32 numBytesWritten = 0; + + hr = lerc_computeCompressedSize_4D((void*)fImg, // raw image data: nDepth values per pixel, row by row, band by band + (uint32)dt_float, nDepth, w, h, nBands, + 0, // nMasks, added in Lerc 3.0 to allow for different masks per band + nullptr, // can pass nullptr if all pixels are valid and nMasks = 0 + maxZErr, // max coding error per pixel + &numBytesNeeded, // size of outgoing Lerc blob + bUsesNoDataArr, // array of size nBands, set to 1 for each band that uses noData value + noDataArr); // array of size nBands, set noData value for each band that uses it - ratio = 4 * w * h * sizeof(float) / (double)numBytesBlob; - cout << "sample 3 compression ratio = " << ratio << ", encode time = " << duration << " ms" << endl; + if (hr) + cout << "lerc_computeCompressedSize(...) failed" << endl; + uint32 numBytesBlob = numBytesNeeded; + Byte* pLercBlob = new Byte[numBytesBlob]; - // decode + t0 = high_resolution_clock::now(); - hr = lerc_getBlobInfo(pLercBlob, numBytesBlob, infoArr, dataRangeArr, 10, 3); - if (hr) - cout << "lerc_getBlobInfo(...) failed" << endl; + hr = lerc_encode_4D((void*)fImg, // raw image data: nDepth values per pixel, row by row, band by band + (uint32)dt_float, nDepth, w, h, nBands, + 0, // nMasks, added in Lerc 3.0 to allow for different masks per band + nullptr, // can pass nullptr if all pixels are valid and nMasks = 0 + maxZErr, // max coding error per pixel + pLercBlob, // buffer to write to, function will fail if buffer too small + numBytesBlob, // buffer size + &numBytesWritten, // num bytes written to buffer + bUsesNoDataArr, // array of size nBands, set to 1 for each band that uses noData value + noDataArr); // array of size nBands, set noData value for each band that uses it - BlobInfo_Print(infoArr); + if (hr) + cout << "lerc_encode(...) failed" << endl; - if (!BlobInfo_Equal(infoArr, 1, w, h, 4, (uint32)dt_float)) - cout << "got wrong lerc info" << endl; + t1 = high_resolution_clock::now(); + auto duration = duration_cast(t1 - t0).count(); - // new data storage - float* fImg3 = new float[4 * w * h]; - memset(fImg3, 0, 4 * w * h * sizeof(float)); + double ratio = nValues * sizeof(float) / (double)numBytesBlob; + cout << "sample 4 compression ratio = " << ratio << ", encode time = " << duration << " ms" << endl; - t0 = high_resolution_clock::now(); + // decode - int nMasks = infoArr[8]; // get the number of valid / invalid byte masks from infoArr + if (hr = lerc_getBlobInfo(pLercBlob, numBytesBlob, infoArr, dataRangeArr, infoArrSize, dataRangeArrSize)) + cout << "lerc_getBlobInfo(...) failed" << endl; - if (nMasks > 0) - { - maskByteImg3 = new Byte[nMasks * w * h]; - memset(maskByteImg3, 0, nMasks * w * h); - } - else - maskByteImg3 = nullptr; + BlobInfo_Print(infoArr); - hr = lerc_decode(pLercBlob, numBytesBlob, nMasks, maskByteImg3, 1, w, h, 4, (uint32)dt_float, (void*)fImg3); - if (hr) - cout << "lerc_decode(...) failed" << endl; + if (!BlobInfo_Equal(infoArr, nDepth, w, h, nBands, (uint32)dt_float)) + cout << "got wrong lerc info" << endl; - t1 = high_resolution_clock::now(); - duration = duration_cast(t1 - t0).count(); + // new data storage + float* fImg2 = new float[nValues]; + memset(fImg2, 0, nValues * sizeof(float)); - // compare to orig + t0 = high_resolution_clock::now(); - maxDelta = 0; - for (int iBand = 0; iBand < 4; iBand++) - { - const float* arr = &fImg[iBand * w * h]; - const float* arr3 = &fImg3[iBand * w * h]; - const Byte* mask = (nMasks == 4) ? &maskByteImg3[iBand * w * h] - : ((nMasks == 1) ? &maskByteImg3[w * h] : nullptr); + // Lerc converts noData values to byte mask wherever possible + int nMasks = infoArr[(int)LercNS::InfoArrOrder::nMasks]; // get the number of valid / invalid byte masks from infoArr + int nUsesNoData = infoArr[(int)LercNS::InfoArrOrder::nUsesNoDataValue]; // 0 - noData value is not used, nBands - noData value used in one or more bands - for (int k = 0, i = 0; i < h; i++) - for (int j = 0; j < w; j++, k++) - if (!mask || mask[k]) - { - double delta = abs(arr3[k] - arr[k]); - if (delta > maxDelta || std::isnan(delta)) - maxDelta = delta; - } - } + Byte* maskByteImg2 = nullptr; + + if (nMasks > 0) + { + maskByteImg2 = new Byte[nMasks * w * h]; + memset(maskByteImg2, 0, nMasks * w * h); + } + + vector bUsesNoDataVec(nBands, 0); + vector noDataVec(nBands, 0); - cout << "max z error per pixel = " << maxDelta << ", decode time = " << duration << " ms" << endl; - cout << endl; + hr = lerc_decode_4D(pLercBlob, numBytesBlob, nMasks, maskByteImg2, nDepth, w, h, nBands, (uint32)dt_float, (void*)fImg2, + &bUsesNoDataVec[0], &noDataVec[0]); + + if (hr) + cout << "lerc_decode(...) failed" << endl; - delete[] fImg; - delete[] fImg3; - delete[] maskByteImg3; - delete[] pLercBlob; - pLercBlob = 0; + t1 = high_resolution_clock::now(); + duration = duration_cast(t1 - t0).count(); + + // compare to orig + + double maxDelta = 0; + bool bNoDataValueHasChanged = false; + + for (int iBand = 0; iBand < nBands; iBand++) + { + if (bUsesNoDataVec[iBand]) // this band uses a noData value; only possible if nDepth > 1 and some pixel has a mix of valid and invalid values + { + if (!bUsesNoDataArr[iBand]) // that would be a bug + printf("Error: band %d changed from not using noData to using noData!\n", iBand); + + if (noDataVec[iBand] != noDataArr[iBand]) + printf("Error: noData value changed for band %d!\n", iBand); + } + + const float* arr = &fImg[nDepth * w * h * iBand]; + const float* arr2 = &fImg2[nDepth * w * h * iBand]; + + const Byte* mask = (nMasks == nBands) ? &maskByteImg2[iBand * w * h] + : ((nMasks == 1) ? maskByteImg2 : nullptr); + + for (int k = 0, i = 0; i < h; i++) + for (int j = 0; j < w; j++, k++) + if (!mask || mask[k]) + { + int m0 = k * nDepth; + for (int m = m0; m < m0 + nDepth; m++) + { + double delta = abs((double)arr2[m] - (double)arr[m]); + + if (bUsesNoDataVec[iBand] && (arr[m] == noDataVal) && delta > 0) + bNoDataValueHasChanged = true; + + if (delta > maxDelta) + maxDelta = delta; + } + } + } + + if (bNoDataValueHasChanged) + cout << "Error: some noData value has changed!" << endl; + + cout << "max z error per pixel = " << maxDelta << ", decode time = " << duration << " ms" << endl; + cout << endl; + + delete[] fImg; + delete[] fImg2; + delete[] maskByteImg2; + delete[] pLercBlob; + } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- +#endif // TestLegacyData + #ifdef TestLegacyData - Byte* pLercBuffer = new Byte[4 * 2048 * 2048]; - Byte* pDstArr = new Byte[4 * 2048 * 2048]; - Byte* pValidBytes = new Byte[1 * 2048 * 2048]; + size_t lercBufferSize = 8 * 1024 * 1024; // 8 MB + Byte* pLercBuffer = new Byte[lercBufferSize]; + + double totalDecodeTime = 0; vector fnVec; - string path = "D:/GitHub/LercOpenSource_v2.5/testData/"; + string path = "D:/GitHub/LercOpenSource_v2.5/testData/"; // windows + string pathDecoded = "C:/Temp/LercDecodedTiles/"; + //string path = "/home/john/Documents/Code/TestData/"; // ubuntu + //string pathDecoded = "/home/john/Documents/Code/DecodedTiles/"; +#ifdef USE_EMSCRIPTEN + path = "PreLoadedFiles/LercBlobs/"; + pathDecoded = "PreLoadedFiles/DecodedTiles/"; +#endif - fnVec.push_back("amazon3.lerc1"); - fnVec.push_back("tuna.lerc1"); - fnVec.push_back("tuna_0_to_1_w1920_h925.lerc1"); - fnVec.push_back("world.lerc1"); - - fnVec.push_back("bluemarble_256_256_3_byte.lerc2"); - fnVec.push_back("california_400_400_1_float.lerc2"); - fnVec.push_back("landsat_512_512_6_byte.lerc2"); - - fnVec.push_back("testall_w30_h20_char.lerc2"); - fnVec.push_back("testall_w30_h20_byte.lerc2"); - fnVec.push_back("testall_w30_h20_short.lerc2"); - fnVec.push_back("testall_w30_h20_ushort.lerc2"); - fnVec.push_back("testall_w30_h20_long.lerc2"); - fnVec.push_back("testall_w30_h20_ulong.lerc2"); - fnVec.push_back("testall_w30_h20_float.lerc2"); - - fnVec.push_back("testall_w1922_h1083_char.lerc2"); - fnVec.push_back("testall_w1922_h1083_byte.lerc2"); - fnVec.push_back("testall_w1922_h1083_short.lerc2"); - fnVec.push_back("testall_w1922_h1083_ushort.lerc2"); - fnVec.push_back("testall_w1922_h1083_long.lerc2"); - fnVec.push_back("testall_w1922_h1083_ulong.lerc2"); - fnVec.push_back("testall_w1922_h1083_float.lerc2"); - - fnVec.push_back("testbytes.lerc2"); - fnVec.push_back("testHuffman_w30_h20_uchar0.lerc2"); - fnVec.push_back("testHuffman_w30_h20_ucharx.lerc2"); - fnVec.push_back("testHuffman_w1922_h1083_uchar.lerc2"); - - fnVec.push_back("testuv_w30_h20_char.lerc2"); - fnVec.push_back("testuv_w30_h20_byte.lerc2"); - fnVec.push_back("testuv_w30_h20_short.lerc2"); - fnVec.push_back("testuv_w30_h20_ushort.lerc2"); - fnVec.push_back("testuv_w30_h20_long.lerc2"); - fnVec.push_back("testuv_w30_h20_ulong.lerc2"); - fnVec.push_back("testuv_w30_h20_float.lerc2"); - - fnVec.push_back("testuv_w1922_h1083_char.lerc2"); - fnVec.push_back("testuv_w1922_h1083_byte.lerc2"); - fnVec.push_back("testuv_w1922_h1083_short.lerc2"); - fnVec.push_back("testuv_w1922_h1083_ushort.lerc2"); - fnVec.push_back("testuv_w1922_h1083_long.lerc2"); - fnVec.push_back("testuv_w1922_h1083_ulong.lerc2"); - fnVec.push_back("testuv_w1922_h1083_float.lerc2"); - - fnVec.push_back("ShortBlob/LercTileCrash.lerc1"); - fnVec.push_back("Fixed_Failures/missedLastBand.lerc1"); - fnVec.push_back("Fixed_Failures/failedOn2ndBand.lerc2"); - fnVec.push_back("Different_Masks/lerc_level_0.lerc2"); + string listFn = path + "_list.txt"; + if (!ReadListFile(listFn, fnVec)) + cout << "Read file " << listFn << " failed." << endl; + + //fnVec.clear(); + //fnVec.push_back("ShortBlob/broken_l2v2.lerc22"); for (size_t n = 0; n < fnVec.size(); n++) { string fn = path; fn += fnVec[n]; - FILE* fp = 0; - fopen_s(&fp, fn.c_str(), "rb"); - fseek(fp, 0, SEEK_END); - size_t fileSize = ftell(fp); // get the file size - fclose(fp); - fp = 0; - - fopen_s(&fp, fn.c_str(), "rb"); - fread(pLercBuffer, 1, fileSize, fp); // read Lerc blob into buffer - fclose(fp); - fp = 0; + size_t fileSize = 0; + if (!ReadFile(fn, pLercBuffer, lercBufferSize, fileSize)) + { + cout << "Read file " << fn << " failed." << endl; + continue; + } - hr = lerc_getBlobInfo(pLercBuffer, (uint32)fileSize, infoArr, dataRangeArr, 10, 3); - if (hr) + if (hr = lerc_getBlobInfo(pLercBuffer, (uint32)fileSize, infoArr, dataRangeArr, infoArrSize, dataRangeArrSize)) cout << "lerc_getBlobInfo(...) failed" << endl; { - int version = infoArr[0]; + int codecVersion = infoArr[0]; uint32 dt = infoArr[1]; - int nDim = infoArr[2]; + int nDepth = infoArr[2]; int w = infoArr[3]; int h = infoArr[4]; int nBands = infoArr[5]; - int nMasks = infoArr[8]; + + int nMasks = infoArr[(int)LercNS::InfoArrOrder::nMasks]; // get the number of valid / invalid byte masks from infoArr + int nUsesNoData = infoArr[(int)LercNS::InfoArrOrder::nUsesNoDataValue]; // if > 0, noData value must be honored in addition to the byte mask + double zMin = dataRangeArr[0]; double zMax = dataRangeArr[1]; + const int bpp[] = { 1, 1, 2, 2, 4, 4, 4, 8 }; + + Byte* pDstArr = new Byte[nBands * w * h * nDepth * bpp[dt]]; + Byte* pValidBytes = new Byte[nMasks * w * h]; + + // allow for noData values, one per band + Byte* pUsesNoData = new Byte[nBands]; + double* pNoDataVal = new double[nBands]; + t0 = high_resolution_clock::now(); - std::string resultMsg = "ok"; - if (0 != lerc_decode(pLercBuffer, (uint32)fileSize, nMasks, pValidBytes, nDim, w, h, nBands, dt, (void*)pDstArr)) + string resultMsg = "ok"; + if (hr = lerc_decode_4D(pLercBuffer, (uint32)fileSize, nMasks, pValidBytes, nDepth, w, h, nBands, dt, (void*)pDstArr, pUsesNoData, pNoDataVal)) resultMsg = "FAILED"; + //memset(pDstArr, 5, nBands * w * h * nDepth * bpp[dt]); // to double check on the CompareTwoTiles() function + t1 = high_resolution_clock::now(); - duration = duration_cast(t1 - t0).count(); + auto duration = duration_cast(t1 - t0).count(); + totalDecodeTime += duration; + + printf("codec = %1d, nDepth = %1d, w = %4d, h = %4d, nBands = %1d, nMasks = %1d, usesNoData = %1d, dt = %1d, min = %10.4lf, max = %16.4lf, time = %4d ms, %s : %s\n", + codecVersion, nDepth, w, h, nBands, nMasks, nUsesNoData ? 1 : 0, dt, zMin, zMax, (int)duration, resultMsg.c_str(), fnVec[n].c_str()); + + string fnDec = pathDecoded; + string s = fnVec[n]; + replace(s.begin(), s.end(), '/', '_'); + fnDec += s; + +#ifdef DumpDecodedTiles + if (!WriteDecodedTile(fnDec, nMasks, pValidBytes, nDepth, w, h, nBands, bpp[dt], pDstArr)) + printf("Write decoded Lerc tile failed for %s\n", fnDec.c_str()); +#endif + +#ifdef CompareAgainstDumpedTiles + Byte* pDstArr2 = new Byte[nBands * w * h * nDepth * bpp[dt]]; + Byte* pValidBytes2 = new Byte[nMasks * w * h]; + + int nMasks2(0), nDepth2(0), w2(0), h2(0), nBands2(0), bpp2(0); + if (!ReadDecodedTile(fnDec, nMasks2, pValidBytes2, nDepth2, w2, h2, nBands2, bpp2, pDstArr2)) + printf("Read decoded Lerc tile failed for %s\n", fnDec.c_str()); + + if (!CompareTwoTiles(nMasks, pValidBytes, nDepth, w, h, nBands, bpp[dt], pDstArr, + nMasks2, pValidBytes2, nDepth2, w2, h2, nBands2, bpp2, pDstArr2, dt)) + printf("Compare two Lerc tiles failed for %s vs %s\n", fnVec[n].c_str(), fnDec.c_str()); - printf("nDim = %1d, w = %4d, h = %4d, nBands = %1d, nMasks = %1d, dt = %1d, min = %10.4f, max = %16.4f, time = %3lld ms, %s : %s\n", - nDim, w, h, nBands, nMasks, dt, zMin, zMax, duration, resultMsg.c_str(), fnVec[n].c_str()); + delete[] pDstArr2; + delete[] pValidBytes2; +#endif + + delete[] pDstArr; + delete[] pValidBytes; + + delete[] pUsesNoData; + delete[] pNoDataVal; } } delete[] pLercBuffer; - delete[] pDstArr; - delete[] pValidBytes; + + printf("Total decode time = %d ms", (int)totalDecodeTime); #endif @@ -520,3 +699,197 @@ } //----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void BlobInfo_Print(const uint32* infoArr) +{ + const uint32* ia = infoArr; + printf("version = %d, dataType = %d, nDepth = %d, nCols = %d, nRows = %d, nBands = %d, \ + nValidPixels = %d, blobSize = %d, nMasks = %d, nDepth = %d, nUsesNoDataValue = %d\n", + ia[0], ia[1], ia[2], ia[3], ia[4], ia[5], ia[6], ia[7], ia[8], ia[9], ia[10]); +} + +bool BlobInfo_Equal(const uint32* infoArr, uint32 nDepth, uint32 nCols, uint32 nRows, uint32 nBands, uint32 dataType) +{ + const uint32* ia = infoArr; + return ia[1] == dataType && ia[2] == nDepth && ia[3] == nCols && ia[4] == nRows && ia[5] == nBands; +} + +//----------------------------------------------------------------------------- + +#ifdef TestLegacyData + +bool ReadListFile(const string& listFn, vector& fnVec) +{ + FILE* fp = fopen(listFn.c_str(), "r"); + if (!fp) + { + printf("Cannot open file %s\n", listFn.c_str()); + return false; + } + + char buffer[1024]; + int num = 0; + int rv = fscanf(fp, "%d", &num); + for (int i = 0; i < num; i++) + { + rv = fscanf(fp, "%s", buffer); + string s = buffer; + fnVec.push_back(s); + } + + fclose(fp); + return true; +} + +bool ReadFile(const string& fn, Byte* pBuffer, size_t bufferSize, size_t& nBytesRead) +{ + nBytesRead = 0; + FILE* fp = fopen(fn.c_str(), "rb"); + if (!fp) + { + printf("Cannot open file %s\n", fn.c_str()); + return false; + } + + fseek(fp, 0, SEEK_END); + size_t fileSize = ftell(fp); // get the file size + fclose(fp); + fp = 0; + + if (fileSize > bufferSize) + { + printf("Lerc blob size %d > buffer size of %d\n", (uint32)fileSize, (uint32)bufferSize); + return false; + } + + fp = fopen(fn.c_str(), "rb"); + nBytesRead = fread(pBuffer, 1, fileSize, fp); // read Lerc blob into buffer + fclose(fp); + return nBytesRead == fileSize; +} + +bool WriteDecodedTile(const string& fn, int nMasks, const Byte* pValidBytes, int nDepth, int w, int h, int nBands, int bpp, const void* pArr) +{ + FILE* fp = fopen(fn.c_str(), "wb"); + if (!fp) + { + printf("Cannot open file %s\n", fn.c_str()); + return false; + } + + int dimensions[] = { nMasks, nDepth, w, h, nBands, bpp }; + fwrite(dimensions, 1, 6 * sizeof(int), fp); + + if (nMasks > 0) + fwrite(pValidBytes, 1, nMasks * w * h, fp); + + fwrite(pArr, 1, nBands * w * h * nDepth * bpp, fp); + fclose(fp); + return true; +} + +bool ReadDecodedTile(const string& fn, int& nMasks, Byte* pValidBytes, int& nDepth, int& w, int& h, int& nBands, int& bpp, void* pArr) +{ + FILE* fp = fopen(fn.c_str(), "rb"); + if (!fp) + { + printf("Cannot open file %s\n", fn.c_str()); + return false; + } + + int dims[6] = { 0 }; + size_t nbRead = fread(dims, 1, 6 * sizeof(int), fp); + nMasks = dims[0]; + nDepth = dims[1]; + w = dims[2]; + h = dims[3]; + nBands = dims[4]; + bpp = dims[5]; + + if (nMasks > 0) + nbRead += fread(pValidBytes, 1, nMasks * w * h, fp); + + nbRead += fread(pArr, 1, nBands * w * h * nDepth * bpp, fp); + fclose(fp); + return true; +} + +bool CompareTwoTiles(int nMasks, const Byte* pValidBytes, int nDepth, int w, int h, int nBands, int bpp, const void* pArr, + int nMasks2, const Byte* pValidBytes2, int nDepth2, int w2, int h2, int nBands2, int bpp2, const void* pArr2, int dt) +{ + if (nMasks2 != nMasks || nDepth2 != nDepth || w2 != w || h2 != h || nBands2 != nBands || bpp2 != bpp) + { + printf("Tile dimensions differ.\n"); + return false; + } + if (0 != memcmp(pValidBytes2, pValidBytes, nMasks * w * h)) + { + printf("Valid byte masks differ.\n"); + return false; + } + + double maxDelta(0); + + for (int iBand = 0; iBand < nBands; iBand++) + { + const Byte* mask = (nMasks == nBands) ? &pValidBytes[iBand * w * h] : (nMasks == 1 ? pValidBytes : nullptr); + + for (int k = 0, i = 0; i < h; i++) + { + for (int j = 0; j < w; j++, k++) + { + if (!mask || mask[k]) + { + for (int m = 0; m < nDepth; m++) + { + int index = (iBand * w * h + k) * nDepth + m; + double delta = 0; + + const Byte* ptr = (const Byte*)pArr + index * bpp; + const Byte* ptr2 = (const Byte*)pArr2 + index * bpp; + + if (dt <= 5) + { + if (0 != memcmp(ptr, ptr2, bpp)) + { + printf("Data differ.\n"); + return false; + } + } + else if (dt == 6) + { + float x(0), x2(0); + memcpy(&x, ptr, bpp); + memcpy(&x2, ptr2, bpp); + delta = fabs(x2 - x); + } + else if (dt == 7) + { + double x(0), x2(0); + memcpy(&x, ptr, bpp); + memcpy(&x2, ptr2, bpp); + delta = fabs(x2 - x); + } + + if (delta > maxDelta) + maxDelta = delta; + } + } + } + } + } + + if (maxDelta > 0) + { + printf("Valid pixel values differ, max delta = %lf.\n", maxDelta); + return false; + } + + return true; +} + +#endif + +//----------------------------------------------------------------------------- +