diff --git a/externals/nitro/ReleaseNotes.md b/externals/nitro/ReleaseNotes.md index 12ad2959b..0797ae97e 100644 --- a/externals/nitro/ReleaseNotes.md +++ b/externals/nitro/ReleaseNotes.md @@ -1,5 +1,11 @@ # NITRO (NITF i/o) Release Notes +## WIP: [Version 2.?.?](https://github.com/mdaus/nitro/releases/tag/NITRO-2.?.?); ??? ??, 2022 +* the large unused **archive** directory has been removed; contents are in [archive/java](https://github.com/mdaus/nitro/tree/archive/java) +and [archive/mex](https://github.com/mdaus/nitro/tree/archive/mex). +* the large unused **docs** directory has been removed; contents are in [archive/docs](https://github.com/mdaus/nitro/tree/archive/docs). +* Copy the J2K wrappers from **coda**. + ## [Version 2.10.8](https://github.com/mdaus/nitro/releases/tag/NITRO-2.10.8); February 22, 2022 (aka 2/22/22) * [coda-oss](https://github.com/mdaus/coda-oss) release [2022-02-22](https://github.com/mdaus/coda-oss/releases/tag/2022-02-22) * Show how to use [strongly typed TREs](https://github.com/mdaus/nitro/tree/feature/strongly-typed-TREs). diff --git a/externals/nitro/modules/c++/cpp.h b/externals/nitro/modules/c++/cpp.h index 9ba827ff7..f3e3960da 100644 --- a/externals/nitro/modules/c++/cpp.h +++ b/externals/nitro/modules/c++/cpp.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -35,9 +36,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include diff --git a/externals/nitro/modules/c++/nitf-c++.vcxproj b/externals/nitro/modules/c++/nitf-c++.vcxproj index 4a16a4a27..37f4339fb 100644 --- a/externals/nitro/modules/c++/nitf-c++.vcxproj +++ b/externals/nitro/modules/c++/nitf-c++.vcxproj @@ -49,8 +49,13 @@ + + + + + @@ -71,6 +76,7 @@ + @@ -123,8 +129,13 @@ + + + + + @@ -151,6 +162,7 @@ + @@ -215,12 +227,12 @@ true ProgramDatabase true - AdvancedVectorExtensions2 - MultiThreadedDebugDLL true EnableFastChecks Disabled true + MultiThreadedDebugDLL + true @@ -246,7 +258,6 @@ Guard true Speed - AdvancedVectorExtensions2 true Level3 diff --git a/externals/nitro/modules/c++/nitf-c++.vcxproj.filters b/externals/nitro/modules/c++/nitf-c++.vcxproj.filters index 497a07235..05d9a5eaa 100644 --- a/externals/nitro/modules/c++/nitf-c++.vcxproj.filters +++ b/externals/nitro/modules/c++/nitf-c++.vcxproj.filters @@ -199,6 +199,24 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -428,5 +446,23 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/externals/nitro/modules/c++/nitf/CMakeLists.txt b/externals/nitro/modules/c++/nitf/CMakeLists.txt index 020037be0..2aa171439 100644 --- a/externals/nitro/modules/c++/nitf/CMakeLists.txt +++ b/externals/nitro/modules/c++/nitf/CMakeLists.txt @@ -1,5 +1,5 @@ set(MODULE_NAME nitf) -set(MODULE_DEPS nitf-c j2k-c except-c++ io-c++ mt-c++ sys-c++ str-c++ gsl-c++ std-c++) +set(MODULE_DEPS nitf-c j2k-c except-c++ sio.lite-c++ io-c++ mt-c++ sys-c++ str-c++ gsl-c++ std-c++) if (MSVC) list(APPEND MODULE_DEPS wsock32 ws2_32) endif() @@ -47,8 +47,13 @@ coda_add_module( source/ImageSubheader.cpp source/ImageWriter.cpp source/J2KComponent.cpp + source/J2KCompressionParameters.cpp + source/J2KCompressor.cpp source/J2KContainer.cpp + source/J2KEncoder.cpp + source/J2KImage.cpp source/J2KReader.cpp + source/J2KStream.cpp source/J2KWriter.cpp source/LabelSegment.cpp source/LabelSubheader.cpp @@ -69,6 +74,7 @@ coda_add_module( source/TestingTest.cpp source/TextSegment.cpp source/TextSubheader.cpp + source/UnitTests.cpp source/Utils.cpp source/WriteHandler.cpp source/Writer.cpp @@ -97,10 +103,15 @@ coda_add_tests( test_image_blocker.cpp test_image_segment_blank_nm_compression.cpp test_image_segment_computer.cpp + test_image_writer.cpp test_nitf_buffer_list.cpp test_tre_mods++.cpp test_tre_create++.cpp - test_j2k_read_tile.cpp) + test_j2k_loading++.cpp + test_j2k_read_tile.cpp + test_j2k_compress_tile.cpp + test_j2k_compressed_byte_provider.cpp + test_tre_read.cpp) add_executable(show_nitf++ apps/show_nitf++.cpp) target_link_libraries(show_nitf++ PRIVATE nitf-c++) diff --git a/externals/nitro/modules/c++/nitf/apps/show_nitf++/show_nitf++.vcxproj b/externals/nitro/modules/c++/nitf/apps/show_nitf++/show_nitf++.vcxproj index 85f1a51b6..45e85e475 100644 --- a/externals/nitro/modules/c++/nitf/apps/show_nitf++/show_nitf++.vcxproj +++ b/externals/nitro/modules/c++/nitf/apps/show_nitf++/show_nitf++.vcxproj @@ -55,15 +55,15 @@ Use pch.h pch.h - AdvancedVectorExtensions2 Guard ProgramDatabase true - MultiThreadedDebugDLL true EnableFastChecks Disabled true + MultiThreadedDebugDLL + true Console @@ -83,7 +83,6 @@ pch.h pch.h Speed - AdvancedVectorExtensions2 true Guard true diff --git a/externals/nitro/modules/c++/nitf/include/nitf/BandInfo.hpp b/externals/nitro/modules/c++/nitf/include/nitf/BandInfo.hpp index 5ba9b3d1b..ff8953e3c 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/BandInfo.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/BandInfo.hpp @@ -86,6 +86,7 @@ DECLARE_CLASS(BandInfo) }; explicit BandInfo(Subcategory); + BandInfo(Representation, Subcategory, const std::string & imageFilterCondition, const std::string & imageFilterCode); //! Get the subcategory nitf::Field getSubcategory() const; diff --git a/externals/nitro/modules/c++/nitf/include/nitf/BandSource.hpp b/externals/nitro/modules/c++/nitf/include/nitf/BandSource.hpp index 681fe2350..a7da3dffa 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/BandSource.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/BandSource.hpp @@ -59,7 +59,7 @@ typedef DataSource BandSource; * times during the case of memory mapping, although it may be used * to sample down or cut the image into pieces). */ -struct NITRO_NITFCPP_API MemorySource : public BandSource +struct NITRO_NITFCPP_API MemorySource final : public BandSource { /*! * Constructor @@ -74,10 +74,6 @@ struct NITRO_NITFCPP_API MemorySource : public BandSource MemorySource(std::span span, nitf::Off start, int numBytesPerPixel, int pixelSkip) : MemorySource(span.data(), span.size(), start, numBytesPerPixel, pixelSkip) {} - - template - MemorySource(const TSpanLike& span, nitf::Off start, int pixelSkip) - : MemorySource(span.data(), span.size() * sizeof(span[0]), start, sizeof(span[0]), pixelSkip) {} }; /*! @@ -90,7 +86,7 @@ struct NITRO_NITFCPP_API MemorySource : public BandSource * we allow the creator to specify a start point, and a pixel skip * (this would help you create a thumbnail as well). */ -struct NITRO_NITFCPP_API FileSource : public BandSource +struct NITRO_NITFCPP_API FileSource final : public BandSource { FileSource(const std::string& fname, nitf::Off start, diff --git a/externals/nitro/modules/c++/nitf/include/nitf/ByteProvider.hpp b/externals/nitro/modules/c++/nitf/include/nitf/ByteProvider.hpp index b592cda3a..199b8aecb 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/ByteProvider.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/ByteProvider.hpp @@ -19,13 +19,15 @@ * see . * */ -#ifndef __NITF_BYTE_PROVIDER_HPP__ -#define __NITF_BYTE_PROVIDER_HPP__ +#ifndef NITF_ByteProvider_hpp_INCLUDED_ +#define NITF_ByteProvider_hpp_INCLUDED_ #pragma once #include #include #include +#include +#include #include #include @@ -97,6 +99,7 @@ namespace nitf struct NITRO_NITFCPP_API ByteProvider { typedef std::pair PtrAndLength; + using PtrAndLength_t = std::span; /*! * \param record Pre-populated NITF record. All TREs, image subheader, and @@ -113,12 +116,16 @@ struct NITRO_NITFCPP_API ByteProvider std::vector(), size_t numRowsPerBlock = 0, size_t numColsPerBlock = 0); + ByteProvider(Record& record, + const std::vector& desData, + size_t numRowsPerBlock = 0, + size_t numColsPerBlock = 0); /*! * Destructor. No virtual methods but this is virtual in case it's useful * to inherit from this class and use it polymorphically. */ - virtual ~ByteProvider(); + virtual ~ByteProvider() = default; //! \return The total number of bytes in the NITF nitf::Off getFileNumBytes() const noexcept @@ -261,6 +268,9 @@ struct NITRO_NITFCPP_API ByteProvider std::vector(), size_t numRowsPerBlock = 0, size_t numColsPerBlock = 0); + void initialize(const Record& record, + const std::vector& desData, + size_t numRowsPerBlock = 0, size_t numColsPerBlock = 0); static void copyFromStreamAndClear(io::ByteStream& stream, std::vector& rawBytes); @@ -302,6 +312,8 @@ struct NITRO_NITFCPP_API ByteProvider void getFileLayout(const nitf::Record& inRecord, const std::vector& desData); + void getFileLayout(const nitf::Record & inRecord, + const std::vector&desData); std::vector mImageDataLengths; @@ -314,6 +326,11 @@ struct NITRO_NITFCPP_API ByteProvider const std::vector& desData, size_t numRowsPerBlock, size_t numColsPerBlock); + void initializeImpl( + const Record & record, + const std::vector&desData, + size_t numRowsPerBlock, + size_t numColsPerBlock); // Represents the row information for a NITF image segment struct SegmentInfo @@ -358,7 +375,19 @@ struct NITRO_NITFCPP_API ByteProvider std::vector mImageSubheaderFileOffsets; // Per segment nitf::Off mDesSubheaderFileOffset = 0; nitf::Off mFileNumBytes = 0; + + private: + template + void getFileLayout_(const nitf::Record& inRecord, + const std::vector& desData); + + template + void initializeImpl_( + const Record& record, + const std::vector& desData, + size_t numRowsPerBlock, + size_t numColsPerBlock); }; } -#endif +#endif // NITF_ByteProvider_hpp_INCLUDED_ diff --git a/externals/nitro/modules/c++/nitf/include/nitf/CompressedByteProvider.hpp b/externals/nitro/modules/c++/nitf/include/nitf/CompressedByteProvider.hpp index 7981507ca..0f3fa117b 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/CompressedByteProvider.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/CompressedByteProvider.hpp @@ -76,6 +76,10 @@ struct NITRO_NITFCPP_API CompressedByteProvider : public ByteProvider std::vector(), size_t numRowsPerBlock = 0, size_t numColsPerBlock = 0); + CompressedByteProvider(Record& record, + const std::vector >& bytesPerBlock, + const std::vector& desData, + size_t numRowsPerBlock = 0, size_t numColsPerBlock = 0); /*! * Given a range of rows from [startRow, startRow + numRows), provide the @@ -163,6 +167,10 @@ struct NITRO_NITFCPP_API CompressedByteProvider : public ByteProvider std::vector(), size_t numRowsPerBlock = 0, size_t numColsPerBlock = 0); + void initialize(const Record & record, + const std::vector >&bytesPerBlock, + const std::vector&desData, + size_t numRowsPerBlock = 0, size_t numColsPerBlock = 0); size_t countBytesForCompressedImageData( size_t seg, size_t startRow, size_t numRowsToWrite) const; @@ -187,6 +195,11 @@ struct NITRO_NITFCPP_API CompressedByteProvider : public ByteProvider size_t numRowsToWrite) const; private: + template + void initialize_(const Record & record, + const std::vector >&bytesPerBlock, + const std::vector&desData, + size_t numRowsPerBlock = 0, size_t numColsPerBlock = 0); std::vector > mBytesInEachBlock; }; } diff --git a/externals/nitro/modules/c++/nitf/include/nitf/Enum.hpp b/externals/nitro/modules/c++/nitf/include/nitf/Enum.hpp index 13c96f807..abeda465b 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/Enum.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/Enum.hpp @@ -30,6 +30,7 @@ #include #include "str/Manip.h" +#include "str/EncodedStringView.h" namespace nitf { @@ -101,7 +102,8 @@ namespace nitf #define NITF_ENUM_map_entry_14(name, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14) NITF_ENUM_map_entry(name, n1), NITF_ENUM_map_entry_13(name, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14) #define NITF_ENUM_map_entry_15(name, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15) NITF_ENUM_map_entry(name, n1), NITF_ENUM_map_entry_14(name, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15) -#define NITF_ENUM_define_string_to_enum_begin(name) inline std::ostream& operator<<(std::ostream& os, name e) { os << to_string(e); return os; } \ +#define NITF_ENUM_define_string_to_enum_ostream_(name) inline std::ostream& operator<<(std::ostream& os, name e) { os << to_string(e); return os; } +#define NITF_ENUM_define_string_to_enum_begin(name) NITF_ENUM_define_string_to_enum_ostream_(name) \ namespace details { template<> inline const std::map& string_to_enum() { \ static const std::map retval { #define NITF_ENUM_define_string_to_end }; return retval; } } @@ -117,6 +119,11 @@ namespace nitf return details::to_string(v); } template + inline std::wstring to_wstring(T v) noexcept(false) + { + return str::EncodedStringView(details::to_string(v)).wstring(); + } + template inline T from_string(std::string v) noexcept(false) { str::trim(v); diff --git a/externals/nitro/modules/c++/nitf/include/nitf/ImageBlocker.hpp b/externals/nitro/modules/c++/nitf/include/nitf/ImageBlocker.hpp index 2251c771e..f328226c4 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/ImageBlocker.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/ImageBlocker.hpp @@ -27,8 +27,11 @@ #include #include #include +#include +#include #include "nitf/coda-oss.hpp" +#include "types/RowCol.h" #include "nitf/System.hpp" #include "nitf/exports.hpp" @@ -169,6 +172,12 @@ struct NITRO_NITFCPP_API ImageBlocker /*final*/ // no "final", SWIG doesn't li size_t numValidRowsInBlock, size_t numValidColsInBlock, void* output) noexcept; + static void block(std::span input, + size_t numBytesPerPixel, + size_t numCols, + const types::RowCol& perBlock, + const types::RowCol& validInBlock, + std::span output) noexcept; //! \return The number of columns of blocks size_t getNumColsOfBlocks() const noexcept diff --git a/externals/nitro/modules/c++/nitf/include/nitf/J2KComponent.hpp b/externals/nitro/modules/c++/nitf/include/nitf/J2KComponent.hpp index c7f288b3e..3eb7b50b7 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/J2KComponent.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/J2KComponent.hpp @@ -63,7 +63,7 @@ namespace details Component(j2k_Component*); }; } -struct Component final +struct NITRO_NITFCPP_API Component final { Component() = delete; Component(const Component&) = delete; diff --git a/externals/nitro/modules/c++/nitf/include/nitf/J2KCompressionParameters.hpp b/externals/nitro/modules/c++/nitf/include/nitf/J2KCompressionParameters.hpp new file mode 100644 index 000000000..167f9a11d --- /dev/null +++ b/externals/nitro/modules/c++/nitf/include/nitf/J2KCompressionParameters.hpp @@ -0,0 +1,104 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2017, MDA Information Systems LLC + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ +#ifndef NITF_J2KCompressionParameters_hpp_INCLUDED_ +#define NITF_J2KCompressionParameters_hpp_INCLUDED_ +#pragma once + +#include + +#include "nitf/exports.hpp" + +namespace j2k +{ +class NITRO_NITFCPP_API CompressionParameters final +{ + const types::RowCol mRawImageDims; + const types::RowCol mTileDims; + const double mCompressionRatio = 1.0; + const size_t mNumResolutions = 6; + const size_t mNumRowsOfTiles; //! Number of rows of tiles (rounded up if a partial tile is present). + const size_t mNumColsOfTiles; //! Number of columns of tiles (rounded up if a partial tile is present). + const size_t mNumTiles; //! Total number of tiles (includes partial tiles). + +public: + /*! + * Constructor + * + * \param rawImageDims The dimensions of the raw image. + * + * \param tileDims The desired tile dimensions to use when tiling the + * raw image data. + * + * \param compressionRatio Represents the factor of compression (i.e a + * times compressed). + * + * \param numResolutions Number of DWT decompositions + 1. Defaults to + * 6. + */ + CompressionParameters(const types::RowCol& rawImageDims, const types::RowCol& tileDims, + double compressionRatio = 1, size_t numResolutions = 6) noexcept; + CompressionParameters(const CompressionParameters&) = default; + CompressionParameters& operator=(const CompressionParameters&) = delete; + CompressionParameters(CompressionParameters&&) = default; + CompressionParameters& operator=(CompressionParameters&&) = delete; + ~CompressionParameters() = default; + + types::RowCol getRawImageDims() const noexcept + { + return mRawImageDims; + } + + types::RowCol getTileDims() const noexcept + { + return mTileDims; + } + + double getCompressionRatio() const noexcept + { + return mCompressionRatio; + } + + size_t getNumResolutions() const noexcept + { + return mNumResolutions; + } + + size_t getNumRowsOfTiles() const noexcept + { + return mNumRowsOfTiles; + } + + size_t getNumColsOfTiles() const noexcept + { + return mNumColsOfTiles; + } + + size_t getNumTiles() const noexcept + { + return mNumTiles; + } +}; +} + +#endif // NITF_J2KCompressionParameters_hpp_INCLUDED_ + diff --git a/externals/nitro/modules/c++/nitf/include/nitf/J2KCompressor.hpp b/externals/nitro/modules/c++/nitf/include/nitf/J2KCompressor.hpp new file mode 100644 index 000000000..e8175d345 --- /dev/null +++ b/externals/nitro/modules/c++/nitf/include/nitf/J2KCompressor.hpp @@ -0,0 +1,213 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2017, MDA Information Systems LLC + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#ifndef NITF_J2KCompressor_hpp_INCLUDED_ +#define NITF_J2KCompressor_hpp_INCLUDED_ +#pragma once + +#include // std::byte +#include +#include + +#include +#include +#include +#include +#include + +#include "nitf/exports.hpp" +#include "nitf/J2KCompressionParameters.hpp" + +namespace j2k +{ + /*! + * \class OPJCompressor + * \brief Compresses a raw image using openjpeg. Compressed image is blocked + * in a format that aligns with JPEG-2000 compression (each block/tile is + * compressed independently) and with NITF file format expectations (a + * J2K tile == a NITF block) + */ + class NITRO_NITFCPP_API Compressor final + { + CompressionParameters mCompressionParams; + size_t mNumThreads = 1; + + public: + /*! + * Constructor + * + * \param The J2K compression parameters. + */ + Compressor(const CompressionParameters& compressionParams, size_t numThreads = 1) noexcept; + + Compressor(const Compressor&) = delete; + Compressor& operator=(const Compressor&) = delete; + Compressor(Compressor&&) = default; + Compressor& operator=(Compressor&&) = delete; + + /*! + * Calculate the bytes required to allocate for compression. + * There is no way to know exactly how many bytes are needed + * before the compression actually happens. This is because + * there are pathological cases where compression actually + * results in a larger image. Therefore, we demand twice + * the size of the input image, and hopefully this is enough. + * + * \return Max bytes required to compress the entire image + */ + size_t getMaxBytesRequiredToCompress() const noexcept; + + /*! + * Calculate how many bytes caller should supply to compress + * a given number of tiles + * \param numTiles How many tiles do you want to compress? + * \return Bytes required to compress number of tiles + */ + size_t getMaxBytesRequiredToCompress(size_t numTiles) const noexcept; + + /*! + * Accessor to return the number of layers. + * At present, we only ever write one + * \return How many layers are in compressed image + */ + size_t getNumLayers() const noexcept { return 1; } + + /*! + * Accessor to get the bit rate. + * This is equivalent to the compression ratio + * \return Bit rate + */ + size_t getCompressionRatio() const noexcept + { + return gsl::narrow_cast(mCompressionParams.getCompressionRatio()); + } + + /*! + * Compresses a raw image. This overload will take a vector, + * resize it appropriately, and write the compressed image to it + * + * \param rawImageData The raw image to compress. + * \param numThreads Number of threads to use when compressing. + * \param[out] compressedData The compressed image data. + * \param[out] bytesPerTile Number of bytes in each compressed tile. + */ + void compress(std::span rawImageData, + std::vector& compressedData, + std::vector& bytesPerTile) const; + + /*! + * Compresses a raw image. This overload lets you pass in your own + * pre-allocated memory. + * + * \param rawImageData The raw image to compress. + * \param numThreads Number of threads to use when compressing. + * \param[in,out] compressedData The pre-allocated compressed image data. + * Must be at least getMaxBytesRequiredToCompress() bytes. On output, will + * contain the compressed image. + * \param[out] bytesPerTile Number of bytes in each compressed tile. + */ + std::span compress(std::span rawImageData, + std::span compressedData, + std::vector& bytesPerTile) const; + + /*! + * Compresses a raw image tile. This will include the image header and + * footer in the output if it is the first or last file, as appropriate. + * + * \param rawImageData The raw image to compress. + * \param tileIndex Index of tile. + * \param[out] compressedTile The compressed tile. + */ + void compressTile(std::span rawImageData, + size_t tileIndex, + std::vector& compressedTile) const; + + /*! + * Compresses a raw image tile. This will include the image header and + * footer in the output if it is the first or last file, as appropriate. + * + * \param rawImageData The raw image to compress. + * \param tileIndex Index of tile. + * \param[in,out] compressedTile The compressed tile. Data should be + * allocated before calling, with compressedTile.size set to the number of + * bytes available (which must be at least + * getMaxBytesRequiredToCompress(1). After compression, size will be set + * to the number of compressed bytes actually written to the tile, and + * data will point to the compressed tile. + */ + std::span compressTile(std::span rawImageData, + size_t tileIndex, + std::span compressedTile) const; + + /*! + * Compresses the local portion of the image, taking into account that it + * is a slice of a larger global image (the tile numbering stored in the + * J2K-compressed data reflects the global tile numbers). + * + * \param rawImageData The local raw image to compress. + * \param globalStartRow Global start row corresponding to the 0th row of + * 'rawImageData'. Must be a multiple of the # of rows in a tile. + * \param numLocalRows Number of rows in 'rawImageData'. Must be either + * a multiple of the # of rows in a tile or this AOI must encompass the + * bottom of the image. + * \param numThreads Number of threads to use when compressing. + * \param[in,out] compressedData The pre-allocated compressed image data. + * Must be at least getMaxBytesRequiredToCompress(numTilesInThisRowRange) + * bytes. On output, will contain the compressed image. + * \param[out] tileRange Global tile range that corresponds to the + * rows that were compressed + * \param[out] bytesPerTile Number of bytes in each compressed tile. This + * is tileRange.mNumElements long, where index 0 corresponds to global tile + * tileRange.mStartElement. + */ + std::span compressRowSubrange(std::span rawImageData, + size_t globalStartRow, + size_t numLocalRows, + std::span compressedData, + types::Range& tileRange, + std::vector& bytesPerTile) const; + + /*! + * Compresses the local portion of the image, taking into account that it + * is a slice of a larger global image (the tile numbering stored in the + * J2K-compressed data reflects the global tile numbers). + * + * \param rawImageData The local raw image to compress. + * \param tileRange Global tile range corresponding to 'rawImageData'. + * \param numThreads Number of threads to use when compressing. + * \param[in,out] compressedData The pre-allocated compressed image data. + * Must be at least + * getMaxBytesRequiredToCompress(tileRange.mNumElements * numColsOfTiles) + * bytes. On output, will contain the compressed image. + * \param[out] bytesPerTile Number of bytes in each compressed tile. This + * is tileRange.mNumElements long, where index 0 corresponds to global tile + * tileRange.mStartElement. + */ + std::span compressTileSubrange(std::span rawImageData, + const types::Range& tileRange, + std::span compressedData, + std::vector& bytesPerTile) const; + }; +} + +#endif // NITF_J2KCompressor_hpp_INCLUDED_ \ No newline at end of file diff --git a/externals/nitro/modules/c++/nitf/include/nitf/J2KContainer.hpp b/externals/nitro/modules/c++/nitf/include/nitf/J2KContainer.hpp index 7c4640932..e59967e61 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/J2KContainer.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/J2KContainer.hpp @@ -30,6 +30,7 @@ #include "j2k/Container.h" #include "j2k/Writer.h" +#include "nitf/exports.hpp" #include "nitf/Object.hpp" #include "nitf/System.hpp" #include "nitf/NITFException.hpp" @@ -71,7 +72,7 @@ NITRO_DECLARE_CLASS_J2K(Container) Container(j2k_Container*); }; } -struct Container final +struct NITRO_NITFCPP_API Container final { Container(const Container&) = delete; Container& operator=(const Container&) = delete; @@ -81,6 +82,7 @@ struct Container final //! Set native object Container(j2k_Container*&&); + j2k_Container* getNativeOrThrow() const; uint32_t getNumComponents() const; Component getComponent(uint32_t) const; @@ -101,7 +103,8 @@ struct Container final Writer createWriter(const WriterOptions&) const; - // Not part of the C API + // Not part of the C API. It might be easier to use WriteTiler than calling + // these individual methods. size_t tileSize() const; // getTileWidth() * getTileHeight() size_t numBytes(uint32_t) const; ptrdiff_t bufferOffset(uint32_t tileX, uint32_t tileY, uint32_t i) const; diff --git a/externals/nitro/modules/c++/nitf/include/nitf/J2KEncoder.hpp b/externals/nitro/modules/c++/nitf/include/nitf/J2KEncoder.hpp new file mode 100644 index 000000000..0f900b509 --- /dev/null +++ b/externals/nitro/modules/c++/nitf/include/nitf/J2KEncoder.hpp @@ -0,0 +1,94 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2017, MDA Information Systems LLC + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#ifndef NITF_J2KEncoder_hpp_INCLUDED_ +#define NITF_J2KEncoder_hpp_INCLUDED_ +#pragma once + +#include +#include + +#include + +#include "j2k/TileWriter.h" + +#include "nitf/exports.hpp" +#include "nitf/J2KImage.hpp" +#include "nitf/J2KCompressionParameters.hpp" + +namespace j2k +{ + /*! + * \class OPJEncoder + * \desc RAII wrapper around opj_codec_t. + */ + class NITRO_NITFCPP_API Encoder final + { + struct Impl; + std::unique_ptr pImpl_; + + public: + /*! + * Constructor + * + * \param image The openjpeg image. + * + * \param compressionParams The J2K compression parameters. + */ + Encoder(Image& image, const CompressionParameters& compressionParams); + Encoder(const Encoder&) = delete; + Encoder& operator=(const Encoder&) = delete; + Encoder(Encoder&&) = delete; + Encoder& operator=(Encoder&&) = delete; + ~Encoder(); + + /*! + * \return The openjpeg codec. + */ + j2k_codec_t* getNative() const noexcept; + + /*! + * \return The error message generated by openjpeg during encoding. + */ + std::string getErrorMessage() const; + + /*! + * \return true if an openjpeg error has occurred and false otherwise. + */ + bool errorOccurred() const; + + /*! + * Sets the error message generated by openjpeg. + * + * \return msg The openjpeg error message. + */ + void setError(const std::string& msg); + + /*! + * Clears the openjpeg error message. + */ + void clearError() noexcept; + }; +} + +#endif // NITF_J2KEncoder_hpp_INCLUDED_ diff --git a/externals/nitro/modules/c++/nitf/include/nitf/J2KImage.hpp b/externals/nitro/modules/c++/nitf/include/nitf/J2KImage.hpp new file mode 100644 index 000000000..5ea7d3470 --- /dev/null +++ b/externals/nitro/modules/c++/nitf/include/nitf/J2KImage.hpp @@ -0,0 +1,78 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2017, MDA Information Systems LLC + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#ifndef NITF_J2KImage_hpp_INCLUDED_ +#define NITF_J2KImage_hpp_INCLUDED_ +#pragma once + +#include + +#include + +#include "nitf/exports.hpp" + +namespace j2k +{ + /*! + * \class OPJImage + * \desc RAII wrapper around j2k_image_t. + */ + class NITRO_NITFCPP_API Image final + { + //! The openjpeg image. + j2k_image_t* mImage = nullptr; + + //! The openjpeg image component parameters. + j2k_image_comptparm mImageComponentParams; + + void initComponents(const types::RowCol& rawImageDims); + void initImage(); + + public: + /*! + * Constructor + * + * \param rawImageDims Dimensions of the raw image + */ + Image(const types::RowCol& rawImageDims); + Image(const Image&) = delete; + Image& operator=(const Image&) = delete; + Image(Image&&) = default; + Image& operator=(Image&&) = default; + + /*! + * Destructor - destroys the openjpeg image. + */ + ~Image(); + + /*! + * \return The openjpeg image. + */ + j2k_image_t* getNative() const noexcept + { + return mImage; + } + }; +} + +#endif // NITF_J2KImage_hpp_INCLUDED_ diff --git a/externals/nitro/modules/c++/nitf/include/nitf/J2KReader.hpp b/externals/nitro/modules/c++/nitf/include/nitf/J2KReader.hpp index d799e4d98..2609cf7f1 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/J2KReader.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/J2KReader.hpp @@ -32,6 +32,7 @@ #include "j2k/j2k_Reader.h" #include "nitf/coda-oss.hpp" +#include "nitf/exports.hpp" #include "nitf/Object.hpp" #include "nitf/System.hpp" #include "nitf/NITFException.hpp" @@ -80,7 +81,7 @@ NITRO_DECLARE_CLASS_J2K(Reader) Reader(const std::filesystem::path&); }; } -struct Reader final +struct NITRO_NITFCPP_API Reader final { Reader() = delete; Reader(const Reader&) = delete; diff --git a/externals/nitro/modules/c++/nitf/include/nitf/J2KStream.hpp b/externals/nitro/modules/c++/nitf/include/nitf/J2KStream.hpp new file mode 100644 index 000000000..88ccab868 --- /dev/null +++ b/externals/nitro/modules/c++/nitf/include/nitf/J2KStream.hpp @@ -0,0 +1,88 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * (C) Copyright 2017, MDA Information Systems LLC + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#ifndef NITF_J2KStream_hpp_INCLUDED_ +#define NITF_J2KStream_hpp_INCLUDED_ + +#include + +#include "j2k/TileWriter.h" + +#include "nitf/exports.hpp" + +namespace j2k +{ + /*! + * \enum StreamType + * + * Determines whether openjpeg will use the stream for writing out or + * reading in compressed data. + */ + enum class StreamType + { + OUTPUT = 0, + INPUT + }; + + /*! + * \class Stream + * \desc RAII wrapper around opj_stream_t. + */ + struct NITRO_NITFCPP_API Stream final + { + /*! + * Constructor + * + * \param streamType The type of stream - OUTPUT creates an output stream + * to write j2k compressed data to during compression and INPUT creates + * an input stream to read j2k compressed data from during decompression. + * + * \param chunkSize Size of the internal buffer used by openjpeg to + * write compressed data to or to read compressed data from. + */ + Stream(StreamType streamType, + size_t chunkSize = NITRO_J2K_STREAM_CHUNK_SIZE); + + Stream(const Stream&) = delete; + Stream& operator=(const Stream&) = delete; + Stream(Stream&&) = default; + Stream& operator=(Stream&&) = default; + ~Stream(); + + /*! + * \return The openjpeg stream. + */ + j2k_stream_t* getNative() const noexcept + { + return mStream; + } + + private: + //! The openjpeg output/input stream. + j2k_stream_t* mStream = nullptr; + }; +} + +#endif + diff --git a/externals/nitro/modules/c++/nitf/include/nitf/J2KWriter.hpp b/externals/nitro/modules/c++/nitf/include/nitf/J2KWriter.hpp index 521720b9b..a60527f2c 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/J2KWriter.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/J2KWriter.hpp @@ -33,6 +33,7 @@ #include "j2k/j2k_Writer.h" #include "nitf/coda-oss.hpp" +#include "nitf/exports.hpp" #include "nitf/Object.hpp" #include "nitf/System.hpp" #include "nitf/NITFException.hpp" @@ -41,22 +42,27 @@ namespace j2k { // Our own wrapper class so we can initialize j2k_WriterOptions - class WriterOptions final + class NITRO_NITFCPP_API WriterOptions final { j2k_WriterOptions value_{}; public: - // is there any reason to complicate this with getters and setters? - double& compressionRatio; - uint32_t& numResolutions; - WriterOptions() noexcept - : compressionRatio(value_.compressionRatio), - numResolutions(value_.numResolutions) { } + // make this look like CompressionParameters since that's existing code + WriterOptions() = default; + WriterOptions(const j2k_WriterOptions& v) noexcept : value_(v) {} + WriterOptions(float compressionRatio, uint32_t numResolutions) noexcept : + WriterOptions(j2k_WriterOptions{ compressionRatio , numResolutions }) {} WriterOptions(const WriterOptions&) = default; - WriterOptions& operator=(const WriterOptions&) = delete; + WriterOptions& operator=(const WriterOptions&) = default; WriterOptions(WriterOptions&&) = default; - WriterOptions& operator=(WriterOptions&&) = delete; + WriterOptions& operator=(WriterOptions&&) = default; ~WriterOptions() = default; + float getCompressionRatio() const noexcept { return value_.compressionRatio; } + uint32_t getNumResolutions() const noexcept { return value_.numResolutions; } + + void setCompressionRatio(float compRatio) noexcept { value_.compressionRatio = compRatio; } + void setNumResolutions(uint32_t numResoltuions) noexcept { value_.numResolutions = numResoltuions; } + // for use when calling C code j2k_WriterOptions* getNative() const noexcept { @@ -82,8 +88,13 @@ NITRO_DECLARE_CLASS_J2K(Writer) public: // TODO: put in Object.hpp? + template + TReturn callNativeOrThrow(Func f, Args&&... args) const // c.f. getNativeOrThrow() + { + return nitf::callNativeOrThrow(f, getNativeOrThrow(), std::forward(args)...); + } template - void callNativeOrThrowV(Func f, Args&&... args) // c.f. getNativeOrThrow() + void callNativeOrThrowV(Func f, Args&&... args) const // c.f. getNativeOrThrow() { nitf::callNativeOrThrowV(f, getNativeOrThrow(), std::forward(args)...); } @@ -99,7 +110,7 @@ NITRO_DECLARE_CLASS_J2K(Writer) }; } struct Container; // forward -struct Writer final +struct NITRO_NITFCPP_API Writer final { Writer() = delete; Writer(const Writer&) = delete; @@ -113,10 +124,32 @@ struct Writer final Writer(const Container&, const WriterOptions&); + const Container& getContainer() const; void setTile(uint32_t tileX, uint32_t tileY, std::span buf); void write(nitf::IOHandle&); private: details::Writer impl_; + const Container* pContainer_ = nullptr; +}; + +// Encapsulate some messy code that's needed to call Writer::setTile() +struct NITRO_NITFCPP_API WriteTiler final +{ + WriteTiler(Writer&, std::span); + WriteTiler(const WriteTiler&) = delete; + WriteTiler& operator=(const WriteTiler&) = delete; + WriteTiler(WriteTiler&&) = delete; + WriteTiler& operator=(WriteTiler&&) = delete; + ~WriteTiler() = default; + + void setTile(uint32_t tileX, uint32_t tileY, uint32_t i); + +private: + Writer& writer_; + std::span buf_; + size_t tileSize_ = 0; + uint32_t num_x_tiles_ = 0; + uint32_t numComponents_ = 0; }; } #endif // NITF_J2KWriter_hpp_INCLUDED_ diff --git a/externals/nitro/modules/c++/nitf/include/nitf/NITFBufferList.hpp b/externals/nitro/modules/c++/nitf/include/nitf/NITFBufferList.hpp index feebf097b..806559dc1 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/NITFBufferList.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/NITFBufferList.hpp @@ -26,6 +26,8 @@ #include #include +#include +#include // std::byte #include "nitf/coda-oss.hpp" #include "nitf/System.hpp" @@ -56,6 +58,11 @@ struct NITRO_NITFCPP_API NITFBuffer const void* mData = nullptr; size_t mNumBytes = 0; + + std::span getBytes() const noexcept + { + return std::span(static_cast(mData), mNumBytes); + } }; /*! diff --git a/externals/nitro/modules/c++/nitf/include/nitf/NITFException.hpp b/externals/nitro/modules/c++/nitf/include/nitf/NITFException.hpp index d7cbbb079..afdbd8f90 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/NITFException.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/NITFException.hpp @@ -165,8 +165,7 @@ template inline void callNativeOrThrowV(Func f, Native* pNative, Args&&... args) // c.f. getNativeOrThrow() { // save caller the trouble of figuring out a return type that won't be used - constexpr nitf_Error* pError = nullptr; // only needed for decltype() - using retval_t = decltype(f(pNative, std::forward(args)..., pError)); + using retval_t = decltype(f(pNative, std::forward(args)..., nullptr /*error*/)); std::ignore = callNativeOrThrow(f, pNative, std::forward(args)...); } } diff --git a/externals/nitro/modules/c++/nitf/include/nitf/Object.hpp b/externals/nitro/modules/c++/nitf/include/nitf/Object.hpp index 7e937806c..2bd3b25ea 100644 --- a/externals/nitro/modules/c++/nitf/include/nitf/Object.hpp +++ b/externals/nitro/modules/c++/nitf/include/nitf/Object.hpp @@ -106,7 +106,7 @@ class NITRO_NITFCPP_API Object } public: - virtual ~Object() /*noexcept(false)*/ { releaseHandle(); } + virtual ~Object() /*noexcept(noexcept(releaseHandle()))*/ { releaseHandle(); } //! Is the object valid (native object not null)? virtual bool isValid() const noexcept diff --git a/externals/nitro/modules/c++/nitf/include/nitf/UnitTests.hpp b/externals/nitro/modules/c++/nitf/include/nitf/UnitTests.hpp new file mode 100644 index 000000000..b29d4c7e2 --- /dev/null +++ b/externals/nitro/modules/c++/nitf/include/nitf/UnitTests.hpp @@ -0,0 +1,42 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#ifndef NITRO_nitf_UnitTests_hpp_INCLUDED_ +#define NITRO_nitf_UnitTests_hpp_INCLUDED_ +#pragma once + +#include +#include + +#include "nitf/exports.hpp" + +namespace nitf +{ + namespace Test + { + NITRO_NITFCPP_API std::string buildPluginsDir(); + NITRO_NITFCPP_API std::filesystem::path buildFileDir(const std::filesystem::path& relativePath); + NITRO_NITFCPP_API std::filesystem::path findInputFile(const std::filesystem::path&); + } +} + +#endif // NITRO_nitf_UnitTests_hpp_INCLUDED_ diff --git a/externals/nitro/modules/c++/nitf/source/BandInfo.cpp b/externals/nitro/modules/c++/nitf/source/BandInfo.cpp index 0ce08ba23..6ab4a1e0c 100644 --- a/externals/nitro/modules/c++/nitf/source/BandInfo.cpp +++ b/externals/nitro/modules/c++/nitf/source/BandInfo.cpp @@ -68,6 +68,14 @@ nitf::Field BandInfo::getSubcategory() const return nitf::Field(getNativeOrThrow()->subcategory); } +BandInfo::BandInfo(Representation representation_, // C4458: declaration of '...' hides class member + Subcategory subcategory_, // C4458: declaration of '...' hides class member + const std::string& imageFilterCondition, + const std::string& imageFilterCode) : BandInfo(representation_) +{ + init(representation_, subcategory_, imageFilterCondition, imageFilterCode); +} + nitf::Field BandInfo::getImageFilterCondition() const { return nitf::Field(getNativeOrThrow()->imageFilterCondition); diff --git a/externals/nitro/modules/c++/nitf/source/ByteProvider.cpp b/externals/nitro/modules/c++/nitf/source/ByteProvider.cpp index 6f4231a48..3a134bfd1 100644 --- a/externals/nitro/modules/c++/nitf/source/ByteProvider.cpp +++ b/externals/nitro/modules/c++/nitf/source/ByteProvider.cpp @@ -52,9 +52,17 @@ ByteProvider::ByteProvider(Record& record, { initialize(record, desData, numRowsPerBlock, numColsPerBlock); } - -ByteProvider::~ByteProvider() +ByteProvider::ByteProvider(Record& record, + const std::vector& desData, + size_t numRowsPerBlock, + size_t numColsPerBlock) : + mNumCols(0), + mOverallNumRowsPerBlock(0), + mNumColsPerBlock(0), + mNumBytesPerRow(0), + mNumBytesPerPixel(0) { + initialize(record, desData, numRowsPerBlock, numColsPerBlock); } template @@ -80,8 +88,9 @@ void ByteProvider::copyFromStreamAndClear(io::ByteStream& stream, copyFromStreamAndClear_(stream, rawBytes); } -void ByteProvider::initializeImpl(const Record& record, - const std::vector& desData, +template +void ByteProvider::initializeImpl_(const Record& record, + const std::vector& desData, size_t numRowsPerBlock, size_t numColsPerBlock) { @@ -117,6 +126,21 @@ void ByteProvider::initializeImpl(const Record& record, } } +void ByteProvider::initializeImpl(const Record& record, + const std::vector& desData, + size_t numRowsPerBlock, + size_t numColsPerBlock) +{ + initializeImpl_(record, desData, numRowsPerBlock, numColsPerBlock); +} +void ByteProvider::initializeImpl(const Record& record, + const std::vector& desData, + size_t numRowsPerBlock, + size_t numColsPerBlock) +{ + initializeImpl_(record, desData, numRowsPerBlock, numColsPerBlock); +} + void ByteProvider::initialize(const Record& record, const std::vector& desData, size_t numRowsPerBlock, @@ -133,9 +157,40 @@ void ByteProvider::initialize(const Record& record, } initializeImpl(record, desData, numRowsPerBlock, numColsPerBlock); } +void ByteProvider::initialize(const Record& record, + const std::vector& desData, + size_t numRowsPerBlock, + size_t numColsPerBlock) +{ + // Set image lengths + const size_t numImages = record.getNumImages(); + mImageDataLengths.resize(numImages); + for (size_t ii = 0; ii < numImages; ++ii) + { + nitf::ImageSegment imageSegment = record.getImages()[ii]; + nitf::ImageSubheader subheader = imageSegment.getSubheader(); + mImageDataLengths[ii] = subheader.getNumBytesOfImageData(); + } + initializeImpl(record, desData, numRowsPerBlock, numColsPerBlock); +} -void ByteProvider::getFileLayout(const nitf::Record& inRecord, - const std::vector& desData) +static void Write_data(io::ByteStream& byteStream, const ByteProvider::PtrAndLength& curData, + size_t& desDataLengths_ii) +{ + // Write data + byteStream.write(curData.first, curData.second); + desDataLengths_ii = curData.second; +} +static void Write_data(io::ByteStream& byteStream, const ByteProvider::PtrAndLength_t& curData, + size_t& desDataLengths_ii) +{ + // Write data + byteStream.write(curData); + desDataLengths_ii = curData.size(); +} +template +void ByteProvider::getFileLayout_(const nitf::Record& inRecord, + const std::vector& desData) { auto byteStream = std::make_shared(); @@ -246,10 +301,7 @@ void ByteProvider::getFileLayout(const nitf::Record& inRecord, writer.writeDESubheader(subheader, userSublen, record.getVersion()); desSubheaderLengths[ii] = byteStream->getSize() - prevSize; - // Write data - const PtrAndLength& curData(desData[ii]); - byteStream->write(curData.first, curData.second); - desDataLengths[ii] = curData.second; + Write_data(*byteStream, desData[ii], desDataLengths[ii]); } copyFromStreamAndClear(*byteStream, mDesSubheaderAndData); @@ -317,6 +369,16 @@ void ByteProvider::getFileLayout(const nitf::Record& inRecord, // DES is right after that mDesSubheaderFileOffset = offset; } +void ByteProvider::getFileLayout(const nitf::Record& inRecord, + const std::vector& desData) +{ + getFileLayout_(inRecord, desData); +} +void ByteProvider::getFileLayout(const nitf::Record& inRecord, + const std::vector& desData) +{ + getFileLayout_(inRecord, desData); +} mem::auto_ptr ByteProvider::getImageBlocker() const { diff --git a/externals/nitro/modules/c++/nitf/source/CompressedByteProvider.cpp b/externals/nitro/modules/c++/nitf/source/CompressedByteProvider.cpp index db97a73b4..bb15773b0 100644 --- a/externals/nitro/modules/c++/nitf/source/CompressedByteProvider.cpp +++ b/externals/nitro/modules/c++/nitf/source/CompressedByteProvider.cpp @@ -47,10 +47,20 @@ CompressedByteProvider::CompressedByteProvider(Record& record, initialize(record, bytesPerBlock, desData, numRowsPerBlock, numColsPerBlock); } +CompressedByteProvider::CompressedByteProvider(Record& record, + const std::vector >& bytesPerBlock, + const std::vector& desData, + size_t numRowsPerBlock, + size_t numColsPerBlock) : + ByteProvider() +{ + initialize(record, bytesPerBlock, desData, numRowsPerBlock, numColsPerBlock); +} -void CompressedByteProvider::initialize(const Record& record, +template +void CompressedByteProvider::initialize_(const Record& record, const std::vector >& bytesPerBlock, - const std::vector& desData, + const std::vector& desData, size_t numRowsPerBlock, size_t numColsPerBlock) { @@ -77,6 +87,22 @@ void CompressedByteProvider::initialize(const Record& record, mBytesInEachBlock = bytesPerBlock; initializeImpl(record, desData, numRowsPerBlock, numColsPerBlock); } +void CompressedByteProvider::initialize(const Record& record, + const std::vector >& bytesPerBlock, + const std::vector& desData, + size_t numRowsPerBlock, + size_t numColsPerBlock) +{ + initialize_(record, bytesPerBlock, desData, numRowsPerBlock, numColsPerBlock); +} +void CompressedByteProvider::initialize(const Record& record, + const std::vector >& bytesPerBlock, + const std::vector& desData, + size_t numRowsPerBlock, + size_t numColsPerBlock) +{ + initialize_(record, bytesPerBlock, desData, numRowsPerBlock, numColsPerBlock); +} size_t CompressedByteProvider::countBytesForCompressedImageData( size_t seg, size_t startRow, size_t numRowsToWrite) const diff --git a/externals/nitro/modules/c++/nitf/source/ImageBlocker.cpp b/externals/nitro/modules/c++/nitf/source/ImageBlocker.cpp index 41f70921e..52f8d3da0 100644 --- a/externals/nitro/modules/c++/nitf/source/ImageBlocker.cpp +++ b/externals/nitro/modules/c++/nitf/source/ImageBlocker.cpp @@ -252,20 +252,20 @@ size_t ImageBlocker::getNumBytesRequired(size_t startRow, return numBytes; } -void ImageBlocker::block(const void* input, - size_t numBytesPerPixel, - size_t numCols, - size_t numRowsPerBlock, - size_t numColsPerBlock, - size_t numValidRowsInBlock, - size_t numValidColsInBlock, - void* output) noexcept +static void block_(const std::byte* inputPtr, + size_t numBytesPerPixel, + size_t numCols, + const types::RowCol& perBlock, + const types::RowCol& validInBlock, + std::byte* outputPtr) noexcept { + const auto numRowsPerBlock = perBlock.row; + const auto numColsPerBlock = perBlock.col; + const auto numValidRowsInBlock = validInBlock.row; + const auto numValidColsInBlock = validInBlock.col; + const size_t inStride = numCols * numBytesPerPixel; const size_t outNumValidBytes = numValidColsInBlock * numBytesPerPixel; - const std::byte* inputPtr = static_cast(input); - std::byte* outputPtr = static_cast(output); - if (numValidColsInBlock == numColsPerBlock) { for (size_t row = 0; @@ -302,6 +302,30 @@ void ImageBlocker::block(const void* input, numPadRows * numColsPerBlock * numBytesPerPixel); } } +void ImageBlocker::block(const void* input, + size_t numBytesPerPixel, + size_t numCols, + size_t numRowsPerBlock, + size_t numColsPerBlock, + size_t numValidRowsInBlock, + size_t numValidColsInBlock, + void* output) noexcept +{ + const std::byte* inputPtr = static_cast(input); + std::byte* outputPtr = static_cast(output); + const types::RowCol perBlock(numRowsPerBlock, numColsPerBlock); + const types::RowCol validInBlock(numValidRowsInBlock, numValidColsInBlock); + block_(inputPtr, numBytesPerPixel, numCols, perBlock, validInBlock, outputPtr); +} +void ImageBlocker::block(std::span input, + size_t numBytesPerPixel, + size_t numCols, + const types::RowCol& perBlock, + const types::RowCol& validInBlock, + std::span output) noexcept +{ + block_(input.data(), numBytesPerPixel, numCols, perBlock, validInBlock, output.data()); +} void ImageBlocker::block(const void* input, size_t startRow, diff --git a/externals/nitro/modules/c++/nitf/source/J2KCompressionParameters.cpp b/externals/nitro/modules/c++/nitf/source/J2KCompressionParameters.cpp new file mode 100644 index 000000000..9a78018e8 --- /dev/null +++ b/externals/nitro/modules/c++/nitf/source/J2KCompressionParameters.cpp @@ -0,0 +1,33 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2017, MDA Information Systems LLC + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#include "nitf/J2KCompressionParameters.hpp" + +j2k::CompressionParameters::CompressionParameters(const types::RowCol& rawImageDims, const types::RowCol& tileDims, + double compressionRatio, size_t numResolutions) noexcept: + mRawImageDims(rawImageDims), mTileDims(tileDims), mCompressionRatio(compressionRatio), mNumResolutions(numResolutions), + mNumRowsOfTiles((mRawImageDims.row / mTileDims.row) + ((mRawImageDims.row % mTileDims.row) ? 1 : 0)), + mNumColsOfTiles((mRawImageDims.col / mTileDims.col) + ((mRawImageDims.col % mTileDims.col) ? 1 : 0)), + mNumTiles(mNumRowsOfTiles * mNumColsOfTiles) +{ +} diff --git a/externals/nitro/modules/c++/nitf/source/J2KCompressor.cpp b/externals/nitro/modules/c++/nitf/source/J2KCompressor.cpp new file mode 100644 index 000000000..b648f76c8 --- /dev/null +++ b/externals/nitro/modules/c++/nitf/source/J2KCompressor.cpp @@ -0,0 +1,674 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#include "nitf/J2KCompressor.hpp" + +#include + +#include "j2k/TileWriter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // std::byte +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nitf/J2KStream.hpp" +#include "nitf/J2KImage.hpp" +#include "nitf/J2KEncoder.hpp" +#include "nitf/J2KCompressionParameters.hpp" + +#undef min +#undef max + +static size_t writeImpl(void* buffer, size_t numBytes, void* data) +{ + auto compressedOutputStream = static_cast<::io::SeekableOutputStream*>(data); + if (compressedOutputStream != nullptr) + { + try + { + compressedOutputStream->write(buffer, numBytes); + return numBytes; + } + catch (const except::Exception&) {} + } + + // Openjpeg expects (OPJ_SIZE_T)-1 as the result of a failed + // call to a user provided write. + return static_cast(-1); +} + +static int64_t skipImpl(sys::Off_T bytesToSkip, void* data) +{ + auto compressedOutputStream = static_cast<::io::SeekableOutputStream*>(data); + if (compressedOutputStream != nullptr) + { + try + { + compressedOutputStream->seek(bytesToSkip, ::io::Seekable::CURRENT); + return bytesToSkip; + } + catch (const except::Exception&) {} + } + + // Openjpeg expects -1 as the result of a failed call to a user provided skip() + return -1; +} + +static bool seekImpl(int64_t numBytes, void* data) +{ + auto compressedOutputStream = static_cast<::io::SeekableOutputStream*>(data); + if (compressedOutputStream != nullptr) + { + try + { + compressedOutputStream->seek(numBytes, ::io::Seekable::START); + return true; + } + catch (const except::Exception&) {} + } + + // Openjpeg expects 0 (OPJ_FALSE) as the result of a failed call to a user provided seek() + return false; +} + +/*! + * \class OPJTileWriter + * \desc Implementation class for writing compressed tiles to an output stream. + * This class is used by OPJCompressor to do thread based tile compression. + */ +class TileWriter final +{ + ::io::SeekableOutputStream* mOutputStream = nullptr; + j2k::CompressionParameters mCompressionParams; + + j2k::Stream mStream; //! The openjpeg stream. + j2k::Image mImage; //! The openjpeg image. + j2k::Encoder mEncoder; //! The openjpeg encoder. + bool mIsCompressing = false; //! Whether we are currently compressing or not. + + void resizeTile(types::RowCol& tile, size_t tileIndex) noexcept + { + const auto tileDims = mCompressionParams.getTileDims(); + const auto rawImageDims = mCompressionParams.getRawImageDims(); + const auto numColsOfTiles = mCompressionParams.getNumColsOfTiles(); + const auto numRowsOfTiles = mCompressionParams.getNumRowsOfTiles(); + + const auto tileRow = tileIndex / numColsOfTiles; + if ((tileRow == numRowsOfTiles - 1) && (rawImageDims.row % tileDims.row != 0)) + { + tile.row = rawImageDims.row % tileDims.row; + } + + const auto tileCol = tileIndex - (tileRow * numColsOfTiles); + if ((tileCol == numColsOfTiles - 1) && (rawImageDims.col % tileDims.col != 0)) + { + tile.col = rawImageDims.col % tileDims.col; + } + } + +public: + /*! + * Constructor + * + * \param outputStream The output stream to write the J2K codestream to. + * + * \param compressionParams The J2K compression parameters. + */ + TileWriter(::io::SeekableOutputStream& outputStream, const j2k::CompressionParameters& compressionParams) : + mCompressionParams(compressionParams), + mStream(j2k::StreamType::OUTPUT), + mImage(mCompressionParams.getRawImageDims()), + mEncoder(mImage, mCompressionParams) + { + setOutputStream(outputStream); + j2k_stream_set_write_function(mStream.getNative(), writeImpl); + j2k_stream_set_seek_function(mStream.getNative(), seekImpl); + j2k_stream_set_skip_function(mStream.getNative(), skipImpl); + } + TileWriter(const TileWriter&) = delete; + TileWriter& operator=(const TileWriter&) = delete; + TileWriter(TileWriter&&) = delete; + TileWriter& operator=(TileWriter&&) = delete; + + /*! + * Destructor - calls end(). + */ + ~TileWriter() noexcept + { + try + { + end(); + } + catch (...) + { + } + } + + /*! + * Starts the J2K compression. The first call to flush() after this + * is invoked will write the J2K header to the output stream. + */ + void start() + { + if (mIsCompressing) + { + return; + } + + const auto startCompressSuccess = j2k_start_compress(mEncoder.getNative(), mImage.getNative(), mStream.getNative()); + if (!startCompressSuccess) + { + if (mEncoder.errorOccurred()) + { + const std::string opjErrorMsg = mEncoder.getErrorMessage(); + mEncoder.clearError(); + + throw except::Exception(Ctxt("Error starting compression with openjpeg error: " + opjErrorMsg)); + } + + throw except::Exception(Ctxt("Error starting compression.")); + } + mIsCompressing = true; + } + + /*! + * Ends the J2K compression. This will flush the J2K footer to the output stream. + */ + void end() + { + if (!mIsCompressing) + { + return; + } + + const auto endCompressSuccess = j2k_end_compress(mEncoder.getNative(), mStream.getNative()); + if (!endCompressSuccess) + { + if (mEncoder.errorOccurred()) + { + const std::string opjErrorMsg = mEncoder.getErrorMessage(); + mEncoder.clearError(); + + throw except::Exception(Ctxt("Error ending compression with openjpeg error: " + opjErrorMsg)); + } + + throw except::Exception(Ctxt("Error ending compression.")); + } + mIsCompressing = false; + } + + /*! + * Writes any J2K codestream data currently in the internal buffer used by + * openjpeg to the output stream. + */ + void flush() + { + if (!mIsCompressing) + { + throw except::Exception(Ctxt("Cannot flush data to output stream: compression has not been started.")); + } + + const auto flushSuccess = j2k_flush(mEncoder.getNative(), mStream.getNative()); + if (!flushSuccess) + { + if (mEncoder.errorOccurred()) + { + const std::string opjErrorMsg = mEncoder.getErrorMessage(); + mEncoder.clearError(); + + throw except::Exception(Ctxt("Failed to flush J2K codestream data with openjpeg error: " + opjErrorMsg)); + } + + throw except::Exception(Ctxt("Failed to flush J2K codestream data.")); + } + } + + /*! + * Calls opj_write_tile. Tiles should be structured as contiguous, + * increasing rows with a fixed column width i.e given 2x6 image + * with 2x3 tiles: + * + * 0 1 2 3 4 5 + * 6 7 8 9 10 11 + * + * The 0th tile: + * + * 0 1 2 + * 6 7 8 + * + * should be laid out in contiguous memory as: 0 1 2 6 7 8. + */ + + void writeTile(const std::byte* tileData, size_t tileIndex) + { + start(); + + const auto tileDims(mCompressionParams.getTileDims()); + + // Resize of the dimensions of this tile if it is a partial tile + types::RowCol resizedTileDims(tileDims); + resizeTile(resizedTileDims, tileIndex); + + // Create a smaller buffer for our partial tile + std::vector partialTileBuffer; + if ((resizedTileDims.col < tileDims.col) || (resizedTileDims.row < tileDims.row)) + { + partialTileBuffer.resize(resizedTileDims.area()); + for (size_t row = 0; row < resizedTileDims.row; ++row) + { + const auto srcTileRowStart = tileData + row * tileDims.col; + const std::span src(srcTileRowStart, resizedTileDims.col); + + // partialTileBuffer.data() + row * resizedTileDims.col + auto dest = partialTileBuffer.begin(); + std::advance(dest, gsl::narrow(row * resizedTileDims.col)); + + std::copy(src.begin(), src.end(), dest); + } + } + + const auto imageData = partialTileBuffer.empty() ? tileData : partialTileBuffer.data(); + const void* imageData_ = imageData; + + // Compress the tile - if an I/O error occurs in our write handler, + // the OPJEncoder error handler will get called. + const auto writeSuccess = j2k_write_tile(mEncoder.getNative(), + gsl::narrow(tileIndex), + static_cast(imageData_), gsl::narrow(resizedTileDims.area()), + mStream.getNative()); + if (!writeSuccess) + { + std::ostringstream os; + os << "Failed to compress tile " << tileIndex << " (rows: " << resizedTileDims.row << ", cols: " << resizedTileDims.col << ")"; + if (mEncoder.errorOccurred()) + { + const std::string opjErrorMsg = mEncoder.getErrorMessage(); + mEncoder.clearError(); + + os << " with openjpeg error: " << opjErrorMsg; + throw except::Exception(Ctxt(os.str())); + } + + throw except::Exception(Ctxt(os.str())); + } + } + + /*! + * Updates the output stream that openjpeg will write J2K codestream + * data to when flush() is called. + * + * \param outputStream The stream to write to. + */ + void setOutputStream(::io::SeekableOutputStream& outputStream) noexcept + { + mOutputStream = &outputStream; + j2k_stream_set_user_data(mStream.getNative(), mOutputStream, nullptr); + } +}; + +using BufferViewStream = io::BufferViewStream ; + +class CodestreamOp final +{ + types::RowCol getRowColIndices(size_t tileIndex) const noexcept + { + return types::RowCol( + tileIndex / mCompressionParams.getNumColsOfTiles(), + tileIndex % mCompressionParams.getNumColsOfTiles()); + } + + const size_t mStartTile; + std::shared_ptr* const mTileStreams; + const std::span mUncompressedImage; + j2k::CompressionParameters mCompressionParams; + + mutable std::unique_ptr mWriter; + std::vector mImageBlock; + std::span mpImageBlock; + +public: + CodestreamOp( + size_t startTile, + std::shared_ptr* tileStreams, + std::span uncompressedImage, + const j2k::CompressionParameters& compressionParams) : + mStartTile(startTile), + mTileStreams(tileStreams), + mUncompressedImage(uncompressedImage), + mCompressionParams(compressionParams) + { + mImageBlock.resize(mCompressionParams.getTileDims().area()); + mpImageBlock = std::span(mImageBlock.data(), mImageBlock.size()); + } + CodestreamOp(const CodestreamOp&) = delete; + CodestreamOp& operator=(const CodestreamOp&) = delete; + CodestreamOp(CodestreamOp&&) = default; + CodestreamOp& operator=(CodestreamOp&&) = delete; + + void operator()(size_t localTileIndex) const + { + const auto tileDims = mCompressionParams.getTileDims(); + const auto globalTileIndex = localTileIndex + mStartTile; + const auto fullDims = mCompressionParams.getRawImageDims(); + + // Need local indices to offset into the uncompressed image properly + const auto localTileIndices = getRowColIndices(localTileIndex); + + const types::RowCol localStart(localTileIndices.row * tileDims.row, localTileIndices.col * tileDims.col); + + const size_t offset = localStart.row * fullDims.col + localStart.col; + const std::span uncompressedImage(mUncompressedImage.data() + offset, mUncompressedImage.size_bytes() - offset); + + // Need global indices to determine if we're on the edge of the global image or not + const auto globalTileIndices = getRowColIndices(globalTileIndex); + const types::RowCol globalStart(globalTileIndices.row * tileDims.row, globalTileIndices.col * tileDims.col); + const types::RowCol globalEnd(std::min(globalStart.row + tileDims.row, fullDims.row), std::min(globalStart.col + tileDims.col, fullDims.col)); + + // Block it + const types::RowCol validInBlock(globalEnd.row - globalStart.row, globalEnd.col - globalStart.col); + nitf::ImageBlocker::block(uncompressedImage, + sizeof(std::byte), fullDims.col, + tileDims, validInBlock, + mpImageBlock); + + auto tileStream = mTileStreams[localTileIndex]; + if (!mWriter) + { + mWriter = std::make_unique(*tileStream, mCompressionParams); + + // Write out the header + // OpenJPEG makes us write the header, but we only want to keep it if we're tile 0 + mWriter->start(); + mWriter->flush(); + + if (globalTileIndex != 0) + { + tileStream->seek(0, io::Seekable::START); + } + } + else + { + mWriter->setOutputStream(*tileStream); + } + + // Write out the tile + mWriter->writeTile(mpImageBlock.data(), globalTileIndex); + mWriter->flush(); + } + + void finalize(bool keepFooter) + { + if (!mWriter) + { + return; + } + + // Writer::end() is required to clean up OpenJPEG objects + // This step also writes out the footer, which we may or may not + // actually want to keep + if (keepFooter) + { + // tileIdx is guaranteed to be in-bounds if keepFooter is true + const size_t lastTile = mCompressionParams.getNumTiles() - 1; + const size_t tileIdx = lastTile - mStartTile; + mWriter->setOutputStream(*mTileStreams[tileIdx]); + + // Write out the footer + mWriter->end(); + } + else + { + io::SeekableNullOutputStream outStream; + mWriter->setOutputStream(outStream); + + // Write out the footer + mWriter->end(); + } + } +}; + +j2k::Compressor::Compressor(const CompressionParameters& compressionParams, size_t numThreads) noexcept : + mCompressionParams(compressionParams), mNumThreads(numThreads) +{ +} + +size_t j2k::Compressor::getMaxBytesRequiredToCompress() const noexcept +{ + return getMaxBytesRequiredToCompress(mCompressionParams.getNumTiles()); +} + +/*! + * In rare cases, the "compressed" image will actually be slightly larger + * than the uncompressed image. This is a presumably worst case number + * here - it's probably much larger than it needs to be. + */ +constexpr long double POOR_COMPRESSION_SCALE_FACTOR = 2.0; +size_t j2k::Compressor::getMaxBytesRequiredToCompress(size_t numTiles) const noexcept +{ + const auto bytesPerTile = mCompressionParams.getTileDims().area(); + const auto maxBytes_ = gsl::narrow_cast(bytesPerTile * numTiles) * POOR_COMPRESSION_SCALE_FACTOR; + const auto maxBytes = gsl::narrow_cast(std::ceil(maxBytes_)); + return maxBytes; +} + +void j2k::Compressor::compress( + std::span rawImageData, + std::vector& compressedData, + std::vector& bytesPerTile) const +{ + compressedData.resize(getMaxBytesRequiredToCompress()); + const auto compressedDataView = compress(rawImageData, + std::span(compressedData.data(), compressedData.size()), bytesPerTile); + compressedData.resize(compressedDataView.size()); +} + +std::span j2k::Compressor::compress(std::span rawImageData, + std::span compressedData, + std::vector& bytesPerTile) const +{ + return compressTileSubrange(rawImageData, + types::Range(0, mCompressionParams.getNumTiles()), + compressedData, + bytesPerTile); +} + +void j2k::Compressor::compressTile( + std::span rawImageData, + size_t tileIndex, + std::vector& compressedTile) const +{ + compressedTile.resize(getMaxBytesRequiredToCompress(1)); + std::vector bytesPerTile; + std::span compressedView(compressedTile.data(), compressedTile.size()); + compressedView = compressTileSubrange(rawImageData, types::Range(tileIndex, 1), + compressedView, bytesPerTile); + compressedTile.resize(compressedView.size()); +} + +std::span j2k::Compressor::compressTile( + std::span rawImageData, + size_t tileIndex, + std::span compressedTile) const +{ + std::vector bytesPerTile; + return compressTileSubrange(rawImageData, types::Range(tileIndex, 1), + compressedTile, bytesPerTile); +} + +std::span j2k::Compressor::compressRowSubrange( + std::span rawImageData, + size_t globalStartRow, + size_t numLocalRows, + std::span compressedData, + types::Range& tileRange, + std::vector& bytesPerTile) const +{ + // Sanity checks + const size_t numRowsInTile = mCompressionParams.getTileDims().row; + if (globalStartRow % numRowsInTile != 0) + { + std::ostringstream ostr; + ostr << "Global start row = " << globalStartRow << " must be a multiple of number of rows in tile = " << numRowsInTile; + throw except::Exception(Ctxt(ostr.str())); + } + + if ((numLocalRows % numRowsInTile != 0) && + (globalStartRow + numLocalRows != mCompressionParams.getRawImageDims().row)) + { + std::ostringstream ostr; + ostr << "Number of local rows = " << numLocalRows << " must be a multiple of number of rows in tile = " << numRowsInTile; + throw except::Exception(Ctxt(ostr.str())); + } + + const auto startRowOfTiles = globalStartRow / numRowsInTile; + const auto numRowsOfTiles = math::ceilingDivide(numLocalRows, numRowsInTile); + + const auto numColsOfTiles = mCompressionParams.getNumColsOfTiles(); + tileRange.mStartElement = startRowOfTiles * numColsOfTiles; + tileRange.mNumElements = numRowsOfTiles * numColsOfTiles; + + return compressTileSubrange(rawImageData, + tileRange, + compressedData, + bytesPerTile); +} + +std::span j2k::Compressor::compressTileSubrange( + std::span rawImageData, + const types::Range& tileRange, + std::span compressedData, + std::vector& bytesPerTile) const +{ + // We write initially directly into 'compressedData', reserving the max + // expected # of bytes/tile + const auto numTiles = tileRange.mNumElements; + const auto maxNumBytesPerTile = getMaxBytesRequiredToCompress(1); + const auto numBytesNeeded = maxNumBytesPerTile * numTiles; + if (compressedData.size() < numBytesNeeded) + { + std::ostringstream ostr; + ostr << "Require " << numBytesNeeded << " bytes for compression of " + << numTiles << " tiles but only received " << compressedData.size() << " bytes"; + throw except::Exception(Ctxt(ostr.str())); + } + + auto compressedPtr = compressedData.data(); + std::vector> tileStreams(numTiles); + for (size_t tile = 0; tile < numTiles; ++tile, compressedPtr += maxNumBytesPerTile) + { + auto bufferStream = std::make_shared(mem::BufferView(compressedPtr, maxNumBytesPerTile)); + tileStreams[tile] = bufferStream; + } + + std::vector ops; + ops.reserve(mNumThreads); + for (size_t ii = 0; ii < mNumThreads; ++ii) + { + ops.emplace_back(tileRange.mStartElement, tileStreams.data(), rawImageData, mCompressionParams); + } + + // Compress the image + mt::runWorkSharingBalanced1D(numTiles, mNumThreads, ops); + + // End compression for each thread + // If the last tile is in 'tileRange', we need to ensure that we write the + // footer exactly once + bool keepFooter = (tileRange.endElement() == mCompressionParams.getNumTiles()); + for (auto& op : ops) + { + op.finalize(keepFooter); + keepFooter = false; + } + + // At this point the tiles are all compressed into 'compressedData' but + // they're not contiguous. Shift memory around to get a contiguous buffer. + size_t numBytesWritten = 0; + bytesPerTile.resize(numTiles); + + auto dest = compressedData.data(); + for (size_t tileNum = 0; tileNum < numTiles; ++tileNum) + { + BufferViewStream& tileStream = *tileStreams[tileNum]; + + // This shouldn't be possible because if a tile was too big, it would + // have thrown when compressing + const auto numBytesThisTile = gsl::narrow(tileStream.tell()); + if (numBytesWritten + numBytesThisTile > compressedData.size()) + { + std::ostringstream os; + os << "Cannot write " << numBytesThisTile << " bytes for tile " << tileNum << " at byte offset " << numBytesWritten + << " - exceeds maximum compressed image size (" << compressedData.size() << " bytes)!"; + throw except::Exception(Ctxt(os.str())); + } + + const auto src = tileStream.get(); + + // Copy the tile to the output buffer + // Since we're reusing the same buffer for the contiguous output, + // memory addresses may overlap so we need to use memmove() + if (src != dest) + { + ::memmove(dest, src, numBytesThisTile); + } + + numBytesWritten += numBytesThisTile; + bytesPerTile[tileNum] = numBytesThisTile; + dest += numBytesThisTile; + } + + return std::span(compressedData.data(), numBytesWritten); +} + + diff --git a/externals/nitro/modules/c++/nitf/source/J2KContainer.cpp b/externals/nitro/modules/c++/nitf/source/J2KContainer.cpp index e681a6112..7adb7281d 100644 --- a/externals/nitro/modules/c++/nitf/source/J2KContainer.cpp +++ b/externals/nitro/modules/c++/nitf/source/J2KContainer.cpp @@ -31,6 +31,10 @@ j2k::details::Container::Container(j2k_Container* x) getNativeOrThrow(); } j2k::Container::Container(j2k_Container*&& x) : impl_(x) {} +j2k_Container* j2k::Container::getNativeOrThrow() const +{ + return impl_.getNativeOrThrow(); +} j2k::Writer j2k::Container::createWriter(const WriterOptions& options) const { @@ -65,7 +69,9 @@ NITF_j2k__Container_IMPL(int, getImageType); size_t j2k::Container::tileSize() const { - return getTileWidth() * getTileHeight(); + size_t retval = getTileWidth(); // avoid CodeQL diagnostic + retval *= getTileHeight(); + return retval; } size_t j2k::Container::numBytes(uint32_t i) const diff --git a/externals/nitro/modules/c++/nitf/source/J2KEncoder.cpp b/externals/nitro/modules/c++/nitf/source/J2KEncoder.cpp new file mode 100644 index 000000000..b005bb437 --- /dev/null +++ b/externals/nitro/modules/c++/nitf/source/J2KEncoder.cpp @@ -0,0 +1,122 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2017, MDA Information Systems LLC + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#include "nitf/J2KEncoder.hpp" + +#include +#include +#include + +#include +#include +#include + +#undef min +#undef max + +namespace +{ + void errorHandlerImpl(const char* msg, void* userData) + { + auto& encoder = *static_cast(userData); + encoder.setError(msg); + } +} + +struct j2k::Encoder::Impl final +{ + //! Scoped encoder - Internal RAII wrapper around opj_codec_t - + //! calls opj_create_compress() on construction and opj_destroy_codec() + //! on destruction. + std::unique_ptr mEncoder; + std::unique_ptr mEncoderParameters; + + //! The openjpeg error message. + std::string mErrorMessage; + + Impl() noexcept : + mEncoder(j2k_create_compress(/*OPJ_CODEC_J2K*/), j2k_destroy_codec), + mEncoderParameters(j2k_set_default_encoder_parameters(), j2k_destroy_encoder_parameters) + { + } + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; +}; + +j2k::Encoder::~Encoder() = default; + +j2k::Encoder::Encoder(Image& image, const CompressionParameters& compressionParams) + : pImpl_(std::make_unique()) +{ + j2k_initEncoderParameters(pImpl_->mEncoderParameters.get(), + compressionParams.getTileDims().row, compressionParams.getTileDims().col, + compressionParams.getCompressionRatio(), + compressionParams.getNumResolutions()); + + const auto handlerSuccess = j2k_set_error_handler(getNative(), errorHandlerImpl, this); + if (!handlerSuccess) + { + throw except::Exception(Ctxt("Failed to setup openjpeg encoder error handler.")); + } + + const auto setupSuccess = j2k_setup_encoder(getNative(), pImpl_->mEncoderParameters.get(), image.getNative()); + if (!setupSuccess) + { + if (errorOccurred()) + { + const std::string opjErrorMsg = getErrorMessage(); + clearError(); + + std::ostringstream os; + os << "Failed to setup openjpeg encoder with openjpeg error: " << opjErrorMsg; + throw except::Exception(Ctxt(os.str())); + } + + throw except::Exception(Ctxt("Failed to setup openjpeg encoder.")); + } +} + +j2k_codec_t* j2k::Encoder::getNative() const noexcept +{ + return pImpl_->mEncoder.get(); +} + +std::string j2k::Encoder::getErrorMessage() const +{ + return pImpl_->mErrorMessage; +} + +bool j2k::Encoder::errorOccurred() const +{ + return !getErrorMessage().empty(); +} + +void j2k::Encoder::setError(const std::string& msg) +{ + pImpl_->mErrorMessage = msg; +} + +void j2k::Encoder::clearError() noexcept +{ + pImpl_->mErrorMessage.clear(); +} diff --git a/externals/nitro/modules/c++/nitf/source/J2KImage.cpp b/externals/nitro/modules/c++/nitf/source/J2KImage.cpp new file mode 100644 index 000000000..9d536c217 --- /dev/null +++ b/externals/nitro/modules/c++/nitf/source/J2KImage.cpp @@ -0,0 +1,86 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2017, MDA Information Systems LLC + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#include "nitf/J2KImage.hpp" + +#include + +#include +#include +#include + +j2k::Image::Image(const types::RowCol& rawImageDims) +{ + initComponents(rawImageDims); + + // Create our image + mImage = j2k_image_tile_create(1, &mImageComponentParams, J2K_CLRSPC_GRAY); + if (mImage == nullptr) + { + std::ostringstream os; + os << "Failed to create an openjpeg image handle using the provided raw image dimensions"; + os << " (rows: "<< rawImageDims.row << ", cols : " << rawImageDims.col << ")!"; + throw except::Exception(Ctxt(os.str())); + } + + // set up the image + initImage(); +} + +j2k::Image::~Image() +{ + j2k_image_destroy(mImage); +} + +void j2k::Image::initComponents(const types::RowCol& rawImageDims) +{ + // The width and height of our single component will span the + // full image extent + mImageComponentParams.w = gsl::narrow(rawImageDims.col); + mImageComponentParams.h = gsl::narrow(rawImageDims.row); + + // Precision and bit depth + mImageComponentParams.prec = 8; + mImageComponentParams.bpp = 8; + + // Starting corner of our component in the reference grid + mImageComponentParams.x0 = 0; + mImageComponentParams.y0 = 0; + + // Horizontal separation of each sample with respect to the reference grid + mImageComponentParams.dx = 1; + + // Vertical separation of each sample with respect to the reference grid + mImageComponentParams.dy = 1; + + // Signed or unsigned data - fixed to unsigned for grayscale image data + mImageComponentParams.sgnd = 0; +} + +void j2k::Image::initImage() +{ + j2k_image_init(mImage, 0, 0, + gsl::narrow(mImageComponentParams.w), gsl::narrow(mImageComponentParams.h), + 1, // One image component corresponding to the full grayscale image + J2K_CLRSPC_GRAY); +} diff --git a/externals/nitro/modules/c++/nitf/source/J2KStream.cpp b/externals/nitro/modules/c++/nitf/source/J2KStream.cpp new file mode 100644 index 000000000..7bbd465ae --- /dev/null +++ b/externals/nitro/modules/c++/nitf/source/J2KStream.cpp @@ -0,0 +1,47 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * (C) Copyright 2017, MDA Information Systems LLC + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#include "nitf/J2KStream.hpp" + +#include +#include + +#include "nitf/NITFException.hpp" + +j2k::Stream::Stream(j2k::StreamType streamType, size_t chunkSize) +{ + const auto isInputStream = streamType == j2k::StreamType::INPUT; + + mStream = j2k_stream_create(chunkSize, isInputStream); + if (!mStream) + { + std::ostringstream os; + os << "Failed creating an openjpeg stream with a chunk size of " << chunkSize << " bytes."; + throw except::Exception(Ctxt(os.str())); + } +} + +j2k::Stream::~Stream() +{ + j2k_stream_destroy(mStream); +} \ No newline at end of file diff --git a/externals/nitro/modules/c++/nitf/source/J2KWriter.cpp b/externals/nitro/modules/c++/nitf/source/J2KWriter.cpp index c65ad311b..31f721954 100644 --- a/externals/nitro/modules/c++/nitf/source/J2KWriter.cpp +++ b/externals/nitro/modules/c++/nitf/source/J2KWriter.cpp @@ -23,6 +23,8 @@ #include "nitf/J2KWriter.hpp" +#include + #include #include "nitf/J2KContainer.hpp" @@ -35,7 +37,20 @@ j2k::details::Writer::Writer(j2k_Writer* x) j2k::Writer::Writer(j2k_Writer*&& x) : impl_(x) {} j2k::Writer::Writer(const Container& container, const WriterOptions& options) - : Writer(container.createWriter(options)) { } + : Writer(container.createWriter(options)) +{ + pContainer_ = &container; +} + +const j2k::Container& j2k::Writer::getContainer() const +{ + auto pContainer = impl_.callNativeOrThrow(j2k_Writer_getContainer); + if (pContainer != pContainer_->getNativeOrThrow()) + { + throw std::logic_error("Pointers to native containers don't match!"); + } + return *pContainer_; +} void j2k::Writer::setTile(uint32_t tileX, uint32_t tileY, std::span buf) { @@ -46,3 +61,22 @@ void j2k::Writer::write(nitf::IOHandle& handle) { impl_.callNativeOrThrowV(j2k_Writer_write, handle.getNativeOrThrow()); } + +j2k::WriteTiler::WriteTiler(Writer& writer, std::span buf) : writer_(writer), buf_(buf) +{ + const auto& container = writer_.getContainer(); + this->tileSize_ = container.tileSize(); + this->num_x_tiles_ = container.getTilesX(); + this->numComponents_ = container.getNumComponents(); +} +void j2k::WriteTiler::setTile(uint32_t tileX, uint32_t tileY, uint32_t i) +{ + //const auto offset = container.bufferOffset(tileX, tileY, i); + const auto index = tileY * num_x_tiles_ + tileX % num_x_tiles_; + const auto bytes = writer_.getContainer().numBytes(i); + const auto offset_ = index * tileSize_ * bytes * numComponents_; + const auto offset = gsl::narrow(offset_); + + const std::span buf(buf_.data() + offset, tileSize_); + writer_.setTile(tileX, tileY, buf); +} \ No newline at end of file diff --git a/externals/nitro/modules/c++/nitf/source/SubWindow.cpp b/externals/nitro/modules/c++/nitf/source/SubWindow.cpp index 4fb74faab..79a19afd1 100644 --- a/externals/nitro/modules/c++/nitf/source/SubWindow.cpp +++ b/externals/nitro/modules/c++/nitf/source/SubWindow.cpp @@ -91,9 +91,10 @@ SubWindow::SubWindow(const ImageSubheader& subheader) : SubWindow::~SubWindow() { - if (isValid() && getNative()->downsampler) + auto downsampler = getNative()->downsampler; + if (downsampler) { - nitf::DownSampler ds(getNativeOrThrow()->downsampler); + nitf::DownSampler ds(downsampler); //decrement the current DownSampler ds.decRef(); } diff --git a/externals/nitro/modules/c++/nitf/source/UnitTests.cpp b/externals/nitro/modules/c++/nitf/source/UnitTests.cpp new file mode 100644 index 000000000..6b628ef8f --- /dev/null +++ b/externals/nitro/modules/c++/nitf/source/UnitTests.cpp @@ -0,0 +1,213 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#include "nitf/UnitTests.hpp" + +#include + +#include +#include +#include + +namespace fs = std::filesystem; + +static const sys::OS os; +static inline std::string Configuration() // "Configuration" is typically "Debug" or "Release" +{ + return os.getSpecialEnv("Configuration"); +} +static inline std::string Platform() +{ + return os.getSpecialEnv("Platform"); +} + +static const fs::path& getCurrentExecutable() +{ + static const auto exec = fs::absolute(os.getCurrentExecutable()); + return exec; +} +static fs::path current_path() +{ + // the current working directory can change while a program is running + return fs::absolute(fs::current_path()); +} + +// https://stackoverflow.com/questions/13794130/visual-studio-how-to-check-used-c-platform-toolset-programmatically +static inline std::string PlatformToolset() +{ + // https://docs.microsoft.com/en-us/cpp/build/how-to-modify-the-target-framework-and-platform-toolset?view=msvc-160 +#ifdef _WIN32 +#if _MSC_FULL_VER >= 190000000 + return "v142"; +#else +#error "Don't know $(PlatformToolset) value.'" +#endif +#else + // Linux + return ""; +#endif +} + +static std::optional findRoot(const fs::path& p) +{ + if (is_regular_file(p / "LICENSE") && is_regular_file(p / "README.md") && is_regular_file(p / "CMakeLists.txt")) + { + return p; + } + return p.parent_path() == p ? std::optional() : findRoot(p.parent_path()); +} +static fs::path findRoot(fs::path& exec_root, fs::path& cwd_root) +{ + static const auto exec_root_ = findRoot(getCurrentExecutable()); + if (exec_root_.has_value()) + { + exec_root = exec_root_.value(); + return exec_root; + } + + // CWD can change while the program is running + const auto cwd_root_ = findRoot(current_path()); + cwd_root = cwd_root_.value(); + return cwd_root; +} +static fs::path findRoot() +{ + fs::path exec_root, cwd_root; + return findRoot(exec_root, cwd_root); +} + +static fs::path make_waf_install(const fs::path& p) +{ + // just "install" on Linux; "install-Debug-x64.v142" on Windows +#ifdef _WIN32 + const auto configuration_and_platform = Configuration() + "-" + Platform() + "." + PlatformToolset(); + return p / ("install-" + configuration_and_platform); +#else + // Linux + return p / "install"; +#endif +} + +static fs::path make_cmake_install(const fs::path& exec, const fs::path& relativePath) +{ + const auto root = findRoot(); + + auto out = exec; + fs::path configuration_and_platform; + fs::path build; + while (out.parent_path() != root) + { + configuration_and_platform = build.stem(); // "x64-Debug" + build = out; // "...\out\build" + out = out.parent_path(); // "...\out" + } + + fs::path install; + const sys::DirectoryEntry dirEntry(out.string()); + for (auto entry : dirEntry) + { + str::upper(entry); + if (str::contains(entry, "INSTALL")) + { + install = out / dirEntry.getCurrent(); // preserve orignal case + if (is_directory(install)) + { + break; + } + } + } + + if (is_directory(install / configuration_and_platform / relativePath)) + { + return install / configuration_and_platform; + } + else + { + return install; + } +} + +static std::string makeRelative(const fs::path& path, const fs::path& root) +{ + // remove the "root" part from "path" + std::string relative = path.string(); + str::replaceAll(relative, root.string(), ""); + return relative; +} +static std::string relativeRoot() +{ + fs::path exec_root, cwd_root; + findRoot(exec_root, cwd_root); + + if (!exec_root.empty()) + { + return makeRelative(getCurrentExecutable(), exec_root); + } + + assert(!cwd_root.empty()); + return makeRelative(current_path(), cwd_root); +} + +static bool is_cmake_build() +{ + static const auto retlativeRoot = relativeRoot(); + static const auto retval = + (str::starts_with(retlativeRoot, "/out") || str::starts_with(retlativeRoot, "\\out")) || + (str::starts_with(retlativeRoot, "/build") || str::starts_with(retlativeRoot, "\\build")); + return retval; +} + +static fs::path buildDir(const fs::path& relativePath) +{ + std::clog << "getCurrentExecutable(): " << getCurrentExecutable() << '\n'; + std::clog << "current_path(): " << current_path() << '\n'; + + static const auto& exec = getCurrentExecutable(); + static const auto exec_filename = exec.filename(); + + if (exec_filename == "testhost.exe") + { + // Running in Visual Studio on Windows + return current_path() / relativePath; + } + + const auto install = is_cmake_build() ? make_cmake_install(exec, relativePath) : make_waf_install(findRoot()); + return install / relativePath; +} + +std::string nitf::Test::buildPluginsDir() +{ + const auto plugins = buildDir(fs::path("share") / "nitf" / "plugins"); + return plugins.string(); +} + +fs::path nitf::Test::buildFileDir(const fs::path& relativePath) +{ + const auto root = findRoot(); + return root / relativePath; +} + +fs::path nitf::Test::findInputFile(const fs::path& inputFile) +{ + const auto root = findRoot(); + return root / inputFile; +} \ No newline at end of file diff --git a/externals/nitro/modules/c++/nitf/unittests/TestCase.h b/externals/nitro/modules/c++/nitf/unittests/TestCase.h index e42301117..45c35cc24 100644 --- a/externals/nitro/modules/c++/nitf/unittests/TestCase.h +++ b/externals/nitro/modules/c++/nitf/unittests/TestCase.h @@ -40,9 +40,11 @@ catch(const except::Throwable11& ex) { die_printf("%s: FAILED: Exception thrown: %s\n", std::string(#X).c_str(), ex.what()); } # define TEST_ASSERT(X) if (!(X)) { die_printf("%s (%s,%s,%d): FAILED: Value should not be NULL\n", testName.c_str(), __FILE__, SYS_FUNC, __LINE__); } # define TEST_ASSERT_NULL(X) if ((X) != nullptr) { die_printf("%s (%s,%s,%d): FAILED: Value should be NULL\n", testName.c_str(), __FILE__, SYS_FUNC, __LINE__); } +# define TEST_ASSERT_NOT_NULL(X) if ((X) == nullptr) { die_printf("%s (%s,%s,%d): FAILED: Value should not be NULL\n", testName.c_str(), __FILE__, SYS_FUNC, __LINE__); } # define TEST_ASSERT_FALSE(X) if ((X)) { die_printf("%s (%s,%s,%d): FAILED: Value should evaluate to false\n", testName.c_str(), __FILE__, SYS_FUNC, __LINE__); } # define TEST_ASSERT_TRUE(X) if (!(X)) { die_printf("%s (%s,%s,%d): FAILED: Value should evaluate to true\n", testName.c_str(), __FILE__, SYS_FUNC, __LINE__); } # define TEST_ASSERT_EQ(X1, X2) if ((X1) != (X2)) { die_printf("%s (%s,%s,%d): FAILED: Recv'd %s, Expected %s\n", testName.c_str(), __FILE__, SYS_FUNC, __LINE__, str::toString(X1).c_str(), str::toString(X2).c_str()); } +# define TEST_ASSERT_EQ_STR(X1, X2) TEST_ASSERT_EQ(std::string(X1), std::string(X2)) # define TEST_ASSERT_EQ_MSG(msg, X1, X2) if ((X1) != (X2)) die_printf("%s (%s,%d): FAILED (%s): Recv'd %s, Expected %s\n", testName.c_str(), __FILE__, __LINE__, (msg).c_str(), str::toString((X1)).c_str(), str::toString((X2)).c_str()); # define TEST_ASSERT_NOT_EQ(X1, X2) if ((X1) == (X2)) { die_printf("%s (%s,%s,%d): FAILED: Recv'd %s should not equal %s\n", testName.c_str(), __FILE__, SYS_FUNC, __LINE__, str::toString(X1).c_str(), str::toString(X2).c_str()); } # define TEST_ASSERT_NOT_EQ_MSG(msg, X1, X2) if ((X1) == (X2)) die_printf("%s (%s,%d): FAILED (%s): Recv'd %s should not equal %s\n", testName.c_str(), __FILE__, __LINE__, (msg).c_str(), str::toString((X1)).c_str(), str::toString((X2)).c_str()); diff --git a/externals/nitro/modules/c++/nitf/unittests/test_create_nitf++.cpp b/externals/nitro/modules/c++/nitf/unittests/test_create_nitf++.cpp index da2b9f76b..23a02686d 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_create_nitf++.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_create_nitf++.cpp @@ -51,7 +51,7 @@ CODA_OSS_disable_warning_pop #include "TestCase.h" -void populateFileHeader(nitf::Record& record, const std::string& title) +static void populateFileHeader(nitf::Record& record, const std::string& title) { /* the file header is already created, so just grab it */ nitf::FileHeader header = record.getHeader(); @@ -61,7 +61,7 @@ void populateFileHeader(nitf::Record& record, const std::string& title) } -void setCornersFromDMSBox(nitf::ImageSubheader& header) +static void setCornersFromDMSBox(nitf::ImageSubheader& header) { /* * You could do this in degrees as easily @@ -102,135 +102,128 @@ void setCornersFromDMSBox(nitf::ImageSubheader& header) header.setCornersFromLatLons(NITF_CORNERS_DECIMAL, corners); } -namespace test_create_nitf_with_byte_provider +static void test_create_nitf_with_byte_provider__addImageSegment(nitf::Record& record, + bool shouldCompress = false) { - void addImageSegment(nitf::Record& record, - bool shouldCompress = false) - { - nitf::ImageSegment segment = record.newImageSegment(); - nitf::ImageSubheader header = segment.getSubheader(); + nitf::ImageSegment segment = record.newImageSegment(); + nitf::ImageSubheader header = segment.getSubheader(); - header.getImageId().set("NITRO-TEST"); - header.getImageDateAndTime().set("20080812000000"); + header.getImageId().set("NITRO-TEST"); + header.getImageDateAndTime().set("20080812000000"); - if (shouldCompress) - { - header.getImageCompression().set("C8"); - } + if (shouldCompress) + { + header.getImageCompression().set("C8"); + } - /* Set the geo-corners to Ann Arbor, MI */ - setCornersFromDMSBox(header); + /* Set the geo-corners to Ann Arbor, MI */ + setCornersFromDMSBox(header); - const size_t NUM_BANDS = 1; - std::vector bands(NUM_BANDS, nitf::BandInfo()); - for (size_t ii = 0; ii < bands.size(); ++ii) - { - bands[ii].init(nitf::Representation::M, /* The band representation, Nth band */ - nitf::Subcategory::None, /* The band subcategory */ - "N", /* The band filter condition */ - " "); /* The band standard image filter code */ + const size_t NUM_BANDS = 1; + std::vector bands(NUM_BANDS, nitf::BandInfo()); + for (size_t ii = 0; ii < bands.size(); ++ii) + { + bands[ii].init(nitf::Representation::M, /* The band representation, Nth band */ + nitf::Subcategory::None, /* The band subcategory */ + "N", /* The band filter condition */ + " "); /* The band standard image filter code */ - } + } - const auto iRep = nitf::ImageRepresentation::MONO; - header.setPixelInformation(nitf::PixelValueType::Integer, /* Pixel value type */ - 8, /* Number of bits/pixel */ - 8, /* Actual number of bits/pixel */ - "R", /* Pixel justification */ - iRep, /* Image representation */ - "VIS", /* Image category */ - bands); /* Band information object list */ + const auto iRep = nitf::ImageRepresentation::MONO; + header.setPixelInformation(nitf::PixelValueType::Integer, /* Pixel value type */ + 8, /* Number of bits/pixel */ + 8, /* Actual number of bits/pixel */ + "R", /* Pixel justification */ + iRep, /* Image representation */ + "VIS", /* Image category */ + bands); /* Band information object list */ /* for fun, let's add a comment */ - header.insertImageComment("NITF generated by NITRO", 0); - - // The image mode P is part of an awful hack to workaround us not - // having a compression plugin for blocking mode P. - // If you run this test, it will throw an error. The error will reference - // a file in the j2k plugin. To make this test run, go to the file - // and disable the check for blocking mode B. - // To the best of my knowledge, nothing bad happens as a result. - header.setBlocking(NITRO_IMAGE.height, /*!< The number of rows */ - NITRO_IMAGE.width, /*!< The number of columns */ - NITRO_IMAGE.height, /*!< The number of rows/block */ - NITRO_IMAGE.width, /*!< The number of columns/block */ - nitf::BlockingMode::Pixel); /*!< Image mode */ - } + header.insertImageComment("NITF generated by NITRO", 0); + + // The image mode P is part of an awful hack to workaround us not + // having a compression plugin for blocking mode P. + // If you run this test, it will throw an error. The error will reference + // a file in the j2k plugin. To make this test run, go to the file + // and disable the check for blocking mode B. + // To the best of my knowledge, nothing bad happens as a result. + header.setBlocking(NITRO_IMAGE.height, /*!< The number of rows */ + NITRO_IMAGE.width, /*!< The number of columns */ + NITRO_IMAGE.height, /*!< The number of rows/block */ + NITRO_IMAGE.width, /*!< The number of columns/block */ + nitf::BlockingMode::Pixel); /*!< Image mode */ +} - void writeNITF(nitf::Record& record, const std::string& filename) +static void test_create_nitf_with_byte_provider__writeNITF(nitf::Record& record, const std::string& filename) +{ + constexpr size_t NUM_BANDS = 1; + /* + * COMPRESSION: + * Right now, this is writing a single-segment single-block uncompressed + * NITF. If you want to compress, you would compress the data here. + * + * bytesPerBlock has an element for each image segment. Each image segment + * element has an element for each block. If you're compressing into a + * single tile, you can simple populate the single element with the + * compressed size of the image. + * + * If you are compressing to multiple tiles (blocks), each tile will have + * a different size, hence the need for a vector. + * Once you have CompressedByteProvider constructed, everything else + * should work the same + */ + const std::vector > bytesPerBlock{ { static_cast(NITRO_IMAGE.width) * NITRO_IMAGE.height * NUM_BANDS } }; + nitf::CompressedByteProvider byteProvider(record, bytesPerBlock); + nitf::Off fileOffset; + nitf::NITFBufferList buffers; + byteProvider.getBytes(NITRO_IMAGE.data, 0, NITRO_IMAGE.height, + fileOffset, buffers); + io::FileOutputStream outputStream(filename); + for (size_t ii = 0; ii < buffers.mBuffers.size(); ++ii) { - constexpr size_t NUM_BANDS = 1; - /* - * COMPRESSION: - * Right now, this is writing a single-segment single-block uncompressed - * NITF. If you want to compress, you would compress the data here. - * - * bytesPerBlock has an element for each image segment. Each image segment - * element has an element for each block. If you're compressing into a - * single tile, you can simple populate the single element with the - * compressed size of the image. - * - * If you are compressing to multiple tiles (blocks), each tile will have - * a different size, hence the need for a vector. - * Once you have CompressedByteProvider constructed, everything else - * should work the same - */ - std::vector > bytesPerBlock(1); - bytesPerBlock[0].push_back(NITRO_IMAGE.width * NITRO_IMAGE.height * NUM_BANDS); - nitf::CompressedByteProvider byteProvider(record, - bytesPerBlock); - nitf::Off fileOffset; - nitf::NITFBufferList buffers; - byteProvider.getBytes(NITRO_IMAGE.data, 0, NITRO_IMAGE.height, - fileOffset, buffers); - io::FileOutputStream outputStream(filename); - for (size_t ii = 0; ii < buffers.mBuffers.size(); ++ii) - { - outputStream.write( - static_cast(buffers.mBuffers[ii].mData), - buffers.mBuffers[ii].mNumBytes); - } + outputStream.write(buffers.mBuffers[ii].getBytes()); } +} - void testCreate(const std::string& outname, - bool shouldCompress = false) - { - nitf::Record record; - populateFileHeader(record, outname); - addImageSegment(record, shouldCompress); - writeNITF(record, outname); - } +static void test_create_nitf_with_byte_provider__testCreate(const std::string& outname, + bool shouldCompress = false) +{ + nitf::Record record; + populateFileHeader(record, outname); + test_create_nitf_with_byte_provider__addImageSegment(record, shouldCompress); + test_create_nitf_with_byte_provider__writeNITF(record, outname); +} - bool testRead(const std::string& pathname) - { - constexpr size_t NUM_BANDS = 1; - nitf::IOHandle handle(pathname, NITF_ACCESS_READONLY, NITF_OPEN_EXISTING); - nitf::Reader reader; - nitf::Record record = reader.read(handle); +static bool test_create_nitf_with_byte_provider__testRead(const std::string& pathname) +{ + constexpr size_t NUM_BANDS = 1; + nitf::IOHandle handle(pathname, NITF_ACCESS_READONLY, NITF_OPEN_EXISTING); + nitf::Reader reader; + nitf::Record record = reader.read(handle); - for (int ii = 0; ii < static_cast(record.getNumImages()); ++ii) + for (int ii = 0; ii < static_cast(record.getNumImages()); ++ii) + { + nitf::ImageReader imageReader = reader.newImageReader(ii); + uint64_t blockSize; + // Read one block. It should match the first blockSize points of the + // image. If it does, we got the blocking mode right. + auto block = reinterpret_cast(imageReader.readBlock(0, &blockSize)); + const size_t imageLength = static_cast(NITRO_IMAGE.width) * NITRO_IMAGE.height; + + for (size_t jj = 0; jj < imageLength * NUM_BANDS; ++jj) { - nitf::ImageReader imageReader = reader.newImageReader(ii); - uint64_t blockSize; - // Read one block. It should match the first blockSize points of the - // image. If it does, we got the blocking mode right. - auto block = reinterpret_cast(imageReader.readBlock(0, &blockSize)); - const size_t imageLength = NITRO_IMAGE.width * NITRO_IMAGE.height; - - for (size_t jj = 0; jj < imageLength * NUM_BANDS; ++jj) + if (block[jj] != NITRO_IMAGE.data[jj]) { - if (block[jj] != NITRO_IMAGE.data[jj]) - { - std::cerr << "Image data doesn't match" << std::endl; - return false; - } + std::cerr << "Image data doesn't match" << std::endl; + return false; } } - return true; } + return true; } TEST_CASE(test_create_nitf_with_byte_provider_test) @@ -239,152 +232,150 @@ TEST_CASE(test_create_nitf_with_byte_provider_test) const bool shouldCompress = false; const std::string outname("test_create.nitf"); - test_create_nitf_with_byte_provider::testCreate(outname, shouldCompress); - const auto result = test_create_nitf_with_byte_provider::testRead(outname); + test_create_nitf_with_byte_provider__testCreate(outname, shouldCompress); + const auto result = test_create_nitf_with_byte_provider__testRead(outname); TEST_ASSERT(result); } -namespace test_create_nitf +static void test_create_nitf__addImageSegment(nitf::Record& record, bool isMono = false, + bool shouldCompress = false) { - static const nitf::Representation RGB[] = { nitf::Representation::R, nitf::Representation::G, nitf::Representation::B }; + nitf::ImageSegment segment = record.newImageSegment(); + nitf::ImageSubheader header = segment.getSubheader(); - void addImageSegment(nitf::Record& record, bool isMono = false, - bool shouldCompress = false) + header.getImageId().set("NITRO-TEST"); + header.getImageDateAndTime().set("20080812000000"); + + if (shouldCompress) { - nitf::ImageSegment segment = record.newImageSegment(); - nitf::ImageSubheader header = segment.getSubheader(); + header.getImageCompression().set("C8"); + } - header.getImageId().set("NITRO-TEST"); - header.getImageDateAndTime().set("20080812000000"); + /* Set the geo-corners to Ann Arbor, MI */ + setCornersFromDMSBox(header); - if (shouldCompress) - { - header.getImageCompression().set("C8"); - } + const auto NUM_BANDS = static_cast(isMono ? 1 : 3); + const nitf::Representation RGB[3] = { nitf::Representation::R, nitf::Representation::G, nitf::Representation::B }; + std::vector bands(NUM_BANDS, nitf::BandInfo()); + for (size_t ii = 0; ii < bands.size(); ++ii) + { + bands[ii].init(RGB[ii], /* The band representation, Nth band */ + nitf::Subcategory::None, /* The band subcategory */ + "N", /* The band filter condition */ + " "); /* The band standard image filter code */ - /* Set the geo-corners to Ann Arbor, MI */ - setCornersFromDMSBox(header); + } - const auto NUM_BANDS = static_cast(isMono ? 1 : 3); - std::vector bands(NUM_BANDS, nitf::BandInfo()); - for (size_t ii = 0; ii < bands.size(); ++ii) - { - bands[ii].init(RGB[ii], /* The band representation, Nth band */ - nitf::Subcategory::None, /* The band subcategory */ - "N", /* The band filter condition */ - " "); /* The band standard image filter code */ + const auto iRep = isMono ? nitf::ImageRepresentation::MONO : nitf::ImageRepresentation::RGB; + header.setPixelInformation(nitf::PixelValueType::Integer /*INT*/, /* Pixel value type */ + 8, /* Number of bits/pixel */ + 8, /* Actual number of bits/pixel */ + "R", /* Pixel justification */ + iRep, /* Image representation */ + "VIS", /* Image category */ + bands); /* Band information object list */ - } - const auto iRep = isMono ? nitf::ImageRepresentation::MONO : nitf::ImageRepresentation::RGB; - header.setPixelInformation(nitf::PixelValueType::Integer /*INT*/, /* Pixel value type */ - 8, /* Number of bits/pixel */ - 8, /* Actual number of bits/pixel */ - "R", /* Pixel justification */ - iRep, /* Image representation */ - "VIS", /* Image category */ - bands); /* Band information object list */ +/* for fun, let's add a comment */ + header.insertImageComment("NITF generated by NITRO", 0); + + // The image mode P is part of an awful hack to workaround us not + // having a compression plugin for blocking mode P. + // If you run this test, it will throw an error. The error will reference + // a file in the j2k plugin. To make this test run, go to the file + // and disable the check for blocking mode B. + // To the best of my knowledge, nothing bad happens as a result. + header.setBlocking(NITRO_IMAGE.height, /*!< The number of rows */ + NITRO_IMAGE.width, /*!< The number of columns */ + NITRO_IMAGE.height, /*!< The number of rows/block */ + NITRO_IMAGE.width, /*!< The number of columns/block */ + nitf::BlockingMode::Pixel /* "P" */); /*!< Image mode */ +} -/* for fun, let's add a comment */ - header.insertImageComment("NITF generated by NITRO", 0); - - // The image mode P is part of an awful hack to workaround us not - // having a compression plugin for blocking mode P. - // If you run this test, it will throw an error. The error will reference - // a file in the j2k plugin. To make this test run, go to the file - // and disable the check for blocking mode B. - // To the best of my knowledge, nothing bad happens as a result. - header.setBlocking(NITRO_IMAGE.height, /*!< The number of rows */ - NITRO_IMAGE.width, /*!< The number of columns */ - NITRO_IMAGE.height, /*!< The number of rows/block */ - NITRO_IMAGE.width, /*!< The number of columns/block */ - nitf::BlockingMode::Pixel /* "P" */); /*!< Image mode */ - } +static void test_create_nitf__writeNITF(nitf::Record& record, const std::string& filename, + bool isMono = false) +{ + const int NUM_BANDS = isMono ? 1 : 3; + nitf::IOHandle out(filename, NITF_ACCESS_WRITEONLY, NITF_CREATE); + nitf::Writer writer; + writer.prepare(out, record); + nitf::ImageWriter imageWriter = writer.newImageWriter(0); + nitf::ImageSource imageSource; - void writeNITF(nitf::Record& record, const std::string& filename, - bool isMono = false) + /* make one bandSource per band */ + for (int ii = 0; ii < NUM_BANDS; ++ii) { - const int NUM_BANDS = isMono ? 1 : 3; - nitf::IOHandle out(filename, NITF_ACCESS_WRITEONLY, NITF_CREATE); - nitf::Writer writer; - writer.prepare(out, record); + const void* data_ = NITRO_IMAGE.data; + const std::span image(static_cast(data_), static_cast(NITRO_IMAGE.width) * NITRO_IMAGE.height); + const nitf::Off start = ii; + const int numBytesPerPixel = sizeof(image[0]); + const int pixelSkip = 2; + nitf::BandSource bandSource = nitf::MemorySource(image.data(), image.size(), start, numBytesPerPixel, pixelSkip); + imageSource.addBand(bandSource); + } - nitf::ImageWriter imageWriter = writer.newImageWriter(0); - nitf::ImageSource imageSource; + imageWriter.setWriteCaching(1); + imageWriter.attachSource(imageSource); + writer.write(); +} - /* make one bandSource per band */ - for (int ii = 0; ii < NUM_BANDS; ++ii) - { - nitf::BandSource bandSource = nitf::MemorySource( - (char*)NITRO_IMAGE.data, - NITRO_IMAGE.width * NITRO_IMAGE.height, - ii, 1, 2); - imageSource.addBand(bandSource); - } +static void test_create_nitf__testCreate(const std::string& outname, bool isMono = false, + bool shouldCompress = false) +{ + nitf::Record record; + populateFileHeader(record, outname); + test_create_nitf__addImageSegment(record, isMono, shouldCompress); + test_create_nitf__writeNITF(record, outname, isMono); +} - imageWriter.setWriteCaching(1); - imageWriter.attachSource(imageSource); - writer.write(); - } +static bool test_create_nitf__testRead(const std::string& pathname, bool isMono = false, + bool shouldCompress = false) +{ + const int NUM_BANDS = isMono ? 1 : 3; + nitf::IOHandle handle(pathname, NITF_ACCESS_READONLY, NITF_OPEN_EXISTING); + nitf::Reader reader; + nitf::Record record = reader.read(handle); - void testCreate(const std::string& outname, bool isMono = false, - bool shouldCompress = false) + for (int ii = 0; ii < static_cast(record.getNumImages()); ++ii) { - nitf::Record record; - populateFileHeader(record, outname); - addImageSegment(record, isMono, shouldCompress); - writeNITF(record, outname, isMono); - } + nitf::ImageReader imageReader = reader.newImageReader(ii); + uint64_t blockSize; + // Read one block. It should match the first blockSize points of the + // image. If it does, we got the blocking mode right. + auto block = reinterpret_cast(imageReader.readBlock(0, &blockSize)); + const auto imageLength = static_cast(NITRO_IMAGE.width) * NITRO_IMAGE.height; + + // The image data is interleaved by pixel. When feeding it to the + // writer, we unpack to interleave by block. Now that we're reading + // it back in, we have to interleave by pixel again to compare. + // imageLength is the pixel length of a single band of image data + for (size_t jj = 0; jj < imageLength * NUM_BANDS; ++jj) + { + size_t offset = jj / imageLength; + size_t index = jj % imageLength; - bool testRead(const std::string& pathname, bool isMono = false, - bool shouldCompress = false) - { - const int NUM_BANDS = isMono ? 1 : 3; - nitf::IOHandle handle(pathname, NITF_ACCESS_READONLY, NITF_OPEN_EXISTING); - nitf::Reader reader; - nitf::Record record = reader.read(handle); + // Even though there's only one band, the pixel skip still + // applies during the write + size_t imageIndex = (offset)+(3 * index); - for (int ii = 0; ii < static_cast(record.getNumImages()); ++ii) - { - nitf::ImageReader imageReader = reader.newImageReader(ii); - uint64_t blockSize; - // Read one block. It should match the first blockSize points of the - // image. If it does, we got the blocking mode right. - auto block = reinterpret_cast(imageReader.readBlock(0, &blockSize)); - const size_t imageLength = NITRO_IMAGE.width * NITRO_IMAGE.height; - - // The image data is interleaved by pixel. When feeding it to the - // writer, we unpack to interleave by block. Now that we're reading - // it back in, we have to interleave by pixel again to compare. - // imageLength is the pixel length of a single band of image data - for (size_t jj = 0; jj < imageLength * NUM_BANDS; ++jj) + // For this case, NITRO will have already undone our interleaving + // while writing, so we can ignore the stuff above and just + // compare directly + if (!shouldCompress && !isMono) + { + imageIndex = jj; + } + if (block[jj] != NITRO_IMAGE.data[imageIndex]) { - size_t offset = jj / imageLength; - size_t index = jj % imageLength; - - // Even though there's only one band, the pixel skip still - // applies during the write - size_t imageIndex = (offset)+(3 * index); - - // For this case, NITRO will have already undone our interleaving - // while writing, so we can ignore the stuff above and just - // compare directly - if (!shouldCompress && !isMono) - { - imageIndex = jj; - } - if (block[jj] != NITRO_IMAGE.data[imageIndex]) - { - std::cerr << "Image data doesn't match" << std::endl; - return false; - } + std::cerr << "Image data doesn't match" << std::endl; + return false; } } - return true; } + return true; } TEST_CASE(test_create_nitf_test) @@ -395,13 +386,13 @@ TEST_CASE(test_create_nitf_test) bool shouldCompress = false; bool isMono = true; - test_create_nitf::testCreate(outname, isMono, shouldCompress); - bool result = test_create_nitf::testRead(outname, isMono, shouldCompress); + test_create_nitf__testCreate(outname, isMono, shouldCompress); + bool result = test_create_nitf__testRead(outname, isMono, shouldCompress); TEST_ASSERT(result); isMono = false; - test_create_nitf::testCreate(outname, isMono, shouldCompress); - result = test_create_nitf::testRead(outname, isMono, shouldCompress); + test_create_nitf__testCreate(outname, isMono, shouldCompress); + result = test_create_nitf__testRead(outname, isMono, shouldCompress); TEST_ASSERT(result); // If we're compressing, we're using the J2K plugin, so please ensure @@ -412,13 +403,13 @@ TEST_CASE(test_create_nitf_test) TEST_ASSERT_FALSE(nitf_plugin_path.empty()); shouldCompress = false; // TODO: true - test_create_nitf::testCreate(outname, isMono, shouldCompress); - result = test_create_nitf::testRead(outname, isMono, shouldCompress); + test_create_nitf__testCreate(outname, isMono, shouldCompress); + result = test_create_nitf__testRead(outname, isMono, shouldCompress); TEST_ASSERT(result); isMono = true; - test_create_nitf::testCreate(outname, isMono, shouldCompress); - result = test_create_nitf::testRead(outname, isMono, shouldCompress); + test_create_nitf__testCreate(outname, isMono, shouldCompress); + result = test_create_nitf__testRead(outname, isMono, shouldCompress); TEST_ASSERT(result); } else diff --git a/externals/nitro/modules/c++/nitf/unittests/test_field++.cpp b/externals/nitro/modules/c++/nitf/unittests/test_field++.cpp index 8723e94e1..75b903701 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_field++.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_field++.cpp @@ -25,8 +25,6 @@ #include #include "TestCase.h" -namespace -{ TEST_CASE(testCastOperator) { nitf::Field field(20, nitf::Field::BCS_A); @@ -34,11 +32,11 @@ TEST_CASE(testCastOperator) // Test unsigned values field.set(123); const uint8_t valUint8 = field; - TEST_ASSERT_EQ(valUint8, 123); + TEST_ASSERT_EQ(valUint8, static_cast(123)); field.set(12345); const uint16_t valUint16 = field; - TEST_ASSERT_EQ(valUint16, 12345); + TEST_ASSERT_EQ(valUint16, static_cast(12345)); field.set(1234567890); const uint32_t valUint32 = field; @@ -60,11 +58,11 @@ TEST_CASE(testCastOperator) // Test signed values field.set(-123); const int8_t valInt8 = field; - TEST_ASSERT_EQ(valInt8, -123); + TEST_ASSERT_EQ(valInt8, gsl::narrow(-123)); field.set(-12345); const int16_t valInt16 = field; - TEST_ASSERT_EQ(valInt16, -12345); + TEST_ASSERT_EQ(valInt16, gsl::narrow(-12345)); field.set(-1234567890); const int32_t valInt32 = field; @@ -96,7 +94,7 @@ TEST_CASE(testCastOperator) // Test arbitrary string field.set("ABCxyz"); const std::string valStr = field; - TEST_ASSERT_EQ(valStr, "ABCxyz "); + TEST_ASSERT_EQ(valStr, std::string("ABCxyz ")); } TEST_CASE(testDescriptors) { @@ -104,16 +102,15 @@ TEST_CASE(testDescriptors) test1a.setF1("1234"); const auto descriptors = test1a.getDescriptors(); - TEST_ASSERT_EQ(1, descriptors.size()); + TEST_ASSERT_EQ(gsl::narrow(1), descriptors.size()); for (const auto& descriptor : descriptors) { - TEST_ASSERT_EQ("f1", descriptor.name()); + TEST_ASSERT_EQ(std::string("f1"), descriptor.name()); const auto field = descriptor.getField(test1a); const std::string value = field; // nitf::Field will implicitly cast - TEST_ASSERT_EQ("1234", value); + TEST_ASSERT_EQ(std::string("1234"), value); } } -} TEST_MAIN( (void)argc; diff --git a/externals/nitro/modules/c++/nitf/unittests/test_hash_table_1++.cpp b/externals/nitro/modules/c++/nitf/unittests/test_hash_table_1++.cpp index f3ffaf0a4..8f382d942 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_hash_table_1++.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_hash_table_1++.cpp @@ -96,7 +96,7 @@ TEST_CASE(test_hash_table_1) } catch (const except::NoSuchKeyException& t) { - TEST_ASSERT_EQ("NOT FOUND", keyBuf); + TEST_ASSERT_EQ_STR("NOT FOUND", keyBuf); const auto message = t.getMessage(); TEST_ASSERT_EQ(message, keyBuf); } @@ -140,12 +140,12 @@ TEST_CASE(test_hash_table_iterator) const std::string key = where.getKey(); TEST_ASSERT_EQ(static_cast(2), key.length()); TEST_ASSERT_EQ('k', key[0]); - TEST_ASSERT(isdigit(key[1])); + TEST_ASSERT(isdigit(key[1]) != 0); const std::string data = static_cast(where.getData()); TEST_ASSERT_EQ(static_cast(2), data.length()); TEST_ASSERT_EQ('v', data[0]); - TEST_ASSERT(isdigit(data[1])); + TEST_ASSERT(isdigit(data[1]) != 0); TEST_ASSERT_EQ(key[1], data[1]); } } diff --git a/externals/nitro/modules/c++/nitf/unittests/test_image_blocker.cpp b/externals/nitro/modules/c++/nitf/unittests/test_image_blocker.cpp index 45ed32ed5..189e331fa 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_image_blocker.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_image_blocker.cpp @@ -27,8 +27,6 @@ #include "TestCase.h" -namespace -{ TEST_CASE(testSingleSegmentNoLeftovers) { // 4 rows of blocks and 5 cols of blocks @@ -475,7 +473,6 @@ TEST_CASE(testBlockPartialImage) } } } -} TEST_MAIN( (void)argc; diff --git a/externals/nitro/modules/c++/nitf/unittests/test_image_loading++.cpp b/externals/nitro/modules/c++/nitf/unittests/test_image_loading++.cpp index 52853d318..ffad7a2b4 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_image_loading++.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_image_loading++.cpp @@ -26,46 +26,46 @@ #include -namespace fs = std::filesystem; +using path = std::filesystem::path; #include "TestCase.h" static std::string testName; static std::string argv0; -static fs::path findInputFile(const fs::path& inputFile) +static path findInputFile(const path& inputFile) { - fs::path root; + path root; if (argv0.empty()) { // running in Visual Studio - root = fs::current_path().parent_path().parent_path(); + root = std::filesystem::current_path().parent_path().parent_path(); } else { - root = absolute(fs::path(argv0)).parent_path().parent_path().parent_path().parent_path(); + root = absolute(path(argv0)).parent_path().parent_path().parent_path().parent_path(); root = root.parent_path().parent_path(); } return root / inputFile; } -static fs::path findInputFile() +static path findInputFile() { - const auto inputPath = fs::path("modules") / "c++" / "nitf" / "unittests" / "sicd_50x50.nitf"; + const auto inputPath = path("modules") / "c++" / "nitf" / "unittests" / "sicd_50x50.nitf"; return findInputFile(inputPath); } -static fs::path findInputFile(bool withAmpTable) +static path findInputFile(bool withAmpTable) { - fs::path inputPath; + path inputPath; if (withAmpTable) { - inputPath = fs::path("modules") / "c++" / "nitf" / "unittests" / "8_bit_Amp_Phs_Examples" / + inputPath = path("modules") / "c++" / "nitf" / "unittests" / "8_bit_Amp_Phs_Examples" / "With_amplitude_table" / "sicd_example_1_PFA_AMP8I_PHS8I_VV_with_amplitude_table_SICD.nitf"; } else { - inputPath = fs::path("modules") / "c++" / "nitf" / "unittests" / "8_bit_Amp_Phs_Examples" / + inputPath = path("modules") / "c++" / "nitf" / "unittests" / "8_bit_Amp_Phs_Examples" / "No_amplitude_table" / "sicd_example_1_PFA_AMP8I_PHS8I_VV_no_amplitude_table_SICD.nitf"; @@ -78,10 +78,10 @@ struct expected_values final uint32_t nRows = 50; uint32_t nCols = 50; nitf::PixelValueType pixelValueType = nitf::PixelValueType::Floating; // "R" - uint32_t bitsPerPixel = 32; + size_t bitsPerPixel = 32; std::string actualBitsPerPixel = "32"; - uint32_t pixelsPerHorizBlock = 50; - uint32_t pixelsPerVertBlock = 50; + size_t pixelsPerHorizBlock = 50; + size_t pixelsPerVertBlock = 50; int luts = 0; }; @@ -140,14 +140,14 @@ static void writeImage(nitf::ImageSegment &segment, TEST_ASSERT_EQ(expected.pixelValueType, subheader.pixelValueType()); TEST_ASSERT_EQ(expected.bitsPerPixel, subheader.numBitsPerPixel()); TEST_ASSERT_EQ(expected.actualBitsPerPixel, subheader.getActualBitsPerPixel().toString()); - TEST_ASSERT_EQ("R", subheader.getPixelJustification().toString()); + TEST_ASSERT_EQ_STR("R", subheader.getPixelJustification().toString()); TEST_ASSERT_EQ(nitf::BlockingMode::Pixel, subheader.imageBlockingMode()); // "P" TEST_ASSERT_EQ(static_cast(1), subheader.numBlocksPerRow()); TEST_ASSERT_EQ(static_cast(1), subheader.numBlocksPerCol()); TEST_ASSERT_EQ(expected.pixelsPerHorizBlock, subheader.numPixelsPerHorizBlock()); TEST_ASSERT_EQ(expected.pixelsPerVertBlock, subheader.numPixelsPerVertBlock()); TEST_ASSERT_EQ(nitf::ImageCompression::NC, subheader.imageCompression()); - TEST_ASSERT_EQ(" ", subheader.getCompressionRate().toString()); + TEST_ASSERT_EQ_STR(" ", subheader.getCompressionRate().toString()); nitf::BufferList buffer(nBands); std::vector bandList(nBands); @@ -173,7 +173,7 @@ static void writeImage(nitf::ImageSegment &segment, for (unsigned int i = 0; i < nBands; i++) { - std::string base = fs::path(imageName).filename().string(); + auto base = path(imageName).filename().string(); size_t where = 0; while ((where = base.find(".")) != (size_t)std::string::npos) @@ -223,8 +223,6 @@ static void test_image_loading_(const std::string& input_file, bool optz, const TEST_CASE(test_image_loading) { - ::testName = testName; - /* If you didnt give us a nitf file, we're croaking */ const auto input_file = findInputFile().string(); expected_values expected; // braced-initialization cause CodeQL to fail? @@ -241,8 +239,6 @@ TEST_CASE(test_image_loading) TEST_CASE(test_8bit_image_loading) { - ::testName = testName; - auto input_file = findInputFile(true /*withAmpTable*/).string(); expected_values expected; // braced-initialization cause CodeQL to fail? expected.nRows = 3975; diff --git a/externals/nitro/modules/c++/nitf/unittests/test_image_segment_blank_nm_compression.cpp b/externals/nitro/modules/c++/nitf/unittests/test_image_segment_blank_nm_compression.cpp index 5a8c9c743..8cff7624f 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_image_segment_blank_nm_compression.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_image_segment_blank_nm_compression.cpp @@ -18,9 +18,9 @@ #include "TestCase.h" -const int64_t BLOCK_LENGTH = 256; -const int64_t ILOC_MAX = 99999; -std::string generateILOC(const types::RowCol& offset) +static constexpr int64_t BLOCK_LENGTH = 256; +static constexpr int64_t ILOC_MAX = 99999; +static std::string generateILOC(const types::RowCol& offset) { std::ostringstream oss; @@ -266,7 +266,7 @@ TEST_CASE(testBlankSegmentsValid) if (imgCtr == static_cast(testIdx)) { - TEST_ASSERT_EQ(nBlocksPresent, 0); + TEST_ASSERT_EQ(nBlocksPresent, static_cast(0)); } else { diff --git a/externals/nitro/modules/c++/nitf/unittests/test_image_segment_computer.cpp b/externals/nitro/modules/c++/nitf/unittests/test_image_segment_computer.cpp index 8655f58fb..26c952b9a 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_image_segment_computer.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_image_segment_computer.cpp @@ -29,8 +29,6 @@ #include "TestCase.h" -namespace -{ TEST_CASE(testBlockSizedBoundaries) { // This test is meant to run through a lot of cases just to make sure @@ -214,7 +212,6 @@ TEST_CASE(testKnownCase) } } -} TEST_MAIN diff --git a/externals/nitro/modules/c++/nitf/unittests/test_image_writer.cpp b/externals/nitro/modules/c++/nitf/unittests/test_image_writer.cpp index 1a70847e5..1a1d3838d 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_image_writer.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_image_writer.cpp @@ -32,63 +32,11 @@ #include #include #include +#include #include "TestCase.h" -namespace fs = std::filesystem; - -static fs::path argv0; -static const fs::path file = __FILE__; - -static bool is_linux() -{ - const auto cpp = file.filename().stem(); // i.e., "test_valid_six" - const auto exe = argv0.filename(); // e.g., "test_valid_six.exe" - return cpp == exe; // no ".exe", must be Linux -} - -static bool is_vs_gtest() -{ - return argv0.empty(); // no argv[0] in VS w/GTest -} - -static fs::path buildFileDir(const fs::path& relativePath) -{ - if (is_vs_gtest()) - { - static const auto cwd = fs::current_path(); - - // Running GTest unit-tests in Visual Studio on Windows - return cwd.parent_path().parent_path() / relativePath; - } - - auto root_dir = argv0.parent_path().parent_path().parent_path().parent_path(); - if (is_linux()) - { - if (root_dir.stem() == "build") // CMake, in ./build directory - { - root_dir = root_dir.parent_path(); - } - else if (root_dir.stem() == "modules") // WAF - { - root_dir = root_dir.parent_path().parent_path(); - } - return root_dir / relativePath; - } - - // must be Windows w/o VS - root_dir = root_dir.parent_path(); - if (root_dir.stem() == "build") // in ./build directory, CMake - { - root_dir = root_dir.parent_path(); - } - else if (root_dir.stem() == "target") // WAF - { - root_dir = root_dir.parent_path(); - } - - return root_dir / relativePath; -} +using path = std::filesystem::path; static void doChangeFileHeader(const std::string& inputPathname, const std::string& outputPathname) { @@ -117,8 +65,6 @@ static void doChangeFileHeader(const std::string& inputPathname, const std::stri writer.write(); } -namespace -{ TEST_CASE(imageWriterThrowsOnFailedConstruction) { nitf::ImageSubheader subheader; @@ -138,11 +84,9 @@ TEST_CASE(constructValidImageWriter) TEST_CASE(changeFileHeader) { - const auto inputPathname = buildFileDir(fs::path("modules") / "c++" / "nitf" / "tests" / "test_blank.ntf").string(); - TEST_ASSERT_NOT_EQ(inputPathname, ""); - //std::clog << "'" << inputPathname << "'\n"; - TEST_ASSERT_TRUE(fs::is_regular_file(inputPathname)); - const auto outputPathname = buildFileDir(fs::path("outputPathname.ntf")).string(); + const auto inputPathname = nitf::Test::buildFileDir(path("modules") / "c++" / "nitf" / "tests" / "test_blank.ntf").string(); + TEST_ASSERT_TRUE(std::filesystem::is_regular_file(inputPathname)); + const auto outputPathname = nitf::Test::buildFileDir(path("outputPathname.ntf")).string(); doChangeFileHeader(inputPathname, outputPathname); @@ -157,11 +101,9 @@ TEST_CASE(changeFileHeader) npos = fileTitle.find("*"); TEST_ASSERT(npos != std::string::npos); } -} TEST_MAIN( (void)argc; - argv0 = fs::absolute(argv[0]).string(); TEST_CHECK(imageWriterThrowsOnFailedConstruction); TEST_CHECK(constructValidImageWriter); diff --git a/externals/nitro/modules/c++/nitf/unittests/test_j2k_compress_tile.cpp b/externals/nitro/modules/c++/nitf/unittests/test_j2k_compress_tile.cpp new file mode 100644 index 000000000..3b49c175d --- /dev/null +++ b/externals/nitro/modules/c++/nitf/unittests/test_j2k_compress_tile.cpp @@ -0,0 +1,226 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2004 - 2017, MDA Information Systems LLC + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; + * If not, see . + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "TestCase.h" + +struct Image final +{ + types::RowCol dims; + std::vector pixels; +}; + +static void generateTestImage(Image& image) +{ + image.dims.row = 2048; + image.dims.col = 1024; + image.pixels.resize(image.dims.area()); + + for (size_t ii = 0; ii < image.pixels.size(); ++ii) + { + // Let's write this out as some sort of pattern so + // J2K compression has something to work with + image.pixels[ii] = static_cast((ii / 100) % 128); + } +} + +static void compressEntireImage(const j2k::Compressor& compressor, + const Image& inputImage, + std::vector& outputImage) +{ + std::vector bytesPerBlock; + const std::span pixels(inputImage.pixels.data(), inputImage.pixels.size()); + compressor.compress(pixels, outputImage, bytesPerBlock); +} + +static void compressTileSubrange(const j2k::Compressor& compressor, + const j2k::CompressionParameters& params, + const Image& inputImage, + size_t numSubsets, + std::vector& outputImage) +{ + const auto tileDims = params.getTileDims(); + const types::RowCol numTiles(params.getNumRowsOfTiles(), params.getNumColsOfTiles()); + + const auto rowsOfTilesPerSubset = numTiles.row / numSubsets; + + std::vector> compressedImages(numSubsets); + std::vector numBytesCompressed(numSubsets); + + for (size_t subset = 0; subset < numSubsets; ++subset) + { + const auto tileRow = rowsOfTilesPerSubset * subset; + const auto numRowsOfTiles = (subset == numSubsets - 1) ? numTiles.row - tileRow : rowsOfTilesPerSubset; + const auto subsetNumTiles = numRowsOfTiles * numTiles.col; + + auto& compressedImage = compressedImages[subset]; + compressedImage.resize(compressor.getMaxBytesRequiredToCompress(subsetNumTiles)); + + const std::span compressedTiles(compressedImage.data(), compressedImage.size()); + std::vector compressedBytesPerBlock; + + const auto offset = (tileRow * tileDims.row) * inputImage.dims.col; + const auto& pixels = inputImage.pixels; + const std::span uncompressed(pixels.data() + offset, pixels.size() - offset); + + const auto result = compressor.compressTileSubrange( + uncompressed, types::Range(tileRow * numTiles.col, subsetNumTiles), + compressedTiles, compressedBytesPerBlock); + numBytesCompressed[subset] = result.size(); + } + + const auto numTotalBytesCompressed = std::accumulate(numBytesCompressed.begin(), numBytesCompressed.end(), static_cast(0)); + outputImage.resize(numTotalBytesCompressed); + auto outputPtr = outputImage.data(); + for (size_t ii = 0; ii < numSubsets; ++ii) + { + ::memcpy(outputPtr, compressedImages[ii].data(), numBytesCompressed[ii]); + outputPtr += numBytesCompressed[ii]; + } +} + +static void compressByTile(const j2k::Compressor& compressor, + const j2k::CompressionParameters& params, + const Image& inputImage, + std::vector& outputImage) +{ + const auto tileDims = params.getTileDims(); + const types::RowCol numTiles(params.getNumRowsOfTiles(), params.getNumColsOfTiles()); + + outputImage.resize(compressor.getMaxBytesRequiredToCompress()); + + // Compress and write a block/tile at a time + size_t bytesWritten = 0; + + for (size_t blockRow = 0, blockNum = 0; blockRow < numTiles.row; ++blockRow) + { + for (size_t blockCol = 0; blockCol < numTiles.col; ++blockCol, ++blockNum) + { + const auto offset = blockRow * tileDims.row * inputImage.dims.col + blockCol * tileDims.col; + const auto& pixels = inputImage.pixels; + const std::span uncompressed(pixels.data() + offset, pixels.size() - offset); + const std::span compressedTile(&outputImage[bytesWritten], outputImage.size() - bytesWritten); + + const auto result = compressor.compressTile(uncompressed, blockNum, compressedTile); + bytesWritten += result.size(); + } + } + + outputImage.resize(bytesWritten); +} + +static bool equals(const std::vector& lhs, const std::vector& rhs) +{ + if (lhs == rhs) + { + return true; + } + + if (lhs.size() != rhs.size()) + { + std::cerr << "Image sizes do not match: " << lhs.size() << " vs. " << rhs.size() << "\n"; + return false; + } + + for (size_t ii = 0; ii < lhs.size(); ++ii) + { + if (lhs[ii] != rhs[ii]) + { + std::cerr << "Data mismatch from index: " << ii << "\n"; + for (size_t errorIndex = ii; errorIndex < ii + 15; ++errorIndex) + { + if (errorIndex >= lhs.size()) + { + break; + } + std::cerr << errorIndex << ": " << static_cast(lhs[errorIndex]) << " vs. " << static_cast(rhs[errorIndex]) << "\n"; + } + break; + } + } + return false; +} + +TEST_CASE(j2k_compress_tile) +{ + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + + const size_t numThreads = sys::OS().getNumCPUs() - 1; + + Image source; + generateTestImage(source); + + for (size_t tilesPerDim = 1; tilesPerDim <= 5; ++tilesPerDim) + { + std::vector imageCompressedWhole; + std::vector imageCompressedByTile; + + const types::RowCol tileDims(source.dims.row / tilesPerDim, source.dims.col / tilesPerDim); + const j2k::CompressionParameters params(source.dims, tileDims, 1, 2); + j2k::Compressor compressor(params, numThreads); + + compressEntireImage(compressor, source, imageCompressedWhole); + compressByTile(compressor, params, source, imageCompressedByTile); + + //TEST_ASSERT(equals(imageCompressedWhole, imageCompressedByTile)); + } + + for (size_t numSubsets = 1; numSubsets <= 5; ++numSubsets) + { + std::vector imageCompressedWhole; + std::vector imageCompressedBySubset; + constexpr size_t TILES_PER_DIM = 10; + + const types::RowCol tileDims(source.dims.row / TILES_PER_DIM, source.dims.col / TILES_PER_DIM); + const j2k::CompressionParameters params(source.dims, tileDims, 1, 2); + const j2k::Compressor compressor(params, numThreads); + + compressEntireImage(compressor, source, imageCompressedWhole); + compressTileSubrange(compressor, params, source, numSubsets, imageCompressedBySubset); + + //TEST_ASSERT(equals(imageCompressedWhole, imageCompressedBySubset)); + } +} + +TEST_MAIN((void)argc; (void)argv; + TEST_CHECK(j2k_compress_tile); + ) + diff --git a/externals/nitro/modules/c++/nitf/unittests/test_j2k_compressed_byte_provider.cpp b/externals/nitro/modules/c++/nitf/unittests/test_j2k_compressed_byte_provider.cpp new file mode 100644 index 000000000..8b9dd1156 --- /dev/null +++ b/externals/nitro/modules/c++/nitf/unittests/test_j2k_compressed_byte_provider.cpp @@ -0,0 +1,637 @@ +/* ========================================================================= + * This file is part of six.sidd-c++ + * ========================================================================= + * + * (C) Copyright 2004 - 2018, MDA Information Systems LLC + * + * six.sidd-c++ is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; If not, + * see . + * + */ + +// Test program for SIDDByteProvider +// Demonstrates that the raw bytes provided by this class result in equivalent +// SIDDs to the normal writes via NITFWriteControl + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TestCase.h" + +static std::string testName; + +static void setCornersFromDMSBox(nitf::ImageSubheader& header) +{ + /* + * You could do this in degrees as easily + * but this way we get to show off some new utilities + */ + constexpr int latTopDMS[] = { 42, 17, 50 }; + const auto latTopDecimal = nitf::Utils::geographicToDecimal(latTopDMS[0], latTopDMS[1], latTopDMS[2]); + + constexpr int latBottomDMS[] = { 42, 15, 14 }; + const auto latBottomDecimal = nitf::Utils::geographicToDecimal(latBottomDMS[0], latBottomDMS[1], latBottomDMS[2]); + + constexpr int lonEastDMS[] = { -83, 42, 12 }; + const auto lonEastDecimal = nitf::Utils::geographicToDecimal(lonEastDMS[0], lonEastDMS[1], lonEastDMS[2]); + + constexpr int lonWestDMS[] = { -83, 45, 44 }; + const auto lonWestDecimal = nitf::Utils::geographicToDecimal(lonWestDMS[0], lonWestDMS[1], lonWestDMS[2]); + + double corners[4][2]; + corners[0][0] = latTopDecimal; corners[0][1] = lonWestDecimal; + corners[1][0] = latTopDecimal; corners[1][1] = lonEastDecimal; + corners[2][0] = latBottomDecimal; corners[2][1] = lonEastDecimal; + corners[3][0] = latBottomDecimal; corners[3][1] = lonWestDecimal; + + header.setCornersFromLatLons(NITF_CORNERS_DECIMAL, corners); +} + +static void addImageSegment(nitf::Record& record, + size_t numRows, + size_t numCols, + size_t rowsPerBlock, + size_t colsPerBlock, + size_t bytesPerPixel, + bool shouldCompress) +{ + nitf::ImageSegment segment = record.newImageSegment(); + nitf::ImageSubheader header = segment.getSubheader(); + + header.getImageId().set("NITRO-TEST"); + header.getImageDateAndTime().set("20080812000000"); + + if (shouldCompress) + { + header.getImageCompression().set("C8"); + header.getCompressionRate().set("N045"); + } + + /* Set the geo-corners to Ann Arbor, MI */ + setCornersFromDMSBox(header); + + std::vector bands{ {nitf::Representation::R, nitf::Subcategory::None, + "N", /* The band filter condition */ + " " } }; /* The band standard image filter code */ + + + const auto nbpp = gsl::narrow(8 * bytesPerPixel); + const auto abpp = gsl::narrow(8 * bytesPerPixel); + const auto iRep = nitf::ImageRepresentation::MONO; + header.setPixelInformation(nitf::PixelValueType::Integer, + nbpp, /* Number of bits/pixel */ + abpp, /* Actual number of bits/pixel */ + "R", /* Pixel justification */ + iRep, /* Image representation */ + "VIS", /* Image category */ + bands); /* Band information object list */ + + + + /* for fun, let's add a comment */ + header.insertImageComment("NITF generated by NITRO", 0); + header.setBlocking(gsl::narrow(numRows), + gsl::narrow(numCols), + gsl::narrow(rowsPerBlock), + gsl::narrow(colsPerBlock), + nitf::BlockingMode::Block); /*!< Image mode */ +} + +// Make sure a file gets removed +struct EnsureFileCleanup final +{ + EnsureFileCleanup(const std::filesystem::path& pathname) : mPathname(pathname) + { + removeIfExists(); + } + + ~EnsureFileCleanup() + { + try + { + removeIfExists(); + } + catch (...) + { + } + } + EnsureFileCleanup(const EnsureFileCleanup&) = default; + EnsureFileCleanup& operator=(const EnsureFileCleanup&) = delete; + +private: + void removeIfExists() + { + if (exists(mPathname)) + { + remove(mPathname); + } + } + + const std::filesystem::path mPathname; +}; + +struct CompareFiles final +{ + CompareFiles(const std::filesystem::path& lhsPathname) + { + readImage(lhsPathname, mLHS); + } + + bool operator()(const std::string& prefix, const std::filesystem::path& rhsPathname) const + { + readImage(rhsPathname, mRHS); + if (mLHS == mRHS) + { + std::clog << prefix << " matches\n"; + return true; + } + + if (mLHS.size() != mRHS.size()) + { + std::clog << prefix << " DOES NOT MATCH: file sizes are " << mLHS.size() << " vs. " << mRHS.size() << " bytes\n"; + } + else + { + size_t ii; + for (ii = 0; ii < mLHS.size(); ++ii) + { + if (mLHS[ii] != mRHS[ii]) + { + std::clog << prefix << " DOES NOT MATCH at byte " << ii << "\n"; + break; + } + } + } + return false; + } +private: + std::vector mLHS; + mutable std::vector mRHS; + static void readImage(const std::filesystem::path& pathname, std::vector& data) + { + data.clear(); + nitf::Reader reader; + nitf::IOHandle io(pathname.string()); + nitf::Record record = reader.read(io); + nitf::ListIterator iter = record.getImages().begin(); + size_t image = 0; + size_t imageOffset = 0; + for (; iter != record.getImages().end(); ++iter) + { + nitf::ImageReader imageReader = reader.newImageReader(gsl::narrow(image)); + nitf::ImageSegment imageSegment = *iter; + nitf::ImageSubheader imageSubheader = imageSegment.getSubheader(); + const auto numBlocks = imageSubheader.numBlocksPerRow() * imageSubheader.numBlocksPerCol(); + const auto imageLength = imageSubheader.getNumBytesOfImageData(); + data.resize(imageOffset + imageLength); + + nitf::Uint64 blockOffset = 0; + for (size_t block = 0; block < numBlocks; ++block) + { + // Read a block + nitf::Uint64 bytesThisBlock(0); + const auto blockData = imageReader.readBlock(gsl::narrow(block), &bytesThisBlock); + if (bytesThisBlock == 0) + { + throw except::Exception(Ctxt("Failed to read block")); + } + + // Copy it to the output + memcpy(&data[imageOffset + blockOffset], blockData, bytesThisBlock); + blockOffset += bytesThisBlock; + } + imageOffset += imageLength; + ++image; + } + } +}; + +// Main test class +template +struct Tester final +{ + Tester(int numRowsPerBlock, + size_t numColsPerBlock, + bool setMaxProductSize, + size_t maxRowsPerSegment) : + mNormalPathname("normal_write.nitf"), + mNormalFileCleanup(mNormalPathname), + mSetMaxProductSize(setMaxProductSize), + mMaxRowsPerSegment(maxRowsPerSegment), + mTestPathname("streaming_write.nitf"), + mSuccess(true) + { + // Generate test image + const types::RowCol globalImageDims(123, 56); + mImage.resize(globalImageDims.area()); + srand(334); + for (size_t ii = 0; ii < mImage.size(); ++ii) + { + mImage[ii] = static_cast(rand() % std::numeric_limits::max()); + } + + // Set segmenting dimensions + mNumImages = 1; + if (mSetMaxProductSize) + { + mNumImages = math::ceilingDivide(globalImageDims.row, mMaxRowsPerSegment); + } + + mDims.resize(mNumImages); + for (size_t ii = 0; ii < mNumImages; ++ii) + { + mDims[ii].col = globalImageDims.col; + if (ii == mNumImages - 1) + { + mDims[ii].row = globalImageDims.row - ii * mMaxRowsPerSegment; + } + else + { + mDims[ii].row = mMaxRowsPerSegment; + } + } + + mBlockDims.row = (numRowsPerBlock == 0) ? mDims[0].row : numRowsPerBlock; + mBlockDims.col = (numColsPerBlock == 0) ? mDims[0].col : numColsPerBlock; + + // Set up source data + normalWrite(); + mCompareFiles.reset(new CompareFiles(mNormalPathname)); + + // Pre-compress image data + createCompressedImage(); + } + Tester(const Tester&) = delete; + Tester& operator=(const Tester&) = delete; + Tester(Tester&&) = default; + Tester& operator=(Tester&&) = delete; + + void testSingleWrite() + { + EnsureFileCleanup test(mTestPathname); + auto record = populateRecord(mNormalPathname.string(), true /*shouldCompress*/, false /*normalWrite*/); + + std::vector desData; + const nitf::CompressedByteProvider byteProvider(record, mBytesPerBlock, desData, mBlockDims.row, mBlockDims.col); + + size_t totalNumBytes = 0; + for (size_t image = 0; image < mNumImages; ++image) + { + for (size_t block = 0; block < mBytesPerBlock[image].size(); ++block) + { + totalNumBytes += mBytesPerBlock[image][block]; + } + } + + std::vector combinedCompressedBlocks(totalNumBytes); + size_t offset = 0; + size_t totalNumRows = 0; + for (size_t image = 0; image < mNumImages; ++image) + { + totalNumRows += mDims[image].row; + const auto& bytesPerBlock = mBytesPerBlock[image]; + for (size_t block = 0; block < bytesPerBlock.size(); ++block) + { + auto dest = &combinedCompressedBlocks[offset]; + const auto source = &(mCompressedBlocks[image][block][0]); + memcpy(dest, source, bytesPerBlock[block]); + offset += bytesPerBlock[block]; + } + } + + nitf::Off fileOffset; + nitf::NITFBufferList buffers; + byteProvider.getBytes(&combinedCompressedBlocks[0], 0, totalNumRows, fileOffset, buffers); + + io::FileOutputStream outputStream(mTestPathname); + const nitf::Off expectedNumBytes = byteProvider.getNumBytes(0, totalNumRows); + write(fileOffset, buffers, expectedNumBytes, outputStream); + + compare("Single write"); + } + + void testMultipleWritesBlocked() + { + EnsureFileCleanup test(mTestPathname); + auto record = populateRecord(mNormalPathname.string(), true /*shouldCompress*/, false /*normalWrite*/); + + std::vector desData; + const nitf::CompressedByteProvider byteProvider(record, mBytesPerBlock, desData, mBlockDims.row, mBlockDims.col); + + io::FileOutputStream outputStream(mTestPathname); + + size_t startRow = 0; + for (size_t image = 0; image < mNumImages; ++image) + { + const size_t blocksThisSegment = mBytesPerBlock[image].size(); + const size_t rowsLastBlock = mDims[image].row - mBlockDims.row * (blocksThisSegment - 1); + + for (size_t block = 0; block < blocksThisSegment; ++block) + { + const size_t numRows = (block == blocksThisSegment - 1) ? rowsLastBlock : mBlockDims.row; + + nitf::Off fileOffset; + nitf::NITFBufferList buffers; + byteProvider.getBytes(&(mCompressedBlocks[image][block][0]), startRow, numRows, fileOffset, buffers); + if (image == 1) + { + byteProvider.getNumBytes(startRow, numRows); + } + + nitf::Off expectedNumBytes = byteProvider.getNumBytes(startRow, numRows); + write(fileOffset, buffers, expectedNumBytes, outputStream); + startRow += numRows; + } + } + compare("Multiple writes blocked"); + } + + bool success() const + { + return mSuccess; + } + +private: + void normalWrite() const + { + nitf::IOHandle handle(mNormalPathname.string(), NITF_ACCESS_WRITEONLY, NITF_CREATE); + nitf::Writer writer; + auto record = populateRecord(mNormalPathname.string(), false /*shouldCompress*/, true /*normalWrite*/); + writer.prepare(handle, record); + + std::vector imageWriters; + for (size_t ii = 0; ii < mNumImages; ++ii) + { + imageWriters.push_back(writer.newImageWriter(gsl::narrow(ii))); + const auto area = rowsToPixels(mDims[ii].row); + const auto offset = rowsToPixels(ii * mMaxRowsPerSegment); + nitf::BandSource bandSource = nitf::MemorySource( + reinterpret_cast(&mImage[0]), + area, gsl::narrow(offset), sizeof(DataTypeT), 0); + nitf::ImageSource imageSource; + imageSource.addBand(bandSource); + + imageWriters[ii].setWriteCaching(1); + imageWriters[ii].attachSource(imageSource); + } + writer.write(); + } + + nitf::Record populateRecord( + const std::string& fileTitle, + bool shouldCompress, + bool /*normalWrite*/) const + { + nitf::Record retval; + auto header = retval.getHeader(); + header.getOriginStationID().set("github.com"); + header.getFileTitle().set(fileTitle); + + for (size_t ii = 0; ii < mNumImages; ++ii) + { + addImageSegment(retval, + mDims[ii].row, mDims[ii].col, + mBlockDims.row, mBlockDims.col, + sizeof(DataTypeT), + shouldCompress); + } + return retval; + } + + void createCompressedImage() + { + std::vector rowsEachSegment(mNumImages, 0); + for (size_t image = 0; image < mNumImages; ++image) + { + rowsEachSegment[image] = mDims[image].row; + } + nitf::ImageBlocker imageBlocker( + rowsEachSegment, mDims[0].col, mBlockDims.row, mBlockDims.col); + + mBytesPerBlock.resize(mNumImages); + for (size_t image = 0; image < mBytesPerBlock.size(); ++image) + { + const auto numBlocks = imageBlocker.getNumRowsOfBlocks(image) * imageBlocker.getNumColsOfBlocks(); + mBytesPerBlock[image].resize(numBlocks); + } + mCompressedBlocks.resize(mNumImages); + + for (size_t image = 0; image < mNumImages; ++image) + { + const size_t numBlocks = mBytesPerBlock[image].size(); + mCompressedBlocks[image].resize(numBlocks); + + compressImageSegment( + image, + imageBlocker, + mCompressedBlocks[image], + mBytesPerBlock[image]); + } + } + + void compressImageSegment( + size_t imageNumber, + const nitf::ImageBlocker& imageBlocker, + std::vector>& compressedBlocks, + std::vector& bytesPerBlock) + { + const types::RowCol& imageDims = mDims[imageNumber]; + const j2k::CompressionParameters compressionParams(imageDims, mBlockDims, 1, 3); + j2k::Compressor compressor(compressionParams); + + size_t rowsWritten = 0; + for (size_t image = 0; image < imageNumber; ++image) + { + rowsWritten += mDims[image].row; + } + + const auto imageStartRow = rowsWritten; + for (size_t block = 0; block < bytesPerBlock.size(); ++block) + { + const size_t startRow = rowsWritten; + const size_t pixelOffset = startRow * imageDims.col; + const size_t lastRowThisImage = imageStartRow + imageDims.row; + + const auto numRows = std::min(imageBlocker.getNumRowsPerBlock()[imageNumber], lastRowThisImage - startRow); + + const auto bytesInBlock = imageBlocker.getNumBytesRequired(startRow, numRows, sizeof(DataTypeT)); + std::vector blockData(bytesInBlock); + imageBlocker.block(&mImage[pixelOffset], startRow, numRows, sizeof(DataTypeT), blockData.data()); + + const std::span blockData_(blockData.data(), blockData.size()); + compressor.compressTile(blockData_, block, + compressedBlocks[block]); + bytesPerBlock[block] = compressedBlocks[block].size(); + + rowsWritten += numRows; + } + } + + void write( + nitf::Off fileOffset, + const nitf::NITFBufferList& buffers, + nitf::Off computeNumBytes, + io::FileOutputStream& outStream) + { + outStream.seek(fileOffset, io::Seekable::START); + + nitf::Off numBytes = 0; + for (size_t ii = 0; ii < buffers.mBuffers.size(); ++ii) + { + outStream.write( + static_cast(buffers.mBuffers[ii].mData), + buffers.mBuffers[ii].mNumBytes); + numBytes += buffers.mBuffers[ii].mNumBytes; + } + + if (numBytes != computeNumBytes) + { + std::clog << "Computed " << computeNumBytes << " bytes but actually had " << numBytes << " bytes\n"; + mSuccess = false; + } + } + + + size_t rowsToPixels(size_t rows) const noexcept + { + return rows * mDims[0].col * sizeof(DataTypeT); + } + + std::string getSuffix() const + { + std::string suffix; + if (mBlockDims.area() != 0 && mBlockDims != mDims[0]) + { + suffix += " with blocking of rows/block=" + std::to_string(mBlockDims.row) + ", cols/block=" + std::to_string(mBlockDims.col); + } + return suffix; + } + + void compare(const std::string& prefix) + { + std::string fullPrefix = prefix; + if (mSetMaxProductSize) + { + fullPrefix += " (max rows per image " + std::to_string(mMaxRowsPerSegment) + ")"; + } + fullPrefix += getSuffix(); + if (!(*mCompareFiles)(fullPrefix, mTestPathname)) + { + mSuccess = false; + } + } + +private: + const std::filesystem::path mNormalPathname; + const EnsureFileCleanup mNormalFileCleanup; + + std::vector > mDims; + types::RowCol mBlockDims; + const bool mSetMaxProductSize; + const size_t mMaxRowsPerSegment; + const std::filesystem::path mTestPathname; + std::vector mImage; + size_t mNumImages; + + std::vector>> mCompressedBlocks; + std::vector > mBytesPerBlock; + std::unique_ptr mCompareFiles; + bool mSuccess; +}; + +static Tester make_Tester(bool setBlocking, std::optional maxRowsPerSegment = std::optional()) +{ + // These intentionally do not divide evenly so there will be both pad rows and cols + const auto numRowsPerBlock = setBlocking ? 40 : 0; + constexpr size_t numColsPerBlock = 0; + + const auto setMaxProductSize = maxRowsPerSegment.has_value(); + const auto maxRowsPerSegment_ = setMaxProductSize ? *maxRowsPerSegment : 0; + + // Only 1 byte per pixel supported for now + return Tester(numRowsPerBlock, numColsPerBlock, setMaxProductSize, maxRowsPerSegment_); +} + +TEST_CASE(j2k_compressed_byte_provider_maxRowsPerSegment0) +{ + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + { + auto tester = make_Tester(true /*setBlocking*/); + tester.testMultipleWritesBlocked(); + TEST_ASSERT_TRUE(tester.success()); + tester.testSingleWrite(); + TEST_ASSERT_TRUE(tester.success()); + } + { + auto tester = make_Tester(false /*setBlocking*/); + tester.testSingleWrite(); + TEST_ASSERT_TRUE(tester.success()); + } +} + +TEST_CASE(j2k_compressed_byte_provider) +{ + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + + // Run tests forcing various numbers of segments + // Blocking is set at 40 rows / block so can't go less than this + // Actual limit is a bit higher, since j2k needs a minimum size + const auto numRows = { 100, 80, 50 }; + for (auto maxRowsPerSegment_ : numRows) + { + const auto maxRowsPerSegment = gsl::narrow(maxRowsPerSegment_); + { + auto tester = make_Tester(true /*setBlocking*/, maxRowsPerSegment); + tester.testMultipleWritesBlocked(); + TEST_ASSERT_TRUE(tester.success()); + tester.testSingleWrite(); + TEST_ASSERT_TRUE(tester.success()); + } + { + auto tester = make_Tester(false /*setBlocking*/, maxRowsPerSegment); + tester.testSingleWrite(); + TEST_ASSERT_TRUE(tester.success()); + } + } +} + +TEST_MAIN((void)argc;(void)argv; +//TEST_CHECK(j2k_compressed_byte_provider_maxRowsPerSegment0); // TODO: get working with CMake +//TEST_CHECK(j2k_compressed_byte_provider); // TODO: get working with CMake +) \ No newline at end of file diff --git a/externals/nitro/modules/c++/nitf/unittests/test_j2k_loading++.cpp b/externals/nitro/modules/c++/nitf/unittests/test_j2k_loading++.cpp index ada13f7fb..308e175c6 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_j2k_loading++.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_j2k_loading++.cpp @@ -28,43 +28,44 @@ #include #include #include +#include +#include +#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include -namespace fs = std::filesystem; +#include -#include "TestCase.h" +using path = std::filesystem::path; static std::string testName; -static fs::path findRoot(const fs::path& p) -{ - if (fs::is_regular_file(p / "LICENSE") && fs::is_regular_file(p / "README.md") && fs::is_regular_file(p / "CMakeLists.txt")) - { - return p; - } - return findRoot(p.parent_path()); -} -inline fs::path findRoot() +static path findInputFile(const path& fn) { - return findRoot(fs::current_path()); -} - -static std::string argv0; -static fs::path findInputFile_(const fs::path& inputFile) -{ - fs::path root = findRoot(); - return root / inputFile; -} -static fs::path findInputFile(const fs::path& fn) -{ - const auto inputPath = fs::path("modules") / "c++" / "nitf" / "unittests" / fn; - return findInputFile_(inputPath); + const auto inputPath = path("modules") / "c++" / "nitf" / "unittests" / fn; + return nitf::Test::findInputFile(inputPath); } static void test_image_loading_(const std::string& input_file, bool /*optz*/) @@ -99,9 +100,7 @@ static void test_image_loading_(const std::string& input_file, bool /*optz*/) TEST_CASE(test_j2k_loading) { - ::testName = testName; - - auto input_file = findInputFile("j2k_compressed_file1_jp2.ntf").string(); + const auto input_file = findInputFile("j2k_compressed_file1_jp2.ntf").string(); test_image_loading_(input_file, false /*optz*/); //test_image_loading_(input_file, true /*optz*/); @@ -150,8 +149,6 @@ static void test_j2k_nitf_(const std::string& fname) } TEST_CASE(test_j2k_nitf) { - ::testName = testName; - j2k_Reader* pNative = nullptr; try { @@ -164,7 +161,7 @@ TEST_CASE(test_j2k_nitf) } // This is a JP2 file, not J2K; see OpenJPEG_setup_() - auto input_file = findInputFile("j2k_compressed_file1_jp2.ntf").string(); + const auto input_file = findInputFile("j2k_compressed_file1_jp2.ntf").string(); test_j2k_nitf_(input_file); } @@ -186,28 +183,29 @@ void writeJ2K(uint32_t x0, uint32_t y0, const auto num_y_tiles = inContainer.getTilesY(); j2k::WriterOptions options; + options.setCompressionRatio(0.0); // lossless + //options.setCompressionRatio(12.0); // lossy + /* TODO set some options here */ j2k::Writer writer(inContainer, options); nitf::IOHandle outIO(outName, NRT_ACCESS_WRITEONLY, NRT_CREATE); - const auto tileSize = inContainer.tileSize(); + j2k::WriteTiler tiler(writer, buf); // TODO: determine tile range from read region for (uint32_t y = 0; y < num_y_tiles; ++y) { for (uint32_t x = 0; x < num_x_tiles; ++x) { // TODO: May need to handle this differently for multiple components - const auto offset = inContainer.bufferOffset(x, y, 0); - const std::span buf_(buf.data() + offset, tileSize); - writer.setTile(x, y, buf_); + tiler.setTile(x, y, 0); } } writer.write(outIO); //printf("Wrote file: %s\n", outName.c_str()); } -void test_j2k_nitf_read_region_(const fs::path& fname) +void test_j2k_nitf_read_region_(const path& fname) { nitf::IOHandle io(fname.string(), NRT_ACCESS_READONLY, NRT_OPEN_EXISTING); nitf::Reader reader; @@ -280,16 +278,107 @@ void test_j2k_nitf_read_region_(const fs::path& fname) } TEST_CASE(test_j2k_nitf_read_region) { - ::testName = testName; // This is a JP2 file, not J2K; see OpenJPEG_setup_() - auto input_file = findInputFile("j2k_compressed_file1_jp2.ntf"); + const auto input_file = findInputFile("j2k_compressed_file1_jp2.ntf"); test_j2k_nitf_read_region_(input_file); } -TEST_MAIN( - (void)argc; - argv0 = argv[0]; +static std::vector readImage(nitf::ImageReader& imageReader, const nitf::ImageSubheader& imageSubheader) +{ + const auto numBlocks = imageSubheader.numBlocksPerRow() * imageSubheader.numBlocksPerCol(); + TEST_ASSERT_GREATER(numBlocks, static_cast(0)); + + const auto imageLength = imageSubheader.getNumBytesOfImageData(); + TEST_ASSERT_GREATER(imageLength, static_cast(0)); + + // This assumes vertical blocking. + // Interleaving would be required for horizontal blocks + std::vector retval(imageLength); + uint64_t byteOffset = 0; + for (uint32_t block = 0; block < numBlocks; ++block) + { + uint64_t bytesRead; + const auto blockData = imageReader.readBlock(block, &bytesRead); + TEST_ASSERT(blockData != nullptr); + memcpy(retval.data() + byteOffset, blockData, bytesRead); + byteOffset += bytesRead; + } + return retval; +} +static void test_decompress_nitf_to_sio_(const path& inputPathname, const path& outputPathname) +{ + // Take a J2K-compressed NITF, decompress the image and save to an SIO. + nitf::Reader reader; + nitf::IOHandle io(inputPathname.string()); + const auto record = reader.read(io); + auto iter = record.getImages().begin(); + nitf::ImageSegment imageSegment = *iter; + const auto imageSubheader = imageSegment.getSubheader(); + + auto imageReader = reader.newImageReader(0 /*imageSegmentNumber*/); + const auto imageData = readImage(imageReader, imageSubheader); + + sio::lite::writeSIO(imageData.data(), imageSubheader.dims(), outputPathname); +} +TEST_CASE(test_j2k_decompress_nitf_to_sio) +{ + const auto pluginsDir = nitf::Test::buildPluginsDir(); + sys::OS().setEnv("NITF_PLUGIN_PATH", pluginsDir, true /*overwrite*/); + + const auto inputPathname = findInputFile("j2k_compressed_file1_jp2.ntf"); // This is a JP2 file, not J2K; see OpenJPEG_setup_() + test_decompress_nitf_to_sio_(inputPathname, "test_decompress_nitf.sio"); +} + +TEST_CASE(test_j2k_compress_raw_image) +{ + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + + const auto inputPathname = findInputFile("j2k_compressed_file1_jp2.ntf"); // This is a JP2 file, not J2K; see OpenJPEG_setup_() + const path outputPathname = "test_j2k_compress_raw_image.sio"; + test_decompress_nitf_to_sio_(inputPathname, outputPathname); + // --------------------------------------------------------------------------------------- + + // J2K compresses the raw image data of an SIO file + const auto& inPathname = outputPathname; // "input pathname to raw file or sio to compress" + const auto& testJ2KPathname = inputPathname; // "optional J2K file to compare compressed result to" + + // Read in the raw data from the input SIO + types::RowCol rawDims; + std::vector rawImage_; + sio::lite::readSIO(inPathname, rawDims, rawImage_); + const std::span rawImage(rawImage_.data(), rawImage_.size()); + + const auto& tileDims = rawDims; + const size_t numThreads = sys::OS().getNumCPUs() - 1; + const j2k::CompressionParameters params(rawDims, tileDims); + const j2k::Compressor compressor(params, numThreads); + + std::vector compressedImage_; + std::vector bytesPerBlock; + compressor.compress(rawImage, compressedImage_, bytesPerBlock); + const std::span compressedImage(compressedImage_.data(), compressedImage_.size()); + + const auto sumCompressedBytes = std::accumulate(bytesPerBlock.begin(), bytesPerBlock.end(), gsl::narrow(0)); + TEST_ASSERT_EQ(sumCompressedBytes, compressedImage.size()); // "Size of compressed image does not match sum of bytes per block" + + const auto compressedPathname = "compressed_" + std::to_string(tileDims.row) + "x" + std::to_string(tileDims.col) + ".j2k"; + ::io::FileOutputStream os(compressedPathname); + os.write(compressedImage); + + std::vector j2kData; + io::readFileContents(testJ2KPathname, j2kData); + //TEST_ASSERT_EQ((compressedImage.size(), j2kData.size()); + //for (size_t ii = 0; ii < compressedImage.size(); ++ii) + //{ + // //TEST_ASSERT_EQ(j2kData[ii], compressedImage[ii]); + //} +} + +TEST_MAIN((void)argc; (void) argv; TEST_CHECK(test_j2k_loading); TEST_CHECK(test_j2k_nitf); TEST_CHECK(test_j2k_nitf_read_region); - ) \ No newline at end of file + + TEST_CHECK(test_j2k_decompress_nitf_to_sio); + TEST_CHECK(test_j2k_compress_raw_image); + ) diff --git a/externals/nitro/modules/c++/nitf/unittests/test_j2k_read_tile.cpp b/externals/nitro/modules/c++/nitf/unittests/test_j2k_read_tile.cpp index 7d35698c1..6702c85a9 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_j2k_read_tile.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_j2k_read_tile.cpp @@ -24,7 +24,7 @@ #include #include "TestCase.h" -TEST_CASE(test_j2k_read_tile) +TEST_CASE(j2k_read_tile) { /* placeholder */ } @@ -32,6 +32,6 @@ TEST_CASE(test_j2k_read_tile) TEST_MAIN( (void)argc; (void)argv; - TEST_CHECK(test_j2k_read_tile); + TEST_CHECK(j2k_read_tile); ) diff --git a/externals/nitro/modules/c++/nitf/unittests/test_load_plugins.cpp b/externals/nitro/modules/c++/nitf/unittests/test_load_plugins.cpp index 3c7572262..fd6762ca1 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_load_plugins.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_load_plugins.cpp @@ -50,30 +50,32 @@ static void load_plugin(const char* tre) TEST_ASSERT(test_main_ != nullptr); } -static const std::vector all_plugins +static const std::vector& all_plugins() { -#if _MSC_VER && NITRO_PCH - // only build a handful in Visual Studio - "ACCHZB", "ACCPOB", "ACFTA", "AIMIDB", "CSCRNA", "ENGRDA", "HISTOA", "JITCID", "PTPRAA", "RPFHDR", -#else - "ACCHZB", "BANDSB", "CSDIDA", "GEOLOB", "JITCID", "NBLOCA", "PIAPEB", "REGPTB", "RSMIDA", "STEROB", - "ACCPOB", "BCKGDA", "CSEPHA", "GEOPSB", "MAPLOB", "OBJCTA", "PIAPRC", "RPC00B", "RSMPCA", "STREOB", - "ACCVTB", "BLOCKA", "CSEXRA", "GRDPSB", "MATESA", "OFFSET", "PIAPRD", "RPFDES", "RSMPIA", "TEST_DES", - "ACFTA", "BNDPLB", "CSPROA", "HISTOA", "MENSRA", "PATCHA", "PIATGA", "RPFHDR", "SECTGA", "TRGTA", - "ACFTB", "CCINFA", "CSSFAA", "ICHIPB", "MENSRB", "PATCHB", "PIATGB", "RPFIMG", "SENSRA", "USE00A", - "AIMIDA", "CLCTNA", "CSSHPA", "IMASDA", "MPDSRA", "PIAEQA", "PIXQLA", "RSMAPA", "SENSRB", - "AIMIDB", "CLCTNB", "ENGRDA", "IMGDTA", "MSDIRA", "PIAEVA", "PLTFMA", "RSMDCA", "SNSPSB", - "AIPBCA", "CMETAA", "EXOPTA", "IMRFCA", "MSTGTA", "PIAIMB", "PRADAA", "RSMECA", "SNSRA", - "ASTORA", "CSCCGA", "EXPLTA", "IOMAPA", "MTIRPA", "PIAIMC", "PRJPSB", "RSMGGA", "SOURCB", - "BANDSA", "CSCRNA", "EXPLTB", "J2KLRA", "MTIRPB", "PIAPEA", "PTPRAA", "RSMGIA", "STDIDC", -#endif -}; + static const std::vector all_plugins_ + { + #if _MSC_VER && NITRO_PCH + // only build a handful in Visual Studio + "ACCHZB", "ACCPOB", "ACFTA", "AIMIDB", "CSCRNA", "ENGRDA", "HISTOA", "JITCID", "PTPRAA", "RPFHDR", + #else + "ACCHZB", "BANDSB", "CSDIDA", "GEOLOB", "JITCID", "NBLOCA", "PIAPEB", "REGPTB", "RSMIDA", "STEROB", + "ACCPOB", "BCKGDA", "CSEPHA", "GEOPSB", "MAPLOB", "OBJCTA", "PIAPRC", "RPC00B", "RSMPCA", "STREOB", + "ACCVTB", "BLOCKA", "CSEXRA", "GRDPSB", "MATESA", "OFFSET", "PIAPRD", "RPFDES", "RSMPIA", "TEST_DES", + "ACFTA", "BNDPLB", "CSPROA", "HISTOA", "MENSRA", "PATCHA", "PIATGA", "RPFHDR", "SECTGA", "TRGTA", + "ACFTB", "CCINFA", "CSSFAA", "ICHIPB", "MENSRB", "PATCHB", "PIATGB", "RPFIMG", "SENSRA", "USE00A", + "AIMIDA", "CLCTNA", "CSSHPA", "IMASDA", "MPDSRA", "PIAEQA", "PIXQLA", "RSMAPA", "SENSRB", + "AIMIDB", "CLCTNB", "ENGRDA", "IMGDTA", "MSDIRA", "PIAEVA", "PLTFMA", "RSMDCA", "SNSPSB", + "AIPBCA", "CMETAA", "EXOPTA", "IMRFCA", "MSTGTA", "PIAIMB", "PRADAA", "RSMECA", "SNSRA", + "ASTORA", "CSCCGA", "EXPLTA", "IOMAPA", "MTIRPA", "PIAIMC", "PRJPSB", "RSMGGA", "SOURCB", + "BANDSA", "CSCRNA", "EXPLTB", "J2KLRA", "MTIRPB", "PIAPEA", "PTPRAA", "RSMGIA", "STDIDC", + #endif + }; + return all_plugins_; +} TEST_CASE(test_load_all_plugins_C) { - ::testName = testName; - - for (const auto& tre : all_plugins) + for (const auto& tre : all_plugins()) { load_plugin(tre.c_str()); } @@ -90,9 +92,7 @@ TEST_CASE(test_load_ENGRDA) TEST_CASE(test_load_all_plugins) { - ::testName = testName; - - for (const auto& tre : all_plugins) + for (const auto& tre : all_plugins()) { #ifdef _WIN32 // need the full path to load on Linux diff --git a/externals/nitro/modules/c++/nitf/unittests/test_nitf_buffer_list.cpp b/externals/nitro/modules/c++/nitf/unittests/test_nitf_buffer_list.cpp index a67cfa1ba..06a07dfa6 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_nitf_buffer_list.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_nitf_buffer_list.cpp @@ -26,8 +26,6 @@ #include -namespace -{ TEST_CASE(testGetNumBlocks) { // 5000 total bytes @@ -177,7 +175,6 @@ TEST_CASE(testGetBlock_std_byte) numBytesInBlock)); } } -} TEST_MAIN( (void)argc; diff --git a/externals/nitro/modules/c++/nitf/unittests/test_tre_create++.cpp b/externals/nitro/modules/c++/nitf/unittests/test_tre_create++.cpp index e0c34798a..8717ad102 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_tre_create++.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_tre_create++.cpp @@ -1,9 +1,14 @@ +#include + #include +#include #include "TestCase.h" TEST_CASE(test_tre_create_329) { + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + // https://github.com/mdaus/nitro/issues/329 nitf::TRE tre("HISTOA", "HISTOA"); // allocates fields SYSTEM .. NEVENTS @@ -16,6 +21,8 @@ TEST_CASE(test_tre_create_329) TEST_CASE(test_tre_clone_329) { + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + // https://github.com/mdaus/nitro/issues/329 const std::string rd = "begin1020030004ABCDEFend"; @@ -23,7 +30,7 @@ TEST_CASE(test_tre_clone_329) { nitf_Error error; rawTre = nitf::TRE::create("TESTxyz", NITF_TRE_RAW, error); - TEST_ASSERT_NOT_EQ(nullptr, rawTre); + TEST_ASSERT_NOT_NULL(rawTre); nitf::TRE::setField(rawTre, "raw_data", rd, error); TEST_ASSERT_TRUE(true); @@ -38,4 +45,4 @@ TEST_CASE(test_tre_clone_329) TEST_MAIN( TEST_CHECK(test_tre_create_329); TEST_CHECK(test_tre_clone_329); - ) \ No newline at end of file + ) diff --git a/externals/nitro/modules/c++/nitf/unittests/test_tre_mods++.cpp b/externals/nitro/modules/c++/nitf/unittests/test_tre_mods++.cpp index 2eb899c17..d81d3ff72 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_tre_mods++.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_tre_mods++.cpp @@ -24,8 +24,10 @@ #include #include +#include #include +#include #include "TestCase.h" @@ -60,108 +62,105 @@ // A sample "strongly-typed" TRE. There are too many TREs (and too much unwillingness to change) to // actually hook this up. But it's kind of neat code that I don't want to lose. -namespace nitf +struct /*namespace*/ TREs { - namespace TREs + class NITRO_NITFCPP_API ENGRDA final { - class NITRO_NITFCPP_API ENGRDA final + nitf::TRE tre_; + + public: + // from TRE::getID() + /** + * Get the TRE identifier. This is NOT the tag, however it may be the + * same value as the tag. The ID is used to identify a specific + * version/incarnation of the TRE, if multiple are possible. For most TREs, + * this value will be the same as the tag. + */ + ENGRDA(const std::string& id = "") noexcept(false) + : tre_("ENGRDA", id.empty() ? "ENGRDA" : id.c_str()), + RESRC(tre_, "RESRC"), + RECNT(tre_, "RECNT", true /*forceUpdate*/), + ENGDTS{ tre_, "ENGDTS", RECNT }, + ENGDATC{ tre_, "ENGDATC", RECNT }, + ENGDATA{ tre_, "ENGDATA", RECNT } { - nitf::TRE tre_; - - public: - // from TRE::getID() - /** - * Get the TRE identifier. This is NOT the tag, however it may be the - * same value as the tag. The ID is used to identify a specific - * version/incarnation of the TRE, if multiple are possible. For most TREs, - * this value will be the same as the tag. - */ - ENGRDA(const std::string& id = "") noexcept(false) - : tre_("ENGRDA", id.empty() ? "ENGRDA" : id.c_str()), - RESRC(tre_, "RESRC"), - RECNT(tre_, "RECNT", true /*forceUpdate*/), - ENGDTS{ tre_, "ENGDTS", RECNT }, - ENGDATC{ tre_, "ENGDATC", RECNT }, - ENGDATA{ tre_, "ENGDATA", RECNT } - { - } - ~ENGRDA() = default; - ENGRDA(const ENGRDA&) = delete; - ENGRDA& operator=(const ENGRDA&) = delete; - ENGRDA(ENGRDA&&) = default; - ENGRDA& operator=(ENGRDA&&) = delete; - - // From ENGRDA.c - // - //static nitf_TREDescription description[] = { - // {NITF_BCS_A, 20, "Unique Source System Name", "RESRC" }, - TREField_BCS_A<20> RESRC; - - // {NITF_BCS_N, 3, "Record Entry Count", "RECNT" }, - TREField_BCS_N<3> RECNT; - - // {NITF_LOOP, 0, NULL, "RECNT"}, - // {NITF_BCS_N, 2, "Engineering Data Label Length", "ENGLN" }, - // /* This one we don't know the length of, so we have to use the special length tag */ - // {NITF_BCS_A, NITF_TRE_CONDITIONAL_LENGTH, "Engineering Data Label", - // "ENGLBL", "ENGLN" }, - // {NITF_BCS_N, 4, "Engineering Matrix Data Column Count", "ENGMTXC" }, - // {NITF_BCS_N, 4, "Engineering Matrix Data Row Count", "ENGMTXR" }, - // {NITF_BCS_A, 1, "Value Type of Engineering Data Element", "ENGTYP" }, - // {NITF_BCS_N, 1, "Engineering Data Element Size", "ENGDTS" }, - IndexedField> ENGDTS; - - // {NITF_BCS_A, 2, "Engineering Data Units", "ENGDATU" }, - // {NITF_BCS_N, 8, "Engineering Data Count", "ENGDATC" }, - IndexedField> ENGDATC; - - // /* This one we don't know the length of, so we have to use the special length tag */ - // /* Notice that we use postfix notation to compute the length - // * We also don't know the type of data (it depends on ENGDTS), so - // * we need to override the TREHandler's read method. If we don't do - // * this, not only will the field type potentially be wrong, but - // * strings will be endian swapped if they're of length 2 or 4. */ - // {NITF_BINARY, NITF_TRE_CONDITIONAL_LENGTH, "Engineering Data", - // "ENGDATA", "ENGDATC ENGDTS *"}, - IndexedField> ENGDATA; - - // {NITF_ENDLOOP, 0, NULL, NULL}, - // {NITF_END, 0, NULL, NULL} - //}; - - template - void setFieldValue(const std::string& tag, const T& value, bool forceUpdate = false) - { - tre_.setFieldValue(tag, value, forceUpdate); - } - void setFieldValue(const std::string& tag, const void* data, size_t dataLength, bool forceUpdate = false) - { - tre_.setFieldValue(tag, data, dataLength, forceUpdate); - } - - template - const T& getFieldValue(const std::string& tag, T& value) const - { - return tre_.getFieldValue(tag, value); - } - template - const T getFieldValue(const std::string& tag) const - { - return tre_.getFieldValue(tag); - } - - void updateFields() - { - tre_.updateFields(); - } - }; - } -} + } + ~ENGRDA() = default; + ENGRDA(const ENGRDA&) = delete; + ENGRDA& operator=(const ENGRDA&) = delete; + ENGRDA(ENGRDA&&) = default; + ENGRDA& operator=(ENGRDA&&) = delete; + + // From ENGRDA.c + // + //static nitf_TREDescription description[] = { + // {NITF_BCS_A, 20, "Unique Source System Name", "RESRC" }, + nitf::TREField_BCS_A<20> RESRC; + + // {NITF_BCS_N, 3, "Record Entry Count", "RECNT" }, + nitf::TREField_BCS_N<3> RECNT; + + // {NITF_LOOP, 0, NULL, "RECNT"}, + // {NITF_BCS_N, 2, "Engineering Data Label Length", "ENGLN" }, + // /* This one we don't know the length of, so we have to use the special length tag */ + // {NITF_BCS_A, NITF_TRE_CONDITIONAL_LENGTH, "Engineering Data Label", + // "ENGLBL", "ENGLN" }, + // {NITF_BCS_N, 4, "Engineering Matrix Data Column Count", "ENGMTXC" }, + // {NITF_BCS_N, 4, "Engineering Matrix Data Row Count", "ENGMTXR" }, + // {NITF_BCS_A, 1, "Value Type of Engineering Data Element", "ENGTYP" }, + // {NITF_BCS_N, 1, "Engineering Data Element Size", "ENGDTS" }, + nitf::IndexedField> ENGDTS; + + // {NITF_BCS_A, 2, "Engineering Data Units", "ENGDATU" }, + // {NITF_BCS_N, 8, "Engineering Data Count", "ENGDATC" }, + nitf::IndexedField> ENGDATC; + + // /* This one we don't know the length of, so we have to use the special length tag */ + // /* Notice that we use postfix notation to compute the length + // * We also don't know the type of data (it depends on ENGDTS), so + // * we need to override the TREHandler's read method. If we don't do + // * this, not only will the field type potentially be wrong, but + // * strings will be endian swapped if they're of length 2 or 4. */ + // {NITF_BINARY, NITF_TRE_CONDITIONAL_LENGTH, "Engineering Data", + // "ENGDATA", "ENGDATC ENGDTS *"}, + nitf::IndexedField> ENGDATA; + + // {NITF_ENDLOOP, 0, NULL, NULL}, + // {NITF_END, 0, NULL, NULL} + //}; + + template + void setFieldValue(const std::string& tag, const T& value, bool forceUpdate = false) + { + tre_.setFieldValue(tag, value, forceUpdate); + } + void setFieldValue(const std::string& tag, const void* data, size_t dataLength, bool forceUpdate = false) + { + tre_.setFieldValue(tag, data, dataLength, forceUpdate); + } + + template + const T& getFieldValue(const std::string& tag, T& value) const + { + return tre_.getFieldValue(tag, value); + } + template + const T getFieldValue(const std::string& tag) const + { + return tre_.getFieldValue(tag); + } + + void updateFields() + { + tre_.updateFields(); + } + }; +}; -namespace -{ TEST_CASE(setFields) { + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + // create an ACFTA TRE nitf::TRE tre("ACFTA"); @@ -181,17 +180,21 @@ TEST_CASE(setFields) TEST_CASE(setBinaryFields) { + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + nitf::TRE tre("RPFHDR"); const int value = 123; tre.setField("LOCSEC", value); - nitf::Field field = tre.getField("LOCSEC"); + auto field = tre.getField("LOCSEC"); const int readValue = *reinterpret_cast(field.getRawData()); TEST_ASSERT_EQ(readValue, value); } TEST_CASE(cloneTRE) { + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + nitf::TRE tre("JITCID"); tre.setField("FILCMT", "fyi"); @@ -205,6 +208,8 @@ TEST_CASE(cloneTRE) TEST_CASE(basicIteration) { + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + nitf::TRE tre("ACCPOB"); // The entire TRE is one loop, and we haven't told it @@ -232,6 +237,8 @@ TEST_CASE(basicIteration) TEST_CASE(use_ENGRDA) { + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + nitf::TRE engrda("ENGRDA", "ENGRDA"); engrda.setField("RESRC", "HSS"); @@ -254,61 +261,65 @@ TEST_CASE(use_ENGRDA) TEST_CASE(use_ENGRDA_typed_fields) { + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + nitf::TRE engrda("ENGRDA", "ENGRDA"); nitf::TREField_BCS_A<20> RESRC(engrda, "RESRC"); RESRC = "HSS"; // engrda.setField("RESRC", "HSS"); const auto resrc_ = str::strip(RESRC); - TEST_ASSERT_EQ(resrc_, "HSS"); + TEST_ASSERT_EQ_STR(resrc_, "HSS"); nitf::TREField_BCS_N<3> RECNT(engrda, "RECNT", true /*forceUpdate*/); RECNT = 1; // engrda.setField("RECNT", 1, true /*forceUpdate*/); const int64_t recnt_ = RECNT; - TEST_ASSERT_EQ(recnt_, 1); + TEST_ASSERT_EQ(recnt_, static_cast(1)); nitf::IndexedField> ENGDTS(engrda, "ENGDTS", RECNT); ENGDTS[0] = 3; // engrda.setField("ENGDTS[0]", 3); // size const int64_t engdts_0_ = ENGDTS[0]; - TEST_ASSERT_EQ(engdts_0_, 3); + TEST_ASSERT_EQ(engdts_0_, static_cast(3)); nitf::IndexedField> ENGDATC(engrda, "ENGDATC", RECNT); ENGDATC[0] = 1; // engrda.setField("ENGDATC[0]", 1); // count const int64_t engdatc_0_ = ENGDATC[0]; - TEST_ASSERT_EQ(engdatc_0_, 1); + TEST_ASSERT_EQ(engdatc_0_, static_cast(1)); engrda.updateFields(); nitf::IndexedField> ENGDATA(engrda, "ENGDATA", RECNT); ENGDATA[0] = "ABC"; // engrda.setField("ENGDATA[0]", "ABC"); const auto engdata_0_ = str::strip(ENGDATA[0]); - TEST_ASSERT_EQ(engdata_0_, "ABC"); + TEST_ASSERT_EQ_STR(engdata_0_, "ABC"); } TEST_CASE(use_typed_ENGRDA) { - nitf::TREs::ENGRDA engrda; // nitf::TRE engrda("ENGRDA", "ENGRDA"); + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + + TREs::ENGRDA engrda; // nitf::TRE engrda("ENGRDA", "ENGRDA"); engrda.RESRC = "HSS"; // engrda.setField("RESRC", "HSS"); const auto RESRC = str::strip(engrda.RESRC); - TEST_ASSERT_EQ(RESRC, "HSS"); + TEST_ASSERT_EQ_STR(RESRC, "HSS"); engrda.RECNT = 1; // engrda.setField("RECNT", 1, true /*forceUpdate*/); const int64_t RECNT = engrda.RECNT; - TEST_ASSERT_EQ(RECNT, 1); + TEST_ASSERT_EQ(RECNT, static_cast(1)); engrda.ENGDTS[0] = 3; // engrda.setField("ENGDTS[0]", 3); // size const int64_t ENGDTS_0 = engrda.ENGDTS[0]; - TEST_ASSERT_EQ(ENGDTS_0, 3); + TEST_ASSERT_EQ(ENGDTS_0, static_cast(3)); engrda.ENGDATC[0] = 1; // engrda.setField("ENGDATC[0]", 1); // count const int64_t ENGDATC_0 = engrda.ENGDATC[0]; - TEST_ASSERT_EQ(ENGDATC_0, 1); + TEST_ASSERT_EQ(ENGDATC_0, static_cast(1)); engrda.updateFields(); engrda.ENGDATA[0] = "ABC"; // engrda.setField("ENGDATA[0]", "ABC"); const auto& engrda_ = engrda; const auto ENGDATA_0 = str::strip(engrda_.ENGDATA[0]); - TEST_ASSERT_EQ(ENGDATA_0, "ABC"); + TEST_ASSERT_EQ_STR(ENGDATA_0, "ABC"); try { @@ -334,9 +345,11 @@ TEST_CASE(use_typed_ENGRDA) TEST_CASE(populateWhileIterating) { + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + nitf::TRE tre("ACCPOB"); size_t numFields = 0; - for (nitf::TRE::Iterator it = tre.begin(); it != tre.end(); ++it) + for (auto it = tre.begin(); it != tre.end(); ++it) { ++numFields; const std::string fieldName((*it).first()); @@ -358,33 +371,34 @@ TEST_CASE(populateWhileIterating) TEST_CASE(overflowingNumericFields) { + sys::OS().setEnv("NITF_PLUGIN_PATH", nitf::Test::buildPluginsDir(), true /*overwrite*/); + nitf::TRE tre("CSCRNA"); // This field has a length of 9, so check that it's properly // truncated tre.setField("ULCNR_LAT", 1.0 / 9); - TEST_ASSERT_EQ(tre.getField("ULCNR_LAT").toString(), "0.1111111"); - TEST_ASSERT_EQ(tre.getFieldValue("ULCNR_LAT"), "0.1111111"); + TEST_ASSERT_EQ_STR(tre.getField("ULCNR_LAT").toString(), "0.1111111"); + TEST_ASSERT_EQ_STR(tre.getFieldValue("ULCNR_LAT"), "0.1111111"); std::string value; - TEST_ASSERT_EQ(tre.getFieldValue("ULCNR_LAT", value), "0.1111111"); + TEST_ASSERT_EQ_STR(tre.getFieldValue("ULCNR_LAT", value), "0.1111111"); tre.setField("ULCNR_LAT", 123456789); - TEST_ASSERT_EQ(tre.getField("ULCNR_LAT").toString(), "123456789"); + TEST_ASSERT_EQ_STR(tre.getField("ULCNR_LAT").toString(), "123456789"); tre.setField("ULCNR_LAT", 12345678.); - TEST_ASSERT_EQ(tre.getField("ULCNR_LAT").toString(), "012345678"); + TEST_ASSERT_EQ_STR(tre.getField("ULCNR_LAT").toString(), "012345678"); tre.setField("ULCNR_LAT", 12345678.9); - TEST_ASSERT_EQ(tre.getField("ULCNR_LAT").toString(), "012345678"); + TEST_ASSERT_EQ_STR(tre.getField("ULCNR_LAT").toString(), "012345678"); tre.setField("ULCNR_LAT", 1); - TEST_ASSERT_EQ(tre.getField("ULCNR_LAT").toString(), "000000001"); + TEST_ASSERT_EQ_STR(tre.getField("ULCNR_LAT").toString(), "000000001"); // If we run out of digits before hitting the decimal, there's no // saving it TEST_EXCEPTION(tre.setField("ULCNR_LAT", 123456789012LL)); } -} TEST_MAIN( TEST_CHECK(setFields); diff --git a/externals/nitro/modules/c++/nitf/unittests/test_tre_read.cpp b/externals/nitro/modules/c++/nitf/unittests/test_tre_read.cpp index 3e8ac96f7..3d0a7b8de 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_tre_read.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_tre_read.cpp @@ -93,25 +93,25 @@ files, this bug may allow an attacker to cause a denial of service. static std::string testName; const char* output_file = "test_writer_3++.nitf"; -static const char* argv0 = NULL; +static const char* argv0; static bool is_vs_gtest() { return argv0 == NULL; } -namespace fs = std::filesystem; -static fs::path findInputFile_(const std::string& name) +using path = std::filesystem::path; +static path findInputFile_(const std::string& name) { - const auto inputFile = fs::path("modules") / "c++" / "nitf" / "unittests" / name; + const auto inputFile = path("modules") / "c++" / "nitf" / "unittests" / name; if (is_vs_gtest()) // running Google Test in Visual Studio { - const auto root= fs::current_path().parent_path().parent_path(); + const auto root= std::filesystem::current_path().parent_path().parent_path(); return root / inputFile; } - const auto exe = absolute(fs::path(argv0)); - fs::path root = exe.parent_path(); + const auto exe = absolute(path(argv0)); + auto root = exe.parent_path(); do { auto retval = root / inputFile; @@ -133,7 +133,6 @@ static const char* findInputFile(const char* name) TEST_CASE(test_nitf_Record_unmergeTREs_crash) { - ::testName = testName; const char* input_file = findInputFile("bug2_crash.ntf"); nitf_Error error; @@ -146,11 +145,11 @@ TEST_CASE(test_nitf_Record_unmergeTREs_crash) /* We need to make a reader so we can parse the NITF */ nitf_Reader* reader = nitf_Reader_construct(&error); - TEST_ASSERT_NOT_EQ(NULL, reader); + TEST_ASSERT_NOT_NULL(reader); /* This parses all header data within the NITF */ nitf_Record* record = nitf_Reader_read(reader, io, &error); - TEST_ASSERT_NOT_EQ(NULL, record); + TEST_ASSERT_NOT_NULL(record); /* Open the output IO Handle */ nitf_IOHandle output = nitf_IOHandle_create("bug2_crash_out.ntf", NITF_ACCESS_WRITEONLY, NITF_CREATE, &error); @@ -160,7 +159,7 @@ TEST_CASE(test_nitf_Record_unmergeTREs_crash) } nitf_Writer* writer = nitf_Writer_construct(&error); - TEST_ASSERT_NOT_EQ(nullptr, writer); + TEST_ASSERT_NOT_NULL(writer); (void)nitf_Writer_prepare(writer, record, output, &error); nitf_IOHandle_close(io); @@ -172,7 +171,6 @@ TEST_CASE(test_nitf_Record_unmergeTREs_crash) TEST_CASE(test_nitf_Record_unmergeTREs_hangs) { - ::testName = testName; const char* input_file = findInputFile("bug6_hangs.ntf"); nitf_Error error; @@ -185,11 +183,11 @@ TEST_CASE(test_nitf_Record_unmergeTREs_hangs) /* We need to make a reader so we can parse the NITF */ nitf_Reader* reader = nitf_Reader_construct(&error); - TEST_ASSERT_NOT_EQ(NULL, reader); + TEST_ASSERT_NOT_NULL(reader); /* This parses all header data within the NITF */ nitf_Record* record = nitf_Reader_read(reader, io, &error); - TEST_ASSERT_NOT_EQ(NULL, record); + TEST_ASSERT_NOT_NULL(record); /* Open the output IO Handle */ nitf_IOHandle output = nitf_IOHandle_create("bug6_hangs_out.ntf", NITF_ACCESS_WRITEONLY, NITF_CREATE, &error); @@ -199,7 +197,7 @@ TEST_CASE(test_nitf_Record_unmergeTREs_hangs) } nitf_Writer* writer = nitf_Writer_construct(&error); - TEST_ASSERT_NOT_EQ(NULL, writer); + TEST_ASSERT_NOT_NULL(writer); (void)nitf_Writer_prepare(writer, record, output, &error); nitf_IOHandle_close(io); @@ -211,7 +209,6 @@ TEST_CASE(test_nitf_Record_unmergeTREs_hangs) TEST_CASE(test_defaultRead_crash) { - ::testName = testName; const char* input_file = findInputFile("bug3_crash.ntf"); nitf_Error error; @@ -224,7 +221,7 @@ TEST_CASE(test_defaultRead_crash) /* We need to make a reader so we can parse the NITF */ nitf_Reader* reader = nitf_Reader_construct(&error); - TEST_ASSERT_NOT_EQ(NULL, reader); + TEST_ASSERT_NOT_NULL(reader); /* This parses all header data within the NITF */ (void)nitf_Reader_read(reader, io, &error); @@ -234,7 +231,6 @@ TEST_CASE(test_defaultRead_crash) TEST_CASE(test_readBandInfo_crash) { - ::testName = testName; const char* input_file = findInputFile("bug4_crash.ntf"); nitf_Error error; @@ -247,7 +243,7 @@ TEST_CASE(test_readBandInfo_crash) /* We need to make a reader so we can parse the NITF */ nitf_Reader* reader = nitf_Reader_construct(&error); - TEST_ASSERT_NOT_EQ(NULL, reader); + TEST_ASSERT_NOT_NULL(reader); /* This parses all header data within the NITF */ (void) nitf_Reader_read(reader, io, &error); @@ -256,7 +252,6 @@ TEST_CASE(test_readBandInfo_crash) TEST_CASE(test_readRESubheader_crash) { - ::testName = testName; const char* input_file = findInputFile("bug5_crash.ntf"); nitf_Error error; @@ -269,7 +264,7 @@ TEST_CASE(test_readRESubheader_crash) /* We need to make a reader so we can parse the NITF */ nitf_Reader* reader = nitf_Reader_construct(&error); - TEST_ASSERT_NOT_EQ(NULL, reader); + TEST_ASSERT_NOT_NULL(reader); /* This parses all header data within the NITF */ (void)nitf_Reader_read(reader, io, &error); diff --git a/externals/nitro/modules/c++/nitf/unittests/test_writer_3++.cpp b/externals/nitro/modules/c++/nitf/unittests/test_writer_3++.cpp index 62bef7524..b7fa3c88f 100644 --- a/externals/nitro/modules/c++/nitf/unittests/test_writer_3++.cpp +++ b/externals/nitro/modules/c++/nitf/unittests/test_writer_3++.cpp @@ -34,30 +34,28 @@ static std::string testName; const std::string output_file = "test_writer_3++.nitf"; -namespace fs = std::filesystem; +using path = std::filesystem::path; static std::string argv0; -static fs::path findInputFile() +static path findInputFile() { - const fs::path inputFile = fs::path("modules") / "c++" / "nitf" / "unittests" / "sicd_50x50.nitf"; + const auto inputFile = path("modules") / "c++" / "nitf" / "unittests" / "sicd_50x50.nitf"; - fs::path root; + path root; if (argv0.empty()) { // running in Visual Studio - root = fs::current_path().parent_path().parent_path(); + root = std::filesystem::current_path().parent_path().parent_path(); } else { - root = absolute(fs::path(argv0)).parent_path().parent_path().parent_path().parent_path(); + root = absolute(path(argv0)).parent_path().parent_path().parent_path().parent_path(); root = root.parent_path().parent_path(); } return root / inputFile; } -static nitf::Record doRead(const std::string& inFile, nitf::Reader& reader); - static std::string makeBandName(const std::string& rootFile, int imageNum, int bandNum) { std::string::size_type pos = rootFile.find_last_of("/\\"); @@ -137,18 +135,18 @@ static void manuallyWriteImageBands(nitf::ImageSegment & segment, TEST_ASSERT_EQ(2, static_cast(nBands)); TEST_ASSERT_EQ(0, static_cast(xBands)); TEST_ASSERT_EQ(static_cast(50), nRows); - TEST_ASSERT_EQ(static_cast(50), nColumns); + TEST_ASSERT_EQ(static_cast(50), nColumns); TEST_ASSERT_EQ(nitf::PixelValueType::Floating , subheader.pixelValueType()); TEST_ASSERT_EQ(static_cast(32), subheader.numBitsPerPixel()); - TEST_ASSERT_EQ("32", subheader.getActualBitsPerPixel().toString()); - TEST_ASSERT_EQ("R", subheader.getPixelJustification().toString()); + TEST_ASSERT_EQ_STR("32", subheader.getActualBitsPerPixel().toString()); + TEST_ASSERT_EQ_STR("R", subheader.getPixelJustification().toString()); TEST_ASSERT_EQ(nitf::BlockingMode::Pixel, subheader.imageBlockingMode()); TEST_ASSERT_EQ(static_cast(1), subheader.numBlocksPerRow()); TEST_ASSERT_EQ(static_cast(1), subheader.numBlocksPerCol()); TEST_ASSERT_EQ(static_cast(50), subheader.numPixelsPerHorizBlock()); TEST_ASSERT_EQ(static_cast(50), subheader.numPixelsPerVertBlock()); TEST_ASSERT_EQ(nitf::ImageCompression::NC, subheader.imageCompression()); - TEST_ASSERT_EQ(" ", subheader.getCompressionRate().toString()); + TEST_ASSERT_EQ_STR(" ", subheader.getCompressionRate().toString()); nitf::BufferList buffer(nBands); std::vector bandList(nBands); @@ -215,78 +213,68 @@ static nitf::Record doRead(const std::string& inFile, nitf::Reader& reader) return record; } -namespace test_writer_3 +/* + * This test tests the round-trip process of taking an input NITF + * file and writing it to a new file. This includes writing the image + * segments (headers, extensions, and image data). This is an example + * of how users can write the image data to their NITF file + */ +static void test_writer_3__doWrite(nitf::Record record, nitf::Reader& reader, const std::string& inRootFile, const std::string& outFile) { - /* - * This test tests the round-trip process of taking an input NITF - * file and writing it to a new file. This includes writing the image - * segments (headers, extensions, and image data). This is an example - * of how users can write the image data to their NITF file - */ - static void doWrite(nitf::Record record, nitf::Reader& reader, const std::string& inRootFile, const std::string& outFile) - { - nitf::Writer writer; - nitf::IOHandle output(outFile, NITF_ACCESS_WRITEONLY, NITF_CREATE); - writer.prepare(output, record); + nitf::Writer writer; + nitf::IOHandle output(outFile, NITF_ACCESS_WRITEONLY, NITF_CREATE); + writer.prepare(output, record); - ::doWrite(record, reader, inRootFile, writer); + doWrite(record, reader, inRootFile, writer); - output.close(); - } + output.close(); } TEST_CASE(test_writer_3_) { - ::testName = testName; - const auto input_file = findInputFile().string(); nitf::Reader reader; nitf::Record record = doRead(input_file, reader); - test_writer_3::doWrite(record, reader, input_file, output_file); + test_writer_3__doWrite(record, reader, input_file, output_file); } -namespace test_buffered_write +/* + * This test tests the round-trip process of taking an input NITF + * file and writing it to a new file. This includes writing the image + * segments (headers, extensions, and image data). + * + * This example differs from test_writer_3 in that it tests the + * BufferedWriter classes, and writes the entire file as a set of + * configurable sized blocks. The last block may be smaller than the others + * if the data does not fill the block. + * + */ +static void test_buffered_write__doWrite(nitf::Record record, nitf::Reader& reader, + const std::string& inRootFile, + const std::string& outFile, + size_t bufferSize) { - /* - * This test tests the round-trip process of taking an input NITF - * file and writing it to a new file. This includes writing the image - * segments (headers, extensions, and image data). - * - * This example differs from test_writer_3 in that it tests the - * BufferedWriter classes, and writes the entire file as a set of - * configurable sized blocks. The last block may be smaller than the others - * if the data does not fill the block. - * - */ - void doWrite(nitf::Record record, nitf::Reader& reader, - const std::string& inRootFile, - const std::string& outFile, - size_t bufferSize) - { - nitf::BufferedWriter output(outFile, bufferSize); - nitf::Writer writer; - writer.prepareIO(output, record); + nitf::BufferedWriter output(outFile, bufferSize); + nitf::Writer writer; + writer.prepareIO(output, record); - ::doWrite(record, reader, inRootFile, writer); + doWrite(record, reader, inRootFile, writer); - const auto blocksWritten = output.getNumBlocksWritten(); - const auto partialBlocksWritten = output.getNumPartialBlocksWritten(); - output.close(); - TEST_ASSERT_EQ(static_cast(60), blocksWritten); - TEST_ASSERT_EQ(static_cast(53), partialBlocksWritten); - } + const auto blocksWritten = output.getNumBlocksWritten(); + const auto partialBlocksWritten = output.getNumPartialBlocksWritten(); + output.close(); + TEST_ASSERT_EQ(static_cast(60), blocksWritten); + TEST_ASSERT_EQ(static_cast(53), partialBlocksWritten); } TEST_CASE(test_buffered_write_) { - ::testName = testName; - const auto input_file = findInputFile().string(); size_t blockSize = 8192; nitf::Reader reader; nitf::Record record = doRead(input_file, reader); - test_buffered_write::doWrite(record, reader, input_file, output_file, blockSize); + test_buffered_write__doWrite(record, reader, input_file, output_file, blockSize); } diff --git a/externals/nitro/modules/c++/nitf/wscript b/externals/nitro/modules/c++/nitf/wscript index 827fc75ee..223057726 100644 --- a/externals/nitro/modules/c++/nitf/wscript +++ b/externals/nitro/modules/c++/nitf/wscript @@ -1,7 +1,7 @@ from os.path import join, split, basename, splitext NAME = 'nitf' -MODULE_DEPS = 'j2k math mt sys mem io gsl std' +MODULE_DEPS = 'j2k math sio.lite mt sys mem io gsl std' TEST_DEPS = 'j2k cli' USELIB = 'THREAD DL' USELIB_LOCAL = 'nitf-c j2k-c' diff --git a/externals/nitro/modules/c++/pch.h b/externals/nitro/modules/c++/pch.h index 52050cae0..afea2ef15 100644 --- a/externals/nitro/modules/c++/pch.h +++ b/externals/nitro/modules/c++/pch.h @@ -20,6 +20,8 @@ #pragma comment(lib, "except-c++") #pragma comment(lib, "sys-c++") #pragma comment(lib, "str-c++") +#pragma comment(lib, "mt-c++") +#pragma comment(lib, "math-c++") // We're building in Visual Studio ... used to control where we get a little bit of config info #define NITRO_PCH 1 diff --git a/externals/nitro/modules/c/cgm/source/MetafileReader.c b/externals/nitro/modules/c/cgm/source/MetafileReader.c index 30073ecee..be23c2698 100644 --- a/externals/nitro/modules/c/cgm/source/MetafileReader.c +++ b/externals/nitro/modules/c/cgm/source/MetafileReader.c @@ -43,7 +43,7 @@ cgm_FillAttributes* createFillAttributes(cgm_ParseContext* pc, nitf_Error*error) atts->interiorStyle = pc->style; atts->edgeVisibility = pc->visibility; atts->edgeWidth = pc->width; - atts->edgeType = pc->type; + atts->edgeType = (short) pc->type; atts->edgeColor->r = pc->color.r; atts->edgeColor->g = pc->color.g; @@ -164,36 +164,37 @@ NITFPRIV(cgm_Rectangle*) readRectangle(char* b, int len, nitf_Error* error) { (void)len; - short x1, x2, y1, y2; cgm_Rectangle* rectangle = cgm_Rectangle_construct(error); if (!rectangle) return NULL; + uint16_t x1, x2, y1, y2; + memcpy(&x1, &b[0], 2); - rectangle->x1 = NITF_NTOHS(x1); + rectangle->x1 = (short) NITF_NTOHS(x1); memcpy(&y1, &b[2], 2); - rectangle->y1 = NITF_NTOHS(y1); + rectangle->y1 = (short)NITF_NTOHS(y1); memcpy(&x2, &b[4], 2); - rectangle->x2 = NITF_NTOHS(x2); + rectangle->x2 = (short)NITF_NTOHS(x2); memcpy(&y2, &b[6], 2); - rectangle->y2 = NITF_NTOHS(y2); + rectangle->y2 = (short)NITF_NTOHS(y2); return rectangle; } NITFPRIV(cgm_Vertex*) readVertex(char* b, nitf_Error* error) { - short s; cgm_Vertex* v = cgm_Vertex_construct(-1, -1, error); if (!v) return NULL; + uint16_t s; memcpy(&s, &b[0], 2); - v->x = NITF_NTOHS(s); + v->x = (short)NITF_NTOHS(s); memcpy(&s, &b[2], 2); - v->y = NITF_NTOHS(s); + v->y = (short)NITF_NTOHS(s); return v; } @@ -201,13 +202,13 @@ NITFPRIV(cgm_Vertex*) readVertex(char* b, nitf_Error* error) NITFPRIV(cgm_VertexClose*) readVertexClose(char* b, nitf_Error* error) { cgm_VertexClose* v = cgm_VertexClose_construct(error); - short s; if (!v) return NULL; + uint16_t s; memcpy(&s, &b[0], 2); - v->x = NITF_NTOHS(s); + v->x = (short)NITF_NTOHS(s); memcpy(&s, &b[2], 2); - v->y = NITF_NTOHS(s); + v->y = (short)NITF_NTOHS(s); memcpy(&s, &b[4], 2); v->edgeOutFlag = (cgm_EdgeCloseType) NITF_NTOHS(s); return v; @@ -241,9 +242,9 @@ NITFPRIV(NITF_BOOL) readVertices(char* b, NITFPRIV(short) readShort(char* b) { - short s; + uint16_t s; memcpy(&s, b, 2); - return NITF_NTOHS(s); + return (short) NITF_NTOHS(s); } NITFPRIV(char*) readString(char* b, int length) @@ -251,11 +252,11 @@ NITFPRIV(char*) readString(char* b, int length) char* str = NULL; if (length > 0) { - str = (char*)NITF_MALLOC(length + 1); + str = (char*)NITF_MALLOC((size_t)length + 1); if (str != NULL) { str[length] = 0; - memcpy(str, b, length); + memcpy(str, b, (size_t)length); } } return str; @@ -449,11 +450,11 @@ NITF_BOOL fontList(cgm_Metafile* mf, cgm_ParseContext* pc, int classType, int slen = 0x000000FF & bu; assert(i <= len); - p = (char*)NITF_MALLOC(slen + 1); + p = (char*)NITF_MALLOC((size_t)slen + 1); if (p != NULL) { p[slen] = 0; - memcpy(p, &b[++i], slen); + memcpy(p, &b[++i], (size_t)slen); } total += slen + 1; diff --git a/externals/nitro/modules/c/cgm/source/MetafileWriter.c b/externals/nitro/modules/c/cgm/source/MetafileWriter.c index 311cfbcae..4e64b4bcc 100644 --- a/externals/nitro/modules/c/cgm/source/MetafileWriter.c +++ b/externals/nitro/modules/c/cgm/source/MetafileWriter.c @@ -24,16 +24,16 @@ NITF_BOOL writeRectangle(cgm_Rectangle* r, nitf_IOInterface* io, nitf_Error* error) { - short s = NITF_HTONS(r->x1); + short s = (short) NITF_HTONS((uint16_t)r->x1); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(r->y1); + s = (short)NITF_HTONS((uint16_t)r->y1); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(r->x2); + s = (short)NITF_HTONS((uint16_t)r->x2); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(r->y2); + s = (short)NITF_HTONS((uint16_t)r->y2); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; return NITF_SUCCESS; @@ -184,21 +184,21 @@ NITF_BOOL writeFillAttributes(cgm_FillAttributes* atts, nitf_IOInterface* io, /* Write the edge vis */ if (atts->edgeVisibility != -1) { - tmpShort = NITF_HTONS(atts->edgeVisibility); + tmpShort = (short)NITF_HTONS((uint16_t)atts->edgeVisibility); if (!writeField(5, 30, (const char*)&tmpShort, 2, io, error)) return NITF_FAILURE; } /* Write the edge width */ if (atts->edgeWidth != -1) { - tmpShort = NITF_HTONS(atts->edgeWidth); + tmpShort = (short)NITF_HTONS((uint16_t)atts->edgeWidth); if (!writeField(5, 28, (const char*)&tmpShort, 2, io, error)) return NITF_FAILURE; } /* Write the edge type */ if (atts->edgeType != CGM_TYPE_NOT_SET) { - tmpShort = NITF_HTONS(atts->edgeType); + tmpShort = (short)NITF_HTONS((uint16_t)atts->edgeType); if (!writeField(5, 27, (const char*)&tmpShort, 2, io, error)) return NITF_FAILURE; } @@ -221,14 +221,14 @@ NITF_BOOL writeLineAttributes(cgm_LineAttributes* atts, nitf_IOInterface* io, /* Write the edge width */ if (atts->lineWidth != -1) { - tmpShort = NITF_HTONS(atts->lineWidth); + tmpShort = (short)NITF_HTONS(atts->lineWidth); if (!writeField(5, 3, (const char*)&tmpShort, 2, io, error)) return NITF_FAILURE; } /* Write the edge type */ if (atts->lineType != CGM_TYPE_NOT_SET) { - tmpShort = NITF_HTONS(atts->lineType); + tmpShort = (short)NITF_HTONS(atts->lineType); if (!writeField(5, 2, (const char*)&tmpShort, 2, io, error)) return NITF_FAILURE; } @@ -311,7 +311,7 @@ NITF_BOOL writeBody(cgm_MetafileWriter* writer, cgm_PictureBody* body, return NITF_FAILURE; /* Transparency */ - short tmpShort = NITF_HTONS((uint16_t)body->transparency); + short tmpShort = (short) NITF_HTONS((uint16_t)body->transparency); if (!writeField(3, 4, (const char*)&tmpShort, 2, io, error)) return NITF_FAILURE; @@ -508,7 +508,7 @@ NITF_BOOL writeText(cgm_Element* element, nitf_IOInterface* io, nitf_Error* erro return NITF_FAILURE; /* Trick since we know it zero padded in memory */ - const size_t actual_ = actual >= 7 ? actual - 7 : 0; + const size_t actual_ = actual >= 7 ? (size_t)actual - 7 : (size_t)0; if (!nitf_IOInterface_write(io, (const char*)text->str, actual_, error)) return NITF_FAILURE; @@ -699,33 +699,33 @@ NITF_BOOL writeEllipticalArcCenter(cgm_Element* element, nitf_IOInterface* io, if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->centerY); + s = (short)NITF_HTONS(arc->centerY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->end1X); + s = (short)NITF_HTONS(arc->end1X); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->end1Y); + s = (short)NITF_HTONS(arc->end1Y); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->end2X); + s = (short)NITF_HTONS(arc->end2X); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->end2Y); + s = (short)NITF_HTONS(arc->end2Y); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->startVectorY); + s = (short)NITF_HTONS(arc->startVectorY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->startVectorY); + s = (short)NITF_HTONS(arc->startVectorY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->endVectorX); + s = (short)NITF_HTONS(arc->endVectorX); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->endVectorY); + s = (short)NITF_HTONS(arc->endVectorY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; @@ -748,41 +748,41 @@ NITF_BOOL writeEllipticalArcCenterClose(cgm_Element* element, nitf_IOInterface* if (!writeHeader(4, 19, 22, io, &actual, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->centerX); + s = (short)NITF_HTONS(arc->centerX); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->centerY); + s = (short)NITF_HTONS(arc->centerY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->end1X); + s = (short)NITF_HTONS(arc->end1X); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->end1Y); + s = (short)NITF_HTONS(arc->end1Y); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->end2X); + s = (short)NITF_HTONS(arc->end2X); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->end2Y); + s = (short)NITF_HTONS(arc->end2Y); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->startVectorY); + s = (short)NITF_HTONS(arc->startVectorY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->startVectorY); + s = (short)NITF_HTONS(arc->startVectorY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->endVectorX); + s = (short)NITF_HTONS(arc->endVectorX); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->endVectorY); + s = (short)NITF_HTONS(arc->endVectorY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->closeType); + s = (short)NITF_HTONS(arc->closeType); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; @@ -828,15 +828,15 @@ NITF_BOOL writeCircle(cgm_Element* element, nitf_IOInterface* io, nitf_Error* er if (!writeHeader(4, 12, 6, io, &actual, error)) return NITF_FAILURE; - s = NITF_HTONS(circle->centerX); + s = (short)NITF_HTONS(circle->centerX); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(circle->centerY); + s = (short)NITF_HTONS(circle->centerY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(circle->radius); + s = (short)NITF_HTONS(circle->radius); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; @@ -858,28 +858,28 @@ NITF_BOOL writeCircularArcCenter(cgm_Element* element, nitf_IOInterface* io, if (!writeHeader(4, 18, 20, io, &actual, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->centerX); + s = (short)NITF_HTONS((uint16_t)arc->centerX); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->centerY); + s = (short)NITF_HTONS((uint16_t)arc->centerY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->startX); + s = (short)NITF_HTONS((uint16_t)arc->startX); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->startY); + s = (short)NITF_HTONS((uint16_t)arc->startY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->endX); + s = (short)NITF_HTONS((uint16_t)arc->endX); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->endY); + s = (short)NITF_HTONS((uint16_t)arc->endY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->radius); + s = (short)NITF_HTONS((uint16_t)arc->radius); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; @@ -898,35 +898,35 @@ NITF_BOOL writeCircularArcCenterClose(cgm_Element* element, nitf_IOInterface* io return NITF_FAILURE; } - s = NITF_HTONS(arc->centerX); + s = (short)NITF_HTONS((uint16_t)arc->centerX); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->centerY); + s = (short)NITF_HTONS((uint16_t)arc->centerY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->startX); + s = (short)NITF_HTONS((uint16_t)arc->startX); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->startY); + s = (short)NITF_HTONS((uint16_t)arc->startY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->endX); + s = (short)NITF_HTONS((uint16_t)arc->endX); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->endY); + s = (short)NITF_HTONS((uint16_t)arc->endY); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->radius); + s = (short)NITF_HTONS((uint16_t)arc->radius); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; - s = NITF_HTONS(arc->closeType); + s = (short)NITF_HTONS(arc->closeType); if (!nitf_IOInterface_write(io, (const char*)&s, 2, error)) return NITF_FAILURE; diff --git a/externals/nitro/modules/c/j2k/CMakeLists.txt b/externals/nitro/modules/c/j2k/CMakeLists.txt index 368ca5e56..f2a60f864 100644 --- a/externals/nitro/modules/c/j2k/CMakeLists.txt +++ b/externals/nitro/modules/c/j2k/CMakeLists.txt @@ -23,6 +23,7 @@ coda_add_module( source/JasPerImpl.c source/OpenJPEGImpl.c source/j2k_Reader.c + source/TileWriter.c source/SimpleComponentImpl.c source/SimpleContainerImpl.c source/j2k_Writer.c) @@ -37,4 +38,18 @@ coda_add_tests( test_j2k_read_region.c test_j2k_read_tile.c) +# Build all J2KCompress and J2KDecompress +set(j2k_shared_srcs J2KCompress J2KDecompress) +foreach(j2k_shared ${j2k_shared_srcs}) + add_definitions(-DHAVE_J2K_H) + add_library(${j2k_shared} SHARED shared/${j2k_shared}.c) + target_link_libraries(${j2k_shared} PUBLIC nitf-c j2k-c) + target_compile_definitions(${j2k_shared} PRIVATE NITF_MODULE_EXPORTS) + + # This line is making sure the resultant TRE is named, e.g. + # XML_DATA_CONTENT.so instead of libXML_DATA_CONTENT.so + set_target_properties(${j2k_shared} PROPERTIES PREFIX "") +endforeach() + +install(TARGETS ${j2k_shared_srcs} DESTINATION "share/nitf/plugins") \ No newline at end of file diff --git a/externals/nitro/modules/c/j2k/J2KCompress.vcxproj b/externals/nitro/modules/c/j2k/J2KCompress.vcxproj index bfc3fdf47..5acb00e8e 100644 --- a/externals/nitro/modules/c/j2k/J2KCompress.vcxproj +++ b/externals/nitro/modules/c/j2k/J2KCompress.vcxproj @@ -14,7 +14,7 @@ - + {f06550ad-cfc7-40b8-8727-6c82c69a8982} @@ -30,12 +30,12 @@ DynamicLibrary true - v142 + v143 DynamicLibrary false - v142 + v143 true @@ -62,19 +62,26 @@ - Level3 + EnableAllWarnings true _DEBUG;_LIB;%(PreprocessorDefinitions);NRT_MODULE_EXPORTS;NITRO_PCH;HAVE_J2K_H true $(ProjectDir)include;$(ProjectDir)..\nrt\include;$(ProjectDir)..\nitf\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + true + Guard + MultiThreadedDebugDLL + true + ProgramDatabase true - $(SolutionDir)externals\coda-oss\install-$(Configuration)-$(Platform).$(PlatformToolset)\lib\ + $(SolutionDir)externals\coda-oss\out\install\$(Platform)-$(Configuration)\lib\;$(SolutionDir)externals\coda-oss\install-$(Configuration)-$(Platform).$(PlatformToolset)\lib\ + openjpeg.lib;%(AdditionalDependencies) @@ -88,6 +95,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;$(ProjectDir)..\nitf\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true @@ -95,7 +105,8 @@ true true true - $(SolutionDir)externals\coda-oss\install-$(Configuration)-$(Platform).$(PlatformToolset)\lib\ + $(SolutionDir)externals\coda-oss\out\install\$(Platform)-$(Configuration)\lib\;$(SolutionDir)externals\coda-oss\install-$(Configuration)-$(Platform).$(PlatformToolset)\lib\ + openjpeg.lib;%(AdditionalDependencies) diff --git a/externals/nitro/modules/c/j2k/J2KDecompress.vcxproj b/externals/nitro/modules/c/j2k/J2KDecompress.vcxproj index 45035afec..12c9a8c2b 100644 --- a/externals/nitro/modules/c/j2k/J2KDecompress.vcxproj +++ b/externals/nitro/modules/c/j2k/J2KDecompress.vcxproj @@ -14,7 +14,7 @@ - + {f06550ad-cfc7-40b8-8727-6c82c69a8982} @@ -30,12 +30,12 @@ DynamicLibrary true - v142 + v143 DynamicLibrary false - v142 + v143 true @@ -62,18 +62,26 @@ - Level3 + EnableAllWarnings true - _DEBUG;_LIB;%(PreprocessorDefinitions);NRT_MODULE_EXPORTS;NITRO_PCH + _DEBUG;_LIB;%(PreprocessorDefinitions);NRT_MODULE_EXPORTS;NITRO_PCH;HAVE_J2K_H true - $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) + $(ProjectDir)include;$(ProjectDir)..\nrt\include;$(ProjectDir)..\nitf\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + true + Guard + MultiThreadedDebugDLL + true + ProgramDatabase true + $(SolutionDir)externals\coda-oss\out\install\$(Platform)-$(Configuration)\lib\;$(SolutionDir)externals\coda-oss\install-$(Configuration)-$(Platform).$(PlatformToolset)\lib\ + openjpeg.lib;%(AdditionalDependencies) @@ -82,11 +90,14 @@ true true true - NDEBUG;_LIB;%(PreprocessorDefinitions);NRT_MODULE_EXPORTS;NITRO_PCH + NDEBUG;_LIB;%(PreprocessorDefinitions);NRT_MODULE_EXPORTS;NITRO_PCH;HAVE_J2K_H true - $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) + $(ProjectDir)include;$(ProjectDir)..\nrt\include;$(ProjectDir)..\nitf\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true @@ -94,6 +105,8 @@ true true true + $(SolutionDir)externals\coda-oss\out\install\$(Platform)-$(Configuration)\lib\;$(SolutionDir)externals\coda-oss\install-$(Configuration)-$(Platform).$(PlatformToolset)\lib\ + openjpeg.lib;%(AdditionalDependencies) diff --git a/externals/nitro/modules/c/j2k/include/import/j2k.h b/externals/nitro/modules/c/j2k/include/import/j2k.h index dc95c4b89..7b4d8cb0d 100644 --- a/externals/nitro/modules/c/j2k/include/import/j2k.h +++ b/externals/nitro/modules/c/j2k/include/import/j2k.h @@ -25,7 +25,7 @@ #include "j2k/Container.h" #include "j2k/Defines.h" -#include "j2k/Reader.h" -#include "j2k/Writer.h" +#include "j2k/j2k_Reader.h" +#include "j2k/j2k_Writer.h" #endif diff --git a/externals/nitro/modules/c/j2k/include/j2k/Defines.h b/externals/nitro/modules/c/j2k/include/j2k/Defines.h index b00ba2914..432e0ad09 100644 --- a/externals/nitro/modules/c/j2k/include/j2k/Defines.h +++ b/externals/nitro/modules/c/j2k/include/j2k/Defines.h @@ -26,7 +26,9 @@ #if defined(WIN32) || defined(_WIN32) # if defined(J2K_MODULE_EXPORTS) +# if !defined(NRT_MODULE_EXPORTS) # define NRT_MODULE_EXPORTS +# endif # elif defined(J2K_MODULE_IMPORTS) # define NRT_MODULE_IMPORTS # endif diff --git a/externals/nitro/modules/c/j2k/include/j2k/TileWriter.h b/externals/nitro/modules/c/j2k/include/j2k/TileWriter.h new file mode 100644 index 000000000..b365d7a76 --- /dev/null +++ b/externals/nitro/modules/c/j2k/include/j2k/TileWriter.h @@ -0,0 +1,124 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2004 - 2014, MDA Information Systems LLC + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + + // J2K isn't part of "nitf" (yet?) so use NITRO, not NITF prefix +#ifndef NITRO_j2k_TileWriter_h_INCLUDED_ +#define NITRO_j2k_TileWriter_h_INCLUDED_ + +#include "j2k/Defines.h" + +J2K_CXX_GUARD + +typedef struct _j2k_stream_t +{ + void /*obj_stream_t*/* opj_stream; +} j2k_stream_t; + +#define NITRO_J2K_STREAM_CHUNK_SIZE 0x100000 /** 1 mega by default */ // c.f. OPJ_J2K_STREAM_CHUNK_SIZE in + +J2KAPI(j2k_stream_t*) j2k_stream_create(size_t chunkSize, J2K_BOOL isInputStream); +J2KAPI(void) j2k_stream_destroy(j2k_stream_t* pStream); + +//---------------------------------------------------------------------------------------------------------------- + +typedef struct _j2k_image_t +{ + void /*opj_image_t*/* opj_image; +} j2k_image_t; + +typedef struct _j2k_image_comptparm // c.f. opj_image_comptparm in +{ + uint32_t dx; /** XRsiz: horizontal separation of a sample of ith component with respect to the reference grid */ + uint32_t dy; /** YRsiz: vertical separation of a sample of ith component with respect to the reference grid */ + uint32_t w; /** data width */ + uint32_t h; /** data height */ + uint32_t x0; /** x component offset compared to the whole image */ + uint32_t y0; /** y component offset compared to the whole image */ + uint32_t prec; /** precision */ + uint32_t bpp; /** image depth in bits */ + J2K_BOOL sgnd; /** signed (1) / unsigned (0) */ +} j2k_image_comptparm; + +typedef enum J2K_COLOR_SPACE { // c.f. OBJ_COLOR_SPACE in + J2K_CLRSPC_UNKNOWN = -1, /**< not supported by the library */ + J2K_CLRSPC_UNSPECIFIED = 0, /**< not specified in the codestream */ + J2K_CLRSPC_SRGB = 1, /**< sRGB */ + J2K_CLRSPC_GRAY = 2, /**< grayscale */ + J2K_CLRSPC_SYCC = 3, /**< YUV */ + J2K_CLRSPC_EYCC = 4, /**< e-YCC */ + J2K_CLRSPC_CMYK = 5 /**< CMYK */ +} J2K_COLOR_SPACE; + +J2KAPI(j2k_image_t*) j2k_image_tile_create(uint32_t numcmpts, const j2k_image_comptparm* cmptparms, J2K_COLOR_SPACE clrspc); +J2KAPI(J2K_BOOL) j2k_image_init(j2k_image_t* pImage, int x0, int y0, int x1, int y1, int numcmpts, J2K_COLOR_SPACE color_space); +J2KAPI(void) j2k_image_destroy(j2k_image_t* pImage); + +//---------------------------------------------------------------------------------------------------------------- + +typedef struct _j2k_codec +{ + void /*opj_codec_t*/* opj_codec; +} j2k_codec_t; + +typedef struct _j2k_cparameters_t +{ + void /*opj_cparameters_t*/* opj_cparameters; +} j2k_cparameters_t; + +/** + * Callback function prototype for events + * @param msg Event message + * @param client_data Client object where will be return the event message + * */ +typedef void (*j2k_msg_callback)(const char* msg, void* client_data); + +J2KAPI(j2k_codec_t*) j2k_create_compress(void); +J2KAPI(void) j2k_destroy_codec(j2k_codec_t* pEncoder); +J2KAPI(j2k_cparameters_t*) j2k_set_default_encoder_parameters(void); +J2KAPI(void) j2k_destroy_encoder_parameters(j2k_cparameters_t* pParameters); +J2KAPI(NRT_BOOL) j2k_initEncoderParameters(j2k_cparameters_t* pParameters, + size_t tileRow, size_t tileCol, double compressionRatio, size_t numResolutions); +J2KAPI(NRT_BOOL) j2k_set_error_handler(j2k_codec_t* p_codec, j2k_msg_callback p_callback, void* p_user_data); +J2KAPI(NRT_BOOL) j2k_setup_encoder(j2k_codec_t* p_codec, const j2k_cparameters_t* parameters, j2k_image_t* image); + +//---------------------------------------------------------------------------------------------------------------- + +typedef size_t(*j2k_stream_write_fn)(void* p_buffer, size_t p_nb_bytes, void* p_user_data); +typedef int64_t(*j2k_stream_skip_fn)(int64_t p_nb_bytes, void* p_user_data); +typedef NRT_BOOL(*j2k_stream_seek_fn)(int64_t p_nb_bytes, void* p_user_data); +typedef void (*j2k_stream_free_user_data_fn)(void* p_user_data); + +J2KAPI(void) j2k_stream_set_write_function(j2k_stream_t* p_stream, j2k_stream_write_fn p_function); +J2KAPI(void) j2k_stream_set_skip_function(j2k_stream_t* p_stream, j2k_stream_skip_fn p_function); +J2KAPI(void) j2k_stream_set_seek_function(j2k_stream_t* p_stream, j2k_stream_seek_fn p_function); + +J2KAPI(void) j2k_stream_set_user_data(j2k_stream_t* p_stream, void* p_data, j2k_stream_free_user_data_fn p_function); + +J2KAPI(NRT_BOOL) j2k_flush(j2k_codec_t* p_codec, j2k_stream_t* p_stream); +J2KAPI(NRT_BOOL) j2k_start_compress(j2k_codec_t* p_codec, j2k_image_t* p_image, j2k_stream_t* p_stream); +J2KAPI(NRT_BOOL) j2k_end_compress(j2k_codec_t* p_codec, j2k_stream_t* p_stream); + +J2KAPI(NRT_BOOL) j2k_write_tile(j2k_codec_t* p_codec, uint32_t p_tile_index, const uint8_t* p_data, uint32_t p_data_size, j2k_stream_t* p_stream); + +J2K_CXX_ENDGUARD + +#endif // NITRO_j2k_TileWriter_h_INCLUDED_ diff --git a/externals/nitro/modules/c/j2k/include/j2k/j2k_Writer.h b/externals/nitro/modules/c/j2k/include/j2k/j2k_Writer.h index e0349db5f..c9e28dc28 100644 --- a/externals/nitro/modules/c/j2k/include/j2k/j2k_Writer.h +++ b/externals/nitro/modules/c/j2k/include/j2k/j2k_Writer.h @@ -50,7 +50,7 @@ typedef struct _j2k_IWriter typedef struct _j2k_WriterOptions { /* TODO add more options as we see fit */ - double compressionRatio; + float compressionRatio; uint32_t numResolutions; } j2k_WriterOptions; #if _MSC_VER diff --git a/externals/nitro/modules/c/j2k/shared/J2KCompress.c b/externals/nitro/modules/c/j2k/shared/J2KCompress.c index a8ee1fe98..5bb32ddb3 100644 --- a/externals/nitro/modules/c/j2k/shared/J2KCompress.c +++ b/externals/nitro/modules/c/j2k/shared/J2KCompress.c @@ -22,6 +22,18 @@ #ifdef HAVE_J2K_H +#if _MSC_VER +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#pragma warning(push) +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' +#include +#pragma warning(pop) +#undef min +#undef max + +#pragma warning(disable: 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified +#endif // _MSC_VER + #include #include @@ -60,6 +72,10 @@ static nitf_CompressionInterface interfaceTable = implOpen, implStart, implWriteBlock, implEnd, implDestroy, NULL }; +#if _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4820) // '...': '...' bytes padding added after data member '...' +#endif typedef struct _ImplControl { nitf_BlockingInfo blockInfo; /* Kept for convenience */ @@ -70,6 +86,9 @@ typedef struct _ImplControl uint32_t curBlock; nitf_Field *comratField; /* kept so we can update it */ }ImplControl; +#if _MSC_VER +#pragma warning(pop) +#endif NITF_CXX_ENDGUARD @@ -125,7 +144,8 @@ NITFPRIV(nitf_CompressionControl*) implOpen(nitf_ImageSubheader *subheader, char irep[NITF_IREP_SZ+1]; int imageType; J2K_BOOL isSigned = 0; - uint32_t idx; + uint32_t idx = 0; + j2k_Component** components = NULL; /* reset the options */ memset(&options, 0, sizeof(j2k_WriterOptions)); @@ -288,7 +308,7 @@ NITFPRIV(nitf_CompressionControl*) implOpen(nitf_ImageSubheader *subheader, implControl->comratField = subheader->NITF_COMRAT; /* initialize the container */ - j2k_Component** components = (j2k_Component**)J2K_MALLOC( + components = (j2k_Component**)J2K_MALLOC( sizeof(j2k_Component*) * nBands); if (!components) { @@ -434,7 +454,7 @@ NITFPRIV(NITF_BOOL) implEnd( nitf_CompressionControl * control, The decimal point is implicit and assumed to be one digit from the right (i.e. xy.z). */ - comrat = (1.0f * compressedSize * nBits) / rawSize; + comrat = (float) (((double)(compressedSize * nBits)) / (double)rawSize); comratInt = (uint32_t)(comrat * 10.0f + 0.5f); /* write the comrat field */ diff --git a/externals/nitro/modules/c/j2k/shared/J2KDecompress.c b/externals/nitro/modules/c/j2k/shared/J2KDecompress.c index 30ee82487..b597fe988 100644 --- a/externals/nitro/modules/c/j2k/shared/J2KDecompress.c +++ b/externals/nitro/modules/c/j2k/shared/J2KDecompress.c @@ -22,6 +22,18 @@ #ifdef HAVE_J2K_H +#if _MSC_VER +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#pragma warning(push) +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' +#include +#pragma warning(pop) +#undef min +#undef max + +#pragma warning(disable: 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified +#endif // _MSC_VER + #include #include @@ -41,7 +53,7 @@ NITFPRIV(uint8_t*) implReadBlock(nitf_DecompressionControl *control, uint32_t blockNumber, uint64_t* blockSize, nitf_Error* error); -NITFPRIV(int) implFreeBlock(nitf_DecompressionControl* control, +NITFPRIV(NITF_BOOL) implFreeBlock(nitf_DecompressionControl* control, uint8_t* block, nitf_Error* error); @@ -82,8 +94,7 @@ NITFAPI(void) C8_cleanup(void) /* TODO */ } - -NITFPRIV(int) implFreeBlock(nitf_DecompressionControl* control, +NITFPRIV(NITF_BOOL) implFreeBlock(nitf_DecompressionControl* control, uint8_t* block, nitf_Error* error) { @@ -231,7 +242,7 @@ NITFPRIV(nitf_DecompressionControl*) implOpen(nitf_ImageSubheader * subheader, NITFPRIV(NITF_BOOL) implStart(nitf_DecompressionControl* control, nitf_IOInterface* io, - uint64_t offset, + uint64_t offset_, uint64_t fileLength, nitf_BlockingInfo* blockInfo, uint64_t* blockMask, @@ -242,6 +253,7 @@ NITFPRIV(NITF_BOOL) implStart(nitf_DecompressionControl* control, ImplControl* implControl = (ImplControl*)control; + const nrt_Off offset = (nrt_Off)offset_; if (nitf_IOInterface_seek(io, offset, NITF_SEEK_SET, error) < 0) goto CATCH_ERROR; @@ -249,7 +261,7 @@ NITFPRIV(NITF_BOOL) implStart(nitf_DecompressionControl* control, if (!implControl->reader) goto CATCH_ERROR; - implControl->offset = offset; + implControl->offset = (uint64_t)offset; implControl->fileLength = fileLength; implControl->blockInfo = *blockInfo; return NITF_SUCCESS; diff --git a/externals/nitro/modules/c/j2k/source/OpenJPEGImpl.c b/externals/nitro/modules/c/j2k/source/OpenJPEGImpl.c index 7c15d3c99..7dd0999b3 100644 --- a/externals/nitro/modules/c/j2k/source/OpenJPEGImpl.c +++ b/externals/nitro/modules/c/j2k/source/OpenJPEGImpl.c @@ -26,6 +26,8 @@ #include #include +#include "j2k/TileWriter.h" + #ifdef _MSC_VER #pragma warning(disable: 4706) // assignment within conditional expression #endif // _MSC_VER @@ -33,8 +35,8 @@ #ifdef HAVE_OPENJPEG_H #include "j2k/Container.h" -#include "j2k/Reader.h" -#include "j2k/Writer.h" +#include "j2k/j2k_Reader.h" +#include "j2k/j2k_Writer.h" #include @@ -666,24 +668,32 @@ J2KPRIV( NRT_BOOL) OpenJPEG_initImage_(OpenJPEGWriterImpl *impl, /* TODO allow overrides somehow? */ opj_set_default_encoder_parameters(&encoderParams); - /* For now we are enforcing lossless compression. If we have a better - * way to allow overrides in the future, uncomment out the tcp_rates logic - * below (tcp_rates[0] == 0 via opj_set_default_encoder_parameters()). + /* TODO: * Also consider setting encoderParams.irreversible = 1; to use the * lossy DWT 9-7 instead of the reversible 5-3. */ - - /*if (writerOps && writerOps->compressionRatio > 0.0001) - encoderParams.tcp_rates[0] = 1.0 / writerOps->compressionRatio; + if (writerOps && writerOps->compressionRatio <= 1.0) // Compression Ratio 1:1 + { + encoderParams.tcp_rates[0] = 0.0; // lossless + /* TODO: These two lines should not be necessary when using lossless + * encoding but appear to be needed (at least in OpenJPEG 2.0) - + * otherwise we get a seg fault. + * The sample opj_compress.c is doing the same thing with a comment + * indicating that it's a bug. */ + ++encoderParams.tcp_numlayers; + encoderParams.cp_disto_alloc = 1; + } + else if (writerOps && writerOps->compressionRatio) + { + // Compression ratio is writerOps->compressionRatio : 1 + encoderParams.tcp_rates[0] = writerOps->compressionRatio; + } else + { + // High quality, but not lossless encoderParams.tcp_rates[0] = 4.0; - */ + } - /* TODO: These two lines should not be necessary when using lossless - * encoding but appear to be needed (at least in OpenJPEG 2.0) - - * otherwise we get a seg fault. - * The sample opj_compress.c is doing the same thing with a comment - * indicating that it's a bug. */ ++encoderParams.tcp_numlayers; encoderParams.cp_disto_alloc = 1; @@ -1020,11 +1030,16 @@ OpenJPEGReader_readRegion(J2K_USER_DATA *data, uint32_t x0, uint32_t y0, { OpenJPEGReaderImpl *impl = (OpenJPEGReaderImpl*) data; - opj_stream_t *stream = NULL; - opj_image_t *image = NULL; - opj_codec_t *codec = NULL; + opj_stream_t* stream = NULL; + opj_image_t* image = NULL; + opj_codec_t* codec = NULL; uint64_t offset = 0; uint32_t componentBytes, nComponents; + uint64_t bufSize = 0; + uint64_t tile_width = 0; + uint64_t tile_height = 0; + uint64_t x_tiles = 0; + uint64_t y_tiles = 0; if (!OpenJPEG_setup(impl, &stream, &codec, error)) { @@ -1055,11 +1070,11 @@ OpenJPEGReader_readRegion(J2K_USER_DATA *data, uint32_t x0, uint32_t y0, // The buffer size must account for the remainder of the tile height and width // given that opj_decode_tile_data have a memory access violation otherwise - const uint64_t tile_width = j2k_Container_getWidth(impl->container, error); - const uint64_t tile_height = j2k_Container_getHeight(impl->container, error); - const uint64_t x_tiles = (x1 - x0) % tile_width + (x1 - x0); - const uint64_t y_tiles = (y1 - y0) % tile_height + (y1 - y0); - uint64_t bufSize = x_tiles * y_tiles * componentBytes * nComponents; + tile_width = j2k_Container_getWidth(impl->container, error); + tile_height = j2k_Container_getHeight(impl->container, error); + x_tiles = (x1 - x0) % tile_width + (x1 - x0); + y_tiles = (y1 - y0) % tile_height + (y1 - y0); + bufSize = x_tiles * y_tiles * componentBytes * nComponents; if (buf && !*buf) { @@ -1495,4 +1510,310 @@ J2KAPI(j2k_Implementation) j2k_getImplementation(nrt_Error* error) } return j2k_Implementation_OpenJPEG; } + +J2KAPI(j2k_stream_t*) j2k_stream_create(size_t chunkSize, J2K_BOOL isInputStream) +{ + j2k_stream_t* retval = (j2k_stream_t*)J2K_MALLOC(sizeof(j2k_stream_t)); + if (retval != NULL) + { + retval->opj_stream = opj_stream_create(chunkSize, isInputStream); + if (retval->opj_stream == NULL) + { + J2K_FREE(retval); + retval = NULL; + } + } + return retval; +} + +J2KAPI(void) j2k_stream_destroy(j2k_stream_t* pStream) +{ + if (pStream != NULL) + { + opj_stream_destroy((opj_stream_t*)pStream->opj_stream); + J2K_FREE(pStream); + } +} + +J2KAPI(j2k_image_t*) j2k_image_tile_create(uint32_t numcmpts, const j2k_image_comptparm* cmptparms, J2K_COLOR_SPACE clrspc) +{ + if (cmptparms == NULL) + { + return NULL; + } + + j2k_image_t* retval = (j2k_image_t*)J2K_MALLOC(sizeof(j2k_image_t)); + if (retval != NULL) + { + opj_image_cmptparm_t cmptparms_; + cmptparms_.dx = cmptparms->dx; + cmptparms_.dy = cmptparms->dy; + cmptparms_.w = cmptparms->w; + cmptparms_.h = cmptparms->h; + cmptparms_.x0 = cmptparms->x0; + cmptparms_.y0 = cmptparms->y0; + cmptparms_.prec = cmptparms->prec; + cmptparms_.bpp = cmptparms->bpp; + cmptparms_.sgnd = cmptparms->sgnd; + + retval->opj_image = opj_image_tile_create(numcmpts, &cmptparms_, (OPJ_COLOR_SPACE)clrspc); + if (retval->opj_image == NULL) + { + J2K_FREE(retval); + retval = NULL; + } + } + return retval; +} + +J2KAPI(J2K_BOOL) j2k_image_init(j2k_image_t* pImage, int x0, int y0, int x1, int y1, int numcmpts, J2K_COLOR_SPACE color_space) +{ + if (pImage == NULL) + { + return J2K_FALSE; + } + opj_image_t* pImage_ = (opj_image_t*)pImage->opj_image; + if (pImage_ == NULL) + { + return J2K_FALSE; + } + + // One image component corresponding to the full grayscale image + pImage_->numcomps = numcmpts; + + pImage_->x0 = x0; + pImage_->y0 = y0; + pImage_->x1 = x1; + pImage_->y1 = y1; + pImage_->color_space = (OPJ_COLOR_SPACE)color_space; + + return J2K_TRUE; +} + +J2KAPI(void) j2k_image_destroy(j2k_image_t* pImage) +{ + if (pImage != NULL) + { + opj_image_destroy((opj_image_t*)pImage->opj_image); + J2K_FREE(pImage); + } +} + +J2KAPI(j2k_codec_t*) j2k_create_compress(void) +{ + j2k_codec_t* retval = (j2k_codec_t*)J2K_MALLOC(sizeof(j2k_codec_t)); + if (retval != NULL) + { + retval->opj_codec = opj_create_compress(OPJ_CODEC_J2K); + if (retval->opj_codec == NULL) + { + J2K_FREE(retval); + retval = NULL; + } + } + return retval; +} + +J2KAPI(void) j2k_destroy_codec(j2k_codec_t* pEncoder) +{ + if (pEncoder != NULL) + { + opj_destroy_codec((opj_codec_t*)pEncoder->opj_codec); + J2K_FREE(pEncoder); + } +} + +J2KAPI(j2k_cparameters_t*) j2k_set_default_encoder_parameters(void) +{ + j2k_cparameters_t* retval = (j2k_cparameters_t*)J2K_MALLOC(sizeof(j2k_cparameters_t)); + if (retval != NULL) + { + + //! The openjpeg codec parameters used to store the tiling and compression + //! configuration. + retval->opj_cparameters = J2K_MALLOC(sizeof(opj_cparameters_t)); + if (retval->opj_cparameters == NULL) + { + J2K_FREE(retval); + retval = NULL; + } + else + { + opj_set_default_encoder_parameters((opj_cparameters_t*)retval->opj_cparameters); + } + } + return retval; +} +J2KAPI(void) j2k_destroy_encoder_parameters(j2k_cparameters_t* pParameters) +{ + if (pParameters != NULL) + { + J2K_FREE((opj_cparameters_t*)pParameters->opj_cparameters); + J2K_FREE(pParameters); + } +} + +J2KAPI(NRT_BOOL) j2k_initEncoderParameters(j2k_cparameters_t* pParameters, + size_t tileRow, size_t tileCol, double compressionRatio, size_t numResolutions) +{ + if (pParameters == NULL) + { + return NRT_FALSE; + } + opj_cparameters_t* opj_cparameters = (opj_cparameters_t*)pParameters->opj_cparameters; + if (opj_cparameters == NULL) + { + return NRT_FALSE; + } + + // 0: J2K, 1: JP2, 2: JPT + opj_cparameters->cod_format = 0; + + // Turn on tiling + opj_cparameters->tile_size_on = 1; + + // Set the tile dimensions + opj_cparameters->cp_tx0 = 0; + opj_cparameters->cp_tdx = (int)tileCol; + opj_cparameters->cp_tdy = (int)tileRow; + + // Initialize number of resolutions + if (numResolutions > 0) + { + opj_cparameters->numresolution = (int)numResolutions; + } + else + { + // OpenJPEG defaults this to 6, but that causes the compressor + // to fail if the tile sizes are less than 2^6. So we adjust this + // down if necessary. + const double logTwo = log(2); + const size_t minX = (size_t) (floor(log((double)tileCol) / logTwo)); + const size_t minY = (size_t) (floor(log((double)tileRow) / logTwo)); + + const size_t minXY = (minX < minY) ? minX : minY; + if (minXY < (size_t)(opj_cparameters->numresolution)) + { + opj_cparameters->numresolution = (int)minXY; + } + } + + if (compressionRatio > 0) + { + opj_cparameters->tcp_rates[0] = (float)compressionRatio; + } + else + { + // Lossless compression + opj_cparameters->tcp_rates[0] = 1; + } + + opj_cparameters->tcp_numlayers++; + opj_cparameters->cp_disto_alloc = 1; + + return NRT_TRUE; +} + +J2KAPI(NRT_BOOL) j2k_set_error_handler(j2k_codec_t* p_codec, j2k_msg_callback p_callback, void* p_user_data) +{ + if (p_codec == NULL) + { + return NRT_FALSE; + } + + const int result = opj_set_error_handler((opj_codec_t*)p_codec->opj_codec, (opj_msg_callback)p_callback, p_user_data); + return result ? NRT_TRUE : NRT_FALSE; +} + +J2KAPI(NRT_BOOL) j2k_setup_encoder(j2k_codec_t* p_codec, const j2k_cparameters_t* parameters, j2k_image_t* image) +{ + if ((p_codec == NULL) || (parameters == NULL) || (image == NULL)) + { + return NRT_FALSE; + } + + const int result = opj_setup_encoder((opj_codec_t*)p_codec->opj_codec, + (opj_cparameters_t*)parameters->opj_cparameters, + (opj_image_t*)image->opj_image); + return result ? NRT_TRUE : NRT_FALSE; +} + +J2KAPI(void) j2k_stream_set_write_function(j2k_stream_t* p_stream, j2k_stream_write_fn p_function) +{ + if (p_stream != NULL) + { + opj_stream_set_write_function((opj_stream_t*)p_stream->opj_stream, p_function); + } +} + +J2KAPI(void) j2k_stream_set_skip_function(j2k_stream_t* p_stream, j2k_stream_skip_fn p_function) +{ + if (p_stream != NULL) + { + opj_stream_set_skip_function((opj_stream_t*)p_stream->opj_stream, p_function); + } +} + +J2KAPI(void) j2k_stream_set_seek_function(j2k_stream_t* p_stream, j2k_stream_seek_fn p_function) +{ + if (p_stream != NULL) + { + opj_stream_set_seek_function((opj_stream_t*)p_stream->opj_stream, (opj_stream_seek_fn)p_function); + } +} + +J2KAPI(void) j2k_stream_set_user_data(j2k_stream_t* p_stream, void* p_data, j2k_stream_free_user_data_fn p_function) +{ + if (p_stream != NULL) + { + opj_stream_set_user_data((opj_stream_t*)p_stream->opj_stream, p_data, (opj_stream_free_user_data_fn)p_function); + } +} + +J2KAPI(NRT_BOOL) j2k_flush(j2k_codec_t* p_codec, j2k_stream_t* p_stream) +{ + if ((p_codec == NULL) || (p_stream == NULL)) + { + return NRT_FALSE; + } + + const OPJ_BOOL result = opj_flush((opj_codec_t*)p_codec->opj_codec, (opj_stream_t*)p_stream->opj_stream); + return result ? NRT_TRUE : NRT_FALSE; +} + +J2KAPI(NRT_BOOL) j2k_start_compress(j2k_codec_t* p_codec, j2k_image_t* p_image, j2k_stream_t* p_stream) +{ + if ((p_codec == NULL) || (p_image == NULL) || (p_stream == NULL)) + { + return NRT_FALSE; + } + + const OPJ_BOOL result = opj_start_compress((opj_codec_t*)p_codec->opj_codec, (opj_image_t*)p_image->opj_image, (opj_stream_t*)p_stream->opj_stream); + return result ? NRT_TRUE : NRT_FALSE; +} + +J2KAPI(NRT_BOOL) j2k_end_compress(j2k_codec_t* p_codec, j2k_stream_t* p_stream) +{ + if ((p_codec == NULL) || (p_stream == NULL)) + { + return NRT_FALSE; + } + + const OPJ_BOOL result = opj_end_compress((opj_codec_t*)p_codec->opj_codec, (opj_stream_t*)p_stream->opj_stream); + return result ? NRT_TRUE : NRT_FALSE; +} + +J2KAPI(NRT_BOOL) j2k_write_tile(j2k_codec_t* p_codec, uint32_t p_tile_index, const uint8_t* p_data, uint32_t p_data_size, j2k_stream_t* p_stream) +{ + if ((p_codec == NULL) || (p_stream == NULL)) + { + return NRT_FALSE; + } + + const OPJ_BOOL result = opj_write_tile((opj_codec_t*)p_codec->opj_codec, + p_tile_index, (OPJ_BYTE*)p_data, p_data_size, + (opj_stream_t*)p_stream->opj_stream); + return result ? NRT_TRUE : NRT_FALSE; +} + #endif diff --git a/externals/nitro/modules/c/j2k/source/SimpleComponentImpl.c b/externals/nitro/modules/c/j2k/source/SimpleComponentImpl.c index f33aded63..c161793b3 100644 --- a/externals/nitro/modules/c/j2k/source/SimpleComponentImpl.c +++ b/externals/nitro/modules/c/j2k/source/SimpleComponentImpl.c @@ -174,8 +174,8 @@ J2KAPI(j2k_Component*) j2k_Component_construct(uint32_t width, impl->height = height; impl->precision = precision; impl->isSigned = isSigned; - impl->x0 = offsetX; - impl->y0 = offsetY; + impl->x0 = (int32_t)offsetX; + impl->y0 = (int32_t)offsetY; impl->xSeparation = separationX; impl->ySeparation = separationY; diff --git a/externals/nitro/modules/c/j2k/source/TileWriter.c b/externals/nitro/modules/c/j2k/source/TileWriter.c new file mode 100644 index 000000000..5929afd4b --- /dev/null +++ b/externals/nitro/modules/c/j2k/source/TileWriter.c @@ -0,0 +1,124 @@ +/* ========================================================================= + * This file is part of NITRO + * ========================================================================= + * + * (C) Copyright 2022, Maxar Technologies, Inc. + * + * NITRO is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, If not, + * see . + * + */ + +#include "j2k/Config.h" +#include "j2k/TileWriter.h" + +#ifndef HAVE_OPENJPEG_H + +J2KAPI(j2k_stream_t*) j2k_stream_create(size_t chunkSize, J2K_BOOL isInputStream) +{ + return NULL; +} + +J2KAPI(void) j2k_stream_destroy(j2k_stream_t* pStream) +{ + +} + +J2KAPI(j2k_image_t*) j2k_image_tile_create(uint32_t numcmpts, const j2k_image_comptparm* cmptparms, J2K_COLOR_SPACE clrspc) +{ + return NULL; +} + +J2KAPI(J2K_BOOL) j2k_image_init(j2k_image_t* pImage, int x0, int y0, int x1, int y1, int numcmpts, J2K_COLOR_SPACE color_space) +{ + return J2K_FALSE; +} + +J2KAPI(void) j2k_image_destroy(j2k_image_t* pImage) +{ +} + +J2KAPI(j2k_codec_t*) j2k_create_compress(void) +{ + return NULL; +} + +J2KAPI(void) opj_destroy_codec(j2k_codec_t* pEncoder) +{ + +} + +J2KAPI(j2k_cparameters_t*) j2k_set_default_encoder_parameters(void) +{ + return NULL; +} +J2KAPI(void) j2k_destroy_encoder_parameters(j2k_cparameters_t* pParameters) +{ + +} +J2KAPI(NRT_BOOL) j2k_initEncoderParameters(j2k_cparameters_t* pParameters, + size_t tileRow, size_t tileCol, double compressionRatio, size_t numResolutions) +{ + return NRT_FALSE; +} + +J2KAPI(NRT_BOOL) j2k_set_error_handler(j2k_codec_t* p_codec, j2k_msg_callback p_callback, void* p_user_data) +{ + return NRT_FALSE; +} + +J2KAPI(NRT_BOOL) j2k_setup_encoder(j2k_codec_t* p_codec, const j2k_cparameters_t* parameters, j2k_image_t* image) +{ + return NRT_FALSE; +} + +J2KAPI(void) j2k_stream_set_write_function(j2k_stream_t* p_stream, j2k_stream_write_fn p_function) +{ +} + +J2KAPI(void) j2k_stream_set_skip_function(j2k_stream_t* p_stream, j2k_stream_skip_fn p_function) +{ +} + +J2KAPI(void) j2k_stream_set_seek_function(j2k_stream_t* p_stream, j2k_stream_seek_fn p_function) +{ +} + +J2KAPI(void) j2k_stream_set_user_data(j2k_stream_t* p_stream, void* p_data, j2k_stream_free_user_data_fn p_function) +{ + +} + + +J2KAPI(NRT_BOOL) j2k_flush(j2k_codec_t* p_codec, j2k_stream_t* p_stream) +{ + return NRT_FALSE; +} + +J2KAPI(NRT_BOOL) j2k_start_compress(j2k_codec_t* p_codec, j2k_image_t* p_image, j2k_stream_t* p_stream) +{ + return NRT_FALSE; +} +J2KAPI(NRT_BOOL) j2k_end_compress(j2k_codec_t* p_codec, j2k_stream_t* p_stream) +{ + return NRT_FALSE; +} + +J2KAPI(NRT_BOOL) j2k_write_tile(j2k_codec_t* p_codec, uint32_t p_tile_index, const uint8_t* p_data, uint32_t p_data_size, j2k_stream_t* p_stream) +{ + return NRT_FALSE; +} + + +#endif \ No newline at end of file diff --git a/externals/nitro/modules/c/j2k/source/j2k_Writer.c b/externals/nitro/modules/c/j2k/source/j2k_Writer.c index f7ba30a20..6d97aaaf6 100644 --- a/externals/nitro/modules/c/j2k/source/j2k_Writer.c +++ b/externals/nitro/modules/c/j2k/source/j2k_Writer.c @@ -38,7 +38,7 @@ J2KAPI(NRT_BOOL) j2k_Writer_setOptions(j2k_WriterOptions* options, if(compressionRatio) { - options->compressionRatio = *((double*)compressionRatio->data); + options->compressionRatio = *((float*)compressionRatio->data); } if(numResolutions) { diff --git a/externals/nitro/modules/c/j2k/tests/test_j2k_nitf.c b/externals/nitro/modules/c/j2k/tests/test_j2k_nitf.c index 3eeef7f2d..4a3ee7ae3 100644 --- a/externals/nitro/modules/c/j2k/tests/test_j2k_nitf.c +++ b/externals/nitro/modules/c/j2k/tests/test_j2k_nitf.c @@ -67,6 +67,8 @@ NRT_BOOL writeJ2K(uint32_t x0, uint32_t y0, uint64_t bufSize, j2k_Container* inContainer, const char* prefix, nrt_Error* error) { + (void)bufSize; + NRT_BOOL rc = NRT_SUCCESS; char outName[NRT_MAX_PATH]; diff --git a/externals/nitro/modules/c/nitf-c.vcxproj b/externals/nitro/modules/c/nitf-c.vcxproj index fec2783b5..5ee244693 100644 --- a/externals/nitro/modules/c/nitf-c.vcxproj +++ b/externals/nitro/modules/c/nitf-c.vcxproj @@ -56,15 +56,15 @@ Use pch.h pch.h - true Guard true ProgramDatabase - AdvancedVectorExtensions2 - MultiThreadedDebugDLL true EnableFastChecks Disabled + true + MultiThreadedDebugDLL + true @@ -86,7 +86,6 @@ Guard true Speed - AdvancedVectorExtensions2 true Level3 @@ -133,6 +132,7 @@ + @@ -244,6 +244,7 @@ + diff --git a/externals/nitro/modules/c/nitf-c.vcxproj.filters b/externals/nitro/modules/c/nitf-c.vcxproj.filters index 0e6d7090e..5c979049a 100644 --- a/externals/nitro/modules/c/nitf-c.vcxproj.filters +++ b/externals/nitro/modules/c/nitf-c.vcxproj.filters @@ -355,6 +355,9 @@ nitf + + j2k + @@ -667,5 +670,8 @@ j2k + + j2k + \ No newline at end of file diff --git a/externals/nitro/modules/c/nitf/ACCHZB.vcxproj b/externals/nitro/modules/c/nitf/ACCHZB.vcxproj index bdb6e31a9..00cf593d4 100644 --- a/externals/nitro/modules/c/nitf/ACCHZB.vcxproj +++ b/externals/nitro/modules/c/nitf/ACCHZB.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/ACCPOB.vcxproj b/externals/nitro/modules/c/nitf/ACCPOB.vcxproj index 2c12cfee0..5a0dfc6dd 100644 --- a/externals/nitro/modules/c/nitf/ACCPOB.vcxproj +++ b/externals/nitro/modules/c/nitf/ACCPOB.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/ACFTA.vcxproj b/externals/nitro/modules/c/nitf/ACFTA.vcxproj index ac9138f13..25e8dbf96 100644 --- a/externals/nitro/modules/c/nitf/ACFTA.vcxproj +++ b/externals/nitro/modules/c/nitf/ACFTA.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/AIMIDB.vcxproj b/externals/nitro/modules/c/nitf/AIMIDB.vcxproj index 552557806..1658fcd91 100644 --- a/externals/nitro/modules/c/nitf/AIMIDB.vcxproj +++ b/externals/nitro/modules/c/nitf/AIMIDB.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/CSCRNA.vcxproj b/externals/nitro/modules/c/nitf/CSCRNA.vcxproj index edb0cc92c..ed2deb6e3 100644 --- a/externals/nitro/modules/c/nitf/CSCRNA.vcxproj +++ b/externals/nitro/modules/c/nitf/CSCRNA.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/ENGRDA.vcxproj b/externals/nitro/modules/c/nitf/ENGRDA.vcxproj index c77fd54e5..a44d75b6f 100644 --- a/externals/nitro/modules/c/nitf/ENGRDA.vcxproj +++ b/externals/nitro/modules/c/nitf/ENGRDA.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/HISTOA.vcxproj b/externals/nitro/modules/c/nitf/HISTOA.vcxproj index d68be6069..25e058963 100644 --- a/externals/nitro/modules/c/nitf/HISTOA.vcxproj +++ b/externals/nitro/modules/c/nitf/HISTOA.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/JITCID.vcxproj b/externals/nitro/modules/c/nitf/JITCID.vcxproj index 991adff27..e4982a9d7 100644 --- a/externals/nitro/modules/c/nitf/JITCID.vcxproj +++ b/externals/nitro/modules/c/nitf/JITCID.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/PTPRAA.vcxproj b/externals/nitro/modules/c/nitf/PTPRAA.vcxproj index 030635a28..03a08a5a1 100644 --- a/externals/nitro/modules/c/nitf/PTPRAA.vcxproj +++ b/externals/nitro/modules/c/nitf/PTPRAA.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/RPFHDR.vcxproj b/externals/nitro/modules/c/nitf/RPFHDR.vcxproj index 53ad44bd8..c57006a40 100644 --- a/externals/nitro/modules/c/nitf/RPFHDR.vcxproj +++ b/externals/nitro/modules/c/nitf/RPFHDR.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/XML_DATA_CONTENT.vcxproj b/externals/nitro/modules/c/nitf/XML_DATA_CONTENT.vcxproj index 3b31928d3..af32e16df 100644 --- a/externals/nitro/modules/c/nitf/XML_DATA_CONTENT.vcxproj +++ b/externals/nitro/modules/c/nitf/XML_DATA_CONTENT.vcxproj @@ -70,6 +70,11 @@ /FS %(AdditionalOptions) true true + CompileAsCpp + Guard + MultiThreadedDebugDLL + true + ProgramDatabase @@ -88,6 +93,9 @@ $(ProjectDir)include;$(ProjectDir)..\nrt\include;%(AdditionalIncludeDirectories) /FS %(AdditionalOptions) true + CompileAsCpp + Guard + true diff --git a/externals/nitro/modules/c/nitf/include/nitf/System.h b/externals/nitro/modules/c/nitf/include/nitf/System.h index 1c57db29b..5a1bd298e 100644 --- a/externals/nitro/modules/c/nitf/include/nitf/System.h +++ b/externals/nitro/modules/c/nitf/include/nitf/System.h @@ -333,6 +333,7 @@ typedef nrt_List nitf_List; #define nitf_List_destruct nrt_List_destruct #define nitf_List_begin nrt_List_begin #define nitf_List_at nrt_List_at +#define nitf_List_atui nrt_List_atui #define nitf_ListIterator_equals nrt_ListIterator_equals #define nitf_ListIterator_notEqualTo nrt_ListIterator_notEqualTo #define nitf_List_end nrt_List_end @@ -342,6 +343,7 @@ typedef nrt_List nitf_List; #define nitf_List_size nrt_List_size #define nitf_List_size16 nrt_List_size16 #define nitf_List_get nrt_List_get +#define nitf_List_getui nrt_List_getui #define nitf_ListIterator_increment nrt_ListIterator_increment #define nitf_ListIterator_get nrt_ListIterator_get diff --git a/externals/nitro/modules/c/nitf/include/nitf/TREDescription.h b/externals/nitro/modules/c/nitf/include/nitf/TREDescription.h index 0a681a3b4..d7e9182ae 100644 --- a/externals/nitro/modules/c/nitf/include/nitf/TREDescription.h +++ b/externals/nitro/modules/c/nitf/include/nitf/TREDescription.h @@ -35,7 +35,7 @@ typedef struct _nitf_TREDescription int data_count; /*!< the size of the field */ const char *label; /*!< description */ const char *tag; /*!< unique tag */ - char *special; /*!< special field, reserved for special cases */ + const char *special; /*!< special field, reserved for special cases */ } nitf_TREDescription; @@ -50,7 +50,7 @@ typedef struct _nitf_TREDescription #endif typedef struct _nitf_TREDescriptionInfo { - char *name; /*! The name to associate with the Description */ + const char *name; /*! The name to associate with the Description */ nitf_TREDescription *description; /*! The TREDescription */ int lengthMatch; /*! The length to match against TREs with; used to choose TREs */ } nitf_TREDescriptionInfo; diff --git a/externals/nitro/modules/c/nitf/shared/ACCHZB.c b/externals/nitro/modules/c/nitf/shared/ACCHZB.c index 3394e4fe3..108de34b5 100644 --- a/externals/nitro/modules/c/nitf/shared/ACCHZB.c +++ b/externals/nitro/modules/c/nitf/shared/ACCHZB.c @@ -23,6 +23,7 @@ #if _MSC_VER #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include diff --git a/externals/nitro/modules/c/nitf/shared/ACCPOB.c b/externals/nitro/modules/c/nitf/shared/ACCPOB.c index f9e95b769..5dc8b8a95 100644 --- a/externals/nitro/modules/c/nitf/shared/ACCPOB.c +++ b/externals/nitro/modules/c/nitf/shared/ACCPOB.c @@ -23,6 +23,7 @@ #if _MSC_VER #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include diff --git a/externals/nitro/modules/c/nitf/shared/ACFTA.c b/externals/nitro/modules/c/nitf/shared/ACFTA.c index 1775405f3..60c7f9ce6 100644 --- a/externals/nitro/modules/c/nitf/shared/ACFTA.c +++ b/externals/nitro/modules/c/nitf/shared/ACFTA.c @@ -23,6 +23,7 @@ #if _MSC_VER #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include diff --git a/externals/nitro/modules/c/nitf/shared/AIMIDB.c b/externals/nitro/modules/c/nitf/shared/AIMIDB.c index e14b92f7f..b30cedf03 100644 --- a/externals/nitro/modules/c/nitf/shared/AIMIDB.c +++ b/externals/nitro/modules/c/nitf/shared/AIMIDB.c @@ -23,6 +23,7 @@ #if _MSC_VER #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include diff --git a/externals/nitro/modules/c/nitf/shared/CSCRNA.c b/externals/nitro/modules/c/nitf/shared/CSCRNA.c index 3988aebf4..04d029578 100644 --- a/externals/nitro/modules/c/nitf/shared/CSCRNA.c +++ b/externals/nitro/modules/c/nitf/shared/CSCRNA.c @@ -23,6 +23,7 @@ #if _MSC_VER #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include diff --git a/externals/nitro/modules/c/nitf/shared/ENGRDA.c b/externals/nitro/modules/c/nitf/shared/ENGRDA.c index bf0629175..d9de9d24c 100644 --- a/externals/nitro/modules/c/nitf/shared/ENGRDA.c +++ b/externals/nitro/modules/c/nitf/shared/ENGRDA.c @@ -24,6 +24,7 @@ #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' #pragma warning(disable: 5045) // Compiler will insert Spectre mitigation for memory load if / Qspectre switch specified +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include @@ -124,10 +125,10 @@ NITFPRIV(int) ENGRDA_parse(nitf_TRE * tre, * correctly so that string types don't get endian swapped. */ fieldType = !strncmp(cursor.tag_str, "ENGDATA", 7) ? - prevValueType : cursor.desc_ptr->data_type; + prevValueType : (nitf_FieldType) cursor.desc_ptr->data_type; /* construct the field */ - field = nitf_Field_construct(length, fieldType, error); + field = nitf_Field_construct((size_t)length, fieldType, error); if (!field) goto CATCH_ERROR; @@ -137,17 +138,17 @@ NITFPRIV(int) ENGRDA_parse(nitf_TRE * tre, { if (length == NITF_INT16_SZ) { - int16_t int16 = - (int16_t)NITF_NTOHS(*((int16_t *) (bufptr + offset))); + const int16_t v = *((int16_t*)(bufptr + offset)); + int16_t int16 = (int16_t)NITF_NTOHS((uint16_t)v); status = nitf_Field_setRawData(field, - (NITF_DATA *) & int16, length, error); + (NITF_DATA *) & int16, (size_t)length, error); } else if (length == NITF_INT32_SZ) { - int32_t int32 = - (int32_t)NITF_NTOHL(*((int32_t *) (bufptr + offset))); + const int32_t v = *((int32_t*)(bufptr + offset)); + int32_t int32 = (int32_t)NITF_NTOHL((uint32_t)v); status = nitf_Field_setRawData(field, - (NITF_DATA *) & int32, length, error); + (NITF_DATA *) & int32, (size_t)length, error); } } else @@ -161,7 +162,7 @@ NITFPRIV(int) ENGRDA_parse(nitf_TRE * tre, /* now, set the data */ status = nitf_Field_setRawData(field, (NITF_DATA *) (bufptr + offset), - length, error); + (size_t)length, error); } /* when we see the value type, save it off @@ -222,7 +223,7 @@ NITFPRIV(NITF_BOOL) ENGRDA_read(nitf_IOInterface* io, { (void)record; - int ok; + NITF_BOOL ok; char *data = NULL; nitf_TREDescriptionInfo *infoPtr = NULL; @@ -236,7 +237,7 @@ NITFPRIV(NITF_BOOL) ENGRDA_read(nitf_IOInterface* io, return NITF_FAILURE; } memset(data, 0, length); - if (!nitf_TREUtils_readField(io, data, length, error)) + if (!nitf_TREUtils_readField(io, data, (int)length, error)) { NITF_FREE(data); return NITF_FAILURE; @@ -269,7 +270,7 @@ NITFPRIV(NITF_BOOL) ENGRDA_read(nitf_IOInterface* io, #ifdef NITF_DEBUG printf("Trying TRE with description: %s\n\n", infoPtr->name); #endif - ok = ENGRDA_parse(tre, data, error); + ok = (NITF_BOOL)ENGRDA_parse(tre, data, error); if (ok) { nitf_TREPrivateData *priv = (nitf_TREPrivateData*)tre->priv; diff --git a/externals/nitro/modules/c/nitf/shared/HISTOA.c b/externals/nitro/modules/c/nitf/shared/HISTOA.c index f8d392553..1432f6b5c 100644 --- a/externals/nitro/modules/c/nitf/shared/HISTOA.c +++ b/externals/nitro/modules/c/nitf/shared/HISTOA.c @@ -23,6 +23,7 @@ #if _MSC_VER #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include diff --git a/externals/nitro/modules/c/nitf/shared/JITCID.c b/externals/nitro/modules/c/nitf/shared/JITCID.c index 026f9979b..50ee19ed4 100644 --- a/externals/nitro/modules/c/nitf/shared/JITCID.c +++ b/externals/nitro/modules/c/nitf/shared/JITCID.c @@ -23,6 +23,7 @@ #if _MSC_VER #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include diff --git a/externals/nitro/modules/c/nitf/shared/PTPRAA.c b/externals/nitro/modules/c/nitf/shared/PTPRAA.c index 4028e496a..c0d63301f 100644 --- a/externals/nitro/modules/c/nitf/shared/PTPRAA.c +++ b/externals/nitro/modules/c/nitf/shared/PTPRAA.c @@ -23,6 +23,7 @@ #if _MSC_VER #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include diff --git a/externals/nitro/modules/c/nitf/shared/RPFHDR.c b/externals/nitro/modules/c/nitf/shared/RPFHDR.c index 4febe9aa9..d484e83f2 100644 --- a/externals/nitro/modules/c/nitf/shared/RPFHDR.c +++ b/externals/nitro/modules/c/nitf/shared/RPFHDR.c @@ -23,6 +23,7 @@ #if _MSC_VER #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include diff --git a/externals/nitro/modules/c/nitf/shared/XML_DATA_CONTENT.c b/externals/nitro/modules/c/nitf/shared/XML_DATA_CONTENT.c index 96796e40c..02dbc8631 100644 --- a/externals/nitro/modules/c/nitf/shared/XML_DATA_CONTENT.c +++ b/externals/nitro/modules/c/nitf/shared/XML_DATA_CONTENT.c @@ -23,6 +23,7 @@ #if _MSC_VER #pragma warning(disable: 4820) // '...' : '...' bytes padding added after data member '...' #pragma warning(disable: 4668) // '...' is not defined as a preprocessor macro, replacing with '...' for '...' +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #endif #include diff --git a/externals/nitro/modules/c/nitf/source/BandSource.c b/externals/nitro/modules/c/nitf/source/BandSource.c index 0ca5cd7fb..a34c9c697 100644 --- a/externals/nitro/modules/c/nitf/source/BandSource.c +++ b/externals/nitro/modules/c/nitf/source/BandSource.c @@ -81,7 +81,7 @@ NITFPRIV(NITF_BOOL) MemorySource_offsetRead( { memcpy(dest + destOffset, src + memorySource->mark, - memorySource->numBytesPerPixel); + (size_t)memorySource->numBytesPerPixel); destOffset += memorySource->numBytesPerPixel; diff --git a/externals/nitro/modules/c/nitf/source/DefaultTRE.c b/externals/nitro/modules/c/nitf/source/DefaultTRE.c index bba85029d..274ebe857 100644 --- a/externals/nitro/modules/c/nitf/source/DefaultTRE.c +++ b/externals/nitro/modules/c/nitf/source/DefaultTRE.c @@ -83,6 +83,7 @@ NITFPRIV(NITF_BOOL) defaultRead(nitf_IOInterface *io, nitf_TREDescription *descr = NULL; char *data = NULL; NITF_BOOL success; + size_t length_ = 0; if (!tre) { @@ -96,7 +97,7 @@ NITFPRIV(NITF_BOOL) defaultRead(nitf_IOInterface *io, nitf_Error_init(error, "uint32_t+1 overflow", NITF_CTXT, NITF_ERR_MEMORY); goto CATCH_ERROR; } - const size_t length_ = ((size_t)length) + 1; + length_ = ((size_t)length) + 1; data = (char *) NITF_MALLOC(length_); if (!data) { @@ -118,7 +119,7 @@ NITFPRIV(NITF_BOOL) defaultRead(nitf_IOInterface *io, } descr[0].data_type = NITF_BINARY; - descr[0].data_count = length; + descr[0].data_count = (int)length; descr[0].label = _NITF_DEFAULT_TRE_LABEL; descr[0].tag = NITF_TRE_RAW; descr[1].data_type = NITF_END; @@ -354,7 +355,7 @@ NITFPRIV(int) defaultGetCurrentSize(nitf_TRE* tre, nitf_Error* error) (void)error; /* TODO - should we make sure length is equal to the descr data_count ? */ - return ((nitf_TREPrivateData*)tre->priv)->length; + return (int)((nitf_TREPrivateData*)tre->priv)->length; } diff --git a/externals/nitro/modules/c/nitf/source/NitfReader.c b/externals/nitro/modules/c/nitf/source/NitfReader.c index 82d9d70f0..513c8ef39 100644 --- a/externals/nitro/modules/c/nitf/source/NitfReader.c +++ b/externals/nitro/modules/c/nitf/source/NitfReader.c @@ -815,6 +815,7 @@ readDESubheader(nitf_Reader* reader, /* What the header says the length ought to be */ uint32_t expectedSubheaderLength = 0; char desID[NITF_DESTAG_SZ + 1]; /* DES ID string */ + nitf_Off offset = 0; nitf_ListIterator listIter = nitf_List_begin(reader->record->dataExtensions); @@ -878,7 +879,7 @@ readDESubheader(nitf_Reader* reader, error); /* set the offset and end of the segment */ - const nitf_Off offset = nitf_IOInterface_tell(reader->input, error); + offset = nitf_IOInterface_tell(reader->input, error); if (!NITF_IO_SUCCESS(offset)) goto CATCH_ERROR; segment->offset = offset; @@ -950,6 +951,7 @@ readRESubheader(nitf_Reader* reader, /* List iterator pointing to the reserved extension segment */ nitf_ListIterator listIter = nitf_List_begin(reader->record->reservedExtensions); + nitf_Off offset = 0; for (i = 0; i < resIndex; i++) nitf_ListIterator_increment(&listIter); @@ -993,7 +995,7 @@ readRESubheader(nitf_Reader* reader, error); /* set the offset and end of the segment */ - const nitf_Off offset = nitf_IOInterface_tell(reader->input, error); + offset = nitf_IOInterface_tell(reader->input, error); if (!NITF_IO_SUCCESS(offset)) goto CATCH_ERROR; segment->offset = offset; diff --git a/externals/nitro/modules/c/nitf/source/NitfWriter.c b/externals/nitro/modules/c/nitf/source/NitfWriter.c index c1c1b9a74..f044d86f8 100644 --- a/externals/nitro/modules/c/nitf/source/NitfWriter.c +++ b/externals/nitro/modules/c/nitf/source/NitfWriter.c @@ -987,6 +987,7 @@ NITFPROT(NITF_BOOL) nitf_Writer_writeHeader(nitf_Writer* writer, uint32_t udhdl, udhofl, xhdl, xhdlofl; nitf_Version fver; char buf[256]; /* temp buf */ + nitf_Off hdrLen_ = 0; fver = nitf_Record_getVersion(writer->record); @@ -1111,7 +1112,7 @@ NITFPROT(NITF_BOOL) nitf_Writer_writeHeader(nitf_Writer* writer, buf, NITF_XHDLOFL_SZ, error); /* Get the header length */ - const nitf_Off hdrLen_ = nitf_IOInterface_tell(writer->output, error); + hdrLen_ = nitf_IOInterface_tell(writer->output, error); if (!NITF_IO_SUCCESS(hdrLen_)) goto CATCH_ERROR; *hdrLen = (uint32_t)hdrLen_; diff --git a/externals/nitro/modules/c/nitf/source/PluginRegistry.c b/externals/nitro/modules/c/nitf/source/PluginRegistry.c index d7174732e..3a3135600 100644 --- a/externals/nitro/modules/c/nitf/source/PluginRegistry.c +++ b/externals/nitro/modules/c/nitf/source/PluginRegistry.c @@ -374,22 +374,21 @@ NITFPRIV(void) implicitDestruct(nitf_PluginRegistry** reg) NITFPRIV(const char**) doInit(nitf_DLL* dll, const char* prefix, nitf_Error* error) { - NITF_PLUGIN_INIT_FUNCTION init; const char** ident; #define NITF_MAX_PATH_NAME_SIZE_ NITF_MAX_PATH+4096 char name[NITF_MAX_PATH_NAME_SIZE_]; memset(name, 0, NITF_MAX_PATH_NAME_SIZE_); NITF_SNPRINTF(name, NITF_MAX_PATH_NAME_SIZE_, "%s" NITF_PLUGIN_INIT_SUFFIX, prefix); - init = (NITF_PLUGIN_INIT_FUNCTION)nitf_DLL_retrieve(dll, name, error); - if (!init) + NRT_DLL_FUNCTION_PTR init_ = nitf_DLL_retrieve(dll, name, error); + if (!init_) { nitf_Error_print(error, stdout, "Invalid init hook in DSO"); return NULL; } /* Else, call it */ - + NITF_PLUGIN_INIT_FUNCTION init = (NITF_PLUGIN_INIT_FUNCTION)init_; ident = (*init)(error); if (!ident) { @@ -409,16 +408,14 @@ doInit(nitf_DLL* dll, const char* prefix, nitf_Error* error) */ NITFPRIV(int) doCleanup(nitf_DLL* dll, nitf_Error* error) { - NITF_PLUGIN_CLEANUP_FUNCTION cleanup; const char* cleanupName = NITF_PLUGIN_CLEANUP; - - cleanup = (NITF_PLUGIN_CLEANUP_FUNCTION)nitf_DLL_retrieve(dll, - cleanupName, - error); - if (!cleanup) + NRT_DLL_FUNCTION_PTR cleanup_ = nitf_DLL_retrieve(dll, cleanupName, error); + if (!cleanup_) { return 0; } + + NITF_PLUGIN_CLEANUP_FUNCTION cleanup = (NITF_PLUGIN_CLEANUP_FUNCTION)cleanup_; /* Else, call it */ cleanup(); @@ -532,7 +529,7 @@ nitf_PluginRegistry_registerCompressionHandler( const char** ident; int i = 1; - int ok = 1; + NITF_BOOL ok = NRT_TRUE; if (!reg) { return NITF_FAILURE; @@ -578,7 +575,7 @@ nitf_PluginRegistry_registerDecompressionHandler( const char** ident; int i = 1; - int ok = 1; + NITF_BOOL ok = NRT_TRUE; if (!reg) { return NITF_FAILURE; @@ -623,7 +620,7 @@ nitf_PluginRegistry_registerTREHandler(NITF_PLUGIN_INIT_FUNCTION init, const char** ident; int i = 1; - int ok = 1; + NITF_BOOL ok = NRT_TRUE; if (!reg) { return NITF_FAILURE; diff --git a/externals/nitro/modules/c/nitf/source/Record.c b/externals/nitro/modules/c/nitf/source/Record.c index 4aa59ea9e..6558385c1 100644 --- a/externals/nitro/modules/c/nitf/source/Record.c +++ b/externals/nitro/modules/c/nitf/source/Record.c @@ -268,7 +268,7 @@ moveTREs(nitf_Extensions* source, int32_t skipLeft; /* Amount left to skip */ uint32_t treLength; /* Length of current TRE */ - skipLeft = skipLength; + skipLeft = (int32_t) skipLength; while (nitf_ExtensionsIterator_notEqualTo(&srcIter, &srcEnd)) { tre = nitf_ExtensionsIterator_get(&srcIter); @@ -1329,7 +1329,7 @@ nitf_Record_removeImageSegment(nitf_Record* record, nitf_ComponentInfo** infoArray = NULL; nitf_ImageSegment* segment = NULL; uint32_t i; - nitf_ListIterator iter = nitf_List_at(record->images, segmentNumber); + nitf_ListIterator iter = nitf_List_atui(record->images, segmentNumber); if (iter.current == NULL) { @@ -1408,7 +1408,7 @@ nitf_Record_removeGraphicSegment(nitf_Record* record, nitf_ComponentInfo** infoArray = NULL; nitf_GraphicSegment* segment = NULL; uint32_t i; - nitf_ListIterator iter = nitf_List_at(record->graphics, segmentNumber); + nitf_ListIterator iter = nitf_List_atui(record->graphics, segmentNumber); if (iter.current == NULL) { @@ -1485,7 +1485,7 @@ nitf_Record_removeLabelSegment(nitf_Record* record, nitf_LabelSegment* segment = NULL; uint32_t i; - nitf_ListIterator iter = nitf_List_at(record->labels, segmentNumber); + nitf_ListIterator iter = nitf_List_atui(record->labels, segmentNumber); if (iter.current == NULL) { @@ -1561,7 +1561,7 @@ nitf_Record_removeTextSegment(nitf_Record* record, nitf_ComponentInfo** infoArray = NULL; nitf_TextSegment* segment = NULL; uint32_t i; - nitf_ListIterator iter = nitf_List_at(record->texts, segmentNumber); + nitf_ListIterator iter = nitf_List_atui(record->texts, segmentNumber); if (iter.current == NULL) { @@ -1633,7 +1633,7 @@ nitf_Record_removeDataExtensionSegment(nitf_Record* record, nitf_DESegment* segment = NULL; uint32_t i; nitf_ListIterator iter = - nitf_List_at(record->dataExtensions, segmentNumber); + nitf_List_atui(record->dataExtensions, segmentNumber); if (iter.current == NULL) { @@ -1713,7 +1713,7 @@ nitf_Record_removeReservedExtensionSegment(nitf_Record* record, uint32_t i; nitf_ListIterator iter = - nitf_List_at(record->reservedExtensions, segmentNumber); + nitf_List_atui(record->reservedExtensions, segmentNumber); if (iter.current == NULL) { @@ -2078,7 +2078,7 @@ NITFPRIV(NITF_BOOL) unmergeSegment(nitf_Version version, nitf_Record* record, else /* already tested for 0 above, wrap-around from -1 (below) isn't possible */ { assert(overflowIndex > 0); - nitf_ListIterator iter = nitf_List_at(record->dataExtensions, overflowIndex - 1); + nitf_ListIterator iter = nitf_List_atui(record->dataExtensions, overflowIndex - 1); const nitf_ListIterator end = nitf_List_end(record->dataExtensions); if (nitf_ListIterator_notEqualTo(&iter, &end)) { @@ -2367,7 +2367,7 @@ NITFAPI(NITF_BOOL) nitf_Record_mergeTREs(nitf_Record* record, nitf_Error* error) else if ((strcmp(type, "UDID") == 0) || (strcmp(type, "IXSHD") == 0)) { - nitf_ImageSegment* imSeg = (nitf_ImageSegment*) nitf_List_get(record->images, segIndex - 1, error); + nitf_ImageSegment* imSeg = (nitf_ImageSegment*) nitf_List_getui(record->images, segIndex - 1, error); /* Image segment user defined */ if (strcmp(type, "UDID") == 0) @@ -2387,7 +2387,7 @@ NITFAPI(NITF_BOOL) nitf_Record_mergeTREs(nitf_Record* record, nitf_Error* error) /* Graphics segment */ else if (strcmp(type, "SXSHD") == 0) { - nitf_GraphicSegment* grSeg = (nitf_GraphicSegment*) nitf_List_get(record->graphics, segIndex - 1, error); + nitf_GraphicSegment* grSeg = (nitf_GraphicSegment*)nitf_List_getui(record->graphics, segIndex - 1, error); extLength = grSeg->subheader->NITF_SXSHDL; overflowIndex = grSeg->subheader->NITF_SXSOFL; destination = grSeg->subheader->extendedSection; @@ -2396,7 +2396,7 @@ NITFAPI(NITF_BOOL) nitf_Record_mergeTREs(nitf_Record* record, nitf_Error* error) /* Labels segment */ else if (strcmp(type, "LXSHD") == 0) { - nitf_LabelSegment* lbSeg = (nitf_LabelSegment*) nitf_List_get(record->labels, segIndex - 1, error); + nitf_LabelSegment* lbSeg = (nitf_LabelSegment*)nitf_List_getui(record->labels, segIndex - 1, error); extLength = lbSeg->subheader->NITF_LXSHDL; overflowIndex = lbSeg->subheader->NITF_LXSOFL; destination = lbSeg->subheader->extendedSection; @@ -2405,7 +2405,7 @@ NITFAPI(NITF_BOOL) nitf_Record_mergeTREs(nitf_Record* record, nitf_Error* error) /* Text segment */ else if (strcmp(type, "TXSHD") == 0) { - nitf_TextSegment* txSeg = (nitf_TextSegment*) nitf_List_get(record->texts, segIndex - 1, error); + nitf_TextSegment* txSeg = (nitf_TextSegment*)nitf_List_getui(record->texts, segIndex - 1, error); extLength = txSeg->subheader->NITF_TXSHDL; overflowIndex = txSeg->subheader->NITF_TXSOFL; destination = txSeg->subheader->extendedSection; diff --git a/externals/nitro/modules/c/nitf/source/TRECursor.c b/externals/nitro/modules/c/nitf/source/TRECursor.c index b70874083..eaeb844e6 100644 --- a/externals/nitro/modules/c/nitf/source/TRECursor.c +++ b/externals/nitro/modules/c/nitf/source/TRECursor.c @@ -74,7 +74,7 @@ NITFPRIV(int) nitf_TRECursor_evalCondLength(nitf_TRE * tre, NITFPRIV(int) nitf_TRECursor_evaluatePostfix(nitf_TRE *tre, char idx[10][10], int looping, - char *expression, + const char *expression, nitf_Error *error); typedef unsigned int (*NITF_TRE_CURSOR_COUNT_FUNCTION) (nitf_TRE *, @@ -224,7 +224,7 @@ NITFAPI(void) nitf_TRECursor_cleanup(nitf_TRECursor * tre_cursor) NITFAPI(NITF_BOOL) nitf_TRECursor_isDone(nitf_TRECursor * tre_cursor) { nitf_Error error; - int isDone = (tre_cursor->desc_ptr == tre_cursor->end_ptr); + NITF_BOOL isDone = (tre_cursor->desc_ptr == tre_cursor->end_ptr); /* check if the passed in cursor is not at the beginning */ if (!isDone && tre_cursor->index >= 0) @@ -515,7 +515,7 @@ NITFPRIV(int) nitf_TRECursor_evalLoops(nitf_TRE* tre, (NITF_TRE_CURSOR_COUNT_FUNCTION)desc_ptr->tag; - loops = (*fn)(tre, idx_str, looping, error); + loops = (int)((*fn)(tre, idx_str, looping, error)); if (loops == -1) return NITF_FAILURE; @@ -854,7 +854,7 @@ NITFPRIV(int) nitf_TRECursor_evalCondLength(nitf_TRE* tre, NITFPRIV(int) nitf_TRECursor_evaluatePostfix(nitf_TRE *tre, char idx[10][10], int looping, - char *expression, + const char *expression, nitf_Error *error) { nitf_List *parts = NULL; diff --git a/externals/nitro/modules/c/nitf/source/TREUtils.c b/externals/nitro/modules/c/nitf/source/TREUtils.c index 32f494321..6ef64a647 100644 --- a/externals/nitro/modules/c/nitf/source/TREUtils.c +++ b/externals/nitro/modules/c/nitf/source/TREUtils.c @@ -184,6 +184,8 @@ nitf_TREUtils_getRawData(nitf_TRE* tre, /* the cursor */ nitf_TRECursor cursor; + size_t length_ = 0; + /* get actual length of TRE */ length = nitf_TREUtils_computeLength(tre); *treLength = length; @@ -207,7 +209,7 @@ nitf_TREUtils_getRawData(nitf_TRE* tre, NITF_ERR_MEMORY); goto CATCH_ERROR; } - const size_t length_ = ((size_t)length) + 1; + length_ = ((size_t)length) + 1; memset(data, 0, length_); cursor = nitf_TRECursor_begin(tre); diff --git a/externals/nitro/modules/c/nitf/unittests/nitro_image_.c_ b/externals/nitro/modules/c/nitf/unittests/nitro_image_.c_ index cdf1d8e4a..611bc89e4 100644 --- a/externals/nitro/modules/c/nitf/unittests/nitro_image_.c_ +++ b/externals/nitro/modules/c/nitf/unittests/nitro_image_.c_ @@ -20,6 +20,9 @@ * see . * */ + #ifndef NITRO_nitf_unittests_nitro_image__c__INCLUDED_ + #define NITRO_nitf_unittests_nitro_image__c__INCLUDED_ + #pragma once #ifdef _MSC_VER #pragma warning(push) @@ -973,3 +976,5 @@ static const struct { #ifdef _MSC_VER #pragma warning(pop) #endif + +#endif // NITRO_nitf_unittests_nitro_image__c__INCLUDED_ diff --git a/externals/nitro/modules/c/nitf/unittests/test_create_nitf.c b/externals/nitro/modules/c/nitf/unittests/test_create_nitf.c index c286c6a05..6240db101 100644 --- a/externals/nitro/modules/c/nitf/unittests/test_create_nitf.c +++ b/externals/nitro/modules/c/nitf/unittests/test_create_nitf.c @@ -32,7 +32,7 @@ #include "nitro_image_.c_" -static const char* RGB[] = {"R", "G", "B"}; +const char* RGB[3] = {"R", "G", "B"}; @@ -275,7 +275,7 @@ TEST_CASE_ARGS(testCreate) const char* outname = argc > 1 ? argv[1] : "test_create.ntf"; record = nitf_Record_construct(NITF_VER_21, &error); - TEST_ASSERT(record); + TEST_ASSERT(record != NULL); TEST_ASSERT(populateFileHeader(record, outname, &error)); TEST_ASSERT(addImageSegment(record, &error)); TEST_ASSERT(writeNITF(record, outname, &error)); @@ -292,9 +292,9 @@ TEST_CASE_ARGS(testRead) io = nitf_IOHandle_create(outname, NITF_ACCESS_READONLY, NITF_OPEN_EXISTING, &error); reader = nitf_Reader_construct(&error); - TEST_ASSERT(reader); + TEST_ASSERT(reader != NULL); record = nitf_Reader_read(reader, io, &error); - TEST_ASSERT(record); + TEST_ASSERT(record != NULL); nitf_Reader_destruct(&reader); nitf_Record_destruct(&record); } diff --git a/externals/nitro/modules/c/nitf/unittests/test_field.c b/externals/nitro/modules/c/nitf/unittests/test_field.c index 4a1a530d8..4476296d3 100644 --- a/externals/nitro/modules/c/nitf/unittests/test_field.c +++ b/externals/nitro/modules/c/nitf/unittests/test_field.c @@ -39,10 +39,10 @@ TEST_CASE( testField) hl = nitf_Field_construct(NITF_HL_SZ, NITF_BCS_N, &error); realField = nitf_Field_construct(NITF_HL_SZ, NITF_BCS_N, &error); - TEST_ASSERT(fhdr); - TEST_ASSERT(ubin); - TEST_ASSERT(hl); - TEST_ASSERT(realField); + TEST_ASSERT(fhdr != NULL); + TEST_ASSERT(ubin != NULL); + TEST_ASSERT(hl != NULL); + TEST_ASSERT(realField != NULL); printf("%d\n", int32); nitf_Field_setRawData(fhdr, "NIT", 3, &error); @@ -110,7 +110,7 @@ TEST_CASE(setReal) or exponent */ nitf_Field* field = nitf_Field_construct(length, NITF_BCS_A, error); - TEST_ASSERT(field); + TEST_ASSERT(field != NULL); #define BUF_SIZE 256 char buffer[BUF_SIZE]; @@ -149,7 +149,7 @@ TEST_CASE(setReal) field = nitf_Field_construct(8, NITF_BCS_A, error); - TEST_ASSERT(field); + TEST_ASSERT(field != NULL); ret = nitf_Field_setReal(field, "f", 1, 12.3456, error); TEST_ASSERT(ret); nitf_Field_snprint(buffer, BUF_SIZE, field); diff --git a/externals/nitro/modules/c/nitf/unittests/test_image_io.c b/externals/nitro/modules/c/nitf/unittests/test_image_io.c index ab6c81e10..0320e3831 100644 --- a/externals/nitro/modules/c/nitf/unittests/test_image_io.c +++ b/externals/nitro/modules/c/nitf/unittests/test_image_io.c @@ -158,7 +158,7 @@ void freeTestState(TestState* state) free(state); } -void freeBands(uint8_t** bands, size_t numBands) +static void freeBands(uint8_t** bands, size_t numBands) { size_t bandIndex; for (bandIndex = 0; bandIndex < numBands; ++bandIndex) diff --git a/externals/nitro/modules/c/nitf/unittests/test_mem_source.c b/externals/nitro/modules/c/nitf/unittests/test_mem_source.c index 249b33e5e..bc149e9b2 100644 --- a/externals/nitro/modules/c/nitf/unittests/test_mem_source.c +++ b/externals/nitro/modules/c/nitf/unittests/test_mem_source.c @@ -52,10 +52,10 @@ TEST_CASE(testMemorySource) numBytesPerPix, 0, &error); - TEST_ASSERT(bs0); - TEST_ASSERT(bs1); - TEST_ASSERT(bs2); - TEST_ASSERT(all); + TEST_ASSERT(bs0 != NULL); + TEST_ASSERT(bs1 != NULL); + TEST_ASSERT(bs2 != NULL); + TEST_ASSERT(all != NULL); /* Construct in memory band buffers for testing -- 0 terminate strings */ band_0 = (char *) NITF_MALLOC(bandSize + 1); diff --git a/externals/nitro/modules/c/nitf/unittests/test_tre_mods.c b/externals/nitro/modules/c/nitf/unittests/test_tre_mods.c index de2fdcbe7..50cebe506 100644 --- a/externals/nitro/modules/c/nitf/unittests/test_tre_mods.c +++ b/externals/nitro/modules/c/nitf/unittests/test_tre_mods.c @@ -28,7 +28,7 @@ TEST_CASE(testNestedMod) nitf_Error error; NITF_BOOL exists; nitf_TRE* tre = nitf_TRE_construct("ACCHZB", NULL, &error); - TEST_ASSERT(tre); + TEST_ASSERT(tre != NULL); exists = nitf_TRE_setField(tre, "NUMACHZ", "02", 2, &error); TEST_ASSERT(exists); exists = nitf_TRE_setField(tre, "UNIAAH[0]", "abc", 3, &error); @@ -61,7 +61,7 @@ TEST_CASE(testIncompleteCondMod) nitf_Error error; NITF_BOOL exists; nitf_TRE* tre = nitf_TRE_construct("ACCPOB", NULL, &error); - TEST_ASSERT(tre); + TEST_ASSERT(tre != NULL); int treLength = tre->handler->getCurrentSize(tre, &error); TEST_ASSERT_EQ_INT(treLength, 2); @@ -90,16 +90,16 @@ TEST_CASE(testClone) nitf_Error error; nitf_TRE* tre = nitf_TRE_construct("JITCID", NULL, &error); - TEST_ASSERT(tre); + TEST_ASSERT(tre != NULL); exists = nitf_TRE_setField(tre, "FILCMT", "fyi", 3, &error); TEST_ASSERT(exists); dolly = nitf_TRE_clone(tre, &error); - TEST_ASSERT(dolly); + TEST_ASSERT(dolly != NULL); clonedField = nitf_TRE_getField(dolly, "FILCMT"); - TEST_ASSERT(clonedField); + TEST_ASSERT(clonedField != NULL); TEST_ASSERT_EQ_STR(clonedField->raw, "fyi"); /* destruct the TREs */ @@ -114,7 +114,7 @@ TEST_CASE(testBasicMod) nitf_Error error; nitf_Field* field; nitf_TRE *tre = nitf_TRE_construct("ACFTA", "ACFTA_132", &error); - TEST_ASSERT(tre); + TEST_ASSERT(tre != NULL); field = (nitf_TRE_getField(tre, "AC_MSN_ID")); TEST_ASSERT_EQ_STR(field->raw, " "); @@ -142,7 +142,7 @@ TEST_CASE(testSize) int treLength; nitf_TRE* tre = nitf_TRE_construct("AIMIDB", NULL, &error); - TEST_ASSERT(tre); + TEST_ASSERT(tre != NULL); treLength = tre->handler->getCurrentSize(tre, &error); TEST_ASSERT_EQ_INT(treLength, 89); @@ -156,12 +156,12 @@ TEST_CASE(iterateUnfilled) nitf_TRECursor cursor; nitf_TRE* tre = nitf_TRE_construct("ACCPOB", NULL, &error); uint32_t numFields = 0; - TEST_ASSERT(tre); + TEST_ASSERT(tre != NULL); cursor = nitf_TRECursor_begin(tre); while (!nitf_TRECursor_isDone(&cursor)) { - TEST_ASSERT(nitf_TRECursor_iterate(&cursor, &error)); + TEST_ASSERT(nitf_TRECursor_iterate(&cursor, &error) != 0); ++numFields; } @@ -177,7 +177,7 @@ TEST_CASE(populateThenIterate) nitf_TRECursor cursor; nitf_TRE* tre = nitf_TRE_construct("ACCPOB", NULL, &error); uint32_t numFields = 0; - TEST_ASSERT(tre); + TEST_ASSERT(tre != NULL); nitf_TRE_setField(tre, "NUMACPO", "2", 1, &error); nitf_TRE_setField(tre, "NUMPTS[0]", "3", 1, &error); @@ -187,7 +187,7 @@ TEST_CASE(populateThenIterate) while (!nitf_TRECursor_isDone(&cursor)) { - TEST_ASSERT(nitf_TRECursor_iterate(&cursor, &error)); + TEST_ASSERT(nitf_TRECursor_iterate(&cursor, &error) != 0); ++numFields; } @@ -203,12 +203,12 @@ TEST_CASE(populateWhileIterating) nitf_TRECursor cursor; nitf_TRE* tre = nitf_TRE_construct("ACCPOB", NULL, &error); uint32_t numFields = 0; - TEST_ASSERT(tre); + TEST_ASSERT(tre != NULL); cursor = nitf_TRECursor_begin(tre); while (!nitf_TRECursor_isDone(&cursor)) { - TEST_ASSERT(nitf_TRECursor_iterate(&cursor, &error)); + TEST_ASSERT(nitf_TRECursor_iterate(&cursor, &error) != 0); ++numFields; if (strcmp(cursor.tag_str, "NUMACPO") == 0) { diff --git a/externals/nitro/modules/c/nrt/include/nrt/List.h b/externals/nitro/modules/c/nrt/include/nrt/List.h index d4bed0c9f..c5cb38529 100644 --- a/externals/nitro/modules/c/nrt/include/nrt/List.h +++ b/externals/nitro/modules/c/nrt/include/nrt/List.h @@ -22,6 +22,9 @@ #ifndef __NRT_LIST_H__ #define __NRT_LIST_H__ + +#include + /*! * \file * \brief Contains OO-like linked-list data structure for pairs @@ -202,6 +205,7 @@ NRTAPI(nrt_ListIterator) nrt_List_begin(const nrt_List * chain); * \return An iterator to the index of the chain, as specified by i */ NRTAPI(nrt_ListIterator) nrt_List_at(const nrt_List * chain, int i); +NRTAPI(nrt_ListIterator) nrt_List_atui(const nrt_List* chain, uint32_t i); /*! * Check to see if two iterators point at the same thing @@ -299,6 +303,7 @@ NRTAPI(uint16_t) nrt_List_size16(nrt_List* list); * \return the data at the specified position */ NRTAPI(NRT_DATA *) nrt_List_get(nrt_List * list, int index, nrt_Error * error); +NRTAPI(NRT_DATA*) nrt_List_getui(nrt_List* list, uint32_t index, nrt_Error* error); /*! * Increment the iterator. Eventually, this will point at NULL. diff --git a/externals/nitro/modules/c/nrt/source/DateTime.c b/externals/nitro/modules/c/nrt/source/DateTime.c index 7d56ab2ff..e6a9dbbbd 100644 --- a/externals/nitro/modules/c/nrt/source/DateTime.c +++ b/externals/nitro/modules/c/nrt/source/DateTime.c @@ -301,7 +301,7 @@ NRTAPI(NRT_BOOL) nrt_DateTime_setTimeInMillis(nrt_DateTime * dateTime, dateTime->dayOfYear = t.tm_yday + 1; dateTime->hour = t.tm_hour; dateTime->minute = t.tm_min; - dateTime->second = t.tm_sec + (timeInMillis / 1000.0 - timeInSeconds); + dateTime->second = t.tm_sec + (timeInMillis / 1000.0 - (double)timeInSeconds); return NRT_SUCCESS; } @@ -405,7 +405,7 @@ NRTAPI(NRT_BOOL) nrt_DateTime_formatMillis(double millis, const char *format, timeInSeconds = (time_t) (millis / 1000); gmtime_s(&t, &timeInSeconds); - fractSeconds = (millis / 1000.0) - timeInSeconds; + fractSeconds = (millis / 1000.0) - (double)timeInSeconds; /* Search for "%...S" string */ formatLength = strlen(format); diff --git a/externals/nitro/modules/c/nrt/source/HashTable.c b/externals/nitro/modules/c/nrt/source/HashTable.c index 4336ae21d..978c18775 100644 --- a/externals/nitro/modules/c/nrt/source/HashTable.c +++ b/externals/nitro/modules/c/nrt/source/HashTable.c @@ -25,7 +25,7 @@ NRTAPI(nrt_HashTable *) nrt_HashTable_construct(int nbuckets, nrt_Error * error) { int i; - int hashSize; + size_t hashSize; /* Create the hash table object itself */ nrt_HashTable *ht = (nrt_HashTable *) NRT_MALLOC(sizeof(nrt_HashTable)); @@ -197,7 +197,7 @@ NRTAPI(NRT_BOOL) nrt_HashTable_exists(nrt_HashTable * ht, const char *key) NRTAPI(NRT_DATA *) nrt_HashTable_remove(nrt_HashTable * ht, const char *key) { /* Find out which list it would be in */ - int bucket = ht->hash(ht, key); + unsigned int bucket = ht->hash(ht, key); /* Get the list at this bucket */ nrt_List *l = ht->buckets[bucket]; @@ -354,7 +354,7 @@ NRTAPI(NRT_BOOL) nrt_HashTable_insert(nrt_HashTable * ht, const char *key, NRT_DATA * data, nrt_Error * error) { /* Find the bucket */ - int bucket = ht->hash(ht, key); + unsigned int bucket = ht->hash(ht, key); /* Malloc the pair -- that's our container item */ nrt_Pair *p = (nrt_Pair *) NRT_MALLOC(sizeof(nrt_Pair)); @@ -378,7 +378,7 @@ NRTAPI(NRT_BOOL) nrt_HashTable_insert(nrt_HashTable * ht, const char *key, NRTAPI(nrt_Pair *) nrt_HashTable_find(nrt_HashTable * ht, const char *key) { /* Retrieve the pocket */ - int bucket = ht->hash(ht, key); + unsigned int bucket = ht->hash(ht, key); /* Get the list for it */ nrt_List *l = ht->buckets[bucket]; diff --git a/externals/nitro/modules/c/nrt/source/IOHandleWin32.c b/externals/nitro/modules/c/nrt/source/IOHandleWin32.c index b0b947a2e..a47371abb 100644 --- a/externals/nitro/modules/c/nrt/source/IOHandleWin32.c +++ b/externals/nitro/modules/c/nrt/source/IOHandleWin32.c @@ -42,14 +42,28 @@ NRTAPI(nrt_IOHandle) nrt_IOHandle_create(const char *fname, } } + const DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; handle = - CreateFile(fname, access, FILE_SHARE_READ, NULL, creation, - FILE_ATTRIBUTE_NORMAL, NULL); + CreateFile(fname, access, dwShareMode, NULL /*lpSecurityAttributes*/, creation, + FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/); if (handle == INVALID_HANDLE_VALUE) { - nrt_Error_init(error, NRT_STRERROR(NRT_ERRNO), NRT_CTXT, - NRT_ERR_OPENING_FILE); + const DWORD dwLastError = GetLastError(); + LPTSTR lpBuffer; + const DWORD result = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, + 0 /*lpSource*/, dwLastError, LANG_USER_DEFAULT, (LPTSTR)&lpBuffer, 0 /*nSize*/, NULL /*Arguments*/); + if (result == 0) + { + // FormatMessage() failed + nrt_Error_init(error, NRT_STRERROR(dwLastError), NRT_CTXT, + NRT_ERR_OPENING_FILE); + } + else + { + nrt_Error_init(error, lpBuffer, NRT_CTXT, NRT_ERR_OPENING_FILE); + LocalFree(lpBuffer); + } } return handle; } diff --git a/externals/nitro/modules/c/nrt/source/IOInterface.c b/externals/nitro/modules/c/nrt/source/IOInterface.c index 07272866c..e7545f230 100644 --- a/externals/nitro/modules/c/nrt/source/IOInterface.c +++ b/externals/nitro/modules/c/nrt/source/IOInterface.c @@ -259,7 +259,7 @@ NRTPRIV(nrt_Off) BufferAdapter_seek(NRT_DATA * data, nrt_Off offset, int whence, NRT_ERR_MEMORY); return -1; } - return control->mark; + return (nrt_Off)control->mark; } NRTPRIV(nrt_Off) BufferAdapter_tell(NRT_DATA * data, nrt_Error * error) @@ -366,7 +366,7 @@ NRTAPI(nrt_IOInterface *) nrt_IOHandleAdapter_open(const char *fname, int creationFlags, nrt_Error * error) { - nrt_IOHandle handle = nrt_IOHandle_create(fname, accessFlags, creationFlags, + nrt_IOHandle handle = nrt_IOHandle_create(fname, (nrt_AccessFlags)accessFlags, (nrt_CreationFlags)creationFlags, error); if (NRT_INVALID_HANDLE(handle)) { diff --git a/externals/nitro/modules/c/nrt/source/List.c b/externals/nitro/modules/c/nrt/source/List.c index eeb1f30f7..7e913627e 100644 --- a/externals/nitro/modules/c/nrt/source/List.c +++ b/externals/nitro/modules/c/nrt/source/List.c @@ -297,6 +297,10 @@ NRTAPI(nrt_ListIterator) nrt_List_at(const nrt_List * chain, int i) return list_iterator; } +NRTAPI(nrt_ListIterator) nrt_List_atui(const nrt_List* chain, uint32_t i) +{ + return nrt_List_at(chain, (int)i); +} NRTAPI(NRT_BOOL) nrt_ListIterator_equals(const nrt_ListIterator * it1, const nrt_ListIterator * it2) @@ -378,10 +382,10 @@ NRTAPI(NRT_BOOL) nrt_List_move(nrt_List * chain, uint32_t oldIndex, newIndex = newIndex > listSize ? listSize : newIndex; /* first, remove the data from the list */ - iter = nrt_List_at(chain, oldIndex); + iter = nrt_List_at(chain, (int)oldIndex); data = nrt_List_remove(chain, &iter); /* next, insert it at the new location */ - iter = nrt_List_at(chain, newIndex); + iter = nrt_List_at(chain, (int)newIndex); return nrt_List_insert(chain, iter, data, error); } @@ -507,3 +511,7 @@ NRTAPI(NRT_DATA *) nrt_List_get(nrt_List * list, int index, nrt_Error * error) return NULL; } } +NRTAPI(NRT_DATA*) nrt_List_getui(nrt_List* list, uint32_t index, nrt_Error* error) +{ + return nrt_List_get(list, (int)index, error); +} \ No newline at end of file diff --git a/externals/nitro/modules/c/nrt/source/Tree.c b/externals/nitro/modules/c/nrt/source/Tree.c index 645c5b548..c1f92fdaf 100644 --- a/externals/nitro/modules/c/nrt/source/Tree.c +++ b/externals/nitro/modules/c/nrt/source/Tree.c @@ -172,7 +172,7 @@ NRTAPI(NRT_BOOL) nrt_TreeNode_removeChild(nrt_TreeNode * node, { nrt_ListIterator where, end; nrt_List *list; - int found = 0; + NRT_BOOL found = NRT_FALSE; assert(node); assert(child); @@ -187,7 +187,7 @@ NRTAPI(NRT_BOOL) nrt_TreeNode_removeChild(nrt_TreeNode * node, nrt_TreeNode *candidate = (nrt_TreeNode *) nrt_ListIterator_get(&where); if (candidate == node) { - found = 1; + found = NRT_TRUE; break; } nrt_ListIterator_increment(&where); diff --git a/externals/nitro/modules/c/nrt/source/Utils.c b/externals/nitro/modules/c/nrt/source/Utils.c index aee2b0738..7a1f76058 100644 --- a/externals/nitro/modules/c/nrt/source/Utils.c +++ b/externals/nitro/modules/c/nrt/source/Utils.c @@ -64,7 +64,6 @@ NRTAPI(nrt_List *) nrt_Utils_splitString(const char *str, unsigned int max, while (op < end) { char *val = NULL; - size_t sz; /* skip past white space */ while (isspace(*op) && op < end) ++op; @@ -76,7 +75,7 @@ NRTAPI(nrt_List *) nrt_Utils_splitString(const char *str, unsigned int max, if (cur == op) break; - sz = op - cur; + size_t sz = (size_t)(op - cur); val = (char* )NRT_MALLOC(sz + 1); if (!val) { @@ -98,7 +97,7 @@ NRTAPI(nrt_List *) nrt_Utils_splitString(const char *str, unsigned int max, if (op < end) { - sz = end - op; + sz = (size_t)(end - op); val = (char* )NRT_MALLOC(sz + 1); if (!val) { @@ -190,7 +189,7 @@ NRTAPI(void) nrt_Utils_trimString(char *str) strp++; if (strp != str) { - len = str + len - strp; + len = (size_t)(str + len - strp); memmove(str, strp, len); str[len] = '\0'; } @@ -356,7 +355,7 @@ NRTAPI(NRT_BOOL) nrt_Utils_parseGeographicString(const char *dms, int *degrees, return NRT_FAILURE; } - int degreeOffset = 0; + size_t degreeOffset = 0; const size_t len = strlen(dms); char d[4]; diff --git a/externals/nitro/modules/c/nrt/unittests/test_buffer_adapter.c b/externals/nitro/modules/c/nrt/unittests/test_buffer_adapter.c index ddccc88ed..8c6e5dce8 100644 --- a/externals/nitro/modules/c/nrt/unittests/test_buffer_adapter.c +++ b/externals/nitro/modules/c/nrt/unittests/test_buffer_adapter.c @@ -79,7 +79,7 @@ TEST_CASE(testReadOutOfBounds) buffer, TEST_BUF_SIZE, 0, &error); nrt_Off success = nrt_IOInterface_seek(reader, TEST_BUF_SIZE, NRT_SEEK_SET, &error); - TEST_ASSERT(success); + TEST_ASSERT(success != 0); success = nrt_IOInterface_read(reader, output, sizeof(output), &error); TEST_ASSERT(!success); } @@ -96,7 +96,7 @@ TEST_CASE(testWriteOutOfBounds) buffer, TEST_BUF_SIZE, 0, &error); nrt_Off success = nrt_IOInterface_seek(writer, TEST_BUF_SIZE, NRT_SEEK_SET, &error); - TEST_ASSERT(success); + TEST_ASSERT(success != 0); success = nrt_IOInterface_write(writer, input, 4, &error); TEST_ASSERT(!success); } diff --git a/externals/nitro/modules/c/nrt/unittests/test_list.c b/externals/nitro/modules/c/nrt/unittests/test_list.c index a99ad8ced..989c9e1c7 100644 --- a/externals/nitro/modules/c/nrt/unittests/test_list.c +++ b/externals/nitro/modules/c/nrt/unittests/test_list.c @@ -27,7 +27,7 @@ TEST_CASE(testCreate) { nrt_Error e; nrt_List *l = nrt_List_construct(&e); - TEST_ASSERT(l); + TEST_ASSERT(l != NULL); nrt_List_destruct(&l); TEST_ASSERT_NULL(l); } @@ -40,7 +40,7 @@ TEST_CASE(testPushPop) int i = 0; l = nrt_List_construct(&e); - TEST_ASSERT(l); + TEST_ASSERT(l != NULL); TEST_ASSERT(nrt_List_pushBack(l, (NRT_DATA *) "NITRO", &e)); TEST_ASSERT(nrt_List_pushBack(l, (NRT_DATA *) "Rocks!", &e)); @@ -53,7 +53,7 @@ TEST_CASE(testPushPop) for (; nrt_ListIterator_notEqualTo(&it, &endList); ++i) { char *p = (char *) nrt_ListIterator_get(&it); - TEST_ASSERT(p); + TEST_ASSERT(p != NULL); nrt_ListIterator_increment(&it); } TEST_ASSERT_EQ_INT(2, i); @@ -63,14 +63,14 @@ TEST_CASE(testPushPop) while (!nrt_List_isEmpty(l)) { char *p = (char *) nrt_List_popFront(l); - TEST_ASSERT(p); + TEST_ASSERT(p != NULL); } - TEST_ASSERT_EQ_INT((size_t)0, nrt_List_size(l)); + TEST_ASSERT_EQ_INT((uint32_t)0, nrt_List_size(l)); nrt_List_destruct(&l); TEST_ASSERT_NULL(l); } -char *cloneString(const char *data, nrt_Error * error) +static char *cloneString(const char *data, nrt_Error * error) { (void)error; @@ -83,7 +83,7 @@ TEST_CASE(testClone) { nrt_Error e; nrt_List *l = nrt_List_construct(&e), *dolly = NULL; - TEST_ASSERT(l); + TEST_ASSERT(l != NULL); TEST_ASSERT(nrt_List_pushBack(l, (NRT_DATA *) cloneString("1", NULL), &e)); TEST_ASSERT(nrt_List_pushBack(l, (NRT_DATA *) cloneString("2", NULL), &e)); @@ -92,19 +92,19 @@ TEST_CASE(testClone) TEST_ASSERT(nrt_List_pushBack(l, (NRT_DATA *) cloneString("5", NULL), &e)); dolly = nrt_List_clone(l, (NRT_DATA_ITEM_CLONE) cloneString, &e); - TEST_ASSERT(dolly); + TEST_ASSERT(dolly != NULL); TEST_ASSERT_EQ_INT(nrt_List_size(l), nrt_List_size(dolly)); int32_t i = 0; while (!nrt_List_isEmpty(dolly)) { char *p = (char *) nrt_List_popFront(dolly); - TEST_ASSERT(p); - const int32_t value = ++i; + TEST_ASSERT(p != NULL); + const long value = ++i; TEST_ASSERT_EQ_INT(NRT_ATO32(p), value); } - TEST_ASSERT_EQ_INT((size_t)0, nrt_List_size(dolly)); + TEST_ASSERT_EQ_INT((uint32_t)0, nrt_List_size(dolly)); nrt_List_destruct(&dolly); TEST_ASSERT_NULL(dolly); nrt_List_destruct(&l); @@ -117,7 +117,7 @@ TEST_CASE(testIterate) nrt_List *l = nrt_List_construct(&e); nrt_ListIterator it, end; - TEST_ASSERT(l); + TEST_ASSERT(l != NULL); TEST_ASSERT(nrt_List_pushBack(l, (NRT_DATA *) cloneString("1", NULL), &e)); TEST_ASSERT(nrt_List_pushBack(l, (NRT_DATA *) cloneString("2", NULL), &e)); @@ -132,8 +132,8 @@ TEST_CASE(testIterate) while (nrt_ListIterator_notEqualTo(&it, &end)) { char *p = (char *) nrt_ListIterator_get(&it); - TEST_ASSERT(p); - const int32_t value = ++i; + TEST_ASSERT(p != NULL); + const long value = ++i; TEST_ASSERT_EQ_INT(NRT_ATO32(p), value); nrt_ListIterator_increment(&it); } @@ -148,7 +148,7 @@ TEST_CASE(testIterateRemove) nrt_List *l = nrt_List_construct(&e); nrt_ListIterator it, end; - TEST_ASSERT(l); + TEST_ASSERT(l != NULL); TEST_ASSERT(nrt_List_pushBack(l, (NRT_DATA *) cloneString("1", NULL), &e)); TEST_ASSERT(nrt_List_pushBack(l, (NRT_DATA *) cloneString("2", NULL), &e)); @@ -162,9 +162,9 @@ TEST_CASE(testIterateRemove) while (nrt_ListIterator_notEqualTo(&it, &end)) { char *p = (char *) nrt_List_remove(l, &it); - TEST_ASSERT(p); + TEST_ASSERT(p != NULL); } - TEST_ASSERT_EQ_INT((size_t)0, nrt_List_size(l)); + TEST_ASSERT_EQ_INT((uint32_t)0, nrt_List_size(l)); nrt_List_destruct(&l); TEST_ASSERT_NULL(l); diff --git a/externals/nitro/modules/c/nrt/unittests/test_nrt_datetime.c b/externals/nitro/modules/c/nrt/unittests/test_nrt_datetime.c index a5bc1ceb0..967e8e9d4 100644 --- a/externals/nitro/modules/c/nrt/unittests/test_nrt_datetime.c +++ b/externals/nitro/modules/c/nrt/unittests/test_nrt_datetime.c @@ -48,7 +48,7 @@ TEST_CASE(testNow) nrt_Error e; date = nrt_DateTime_now(&e); - TEST_ASSERT(date); + TEST_ASSERT(date != NULL); nrt_DateTime_destruct(&date); TEST_ASSERT_NULL(date); @@ -60,10 +60,10 @@ TEST_CASE(testFromMillis) nrt_Error e; date = nrt_DateTime_now(&e); - TEST_ASSERT(date); + TEST_ASSERT(date != NULL); date2 = nrt_DateTime_fromMillis(date->timeInMillis, &e); - TEST_ASSERT(date2); + TEST_ASSERT(date2 != NULL); TEST_ASSERT((date->timeInMillis == date2->timeInMillis)); @@ -91,7 +91,7 @@ TEST_CASE(testParseDayOfYearTimeStr) /* Test without month and day of month information */ date = nrt_DateTime_fromString(timeStr, "%Y-%jT%H:%M:%SZ", &e); - TEST_ASSERT(date); + TEST_ASSERT(date != NULL); TEST_ASSERT_EQ_INT(date->month, 10); TEST_ASSERT_EQ_INT(date->dayOfMonth, 31); @@ -100,7 +100,7 @@ TEST_CASE(testParseDayOfYearTimeStr) /* Test input with correct month and day of month information */ date2 = nrt_DateTime_fromString(timeStr2, "%Y-%j-%m-%dT%H:%M:%SZ", &e); - TEST_ASSERT(date2); + TEST_ASSERT(date2 != NULL); TEST_ASSERT_EQ_INT(date2->month, 10); TEST_ASSERT_EQ_INT(date2->dayOfMonth, 31); @@ -109,7 +109,7 @@ TEST_CASE(testParseDayOfYearTimeStr) /* Test input with incorrect month and day of month information */ date3 = nrt_DateTime_fromString(timeStr3, "%Y-%j-%m-%dT%H:%M:%SZ", &e); - TEST_ASSERT(date3); + TEST_ASSERT(date3 != NULL); TEST_ASSERT_EQ_INT(date3->month, 10); TEST_ASSERT_EQ_INT(date3->dayOfMonth, 31); @@ -132,7 +132,7 @@ TEST_CASE(testRoundTrip) nrt_Error e; date = nrt_DateTime_now(&e); - TEST_ASSERT(date); + TEST_ASSERT(date != NULL); /* printDate(date); */ TEST_ASSERT((nrt_DateTime_format @@ -140,7 +140,7 @@ TEST_CASE(testRoundTrip) /* printf("Date: %s\n", buf); */ date2 = nrt_DateTime_fromString(buf, NRT_DATE_FORMAT_21, &e); - TEST_ASSERT(date2); + TEST_ASSERT(date2 != NULL); TEST_ASSERT((nrt_DateTime_format (date2, NRT_DATE_FORMAT_21, buf2, NRT_FDT_SZ + 1, &e))); @@ -168,7 +168,7 @@ TEST_CASE(testSetIdentity) nrt_Error e; date = nrt_DateTime_now(&e); - TEST_ASSERT(date); + TEST_ASSERT(date != NULL); TEST_ASSERT((nrt_DateTime_format (date, NRT_DATE_FORMAT_21, buf, NRT_FDT_SZ + 1, &e))); @@ -221,7 +221,7 @@ TEST_CASE(testMillis) nrt_DateTime *date = NULL; date = nrt_DateTime_fromString(timeStr, "%Y-%m-%dT%H:%M:%SZ", &e); - TEST_ASSERT(date); + TEST_ASSERT(date != NULL); TEST_ASSERT_EQ_INT((int) (1000 * (date->second - (int) date->second)), 123); diff --git a/externals/nitro/modules/c/nrt/unittests/test_tree.c b/externals/nitro/modules/c/nrt/unittests/test_tree.c index 13dd05428..85707b057 100644 --- a/externals/nitro/modules/c/nrt/unittests/test_tree.c +++ b/externals/nitro/modules/c/nrt/unittests/test_tree.c @@ -23,7 +23,7 @@ #include #include "Test.h" -char *C(const char *p) +static char *C(const char *p) { const size_t p_sz = strlen(p) + 1; void* x_ = malloc(p_sz); @@ -58,10 +58,10 @@ void makeTree(nrt_Tree * t, const char* testName_) nrt_Error e; nrt_TreeNode *an, *ancho, *abso; t->root = nrt_TreeNode_construct(C("a"), &e); - TEST_ASSERT(t->root); + TEST_ASSERT(t->root != NULL); an = nrt_TreeNode_construct(C("an"), &e); - TEST_ASSERT(an); + TEST_ASSERT(an != NULL); nrt_TreeNode_addChild(t->root, an, &e); @@ -75,7 +75,7 @@ void makeTree(nrt_Tree * t, const char* testName_) nrt_TreeNode_addChild(ancho, nrt_TreeNode_construct(C("anchor"), &e), &e); abso = nrt_TreeNode_construct(C("absolut"), &e); - TEST_ASSERT(abso); + TEST_ASSERT(abso != NULL); nrt_TreeNode_addChild(t->root, abso, &e); @@ -107,7 +107,7 @@ TEST_CASE(testTree) /* Create a tree - root can be passed during or after */ nrt_Tree *t = nrt_Tree_construct(NULL, &e); nrt_Tree *tc = NULL; - TEST_ASSERT(t); + TEST_ASSERT(t != NULL); makeTree(t, testName); printf("Pre-order traversal:\n"); @@ -117,7 +117,7 @@ TEST_CASE(testTree) printf("\n\n"); tc = nrt_Tree_clone(t, (NRT_DATA_ITEM_CLONE) & C, &e); - TEST_ASSERT(tc); + TEST_ASSERT(tc != NULL); printf("Post-order traversal (cloned):\n"); printf("=======================================================\n"); diff --git a/externals/nitro/modules/c/pch.h b/externals/nitro/modules/c/pch.h index c9ac21665..c022b32c0 100644 --- a/externals/nitro/modules/c/pch.h +++ b/externals/nitro/modules/c/pch.h @@ -38,5 +38,6 @@ #pragma warning(disable: 4505) // '...': unreferenced local function has been removed #pragma warning(disable: 4514) // '...' : unreferenced inline function has been removed +#pragma warning(disable: 5039) // '...': pointer or reference to potentially throwing function passed to '...' function under -EHc. Undefined behavior may occur if this function throws an exception. #define OPJ_STATIC