diff -Nru charls-2.1.0+dfsg/appveyor.yml charls-2.2.0+dfsg/appveyor.yml --- charls-2.1.0+dfsg/appveyor.yml 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/appveyor.yml 2021-01-10 18:07:25.000000000 +0000 @@ -1,9 +1,8 @@ -version: 2.1.0.{build} +version: 2.1.1.{build} os: Visual Studio 2019 configuration: - Debug - Release -- Checked platform: - x86 - x64 diff -Nru charls-2.1.0+dfsg/CHANGELOG.md charls-2.2.0+dfsg/CHANGELOG.md --- charls-2.1.0+dfsg/CHANGELOG.md 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/CHANGELOG.md 2021-01-10 18:07:25.000000000 +0000 @@ -4,21 +4,58 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Next-Release] + +## [2.2.0] - 2021-1-10 + +### Added + +- Added pkg-config charls.pc file to help in detect the CharLS library (see [#76](https://github.com/team-charls/charls/issues/76)) +- Added standard CMake variable BUILD_SHARED_LIBS as an option to make it visible in the CMake GUI (see [#66](https://github.com/team-charls/charls/issues/66)) +- The PowerPC Little Endian (ppc64le) platform has been added as supported architecture + +### Fixed + +- Fixed [#21](https://github.com/team-charls/charls/issues/21), Building with UBSAN, will report runtime error: left shift + of 4031 by 63 places cannot be represented in type 'long int' +- Fixed [#25](https://github.com/team-charls/charls/issues/25), CharLS fails to read LSE marker segment after first SOS segment +- Fixed [#26](https://github.com/team-charls/charls/issues/26), CharLS should only use the valid bits from the passed input buffer +- Fixed [#36](https://github.com/team-charls/charls/issues/36), CharLS should remain stable from bad input (several issues found by fuzzy testing) +- Fixed [#60](https://github.com/team-charls/charls/issues/60), Visual Studio 2015 C++ compiler cannot compile certain constexpr constructions +- Fixed [#62](https://github.com/team-charls/charls/issues/62), Missing includes in jpegls_error.cpp when using libc++ (and not libstdc++) +- Fixed [#70](https://github.com/team-charls/charls/issues/70), The C and C++ sample don't swap the pixels from a .bmp file horizontal +- Fixed [#79](https://github.com/team-charls/charls/issues/79), Wrong JPEG-LS encoding when stride is non-default (stride != 0), + component count > 1 and interleave_mode is none + +### Changed + +- The API has been extended with additional annotations to assist the static analyzer in the MSVC and GCC/clang compilers +- The size check for a Start Of Scan (SOS) segment is now exact for improved compatibility with fuzzy testing +- The minimum support version of CMake is now 3.13 (was 3.9), 3.13 is needed for add_link_options +- The Windows static library and DLL are now compiled with the Control Flow Guard (/guard:cf) option enabled for enhanced security +- The .NET adapter has been upgraded to .NET 5 and moved to its own [repository](https://github.com/team-charls/charls-native-dotnet) +This has been done to make it possible to have different release cycles. + +### Removed + +- The legacy methods JpegLsEncodeStream, JpegLsDecodeStream and JpegLsReadHeaderStream have been removed as exported methods. + These methods were not part of the public API and only used by by the charlstest application + ## [2.1.0] - 2019-12-29 ### Added - Two new C++ classes (jpegls_encoder \ jpegls_decoder) have been added to make it much easier to use CharLS from C++ -- A new C API (charls_xxx functions) was added to provide a more stable ABI for future updates. The old API calls are internally forwarded to the new API. +- A new C API (charls_xxx functions) was added to provide a more stable ABI for future updates. The old API calls are internally forwarded to the new API - CharLS can now read and write JPEG-LS standard SPIFF headers - Support has been added to detect the unsupported JPEG-LS extension (ISO/IEC 14495-2) SOF_57 marker and IDs in LSE marker -- The unit test project has been extended and now includes 188 tests. +- The unit test project has been extended and now includes 188 tests - Support has been added to encode\decode 4 component images in all interleave modes ### Deprecated -- The legacy 1.x\2.0 C API has been marked as deprecated. This legacy API will be maintained until the next major upgrade. - Future 2.x updates will start to mark the legacy types and functions with the C++ ```[[deprecated]]``` attribute. +- The legacy 1.x\2.0 C API has been marked as deprecated. This legacy API will be maintained until the next major upgrade + Future 2.x updates will start to mark the legacy types and functions with the C++ ```[[deprecated]]``` attribute ### Changed @@ -40,8 +77,8 @@ ### Fixed - Fixed [#7](https://github.com/team-charls/charls/issues/7), How to compile CharLS with Xcode has been documented in the Wiki -- Fixed [#44](https://github.com/team-charls/charls/issues/44), Only the API functions should be exported from a Linux shared library - Fixes [#35](https://github.com/team-charls/charls/issues/35), Encoding will fail if the bit per sample is greater than 8, and a custom RESET value is used +- Fixed [#44](https://github.com/team-charls/charls/issues/44), Only the API functions should be exported from a Linux shared library - Fixes [#51](https://github.com/team-charls/charls/issues/51), The default threshold values are not corrected computed for 6 bit images or less - Fixed the ASSERT in the ModuloRange function, which would trigger false assertions in debug builds @@ -54,7 +91,8 @@ ### Fixed -- Fixes [#10](https://github.com/team-charls/charls/issues/10), Fixed the problem that "output buffer to small" was not detected when writing encoded bytes to a fixed output buffer. This could cause memory corruption problems +- Fixes [#10](https://github.com/team-charls/charls/issues/10), Fixed the problem that "output buffer to small" was not + detected when writing encoded bytes to a fixed output buffer. This could cause memory corruption problems - Fixes [11](https://github.com/team-charls/charls/issues/11), Update charlstest to return EXIT_SUCCESS/FAILURE - Fixed the issue that DecodeToPnm would set params.colorTransform = ColorTransformation::BigEndian but the library didn’t support this option during decoding diff -Nru charls-2.1.0+dfsg/CharLS.sln charls-2.2.0+dfsg/CharLS.sln --- charls-2.1.0+dfsg/CharLS.sln 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/CharLS.sln 2021-01-10 18:07:25.000000000 +0000 @@ -7,10 +7,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{BE733144-010D-4F7F-A619-0B73D8461D60}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Native", "Native", "{1C1CF63C-A53A-449F-90D3-63321015FD65}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".NET", ".NET", "{A81B52AC-655D-4D1F-B21B-9BC4529AB447}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CharLS", "src\CharLS.vcxproj", "{1E31F9F1-F175-4082-B3E2-B1F0ECA3F44C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5F4C6204-36F8-4D63-BF33-ECFCB5D1D114}" @@ -19,25 +15,22 @@ .editorconfig = .editorconfig CHANGELOG.md = CHANGELOG.md cpp.hint = cpp.hint + default.ruleset = default.ruleset default.ruleset.md = default.ruleset.md Directory.Build.props = Directory.Build.props LICENSE.md = LICENSE.md README.md = README.md + SECURITY.md = SECURITY.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CharLSNetTest", "dotnet\test\CharLSNetTest.csproj", "{002B897F-9D96-4A99-853F-53806C39559D}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Convert", "samples\convert.c\Convert.vcxproj", "{F42C0547-FEB3-40F4-9379-0FF87B44FA0F}" - ProjectSection(ProjectDependencies) = postProject - {1E31F9F1-F175-4082-B3E2-B1F0ECA3F44C} = {1E31F9F1-F175-4082-B3E2-B1F0ECA3F44C} - EndProjectSection +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "convert-c", "samples\convert.c\convert-c.vcxproj", "{F42C0547-FEB3-40F4-9379-0FF87B44FA0F}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CharLSUnitTest", "unittest\CharLSUnitTest.vcxproj", "{4A912445-1E83-41FA-8B80-C0A9BD4E9289}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CharLSNet", "dotnet\src\CharLSNet.csproj", "{CFC22FDD-A405-486A-AEFE-4231424B0AE5}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "convert-cpp", "samples\convert.cpp\convert-cpp.vcxproj", "{E09F024E-A125-48AA-8E9D-7D1302BEAC97}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FuzzTest", "fuzztest\FuzzTest.vcxproj", "{5637C116-ABF5-4274-A71F-34433713A538}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Checked|x64 = Checked|x64 @@ -72,18 +65,6 @@ {1E31F9F1-F175-4082-B3E2-B1F0ECA3F44C}.Release|x64.Build.0 = Release|x64 {1E31F9F1-F175-4082-B3E2-B1F0ECA3F44C}.Release|x86.ActiveCfg = Release|Win32 {1E31F9F1-F175-4082-B3E2-B1F0ECA3F44C}.Release|x86.Build.0 = Release|Win32 - {002B897F-9D96-4A99-853F-53806C39559D}.Checked|x64.ActiveCfg = Checked|x64 - {002B897F-9D96-4A99-853F-53806C39559D}.Checked|x64.Build.0 = Checked|x64 - {002B897F-9D96-4A99-853F-53806C39559D}.Checked|x86.ActiveCfg = Checked|x86 - {002B897F-9D96-4A99-853F-53806C39559D}.Checked|x86.Build.0 = Checked|x86 - {002B897F-9D96-4A99-853F-53806C39559D}.Debug|x64.ActiveCfg = Debug|x64 - {002B897F-9D96-4A99-853F-53806C39559D}.Debug|x64.Build.0 = Debug|x64 - {002B897F-9D96-4A99-853F-53806C39559D}.Debug|x86.ActiveCfg = Debug|x86 - {002B897F-9D96-4A99-853F-53806C39559D}.Debug|x86.Build.0 = Debug|x86 - {002B897F-9D96-4A99-853F-53806C39559D}.Release|x64.ActiveCfg = Release|x64 - {002B897F-9D96-4A99-853F-53806C39559D}.Release|x64.Build.0 = Release|x64 - {002B897F-9D96-4A99-853F-53806C39559D}.Release|x86.ActiveCfg = Release|x86 - {002B897F-9D96-4A99-853F-53806C39559D}.Release|x86.Build.0 = Release|x86 {F42C0547-FEB3-40F4-9379-0FF87B44FA0F}.Checked|x64.ActiveCfg = Checked|x64 {F42C0547-FEB3-40F4-9379-0FF87B44FA0F}.Checked|x64.Build.0 = Checked|x64 {F42C0547-FEB3-40F4-9379-0FF87B44FA0F}.Checked|x86.ActiveCfg = Checked|Win32 @@ -108,18 +89,6 @@ {4A912445-1E83-41FA-8B80-C0A9BD4E9289}.Release|x64.Build.0 = Release|x64 {4A912445-1E83-41FA-8B80-C0A9BD4E9289}.Release|x86.ActiveCfg = Release|Win32 {4A912445-1E83-41FA-8B80-C0A9BD4E9289}.Release|x86.Build.0 = Release|Win32 - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Checked|x64.ActiveCfg = Checked|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Checked|x64.Build.0 = Checked|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Checked|x86.ActiveCfg = Checked|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Checked|x86.Build.0 = Checked|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Debug|x64.ActiveCfg = Debug|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Debug|x64.Build.0 = Debug|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Debug|x86.ActiveCfg = Debug|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Debug|x86.Build.0 = Debug|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Release|x64.ActiveCfg = Release|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Release|x64.Build.0 = Release|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Release|x86.ActiveCfg = Release|Any CPU - {CFC22FDD-A405-486A-AEFE-4231424B0AE5}.Release|x86.Build.0 = Release|Any CPU {E09F024E-A125-48AA-8E9D-7D1302BEAC97}.Checked|x64.ActiveCfg = Checked|x64 {E09F024E-A125-48AA-8E9D-7D1302BEAC97}.Checked|x64.Build.0 = Checked|x64 {E09F024E-A125-48AA-8E9D-7D1302BEAC97}.Checked|x86.ActiveCfg = Checked|Win32 @@ -132,17 +101,24 @@ {E09F024E-A125-48AA-8E9D-7D1302BEAC97}.Release|x64.Build.0 = Release|x64 {E09F024E-A125-48AA-8E9D-7D1302BEAC97}.Release|x86.ActiveCfg = Release|Win32 {E09F024E-A125-48AA-8E9D-7D1302BEAC97}.Release|x86.Build.0 = Release|Win32 + {5637C116-ABF5-4274-A71F-34433713A538}.Checked|x64.ActiveCfg = Checked|x64 + {5637C116-ABF5-4274-A71F-34433713A538}.Checked|x64.Build.0 = Checked|x64 + {5637C116-ABF5-4274-A71F-34433713A538}.Checked|x86.ActiveCfg = Checked|Win32 + {5637C116-ABF5-4274-A71F-34433713A538}.Checked|x86.Build.0 = Checked|Win32 + {5637C116-ABF5-4274-A71F-34433713A538}.Debug|x64.ActiveCfg = Debug|x64 + {5637C116-ABF5-4274-A71F-34433713A538}.Debug|x64.Build.0 = Debug|x64 + {5637C116-ABF5-4274-A71F-34433713A538}.Debug|x86.ActiveCfg = Debug|Win32 + {5637C116-ABF5-4274-A71F-34433713A538}.Debug|x86.Build.0 = Debug|Win32 + {5637C116-ABF5-4274-A71F-34433713A538}.Release|x64.ActiveCfg = Release|x64 + {5637C116-ABF5-4274-A71F-34433713A538}.Release|x64.Build.0 = Release|x64 + {5637C116-ABF5-4274-A71F-34433713A538}.Release|x86.ActiveCfg = Release|Win32 + {5637C116-ABF5-4274-A71F-34433713A538}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {7185AD7F-57BA-42C7-A715-239CEA8ADC31} = {1C1CF63C-A53A-449F-90D3-63321015FD65} - {1E31F9F1-F175-4082-B3E2-B1F0ECA3F44C} = {1C1CF63C-A53A-449F-90D3-63321015FD65} - {002B897F-9D96-4A99-853F-53806C39559D} = {A81B52AC-655D-4D1F-B21B-9BC4529AB447} {F42C0547-FEB3-40F4-9379-0FF87B44FA0F} = {BE733144-010D-4F7F-A619-0B73D8461D60} - {4A912445-1E83-41FA-8B80-C0A9BD4E9289} = {1C1CF63C-A53A-449F-90D3-63321015FD65} - {CFC22FDD-A405-486A-AEFE-4231424B0AE5} = {A81B52AC-655D-4D1F-B21B-9BC4529AB447} {E09F024E-A125-48AA-8E9D-7D1302BEAC97} = {BE733144-010D-4F7F-A619-0B73D8461D60} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff -Nru charls-2.1.0+dfsg/CharLS.sln.DotSettings charls-2.2.0+dfsg/CharLS.sln.DotSettings --- charls-2.1.0+dfsg/CharLS.sln.DotSettings 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/CharLS.sln.DotSettings 2021-01-10 18:07:25.000000000 +0000 @@ -1,9 +1,13 @@  + True DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW + DO_NOT_SHOW DO_NOT_SHOW + DO_NOT_SHOW DO_NOT_SHOW + DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW @@ -16,39 +20,49 @@ DO_NOT_SHOW DO_NOT_SHOW WARNING - DO_NOT_SHOW DO_NOT_SHOW - <NamingElement Priority="1"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> - <NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="_" Style="aa_bb" /></NamingElement> - <NamingElement Priority="9"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> + <NamingElement Priority="1"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="__interface" /><type Name="class" /><type Name="struct" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy></NamingElement> + <NamingElement Priority="10"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="_" Style="aa_bb" /></NamingElement> + <NamingElement Priority="9"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="member function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> <NamingElement Priority="11"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="PUBLIC"><type Name="class field" /><type Name="struct field" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> - <NamingElement Priority="2"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="enum" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> - <NamingElement Priority="13"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="enumerator" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> - <NamingElement Priority="15"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="True" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global variable" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> - <NamingElement Priority="8"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global function" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> - <NamingElement Priority="7"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global variable" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> - <NamingElement Priority="6"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="local variable" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> + <NamingElement Priority="2"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="enum" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> + <NamingElement Priority="13"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="scoped enumerator" /><type Name="unscoped enumerator" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb"><ExtraRule Prefix="" Suffix="" Style="AA_BB" /></Policy></NamingElement> + <NamingElement Priority="15"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="True" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global variable" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy></NamingElement> + <NamingElement Priority="8"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global function" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy></NamingElement> + <NamingElement Priority="7"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="global variable" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> + <NamingElement Priority="6"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="local variable" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> + <NamingElement Priority="19"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="macro" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB"><ExtraRule Prefix="" Suffix="_" Style="AA_BB" /></Policy></NamingElement> <NamingElement Priority="14"><Descriptor Static="True" Constexpr="Indeterminate" Const="True" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="class field" /><type Name="local variable" /><type Name="struct field" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> - <NamingElement Priority="5"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="parameter" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> - <NamingElement Priority="4"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="template parameter" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> + <NamingElement Priority="5"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="function parameter" /><type Name="lambda parameter" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> + <NamingElement Priority="4"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="non-type template parameter" /><type Name="type template parameter" /></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></NamingElement> <NamingElement Priority="17"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="type alias" /><type Name="typedef" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> <NamingElement Priority="12"><Descriptor Static="Indeterminate" Constexpr="Indeterminate" Const="Indeterminate" Volatile="Indeterminate" Accessibility="NOT_APPLICABLE"><type Name="union member" /></Descriptor><Policy Inspect="False" Prefix="" Suffix="" Style="aa_bb" /></NamingElement> + False + True + True True + True True True True True True True + True True + True + True True True True True True + True True + True True True + True True True True @@ -57,7 +71,9 @@ True True True + True True + True True True True @@ -66,6 +82,7 @@ True True True + True True True True diff -Nru charls-2.1.0+dfsg/.clang-format charls-2.2.0+dfsg/.clang-format --- charls-2.1.0+dfsg/.clang-format 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/.clang-format 2021-01-10 18:07:25.000000000 +0000 @@ -3,15 +3,15 @@ BasedOnStyle: LLVM Language: Cpp - -# Use features of C++11, C++14 and C++17 (e.g. A> instead of A >) -Standard: Cpp11 +Standard: Latest UseTab: Never IndentWidth: 4 -ColumnLimit: 0 PointerAlignment: Left +# Use the github de facto limit of 125 +ColumnLimit: 125 + BreakBeforeBraces: Custom BraceWrapping: AfterClass: true @@ -28,8 +28,9 @@ AccessModifierOffset: -4 AlignTrailingComments: true -AllowShortFunctionsOnASingleLine: true -AllowShortIfStatementsOnASingleLine: true +AlignEscapedNewlines: DontAlign +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never AlwaysBreakTemplateDeclarations: true BreakConstructorInitializers: AfterColon CompactNamespaces: true @@ -38,4 +39,6 @@ SpaceBeforeCpp11BracedList: false KeepEmptyLinesAtTheStartOfBlocks: false MaxEmptyLinesToKeep: 2 -NamespaceIndentation: None \ No newline at end of file +NamespaceIndentation: None + +StatementMacros: ['TEST_CLASS', 'TEST_METHOD'] \ No newline at end of file diff -Nru charls-2.1.0+dfsg/.clang-tidy charls-2.2.0+dfsg/.clang-tidy --- charls-2.1.0+dfsg/.clang-tidy 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/.clang-tidy 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,89 @@ +# Copyright (c) Team CharLS. +# SPDX-License-Identifier: BSD-3-Clause + +# The approach for using clang tidy is to enable all warnings unless it adds no practical value to the CharLS projects +# Having all warnings enables helps to find problems when new code is added to the project. Some warnings are however +# to noisy and adding NO_LINT suppressions make the code less readable. +# +# -fuchsia-* => Rationale: Rules only apply to Fuchsia projects are out of scope for CharLS +# -google-* => Rationale: Rules only apply to Google projects are out of scope for CharLS +# -android-* => Rationale: Rules only apply to Android projects are out of scope for CharLS +# -llvmlibc-* => Rationale: Rules only apply to LLVM Libc and are out of scope for CharLS +# -llvm-header-guard => Rationale: #pragma once is used +# -hicpp-no-array-decay => Rationale: alias for cppcoreguidelines-pro-bounds-constant-array-index +# -hicpp-signed-bitwise => Rationale: Bad test, types are checked, not values. +# -clang-diagnostic-c++98-compat* => Rationale: CharLS targets C++14 +# -clang-diagnostic-c++98-c++11-compat => Rationale: CharLS targets C++14 +# -clang-diagnostic-unused-macros => Rationale: Macros defined in header are reported as problem +# -clang-diagnostic-sign-conversion => Rationale: warning will be enabled in additional steps +# -clang-diagnostic-switch-enum => Rationale: options are handled by default case +# -clang-diagnostic-exit-time-destructors => Rationale: Acceptable construction +# -clang-diagnostic-pragma-once-outside-header => Rationale: Generates false warnings for usage in header files +# -clang-analyzer-core.NonNullParamChecker => Rationale: cannot be effective disabled, already checked by other checkers. +# -misc-non-private-member-variables-in-classes => Rationale: design can be ok, manual review is better +# -modernize-use-trailing-return-type => Rationale: A style recommendation, this style is selected for CharLS +# -readability-magic-numbers => Rationale: To critical rule, used numbers are logical +# -readability-named-parameter => Rationale: to many non problematic warnings +# -readability-implicit-bool-conversion => Rationale: style issue +# -cppcoreguidelines-avoid-magic-numbers => Rationale: Alias +# -cppcoreguidelines-pro-bounds-pointer-arithmetic => Rationale: usage is required in codec implementation +# -cppcoreguidelines-pro-type-reinterpret-cast => Rationale: To strict for conditions that require its usage +# -cppcoreguidelines-macro-usage => Rationale: Many false warnings +# -cppcoreguidelines-pro-bounds-array-to-pointer-decay => Span is not available +# -cppcoreguidelines-pro-type-const-cast => Rationale: const cast is needed for legacy functions +# -cppcoreguidelines-pro-type-union-access => Rationale: std::variant not an option as target is C++14 +# -cppcoreguidelines-non-private-member-variables-in-classes => Warning is too strict, manual review code review is preferred +# -cppcoreguidelines-pro-bounds-constant-array-index => gsl:at is not used by design +# -cppcoreguidelines-init-variables => reports false warnings for out parameters (other checkers are better to detect these problems) +# -cert-msc32-c => Rationale: predictable seed is by design (random used for testing, not crypto) +# -cert-msc51-cpp => Rationale: alias for cert-msc32-c +# -cert-err58-cpp => Rationale: Only exception that could be thrown is out of memory + +--- +Checks: '*, + -fuchsia-*, + -google-*, + -android-*, + -llvmlibc-*, + -llvm-header-guard, + -hicpp-no-array-decay, + -hicpp-signed-bitwise, + -clang-diagnostic-c++98-compat*, + -clang-diagnostic-c++98-c++11-compat, + -clang-diagnostic-unused-macros, + -clang-diagnostic-sign-conversion, + -clang-diagnostic-switch-enum, + -clang-diagnostic-exit-time-destructors, + -clang-diagnostic-pragma-once-outside-header, + -clang-analyzer-core.NonNullParamChecker, + -misc-non-private-member-variables-in-classes, + -modernize-use-trailing-return-type, + -readability-magic-numbers, + -readability-named-parameter, + -readability-implicit-bool-conversion, + -cppcoreguidelines-avoid-magic-numbers, + -cppcoreguidelines-pro-bounds-pointer-arithmetic, + -cppcoreguidelines-pro-type-reinterpret-cast, + -cppcoreguidelines-macro-usage, + -cppcoreguidelines-pro-bounds-array-to-pointer-decay, + -cppcoreguidelines-pro-type-const-cast, + -cppcoreguidelines-pro-type-union-access, + -cppcoreguidelines-non-private-member-variables-in-classes, + -cppcoreguidelines-pro-bounds-constant-array-index, + -cppcoreguidelines-init-variables, + -cert-msc32-c, + -cert-msc51-cpp, + -cert-err58-cpp, + -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling' +WarningsAsErrors: false +HeaderFilterRegex: '' +FormatStyle: none +CheckOptions: + - key: readability-braces-around-statements.ShortStatementLines + value: '2' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '2' + - key: hicpp-braces-around-statements.ShortStatementLines + value: '2' + - key: readability-implicit-bool-conversion.AllowPointerConditions + value: '1' diff -Nru charls-2.1.0+dfsg/CMakeLists.txt charls-2.2.0+dfsg/CMakeLists.txt --- charls-2.1.0+dfsg/CMakeLists.txt 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/CMakeLists.txt 2021-01-10 18:07:25.000000000 +0000 @@ -1,9 +1,19 @@ # Copyright (c) Team CharLS. # SPDX-License-Identifier: BSD-3-Clause -cmake_minimum_required(VERSION 3.9...3.14) +cmake_minimum_required(VERSION 3.13...3.19) -project(charls VERSION 2.1.0 LANGUAGES C CXX) +# Extract the version info from version.h +file(READ "include/charls/version.h" version) +string(REGEX MATCH "CHARLS_VERSION_MAJOR ([0-9]*)" _ ${version}) +set(version_major ${CMAKE_MATCH_1}) +string(REGEX MATCH "CHARLS_VERSION_MINOR ([0-9]*)" _ ${version}) +set(version_minor ${CMAKE_MATCH_1}) +string(REGEX MATCH "CHARLS_VERSION_PATCH ([0-9]*)" _ ${version}) +set(version_patch ${CMAKE_MATCH_1}) +message(STATUS "CharLS version: ${version_major}.${version_minor}.${version_patch}") + +project(charls VERSION ${version_major}.${version_minor}.${version_patch} LANGUAGES C CXX) # Determine if project is built as a subproject (using add_subdirectory) or if it is the master project. set(MASTER_PROJECT OFF) @@ -14,9 +24,13 @@ # The basic options to control what is build extra. option(CHARLS_BUILD_TESTS "Build test application" ${MASTER_PROJECT}) +option(CHARLS_BUILD_FUZZ_TEST "Build AFL fuzzer application" ${MASTER_PROJECT}) option(CHARLS_BUILD_SAMPLES "Build sample applications" ${MASTER_PROJECT}) option(CHARLS_INSTALL "Generate the install target." ${MASTER_PROJECT}) +# Provide BUILD_SHARED_LIBS as an option for GUI tools +option(BUILD_SHARED_LIBS "Will control if charls lib is build as shared lib/DLL or static library") + # The options used by the CI builds to ensure the source remains warning free. # Not enabled by default to make CharLS package and end-user friendly. option(CHARLS_PEDANTIC_WARNINGS "Enable extra warnings and static analysis." OFF) @@ -97,6 +111,33 @@ if(MSVC) set(PEDANTIC_CXX_COMPILE_FLAGS /W4) set(WERROR_FLAG /WX) + + # Exception settings are not set for ARM(64) when building for Ninja + string(TOLOWER ${CMAKE_CXX_COMPILER} CXX_COMPILER_LOWER) + string(FIND ${CXX_COMPILER_LOWER} "arm" ARM_DETECTED) + if(${ARM_DETECTED} GREATER 0) + add_compile_options("/EHsc") + endif() + + # Remove option /GR (/GR is added by default by CMake). /GR is already the default + # and this makes it possible to use /GR- without warnings. + string(REGEX REPLACE " /GR" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + + # All C and C++ source files are in utf-8 without signature (bom), MSVC requires utf-8 switch to read files correctly. + add_compile_options("/utf-8") + + # Zc:__cplusplus: Will configure the MSVC compiler to use the correct value for the __cplusplus macro + # (introduced with Visual Studio 2017 version 15.7) + if(MSVC_VERSION GREATER_EQUAL 1914) + add_compile_options("/Zc:__cplusplus") + endif() + + # /Zc:throwingNew: Will configure the MSVC compiler that only the standard throwing operator new is used. + add_compile_options("/Zc:throwingNew") + + # /guard:cf: Will enable Control Flow Guard, which provides addition compile time and runtime checks. + add_compile_options("/guard:cf") + add_link_options("/guard:cf") endif() # When enabled apply the pedantic warnings options and warnings as errors to globally. @@ -120,6 +161,10 @@ add_subdirectory(test) endif() +if(CHARLS_BUILD_FUZZ_TEST) + add_subdirectory(fuzztest) +endif() + if(CHARLS_BUILD_SAMPLES) add_subdirectory(samples) endif() \ No newline at end of file diff -Nru charls-2.1.0+dfsg/CMakeSettings.json charls-2.2.0+dfsg/CMakeSettings.json --- charls-2.1.0+dfsg/CMakeSettings.json 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/CMakeSettings.json 2021-01-10 18:07:25.000000000 +0000 @@ -96,6 +96,29 @@ "type": "STRING" } ] + }, + { + "name": "ARM64-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", + "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", + "cmakeCommandArgs": "-DBUILD_SHARED_LIBS=YES", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "inheritEnvironments": [ "msvc_arm64_x64" ], + "variables": [ + { + "name": "CHARLS_THREAT_WARNINGS_AS_ERRORS", + "value": "On", + "type": "STRING" + }, + { + "name": "CHARLS_PEDANTIC_WARNINGS", + "value": "On", + "type": "STRING" + } + ] } ] } \ No newline at end of file diff -Nru charls-2.1.0+dfsg/cpp.hint charls-2.2.0+dfsg/cpp.hint --- charls-2.1.0+dfsg/cpp.hint 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/cpp.hint 2021-01-10 18:07:25.000000000 +0000 @@ -18,3 +18,20 @@ #define FORCE_INLINE #define MSVC_WARNING_SUPPRESS(x) #define MSVC_WARNING_UNSUPPRESS() +#define MSVC_WARNING_SUPPRESS_NEXT_LINE(x) +#define MSVC_CONST +#define IN_ +#define IN_OPT_ +#define IN_Z_ +#define IN_READS_BYTES_(size) +#define OUT_ +#define OUT_OPT_ +#define OUT_WRITES_BYTES_(size) +#define OUT_WRITES_Z_(size_in_bytes) +#define RETURN_TYPE_SUCCESS_(expr) +#define CHARLS_ATTRIBUTE(a) +#define DISABLE_DEPRECATED_WARNING +#define RESTORE_DEPRECATED_WARNING +#define CHARLS_C_VOID +#define CHARLS_CHECK_RETURN +#define CHARLS_RET_MAY_BE_NULL diff -Nru charls-2.1.0+dfsg/debian/changelog charls-2.2.0+dfsg/debian/changelog --- charls-2.1.0+dfsg/debian/changelog 2020-11-12 08:34:06.000000000 +0000 +++ charls-2.2.0+dfsg/debian/changelog 2021-01-27 15:59:40.000000000 +0000 @@ -1,3 +1,22 @@ +charls (2.2.0+dfsg-2) unstable; urgency=medium + + * Team upload. + * d/rules: Make sure to install cmake specific files + + -- Mathieu Malaterre Wed, 27 Jan 2021 16:59:40 +0100 + +charls (2.2.0+dfsg-1) unstable; urgency=medium + + * Team upload. + * New upstream version 2.2.0+dfsg + * d/patches: Refresh patches for 2.2.0 release + * d/control: Bump Std-Vers to 4.5.1, no changes needed + * d/copyright: Remove binary file + * d/symbols: Update the symbol file with the newly added one + * d/control: Make sure to require cmake >= 3.13 + + -- Mathieu Malaterre Wed, 27 Jan 2021 16:07:22 +0100 + charls (2.1.0+dfsg-8) unstable; urgency=medium [ Mathieu Malaterre ] diff -Nru charls-2.1.0+dfsg/debian/control charls-2.2.0+dfsg/debian/control --- charls-2.1.0+dfsg/debian/control 2020-11-12 08:29:50.000000000 +0000 +++ charls-2.2.0+dfsg/debian/control 2021-01-27 15:06:41.000000000 +0000 @@ -4,8 +4,8 @@ Shayan Doust Section: science Priority: optional -Build-Depends: cmake, debhelper-compat (= 13) -Standards-Version: 4.5.0 +Build-Depends: cmake (>= 3.13), debhelper-compat (= 13) +Standards-Version: 4.5.1 Vcs-Browser: https://salsa.debian.org/med-team/charls Vcs-Git: https://salsa.debian.org/med-team/charls.git Homepage: https://github.com/team-charls/charls diff -Nru charls-2.1.0+dfsg/debian/copyright charls-2.2.0+dfsg/debian/copyright --- charls-2.1.0+dfsg/debian/copyright 2020-11-12 08:29:50.000000000 +0000 +++ charls-2.2.0+dfsg/debian/copyright 2021-01-27 14:59:12.000000000 +0000 @@ -9,7 +9,7 @@ */test/*.dcm */test/*.ppm */test/MR2_UNC - *.vcproj* + */src/charls.rc *.vcxproj* Files: * diff -Nru charls-2.1.0+dfsg/debian/libcharls2.symbols charls-2.2.0+dfsg/debian/libcharls2.symbols --- charls-2.1.0+dfsg/debian/libcharls2.symbols 2020-10-30 17:29:25.000000000 +0000 +++ charls-2.2.0+dfsg/debian/libcharls2.symbols 2021-01-27 15:01:18.000000000 +0000 @@ -10,6 +10,7 @@ charls_jpegls_decoder_create@Base 2.1.0+dfsg charls_jpegls_decoder_decode_to_buffer@Base 2.1.0+dfsg charls_jpegls_decoder_destroy@Base 2.1.0+dfsg + charls_jpegls_decoder_get_color_transformation@Base 2.2.0+dfsg charls_jpegls_decoder_get_destination_size@Base 2.1.0+dfsg charls_jpegls_decoder_get_frame_info@Base 2.1.0+dfsg charls_jpegls_decoder_get_interleave_mode@Base 2.1.0+dfsg diff -Nru charls-2.1.0+dfsg/debian/patches/noexplicitstd.patch charls-2.2.0+dfsg/debian/patches/noexplicitstd.patch --- charls-2.1.0+dfsg/debian/patches/noexplicitstd.patch 2020-10-29 16:33:56.000000000 +0000 +++ charls-2.2.0+dfsg/debian/patches/noexplicitstd.patch 2021-01-27 15:00:33.000000000 +0000 @@ -3,9 +3,11 @@ Forwarded: no Last-Update: 2019-01-03 +Index: charls/CMakeLists.txt +=================================================================== --- charls.orig/CMakeLists.txt +++ charls/CMakeLists.txt -@@ -23,7 +23,7 @@ +@@ -37,7 +37,7 @@ option(CHARLS_PEDANTIC_WARNINGS "Enable option(CHARLS_THREAT_WARNINGS_AS_ERRORS "Treat Warnings as Errors." OFF) # CharLS requires C++14 or newer. diff -Nru charls-2.1.0+dfsg/debian/patches/stl_symbols_hack.patch charls-2.2.0+dfsg/debian/patches/stl_symbols_hack.patch --- charls-2.1.0+dfsg/debian/patches/stl_symbols_hack.patch 2020-10-29 16:56:11.000000000 +0000 +++ charls-2.2.0+dfsg/debian/patches/stl_symbols_hack.patch 2021-01-27 15:00:28.000000000 +0000 @@ -33,9 +33,9 @@ =================================================================== --- charls.orig/src/CMakeLists.txt +++ charls/src/CMakeLists.txt -@@ -40,6 +40,8 @@ set(CHARLS_PUBLIC_HEADERS +@@ -54,6 +54,8 @@ endforeach() set_target_properties(charls PROPERTIES CXX_VISIBILITY_PRESET hidden) - set_property(TARGET charls PROPERTY PUBLIC_HEADER ${CHARLS_PUBLIC_HEADERS}) + set_property(TARGET charls PROPERTY PUBLIC_HEADER ${HEADERS}) +set_property(TARGET charls PROPERTY LINK_FLAGS "-Wl,--version-script=../exports.version") + diff -Nru charls-2.1.0+dfsg/debian/rules charls-2.2.0+dfsg/debian/rules --- charls-2.1.0+dfsg/debian/rules 2020-11-09 13:38:08.000000000 +0000 +++ charls-2.2.0+dfsg/debian/rules 2021-01-27 15:57:39.000000000 +0000 @@ -31,6 +31,8 @@ # Development Package dh_install -p$(pkg_dev) usr/include dh_install -p$(pkg_dev) usr/lib/$(DEB_HOST_MULTIARCH)/lib*.so + dh_install -p$(pkg_dev) usr/lib/$(DEB_HOST_MULTIARCH)/cmake/charls/*.cmake + dh_install -p$(pkg_dev) usr/lib/$(DEB_HOST_MULTIARCH)/pkgconfig/charls.pc # See #971425, #971431 & #971435: dh_link -p$(pkg_run) usr/lib/$(DEB_HOST_MULTIARCH)/libcharls.so.2 usr/lib/$(DEB_HOST_MULTIARCH)/libCharLS.so.2 dh_link -p$(pkg_dev) usr/lib/$(DEB_HOST_MULTIARCH)/libcharls.so usr/lib/$(DEB_HOST_MULTIARCH)/libCharLS.so diff -Nru charls-2.1.0+dfsg/default.ruleset charls-2.2.0+dfsg/default.ruleset --- charls-2.1.0+dfsg/default.ruleset 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/default.ruleset 2021-01-10 18:07:25.000000000 +0000 @@ -1,5 +1,5 @@  - + @@ -9,9 +9,6 @@ - - - diff -Nru charls-2.1.0+dfsg/default.ruleset.md charls-2.2.0+dfsg/default.ruleset.md --- charls-2.1.0+dfsg/default.ruleset.md 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/default.ruleset.md 2021-01-10 18:07:25.000000000 +0000 @@ -1,8 +1,5 @@ # Comments on disabled Visual Studio C++ Core Guidelines Rules -C26050: Unclear warning of the initializer itself --> Rationale: false warning - C26426: Global initializer calls a non-constexpr function 'xxx' -> Rationale: many false warnings. CharLS is a library, globals are correctly initialized. @@ -12,7 +9,7 @@ C26446: Prefer to use gsl::at() instead of unchecked subscript operator. -> Rationale: CharLS require good performance, gsl:at() cannot be used. debug STL already checks. -C26472: Don't use static_cast for arithmetic conversios +C26472: Don't use static_cast for arithmetic conversions -> Rationale: can only be solved with gsl::narrow_cast C26481: Do not pass an array as a single pointer. @@ -21,12 +18,6 @@ C26482: Only index into arrays using constant expressions. -> Rationale: static analysis can verify access, std::array during runtime (debug) -C26487: Don't return a pointer that may be invalid (lifetime.4). --> Rationale: many false warnings (VS 2019 16.0.0 Preview 1) - -C26489: Don't dereference a pointer that may be invalid --> Rationale: many false warnings (known defect in VS 2017 15.9.0) - C26490: Don't use reinterpret_cast -> Rationale: required to cast unsigned char* to char*. @@ -34,4 +25,4 @@ -> Rationale: required for some special cases. C26494: Variable 'x' is uninitialized. Always initialize an object --> Rationale: many false warnings, other analyzers are better. \ No newline at end of file +-> Rationale: many false warnings, other analyzers are better. diff -Nru charls-2.1.0+dfsg/Directory.Build.props charls-2.2.0+dfsg/Directory.Build.props --- charls-2.1.0+dfsg/Directory.Build.props 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/Directory.Build.props 2021-01-10 18:07:25.000000000 +0000 @@ -2,15 +2,12 @@ - $(MSBuildThisFileDirectory)bin\$(Configuration)\$(Platform)\ - $(MSBuildThisFileDirectory)bin\$(Configuration)\x86\ - - - $(OutDir) + $(MSBuildThisFileDirectory)bin\$(Platform)\$(Configuration)\ + $(MSBuildThisFileDirectory)bin\x86\$(Configuration)\ - $(MSBuildThisFileDirectory)intermediate\$(MSBuildProjectName)\$(Platform)\$(Configuration)\ + $(MSBuildThisFileDirectory)intermediate\$(MSBuildProjectName)\x86\$(Configuration)\ @@ -35,14 +35,8 @@ true - - /Zc:__cplusplus /Zc:throwingNew - /wd4061 /wd4191 /wd4263 /wd4264 /wd4266 /wd4365 /wd4464 /wd4514 /wd4571 /wd4623 /wd4625 /wd4626 /wd4668 - /wd4710 /wd4711 /wd4738 /wd4820 /wd5026 /wd5027 /wd5045 - + 4061;4365;4464;4514;4571;4623;4625;4626;4668;4710;4711;4738;4820;5026;5027;5045 + + + /Zc:__cplusplus /Zc:throwingNew /utf-8 %(AdditionalOptions) true @@ -81,15 +78,69 @@ true + + Caret + + + true + + ProgramDatabase + + + true + + + DebugFull + + + + $(MSBuildThisFileDirectory)include + - + true + + + Disabled + _DEBUG;%(PreprocessorDefinitions) + + + false + + + _DEBUG;%(PreprocessorDefinitions) + + + - true - true + true + true + true + true + + + false + + + + + MaxSpeed + true + true + NDEBUG;%(PreprocessorDefinitions) + Guard + + + true + true + false + + + \ No newline at end of file diff -Nru charls-2.1.0+dfsg/doc/requirements.md charls-2.2.0+dfsg/doc/requirements.md --- charls-2.1.0+dfsg/doc/requirements.md 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/doc/requirements.md 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,45 @@ +# Style and Design + +## Introduction + +The purpose of this document is to capture the requirements that are in and out scope. + +## In Scope + +### R1 Decode from a memory buffer to a memory buffer + +The typical use case is a client application that load JPEG-LS encoded data from a file into a memory buffer +and call decode to get a decoded image that can be displayed on the screen. + +### R2 Encode from a memory buffer to a memory buffer + +The typical use case is a client application that has an image in memory that needs to be saved. +The library will encode the image to a JPEG-LS byte stream in a memory buffer, which the application +then can save. + +## Out Scope + +### Decode from a byte stream to a memory buffer + +The typical use case is a client application that want to load a JPEG-LS encoded byte stream +from file. The library should then also handling the loading of this byte stream. +To make this process generic, the client needs to provide a callback function that the library +can use when it needs more bytes. +This requirement is out scope as the use case is already covered by R1. The only advantage is that library can +the byte stream in chunks. For the typical image size it is however more efficient to let the client code do +the loading of the byte stream in a buffer. + +### Decode from a byte stream to a byte stream + +The typical use case for this requirement is decoding of very large images, which would normally cause out-of-memory +conditions. As there is not a direct need for this requirement, it is considered out of scope. + +### Encode from a memory buffer to a byte stream + +The typical use case for this requirement is save images directly to a file. This is already covered by R2, +using a callback function, would not increase the performance. + +### Encode from a byte stream to a byte stream + +The typical use case for this requirement is to encode images on a storage medium to JPEG-LS with strict memory +requirements. diff -Nru charls-2.1.0+dfsg/doc/style_and_design.md charls-2.2.0+dfsg/doc/style_and_design.md --- charls-2.1.0+dfsg/doc/style_and_design.md 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/doc/style_and_design.md 2021-01-10 18:07:25.000000000 +0000 @@ -15,7 +15,7 @@ ### Template: class vs typename -The typename keyword is the prefered keyword to "mark" template variables. +The typename keyword is the preferred keyword to "mark" template variables. ### Prevent header file double include @@ -29,9 +29,9 @@ ## Exceptions and Error Handling -* C++ exceptions should be derived from `std::exception`. (Common accepted idom) +* C++ exceptions should be derived from `std::exception`. (Common accepted idiom) * The exception should be convertible to an error code to support the C API -* An english error text that describes the problem is extreme usefull. +* An english error text that describes the problem is extreme useful. => Design: std::system_error is the standard solution to throw exceptions from libraries (CharLS is a library) @@ -94,14 +94,40 @@ * There can be an explicit check and an error return value. -* The pointer can be deferenced directly and passing NULL will just crash the process. +* The pointer can be dereferenced directly and passing NULL will just crash the process. -Passing a NULL pointer is a defect of the calling application, it is however helpfull to +Passing a NULL pointer is a defect of the calling application, it is however helpful to not generate an access violation inside the library module. If the library is build without symbol info, it is difficult for the user to detect this mistake. Returning a "bad parameter" -error is in this case more helpfull. +error is in this case more helpful. Note: NULL is the only special value that can be checked, but also the common mistake. +### Variable names versus JPEG-LS Standard + +The JPEG-LS standard uses pseudo-code to define certain parts of the algorithm. It makes +sense to define a good naming convention. Not all JPEG-LS names are good C++ variable\parameter names. + +| JPEG-LS Symbol | C++ name | Description | +| -------------- | ------------------------ |------------ | +| a, b, c, d | a, b, c, d | positions of samples in the causal template | +| bpp | bits_per_pixel | number of bits needed to represent MAXVAL (not less than 2) | +| D1, D2, D3, Di | d1, d2, d3, di | local gradients | +| EMErrval | e_mapped_error_value | Errval mapped to non-negative integers in run interruption mode | +| Errval | error_value | prediction error (quantized or unquantized, before and after modulo reduction) | +| ILV | interleave_mode | indication of the interleave mode used for the scan | +| LIMIT | limit | the value of glimit for a sample encoded in regular mode | +| J[0..31] | J[0..31] | 32 variables indicating order of run-length codes | +| k | k (or golomb_code) | Golomb coding variable for regular mode | +| MErrval | mapped_error_value | Errval mapped to non-negative integers in regular mode | +| MAXVAL | maximum_sample_value | maximum possible image sample value over all components of a scan | +| NEAR | near_lossless | difference bound for near-lossless coding | +| Px | predicted_value | predicted value for the current sample | +| Q1, Q2, Q3, Qi | q1, q2, q3, qi | region numbers to quantize local gradients | +| qbpp | quantized_bits_per_pixel | number of bits needed to represent a mapped error value | +| Ra, Rb, Rc, Rd | ra, rb, rc, rd | reconstructed values of samples in the causal template | +| RANGE | range | range of prediction error representation +| RESET | reset_threshold | threshold value at which A, B, and N are halved | + ### Supported C++ language CharLS currently targets C++14 on the main branch. This will be done until December 2022 (5 years after the release of C++17) @@ -143,7 +169,7 @@ ### External components \ Package Manager -One of the missing features of C++ is a standard Package Manager. The following packages would be usefull to use: +One of the missing features of C++ is a standard Package Manager. The following packages would be useful to use: * Cross-platform unit test library (for example Catch2) * Library to read Anymap files (for example Netpbm) diff -Nru charls-2.1.0+dfsg/dotnet/src/AssemblyInfo.cs charls-2.2.0+dfsg/dotnet/src/AssemblyInfo.cs --- charls-2.1.0+dfsg/dotnet/src/AssemblyInfo.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/AssemblyInfo.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System; -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: CLSCompliant(true)] -[assembly: ComVisible(false)] Binary files /tmp/tmptxyqr8/iDZBmIvU8B/charls-2.1.0+dfsg/dotnet/src/CharLSKeyPair.snk and /tmp/tmptxyqr8/i0J_6ju0mM/charls-2.2.0+dfsg/dotnet/src/CharLSKeyPair.snk differ diff -Nru charls-2.1.0+dfsg/dotnet/src/CharLSNet.csproj charls-2.2.0+dfsg/dotnet/src/CharLSNet.csproj --- charls-2.1.0+dfsg/dotnet/src/CharLSNet.csproj 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/CharLSNet.csproj 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ - - - - - netstandard2.0 - - ".NET interop layer for the CharLS JPEG-LS codec" - Team-CharLS - Team-CharLS - 2.1.0 - 2.0.0.0 - 2.1.0.0 - Copyright 2019 Team CharLS - AnyCPU - Debug;Release;Checked - CharLS.ruleset - $(OutDir)CharLSNet.xml - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - - diff -Nru charls-2.1.0+dfsg/dotnet/src/CharLS.ruleset charls-2.2.0+dfsg/dotnet/src/CharLS.ruleset --- charls-2.1.0+dfsg/dotnet/src/CharLS.ruleset 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/CharLS.ruleset 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -Nru charls-2.1.0+dfsg/dotnet/src/CodeAnalysisDictionary.xml charls-2.2.0+dfsg/dotnet/src/CodeAnalysisDictionary.xml --- charls-2.1.0+dfsg/dotnet/src/CodeAnalysisDictionary.xml 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/CodeAnalysisDictionary.xml 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ - - - - - - - lossy - jfif - bgr - jls - BitsPerSample - Rgb - - - - - - - - - diff -Nru charls-2.1.0+dfsg/dotnet/src/GlobalSuppressions.cs charls-2.2.0+dfsg/dotnet/src/GlobalSuppressions.cs --- charls-2.1.0+dfsg/dotnet/src/GlobalSuppressions.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/GlobalSuppressions.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "CharLS", Justification = "At least 1 main namespace is needed.")] -[assembly: SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Scope = "member", Target = "CharLS.JpegLSMetadataInfo.#ObjectInvariant()", Justification = "Needed for Code Contracts")] -[assembly: SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "CharLS.JpegLSMetadataInfo.#ObjectInvariant()", Justification = "Needed for Code Contracts")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements must be ordered by access", Justification = "Not possible for StructLayout tagged classes", Scope = "member", Target = "~F:CharLS.JlsParameters.OutputBgr")] -[assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1202:Elements must be ordered by access", Justification = "Not possible for StructLayout tagged classes", Scope = "member", Target = "~F:CharLS.JlsParameters.Jfif")] diff -Nru charls-2.1.0+dfsg/dotnet/src/JfifParameters.cs charls-2.2.0+dfsg/dotnet/src/JfifParameters.cs --- charls-2.1.0+dfsg/dotnet/src/JfifParameters.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/JfifParameters.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System; -using System.Runtime.InteropServices; - -namespace CharLS -{ - /// - /// Encapsulates the parameters that will be written to the JFIF header. - /// Since JFIF 1.02 thumbnails should preferable be created in extended segments. - /// - /// - /// Some fields are not used but defined to ensure memory layout and size is identical with the native structure. - /// - [StructLayout(LayoutKind.Sequential, Pack = 8)] - internal struct JfifParameters - { - /// - /// Indicates the JFIF version. First byte is major version (currently 0x01), Second byte is minor version (currently 0x02). - /// - internal int Version; - - /// - /// Units for pixel density fields. 0 - No units, aspect ratio only specified. 1 - Pixels per inch. 2 - Pixels per centimeter. - /// - internal int Units; - - /// - /// Integer horizontal pixel density. - /// - internal int DensityX; - - /// - /// Integer vertical pixel density. - /// - internal int DensityY; - - private readonly int thumbX; // note: passing a thumbnail to add to the byte stream is currently not supported in the .NET layer. - private readonly int thumbY; - private readonly IntPtr dataThumbnail; // user must set buffer which size is thumbX * thumbY * 3(RGB) before JpegLsDecode() - } -} diff -Nru charls-2.1.0+dfsg/dotnet/src/JlsParameters.cs charls-2.2.0+dfsg/dotnet/src/JlsParameters.cs --- charls-2.1.0+dfsg/dotnet/src/JlsParameters.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/JlsParameters.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System.Runtime.InteropServices; - -namespace CharLS -{ - [StructLayout(LayoutKind.Sequential, Pack = 8)] - internal struct JlsParameters - { - internal int Width; - internal int Height; - internal int BitsPerSample; - internal int BytesPerLine; - internal int Components; - internal int AllowedLossyError; - internal JpegLSInterleaveMode InterleaveMode; - private readonly int colorTransform; // note: not used in this adapter interface. - internal bool OutputBgr; - private readonly JpegLSPresetCodingParameters custom; // note: not used in this adapter interface. - internal JfifParameters Jfif; - } -} diff -Nru charls-2.1.0+dfsg/dotnet/src/JpegLSCodec.cs charls-2.2.0+dfsg/dotnet/src/JpegLSCodec.cs --- charls-2.1.0+dfsg/dotnet/src/JpegLSCodec.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/JpegLSCodec.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,326 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; - -namespace CharLS -{ - /// - /// Provides methods and for compressing and decompressing arrays using the JPEG-LS algorithm. - /// - /// - /// This class represents the JPEG-LS algorithm, an industry standard algorithm for lossless and near-lossless - /// image data compression and decompression. - /// - public static class JpegLSCodec - { - // Design notes: - // - The words compress/decompress are used as these are the terms used by the .NET BCLs (System.IO.Compression namespace) - // The CharLS C API uses the terms encode/decode. - // - The input/output buffers parameters are using the common .NET order, which is different the CharLS C API. - - /// - /// Compresses the specified image passed in the source pixel buffer. - /// - /// The meta info that describes the format and type of the pixels. - /// An array of bytes that represents the content of a bitmap image. - /// if set to true a JFIF header will be added to the encoded byte stream. - /// An arraySegment with a reference to the byte array with the compressed data in the JPEG-LS format. - /// info -or- pixels is null. - /// info.Width -or- info.Height contains an invalid value. - /// The compressed output doesn't fit into the maximum defined output buffer. - public static ArraySegment Compress(JpegLSMetadataInfo info, byte[] pixels, bool jfifHeader = false) - { - if (pixels == null) - throw new ArgumentNullException(nameof(pixels)); - - var pixelCount = pixels.Length; - return Compress(info, pixels, pixelCount, jfifHeader); - } - - /// - /// Compresses the specified image passed in the source pixel buffer. - /// - /// The meta info that describes the format and type of the pixels. - /// An array of bytes that represents the content of a bitmap image. - /// The number of pixel in the pixel array. - /// if set to true a JFIF header will be added to the encoded byte stream. - /// An arraySegment with a reference to the byte array with the compressed data in the JPEG-LS format. - /// info -or- pixels is null. - /// info.Width -or- info.Height -or- pixelCount contains an invalid value. - /// The compressed output doesn't fit into the maximum defined output buffer. - public static ArraySegment Compress(JpegLSMetadataInfo info, byte[] pixels, int pixelCount, bool jfifHeader = false) - { - if (info == null) - throw new ArgumentNullException(nameof(info)); - if (pixels == null) - throw new ArgumentNullException(nameof(pixels)); - if (pixelCount <= 0 || pixelCount > pixels.Length) - throw new ArgumentOutOfRangeException(nameof(pixelCount), "pixelCount <= 0 || pixelCount > pixels.Length"); - - const int JpegLSHeaderLength = 100; - - // Assume compressed size <= uncompressed size (covers 99% of the cases). - var buffer = new byte[pixels.Length + JpegLSHeaderLength]; - - if (!TryCompress(info, pixels, pixels.Length, jfifHeader, buffer, buffer.Length, out var compressedCount)) - { - // Increase output buffer to hold compressed data. - buffer = new byte[(int)(pixels.Length * 1.5) + JpegLSHeaderLength]; - - if (!TryCompress(info, pixels, pixels.Length, jfifHeader, buffer, buffer.Length, out compressedCount)) - throw new InvalidDataException("Compression failed: compressed output larger then 1.5 * input."); - } - - return new ArraySegment(buffer, 0, compressedCount); - } - - /// - /// Tries the compress the array with pixels into the provided buffer. - /// - /// The meta info that describes the format and type of the pixels. - /// An array of bytes that represents the content of a bitmap image. - /// The number of pixel in the pixel array. - /// if set to true a JFIF header will be added to the encoded byte stream. - /// The destination buffer that will hold the JPEG-LS compressed (encoded) bit stream. - /// Length of the destination buffer that can be used (can be less then the length of the destination array). - /// The number of bytes that have been compressed (encoded) into the destination array. - /// true when the compressed bit stream fits into the destination array, otherwise false. - /// info -or- pixels is null. - /// info.Width -or- info.Height -or- pixelCount -or- destinationLength contains an invalid value. - public static bool TryCompress(JpegLSMetadataInfo info, byte[] pixels, int pixelCount, bool jfifHeader, byte[] destination, int destinationLength, out int compressedCount) - { - if (info == null) - throw new ArgumentNullException(nameof(info)); - if (pixels == null) - throw new ArgumentNullException(nameof(pixels)); - if (pixelCount <= 0 || pixelCount > pixels.Length) - throw new ArgumentOutOfRangeException(nameof(pixelCount), "pixelCount <= 0 || pixelCount > pixels.Length"); - if (destination == null) - throw new ArgumentNullException(nameof(destination)); - if (destinationLength <= 0 || destinationLength > destination.Length) - throw new ArgumentOutOfRangeException(nameof(destinationLength), "destination <= 0 || destinationCount > destination.Length"); - - var parameters = default(JlsParameters); - info.CopyTo(ref parameters); - if (jfifHeader) - { - parameters.Jfif.Version = (1 << 8) + 2; // JFIF version 1.02 - parameters.Jfif.Units = 0; // No units, aspect ratio only specified - parameters.Jfif.DensityX = 1; // use standard 1:1 aspect ratio. (density should always be set to non-zero values). - parameters.Jfif.DensityY = 1; - } - - JpegLSError result; - if (Environment.Is64BitProcess) - { - result = SafeNativeMethods.JpegLsEncodeX64(destination, destinationLength, out var count, pixels, pixelCount, ref parameters, IntPtr.Zero); - compressedCount = (int)count; - } - else - { - result = SafeNativeMethods.JpegLsEncodeX86(destination, destinationLength, out compressedCount, pixels, pixelCount, ref parameters, IntPtr.Zero); - } - - if (result == JpegLSError.SourceBufferTooSmall) - return false; - - HandleResult(result); - return true; - } - - /// - /// Gets the metadata info as stored in a JPEG-LS compressed bit stream. - /// - /// The JPEG-LS compressed bit stream. - /// An JpegLSMetadataInfo instance. - /// source is null. - /// Thrown when the source array contains invalid compressed data. - public static JpegLSMetadataInfo GetMetadataInfo(byte[] source) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - - return GetMetadataInfo(source, source.Length); - } - - /// - /// Gets the metadata info as stored in a JPEG-LS compressed bit stream. - /// - /// The JPEG-LS compressed bit stream. - /// The count of bytes that are valid in the array. - /// An JpegLSMetadataInfo instance. - /// source is null. - /// count contains an invalid value. - /// Thrown when the source array contains invalid compressed data. - public static JpegLSMetadataInfo GetMetadataInfo(byte[] source, int count) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (count < 0 || count > source.Length) - throw new ArgumentOutOfRangeException(nameof(count), "count < 0 || count > source.Length"); - - JpegLsReadHeaderThrowWhenError(source, count, out var info); - return new JpegLSMetadataInfo(ref info); - } - - /// - /// Decompresses the JPEG-LS encoded data passed in the source byte array. - /// - /// The byte array that contains the JPEG-LS encoded data to decompress. - /// A byte array with the pixel data. - /// source is null. - /// Thrown when the source array contains invalid compressed data. - public static byte[] Decompress(byte[] source) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - - return Decompress(source, source.Length); - } - - /// - /// Decompresses the JPEG-LS encoded data passed in the source byte array. - /// - /// The byte array that contains the JPEG-LS encoded data to decompress. - /// The number of bytes of the array to decompress. - /// A byte array with the pixel data. - /// source is null. - /// count contains an invalid value. - /// Thrown when the source array contains invalid compressed data. - public static byte[] Decompress(byte[] source, int count) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (count < 0 || count > source.Length) - throw new ArgumentOutOfRangeException(nameof(count), "count < 0 || count > source.Length"); - - JpegLsReadHeaderThrowWhenError(source, count, out var info); - - var destination = new byte[GetUncompressedSize(ref info)]; - Decompress(source, count, destination); - return destination; - } - - /// - /// Decompresses the JPEG-LS encoded data passed in the source byte array into the destination array. - /// - /// The byte array that contains the JPEG-LS encoded data to decompress. - /// The number of bytes of the array to decompress. - /// The byte array that will hold the pixels when the function returns. - /// source -or- pixels is null. - /// count contains an invalid value. - /// Thrown when the destination array is too small to hold the decompressed pixel data. - /// Thrown when the source array contains an invalid encoded JPEG-LS bit stream. - public static void Decompress(byte[] source, int count, byte[] pixels) - { - if (source == null) - throw new ArgumentNullException(nameof(source)); - if (count < 0 || count > source.Length) - throw new ArgumentOutOfRangeException(nameof(count), "count < 0 || count > source.Length"); - if (pixels == null) - throw new ArgumentNullException(nameof(pixels)); - - var error = Environment.Is64BitProcess ? - SafeNativeMethods.JpegLsDecodeX64(pixels, pixels.Length, source, count, IntPtr.Zero, IntPtr.Zero) : - SafeNativeMethods.JpegLsDecodeX86(pixels, pixels.Length, source, count, IntPtr.Zero, IntPtr.Zero); - HandleResult(error); - } - - private static void JpegLsReadHeaderThrowWhenError(byte[] source, int length, out JlsParameters info) - { - var result = Environment.Is64BitProcess ? - SafeNativeMethods.JpegLsReadHeaderX64(source, length, out info, IntPtr.Zero) : - SafeNativeMethods.JpegLsReadHeaderX86(source, length, out info, IntPtr.Zero); - HandleResult(result); - } - - private static int GetUncompressedSize(ref JlsParameters info) - { - var size = info.Width * info.Height * info.Components * ((info.BitsPerSample + 7) / 8); - return size; - } - - private static void HandleResult(JpegLSError result) - { - Exception exception; - - switch (result) - { - case JpegLSError.None: - return; - - case JpegLSError.TooMuchEncodedData: - case JpegLSError.ParameterValueNotSupported: - case JpegLSError.InvalidEncodedData: - case JpegLSError.SourceBufferTooSmall: - case JpegLSError.BitDepthForTransformNotSupported: - case JpegLSError.ColorTransformNotSupported: - case JpegLSError.EncodingNotSupported: - case JpegLSError.UnknownJpegMarkerFound: - case JpegLSError.JpegMarkerStartByteNotFound: - case JpegLSError.StartOfImageMarkerNotFound: - case JpegLSError.StartOfFrameMarkerNotFound: - case JpegLSError.InvalidMarkerSegmentSize: - case JpegLSError.DuplicateStartOfImageMarker: - case JpegLSError.DuplicateStartOfFrameMarker: - case JpegLSError.DuplicateComponentIdInStartOfFrameSegment: - case JpegLSError.UnexpectedEndOfImageMarker: - case JpegLSError.InvalidJpeglsPresetParameterType: - case JpegLSError.JpeglsPresetExtendedParameterTypeNotSupported: - case JpegLSError.MissingEndOfSpiffDirectory: - case JpegLSError.InvalidParameterWidth: - case JpegLSError.InvalidParameterHeight: - case JpegLSError.InvalidParameterComponentCount: - case JpegLSError.InvalidParameterBitsPerSample: - case JpegLSError.InvalidParameterInterleaveMode: - case JpegLSError.UnexpectedFailure: - case JpegLSError.NotEnoughMemory: - exception = new InvalidDataException(GetErrorMessage(result)); - break; - - case JpegLSError.InvalidArgument: - case JpegLSError.DestinationBufferTooSmall: - case JpegLSError.InvalidArgumentWidth: - case JpegLSError.InvalidArgumentHeight: - case JpegLSError.InvalidArgumentComponentCount: - case JpegLSError.InvalidArgumentBitsPerSample: - case JpegLSError.InvalidArgumentInterleaveMode: - case JpegLSError.InvalidArgumentNearLossless: - case JpegLSError.InvalidArgumentPresetCodingParameters: - case JpegLSError.InvalidArgumentSpiffEntrySize: - case JpegLSError.InvalidArgumentColorTransformation: - exception = new ArgumentException(GetErrorMessage(result)); - break; - - case JpegLSError.InvalidOperation: - exception = new InvalidOperationException(GetErrorMessage(result)); - break; - - default: - Debug.Assert(false, "C# and native implementation mismatch"); - - // ReSharper disable once HeuristicUnreachableCode - exception = new InvalidOperationException(GetErrorMessage(result)); - break; - } - - var data = exception.Data; - - // ReSharper disable once PossibleNullReferenceException - data.Add(nameof(JpegLSError), result); - throw exception; - } - - private static string GetErrorMessage(JpegLSError result) - { - var message = Environment.Is64BitProcess ? - SafeNativeMethods.CharLSGetErrorMessageX64((int)result) : - SafeNativeMethods.CharLSGetErrorMessageX86((int)result); - return Marshal.PtrToStringAnsi(message); - } - } -} diff -Nru charls-2.1.0+dfsg/dotnet/src/JpegLSError.cs charls-2.2.0+dfsg/dotnet/src/JpegLSError.cs --- charls-2.1.0+dfsg/dotnet/src/JpegLSError.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/JpegLSError.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,207 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -namespace CharLS -{ - /// - /// Defines the result codes that the native CharLS implementation can return (see enumeration charls::jpegls_errc in public_types.h). - /// - public enum JpegLSError - { - /// - /// The operation completed without errors. - /// - None = 0, - - /// - /// This error is returned when one of the arguments is invalid and no specific reason is available. - /// - InvalidArgument = 1, - - /// - /// The parameter value not supported. - /// - ParameterValueNotSupported = 2, - - /// - /// The destination buffer is too small to hold all the output. - /// - DestinationBufferTooSmall = 3, - - /// - /// The source buffer is too small, more input data was expected. - /// - SourceBufferTooSmall = 4, - - /// - /// This error is returned when the encoded bit stream contains a general structural problem. - /// - InvalidEncodedData = 5, - - /// - /// Too much compressed data. The decoding process is ready but the input buffer still contains encoded data. - /// - TooMuchEncodedData = 6, - - /// - /// This error is returned when a method call is invalid for the current state. - /// - InvalidOperation = 7, - - /// - /// The bit depth for transformation is not supported. - /// - BitDepthForTransformNotSupported = 8, - - /// - /// The color transform is not supported. - /// - ColorTransformNotSupported = 9, - - /// - /// This error is returned when an encoded frame is found that is not encoded with the JPEG-LS algorithm. - /// - EncodingNotSupported = 10, - - /// - /// This error is returned when an unknown JPEG marker code is detected in the encoded bit stream. - /// - UnknownJpegMarkerFound = 11, - - /// - /// This error is returned when the algorithm expect a 0xFF code (indicates start of a JPEG marker) but none was found. - /// - JpegMarkerStartByteNotFound = 12, - - /// - /// This error is returned when the implementation could not allocate memory for its internal buffers. - /// - NotEnoughMemory = 13, - - /// - /// This error is returned when the implementation encountered a failure it didn't expect. No guarantees can be given for the state after this error. - /// - UnexpectedFailure = 14, - - /// - /// This error is returned when the first JPEG marker is not the SOI (Start Of Image) marker. - /// - StartOfImageMarkerNotFound = 15, - - /// - /// This error is returned when the SOF JPEG marker is not found before the SOS (Start of Scan) marker. - /// - StartOfFrameMarkerNotFound = 16, - - /// - /// This error is returned when the segment size of a marker segment is invalid. - /// - InvalidMarkerSegmentSize = 17, - - /// - /// This error is returned when the stream contains more then one SOI (Start Of Image) marker. - /// - DuplicateStartOfImageMarker = 18, - - /// - /// This error is returned when the stream contains more then one SOF (Start Of Frame) marker. - /// - DuplicateStartOfFrameMarker = 19, - - /// - /// This error is returned when the stream contains duplicate component identifiers in the SOF segment. - /// - DuplicateComponentIdInStartOfFrameSegment = 20, - - /// - /// This error is returned when the stream contains an unexpected EOI marker. - /// - UnexpectedEndOfImageMarker = 21, - - /// - /// This error is returned when the stream contains an invalid type parameter in the JPEG-LS segment. - /// - InvalidJpeglsPresetParameterType = 22, - - /// - /// This error is returned when the stream contains an unsupported type parameter in the JPEG-LS segment. - /// - JpeglsPresetExtendedParameterTypeNotSupported = 23, - - /// - /// This error is returned when the stream contains a SPIFF header but not an SPIFF end-of-directory entry. - /// - MissingEndOfSpiffDirectory = 24, - - /// - /// The argument for the width parameter is outside the range [1, 65535]. - /// - InvalidArgumentWidth = 100, - - /// - /// The argument for the height parameter is outside the range [1, 65535]. - /// - InvalidArgumentHeight = 101, - - /// - /// The argument for the component count parameter is outside the range [1, 255]. - /// - InvalidArgumentComponentCount = 102, - - /// - /// The argument for the bit per sample parameter is outside the range [2, 16]. - /// - InvalidArgumentBitsPerSample = 103, - - /// - /// The argument for the interleave mode is not (None, Sample, Line) or invalid in combination with component count. - /// - InvalidArgumentInterleaveMode = 104, - - /// - /// The argument for the near lossless parameter is outside the range [0, 255]. - /// - InvalidArgumentNearLossless = 105, - - /// - /// The argument for the JPEG-LS preset coding parameters is not valid, see ISO/IEC 14495-1, - /// C.2.4.1.1, Table C.1 for the ranges of valid values. - /// - InvalidArgumentPresetCodingParameters = 106, - - /// - /// The argument for the entry size parameter is outside the range [0, 65528]. - /// - InvalidArgumentSpiffEntrySize = 110, - - /// - /// The argument for the color component is not (None, Hp1, Hp2, Hp3) or invalid in combination with component count. - /// - InvalidArgumentColorTransformation = 111, - - /// - /// This error is returned when the width parameter is defined more then once in an incompatible way. - /// - InvalidParameterWidth = 200, - - /// - /// This error is returned when the height parameter is defined more then once in an incompatible way. - /// - InvalidParameterHeight = 201, - - /// - /// This error is returned when the stream contains a component count parameter outside the range [1,255] - /// - InvalidParameterComponentCount = 202, - - /// - /// This error is returned when the stream contains a bits per sample (sample precision) parameter outside the range [2,16] - /// - InvalidParameterBitsPerSample = 203, - - /// - /// This error is returned when the stream contains an interleave mode (ILV) parameter outside the range [0, 2] - /// - InvalidParameterInterleaveMode = 204 - } -} diff -Nru charls-2.1.0+dfsg/dotnet/src/JpegLSInterleaveMode.cs charls-2.2.0+dfsg/dotnet/src/JpegLSInterleaveMode.cs --- charls-2.1.0+dfsg/dotnet/src/JpegLSInterleaveMode.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/JpegLSInterleaveMode.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -namespace CharLS -{ - /// - /// Defines the interleave mode for multi-component (color) pixel data. - /// - public enum JpegLSInterleaveMode - { - /// - /// The encoded pixel data is not interleaved but stored as component for component: RRRGGGBBB. - /// Also default option for pixel data with only 1 component. - /// - None = 0, - - /// - /// The interleave mode is by line. A full line of each - /// component is encoded before moving to the next line. - /// - Line = 1, - - /// - /// The data is stored by sample (pixel). For color image this is the format like RGBRGBRGB. - /// - Sample = 2 - } -} diff -Nru charls-2.1.0+dfsg/dotnet/src/JpegLSMetadataInfo.cs charls-2.2.0+dfsg/dotnet/src/JpegLSMetadataInfo.cs --- charls-2.1.0+dfsg/dotnet/src/JpegLSMetadataInfo.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/JpegLSMetadataInfo.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,208 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System; -using System.Globalization; -using System.IO; - -namespace CharLS -{ - /// - /// - /// Contains meta information about a compressed JPEG-LS stream or info how to compress. - /// - /// - /// This 'info' class is used for 2 purposes: - /// 1) The JPEG-LS algorithm needs information which type of bitmap is stored in an array bytes. - /// This information is needed to correctly compress (encode) the pixels to a JPEG-LS byte stream. - /// 2) Information which kind of bitmap is stored in a JPEG-LS byte stream is stored in the JPEG-LS byte stream. - /// This information can be extracted without decompressing (decoding) the byte stream. This information is often - /// required to prepare output buffers to received the decompressed byte stream. - /// - public sealed class JpegLSMetadataInfo : IEquatable - { - /// - /// Initializes a new instance of the class. - /// - /// - /// Initializes a minimal instance with: - /// Width = 1 - /// Height = 1 - /// BitsPerComponent = 2 - /// ComponentCount = 1. - /// - public JpegLSMetadataInfo() - : this(1, 1, 2, 1) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The width of the bitmap. - /// The height of the bitmap. - /// The number of bits per component. Typical 8 for color and 2 to 16 for monochrome bitmaps. - /// The component count. Typical 1 for monochrome images and 3 for color images. - public JpegLSMetadataInfo(int width, int height, int bitsPerComponent, int componentCount) - { - Width = width; - Height = height; - BitsPerComponent = bitsPerComponent; - ComponentCount = componentCount; - } - - /// - /// Initializes a new instance of the class. - /// - /// The parameters. - /// Thrown when one the values are out of bounds. - internal JpegLSMetadataInfo(ref JlsParameters parameters) - { - if (parameters.BitsPerSample < 2) - throw new InvalidDataException("parameters.BitsPerSample < 2"); - if (parameters.Components < 1) - throw new InvalidDataException("parameters.Components < 1"); - - Width = parameters.Width; - Height = parameters.Height; - ComponentCount = parameters.Components; - BitsPerComponent = parameters.BitsPerSample; - AllowedLossyError = parameters.AllowedLossyError; - InterleaveMode = parameters.InterleaveMode; - } - - /// - /// Gets or sets the width of the image in pixels. - /// - /// The width. - public int Width { get; set; } - - /// - /// Gets or sets the height of the image in pixels. - /// - /// The height. - public int Height { get; set; } - - /// - /// Gets or sets the bits per component. - /// Typical 8 for a color component and between 2 and 16 for a monochrome component. - /// - /// The bits per sample. - public int BitsPerComponent { get; set; } - - /// - /// Gets or sets the bytes per line. - /// - /// The bytes per line. - public int BytesPerLine { get; set; } - - /// - /// Gets or sets the component count per pixel. - /// Typical 1 for monochrome images and 3 for color images. - /// - /// The component count. - public int ComponentCount { get; set; } - - /// - /// Gets or sets the allowed error value for non-lossless compression. - /// - /// The allowed error value. - public int AllowedLossyError { get; set; } - - /// - /// Gets or sets the interleave mode. - /// - /// The interleave mode. - public JpegLSInterleaveMode InterleaveMode { get; set; } - - /// - /// Gets or sets a value indicating whether CharLS will perform a RGB to BGR conversion. - /// - /// - /// true if CharLS should perform a conversion; otherwise, false. - /// - public bool OutputBgr { get; set; } - - /// - /// Gets the size of an byte array needed to hold the uncompressed pixels. - /// - /// The size of byte array. - public int UncompressedSize => Width * Height * ComponentCount * ((BitsPerComponent + 7) / 8); - - /// - /// Returns a that represents this instance. - /// - /// - /// A that represents this instance. - /// - public override string ToString() - { - return string.Format(CultureInfo.InvariantCulture, "Width = {0}, Height = {1}, BitsPerSample = {2}, ComponentCount = {3}, AllowedLossyError = {4}", - Width, Height, BitsPerComponent, ComponentCount, AllowedLossyError); - } - - /// - /// Determines whether the specified is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public override bool Equals(object obj) - { - return Equals(obj as JpegLSMetadataInfo); - } - - /// - /// - /// Determines whether the specified is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public bool Equals(JpegLSMetadataInfo other) - { - if (other == null) - return false; - - return Width == other.Width && - Height == other.Height && - ComponentCount == other.ComponentCount && - BitsPerComponent == other.BitsPerComponent && - AllowedLossyError == other.AllowedLossyError && - InterleaveMode == other.InterleaveMode && - OutputBgr == other.OutputBgr; - } - - /// - /// Serves as a hash function for a particular type. - /// - /// - /// A hash code for the current . - /// - public override int GetHashCode() - { - unchecked - { - var result = Width; - result = (result * 397) ^ Height; - result = (result * 397) ^ BitsPerComponent; - result = (result * 397) ^ ComponentCount; - return result; - } - } - - internal void CopyTo(ref JlsParameters parameters) - { - parameters.Width = Width; - parameters.Height = Height; - parameters.BitsPerSample = BitsPerComponent; - parameters.BytesPerLine = BytesPerLine; - parameters.Components = ComponentCount; - parameters.AllowedLossyError = AllowedLossyError; - parameters.InterleaveMode = InterleaveMode; - parameters.OutputBgr = OutputBgr; - } - } -} diff -Nru charls-2.1.0+dfsg/dotnet/src/JpegLSPresetCodingParameters.cs charls-2.2.0+dfsg/dotnet/src/JpegLSPresetCodingParameters.cs --- charls-2.1.0+dfsg/dotnet/src/JpegLSPresetCodingParameters.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/JpegLSPresetCodingParameters.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System.Runtime.InteropServices; - -namespace CharLS -{ - [StructLayout(LayoutKind.Sequential, Pack = 8)] - internal struct JpegLSPresetCodingParameters - { - internal int MaximumSampleValue; - internal int Threshold1; - internal int Threshold2; - internal int Threshold3; - internal int ResetValue; - } -} diff -Nru charls-2.1.0+dfsg/dotnet/src/SafeNativeMethods.cs charls-2.2.0+dfsg/dotnet/src/SafeNativeMethods.cs --- charls-2.1.0+dfsg/dotnet/src/SafeNativeMethods.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/SafeNativeMethods.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System; -using System.Runtime.InteropServices; - -namespace CharLS -{ - internal static class SafeNativeMethods - { - private const string NativeX86Library = "charls-2-x86.dll"; - private const string NativeX64Library = "charls-2-x64.dll"; - - [DllImport(NativeX86Library, SetLastError = false, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, EntryPoint = "JpegLsReadHeader")] - internal static extern JpegLSError JpegLsReadHeaderX86([In] byte[] compressedSource, int compressedLength, out JlsParameters info, IntPtr reserved); - - [DllImport(NativeX64Library, SetLastError = false, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, EntryPoint = "JpegLsReadHeader")] - internal static extern JpegLSError JpegLsReadHeaderX64([In] byte[] compressedSource, long compressedLength, out JlsParameters info, IntPtr reserved); - - [DllImport(NativeX86Library, SetLastError = false, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, EntryPoint = "JpegLsDecode")] - internal static extern JpegLSError JpegLsDecodeX86( - [Out] byte[] uncompressedData, - int uncompressedLength, - [In] byte[] compressedData, - int compressedLength, - IntPtr info, - IntPtr reserved); - - [DllImport(NativeX64Library, SetLastError = false, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, EntryPoint = "JpegLsDecode")] - internal static extern JpegLSError JpegLsDecodeX64( - [Out] byte[] uncompressedData, - long uncompressedLength, - [In] byte[] compressedData, - long compressedLength, - IntPtr info, - IntPtr reserved); - - [DllImport(NativeX86Library, SetLastError = false, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, EntryPoint = "JpegLsEncode")] - internal static extern JpegLSError JpegLsEncodeX86( - [Out] byte[] compressedData, - int compressedLength, - out int byteCountWritten, - [In] byte[] uncompressedData, - int uncompressedLength, - [In] ref JlsParameters info, - IntPtr reserved); - - [DllImport(NativeX64Library, SetLastError = false, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, EntryPoint = "JpegLsEncode")] - internal static extern JpegLSError JpegLsEncodeX64( - [Out] byte[] compressedData, - long compressedLength, - out long byteCountWritten, - [In] byte[] uncompressedData, - long uncompressedLength, - [In] ref JlsParameters info, - IntPtr reserved); - - [DllImport(NativeX86Library, SetLastError = false, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, EntryPoint = "charls_get_error_message")] - internal static extern IntPtr CharLSGetErrorMessageX86(int errorValue); - - [DllImport(NativeX64Library, SetLastError = false, CharSet = CharSet.Ansi, BestFitMapping = false, ThrowOnUnmappableChar = true, EntryPoint = "charls_get_error_message")] - internal static extern IntPtr CharLSGetErrorMessageX64(int errorValue); - } -} diff -Nru charls-2.1.0+dfsg/dotnet/src/stylecop.json charls-2.2.0+dfsg/dotnet/src/stylecop.json --- charls-2.1.0+dfsg/dotnet/src/stylecop.json 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/src/stylecop.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", - "settings": { - "documentationRules": { - "companyName": "CharLS Team", - "copyrightText": "Copyright (c) Team CharLS.\nSPDX-License-Identifier: BSD-3-Clause", - "xmlHeader": false, - "documentInterfaces": true, - "documentInternalElements": false - }, - "orderingRules": { - "usingDirectivesPlacement": "outsideNamespace" - } - } -} diff -Nru charls-2.1.0+dfsg/dotnet/test/CharLSNetTest.csproj charls-2.2.0+dfsg/dotnet/test/CharLSNetTest.csproj --- charls-2.1.0+dfsg/dotnet/test/CharLSNetTest.csproj 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/test/CharLSNetTest.csproj 1970-01-01 00:00:00.000000000 +0000 @@ -1,115 +0,0 @@ - - - - - net472 - - false - - x86;x64 - - Debug;Release;Checked - CharLSNetTest.ruleset - - - - - all - runtime; build; native; contentfiles; analyzers - - - - - - - - - DataFiles\T16E0.JLS - PreserveNewest - - - DataFiles\T16E3.JLS - PreserveNewest - - - DataFiles\T16E3.pgm - PreserveNewest - - - DataFiles\T8C0E0.JLS - PreserveNewest - - - DataFiles\T8C0E3.JLS - PreserveNewest - - - DataFiles\T8C1E0.JLS - PreserveNewest - - - DataFiles\T8C1E3.JLS - PreserveNewest - - - DataFiles\T8C2E0.JLS - PreserveNewest - - - DataFiles\T8C2E3.JLS - PreserveNewest - - - DataFiles\T8NDE0.JLS - PreserveNewest - - - DataFiles\T8NDE3.JLS - PreserveNewest - - - DataFiles\T8SSE0.JLS - PreserveNewest - - - DataFiles\T8SSE3.JLS - PreserveNewest - - - DataFiles\TEST16.PGM - PreserveNewest - - - DataFiles\TEST8.PPM - PreserveNewest - - - DataFiles\TEST8B.PGM - PreserveNewest - - - DataFiles\TEST8BS2.PGM - PreserveNewest - - - DataFiles\TEST8G.PGM - PreserveNewest - - - DataFiles\TEST8GR4.PGM - PreserveNewest - - - DataFiles\TEST8R.PGM - PreserveNewest - - - - - - {6f9955c1-a285-4de0-b3e6-f69eb08bbd7b} - CharLSNet - - - - diff -Nru charls-2.1.0+dfsg/dotnet/test/CharLSNetTest.ruleset charls-2.2.0+dfsg/dotnet/test/CharLSNetTest.ruleset --- charls-2.1.0+dfsg/dotnet/test/CharLSNetTest.ruleset 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/test/CharLSNetTest.ruleset 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff -Nru charls-2.1.0+dfsg/dotnet/test/JpegLSCodecTest.cs charls-2.2.0+dfsg/dotnet/test/JpegLSCodecTest.cs --- charls-2.1.0+dfsg/dotnet/test/JpegLSCodecTest.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/test/JpegLSCodecTest.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,202 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using NUnit.Framework; - -namespace CharLS.Test -{ - [TestFixture] - public class JpegLSCodecTest - { - [Test] - public void GetMetadataInfoFromLosslessEncodedColorImage() - { - var source = ReadAllBytes("T8C0E0.JLS"); - var info = JpegLSCodec.GetMetadataInfo(source); - var expected = new JpegLSMetadataInfo { Height = 256, Width = 256, BitsPerComponent = 8, ComponentCount = 3 }; - - Assert.AreEqual(expected, info); - } - - [Test] - public void GetMetadataInfoFromNearLosslessEncodedColorImage() - { - var source = ReadAllBytes("T8C0E3.JLS"); - var info = JpegLSCodec.GetMetadataInfo(source); - var expected = new JpegLSMetadataInfo { Height = 256, Width = 256, BitsPerComponent = 8, ComponentCount = 3, AllowedLossyError = 3 }; - - Assert.AreEqual(expected, info); - - info = JpegLSCodec.GetMetadataInfo(source, source.Length); - Assert.AreEqual(expected, info); - } - - [Test] - public void Decompress() - { - var source = ReadAllBytes("T8C0E0.JLS"); - var expected = ReadAllBytes("TEST8.PPM", 15); - var uncompressed = JpegLSCodec.Decompress(source); - - var info = JpegLSCodec.GetMetadataInfo(source); - if (info.InterleaveMode == JpegLSInterleaveMode.None && info.ComponentCount == 3) - { - expected = TripletToPlanar(expected, info.Width, info.Height); - } - - Assert.AreEqual(expected, uncompressed); - } - - [Test] - public void Compress() - { - var info = new JpegLSMetadataInfo(256, 256, 8, 3); - - var uncompressedOriginal = ReadAllBytes("TEST8.PPM", 15); - uncompressedOriginal = TripletToPlanar(uncompressedOriginal, info.Width, info.Height); - - var compressedSegment = JpegLSCodec.Compress(info, uncompressedOriginal); - var compressed = new byte[compressedSegment.Count]; - Array.Copy(compressedSegment.Array, compressed, compressed.Length); - - var compressedInfo = JpegLSCodec.GetMetadataInfo(compressed); - Assert.AreEqual(info, compressedInfo); - - var uncompressed = JpegLSCodec.Decompress(compressed); - Assert.AreEqual(info.UncompressedSize, uncompressed.Length); - Assert.AreEqual(uncompressedOriginal, uncompressed); - } - - - [Test] - public void CompressPartOfInputBuffer() - { - var info = new JpegLSMetadataInfo(256, 256, 8, 3); - - var uncompressedOriginal = ReadAllBytes("TEST8.PPM", 15); - uncompressedOriginal = TripletToPlanar(uncompressedOriginal, info.Width, info.Height); - - var compressedSegment = JpegLSCodec.Compress(info, uncompressedOriginal, uncompressedOriginal.Length); - var compressed = new byte[compressedSegment.Count]; - Array.Copy(compressedSegment.Array, compressed, compressed.Length); - - var compressedInfo = JpegLSCodec.GetMetadataInfo(compressed); - Assert.AreEqual(info, compressedInfo); - - var uncompressed = JpegLSCodec.Decompress(compressed); - Assert.AreEqual(info.UncompressedSize, uncompressed.Length); - Assert.AreEqual(uncompressedOriginal, uncompressed); - } - - [Test] - public void CompressOneByOneColor() - { - var info = new JpegLSMetadataInfo(1, 1, 8, 3); - var uncompressedOriginal = new byte[] { 77, 33, 255 }; - - var compressedSegment = JpegLSCodec.Compress(info, uncompressedOriginal); - var compressed = new byte[compressedSegment.Count]; - Array.Copy(compressedSegment.Array, compressed, compressed.Length); - - var uncompressed = JpegLSCodec.Decompress(compressed); - Assert.AreEqual(info.UncompressedSize, uncompressed.Length); - Assert.AreEqual(uncompressedOriginal, uncompressed); - } - - [Test] - public void Compress2BitMonochrome() - { - var info = new JpegLSMetadataInfo(1, 1, 2, 1); - var uncompressedOriginal = new byte[] { 1 }; - - var compressedSegment = JpegLSCodec.Compress(info, uncompressedOriginal); - var compressed = new byte[compressedSegment.Count]; - Array.Copy(compressedSegment.Array, compressed, compressed.Length); - - var uncompressed = JpegLSCodec.Decompress(compressed); - Assert.AreEqual(info.UncompressedSize, uncompressed.Length); - Assert.AreEqual(uncompressedOriginal, uncompressed); - } - - [Test] - public void DecompressBitStreamWithNoMarkerStart() - { - var compressed = new byte[] { 0x33, 0x33 }; - - var exception = Assert.Throws(() => JpegLSCodec.Decompress(compressed)); - Assert.AreEqual(JpegLSError.JpegMarkerStartByteNotFound, exception.Data["JpegLSError"]); - } - - [Test] - public void DecodeBitStreamWithUnsupportedEncoding() - { - var compressed = new byte[] - { - 0xFF, 0xD8, // Start Of Image (JPEG_SOI) - 0xFF, 0xC3, // Start Of Frame (lossless, Huffman) (JPEG_SOF_3) - 0x00, 0x00 // Length of data of the marker - }; - var exception = Assert.Throws(() => JpegLSCodec.Decompress(compressed)); - Assert.AreEqual(JpegLSError.EncodingNotSupported, exception.Data["JpegLSError"]); - } - - [Test] - public void TestDecodeBitStreamWithUnknownJpegMarker() - { - var compressed = new byte[] - { - 0xFF, 0xD8, // Start Of Image (JPEG_SOI) - 0xFF, 0x01, // Undefined marker - 0x00, 0x00 // Length of data of the marker - }; - - var exception = Assert.Throws(() => JpegLSCodec.Decompress(compressed)); - Assert.AreEqual(JpegLSError.UnknownJpegMarkerFound, exception.Data["JpegLSError"]); - } - - private static byte[] TripletToPlanar(IList buffer, int width, int height) - { - var result = new byte[buffer.Count]; - - int bytePlaneCount = width * height; - for (int i = 0; i < bytePlaneCount; i++) - { - result[i] = buffer[i * 3]; - result[i + bytePlaneCount] = buffer[(i * 3) + 1]; - result[i + (2 * bytePlaneCount)] = buffer[(i * 3) + 2]; - } - - return result; - } - - private static byte[] ReadAllBytes(string path, int bytesToSkip = 0) - { - var fullPath = DataFileDirectory + path; - - if (bytesToSkip == 0) - return File.ReadAllBytes(fullPath); - - using (var stream = File.OpenRead(fullPath)) - { - var result = new byte[new FileInfo(fullPath).Length - bytesToSkip]; - - stream.Seek(bytesToSkip, SeekOrigin.Begin); - stream.Read(result, 0, result.Length); - return result; - } - } - - private static string DataFileDirectory - { - get - { - var assemblyLocation = new Uri(Assembly.GetExecutingAssembly().CodeBase); - return Path.GetDirectoryName(assemblyLocation.LocalPath) + @"\DataFiles\"; - } - } - } -} diff -Nru charls-2.1.0+dfsg/dotnet/test/JpegLSMetadataInfoTest.cs charls-2.2.0+dfsg/dotnet/test/JpegLSMetadataInfoTest.cs --- charls-2.1.0+dfsg/dotnet/test/JpegLSMetadataInfoTest.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/test/JpegLSMetadataInfoTest.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using NUnit.Framework; - -namespace CharLS.Test -{ - [TestFixture] - public class JpegLSMetadataInfoTest - { - [Test] - public void ConstructDefault() - { - var info = new JpegLSMetadataInfo(); - - Assert.AreEqual(1, info.Width); - Assert.AreEqual(1, info.Height); - Assert.AreEqual(1, info.ComponentCount); - Assert.AreEqual(2, info.BitsPerComponent); - Assert.AreEqual(0, info.AllowedLossyError); - Assert.AreEqual(0, info.BytesPerLine); - Assert.IsFalse(info.OutputBgr); - Assert.AreEqual(JpegLSInterleaveMode.None, info.InterleaveMode); - } - - [Test] - public void ConstructAndModify() - { - var info = new JpegLSMetadataInfo - { - BytesPerLine = 2, - InterleaveMode = JpegLSInterleaveMode.Sample, - OutputBgr = true - }; - - Assert.AreEqual(2, info.BytesPerLine); - Assert.AreEqual(JpegLSInterleaveMode.Sample, info.InterleaveMode); - Assert.IsTrue(info.OutputBgr); - - info.InterleaveMode = JpegLSInterleaveMode.Line; - Assert.AreEqual(JpegLSInterleaveMode.Line, info.InterleaveMode); - } - - [Test] - public void EquatableSameObjects() - { - var a = new JpegLSMetadataInfo(); - var b = new JpegLSMetadataInfo(); - - Assert.IsTrue(a.Equals(b)); - Assert.IsTrue(a.Equals((object)b)); - Assert.AreEqual(a, b); - Assert.AreEqual(b, a); - Assert.AreEqual(a.GetHashCode(), b.GetHashCode()); - } - - [Test] - public void EquatableDifferentObjects() - { - var a = new JpegLSMetadataInfo(); - var b = new JpegLSMetadataInfo { Height = 2 }; - - Assert.IsFalse(a.Equals(b)); - Assert.IsFalse(a.Equals((object)b)); - } - - [Test] - public void EquatableWithNull() - { - var a = new JpegLSMetadataInfo(); - - Assert.IsFalse(a.Equals(null)); - Assert.IsFalse(a.Equals((object)null)); - } - } -} diff -Nru charls-2.1.0+dfsg/dotnet/test/Properties/AssemblyInfo.cs charls-2.2.0+dfsg/dotnet/test/Properties/AssemblyInfo.cs --- charls-2.1.0+dfsg/dotnet/test/Properties/AssemblyInfo.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/dotnet/test/Properties/AssemblyInfo.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyDescription("")] -[assembly: AssemblyCopyright("Copyright (c) Team CharLS")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] diff -Nru charls-2.1.0+dfsg/.editorconfig charls-2.2.0+dfsg/.editorconfig --- charls-2.1.0+dfsg/.editorconfig 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/.editorconfig 2021-01-10 18:07:25.000000000 +0000 @@ -9,49 +9,16 @@ indent_style = space trim_trailing_whitespace = true -[*.{h,cpp}] +[*.md] +trim_trailing_whitespace = false +insert_final_newline = true + +[*.{h,cpp,c}] indent_size = 4 insert_final_newline = true [CMakeLists.txt] indent_size = 2 -[*.cs] -indent_size = 4 - -# New line preferences -csharp_new_line_before_catch = true -csharp_new_line_before_else = true -csharp_new_line_before_open_brace = control_blocks, types, methods, object_collection, accessors, properties - -# Indentation preferences -csharp_indent_case_contents = true -csharp_indent_switch_labels = true - -# Space preferences -csharp_space_after_cast = false -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_after_keywords_in_control_flow_statements = true -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_parameter_list_parentheses = false -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_declaration_parameter_list_parentheses = false - -csharp_preserve_single_line_blocks = true - -csharp_style_expression_bodied_constructors = false:suggestion -csharp_style_expression_bodied_methods = false:suggestion -csharp_style_expression_bodied_properties = true:suggestion -csharp_style_inlined_variable_declaration = false:suggestion -csharp_style_var_for_built_in_types = true:suggestion -csharp_style_var_when_type_is_apparent = true:suggestion - -dotnet_sort_system_directives_first = true - -dotnet_style_predefined_type_for_member_access = true:suggestion -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -dotnet_style_qualification_for_field = false:suggestion -dotnet_style_qualification_for_method = false:suggestion -dotnet_style_qualification_for_property = false:suggestion +[*.manifest] +indent_size = 2 \ No newline at end of file diff -Nru charls-2.1.0+dfsg/fuzztest/CMakeLists.txt charls-2.2.0+dfsg/fuzztest/CMakeLists.txt --- charls-2.1.0+dfsg/fuzztest/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/fuzztest/CMakeLists.txt 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,15 @@ +# Copyright (c) Team CharLS. +# SPDX-License-Identifier: BSD-3-Clause + +add_executable(fuzztest "") + +target_sources(fuzztest PRIVATE main.cpp) + +set_target_properties(fuzztest PROPERTIES CXX_VISIBILITY_PRESET hidden) + +target_link_libraries(fuzztest PRIVATE charls) + +if(MSVC) + # POSIX functions are used, required for AFL. + target_compile_definitions(fuzztest PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() \ No newline at end of file diff -Nru charls-2.1.0+dfsg/fuzztest/main.cpp charls-2.2.0+dfsg/fuzztest/main.cpp --- charls-2.1.0+dfsg/fuzztest/main.cpp 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/fuzztest/main.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,103 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#include + +#ifdef _MSC_VER + +#include + +#else +#include + +#define _write write +#define _read read +#define _open open + +#endif + +#include + +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif + +#ifndef __AFL_INIT +#define __AFL_INIT() // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) +#endif + +#ifndef __AFL_LOOP +#define __AFL_LOOP(a) true // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp) +#endif + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + +namespace { + +auto generate_once() +{ + const std::vector source(3); + return charls::jpegls_encoder::encode(source, {1, 1, 8, 3}); +} + +} // namespace + + +int main(const int argc, const char* const argv[]) // NOLINT(bugprone-exception-escape) +{ + int fd{}; + if (argc == 2) + { + if (argv[1][0] == '\0') + { + try + { + // Write some small-ish JPEG-LS file to stdout + auto encoded_data{generate_once()}; + const int result{ + static_cast(_write(1, encoded_data.data(), static_cast(encoded_data.size())))}; + return result != -1 && result == static_cast(encoded_data.size()) ? EXIT_SUCCESS : EXIT_FAILURE; + } + catch (const std::exception& error) + { + std::cerr << "Failed to create the once: " << error.what() << '\n'; + } + return EXIT_FAILURE; + } + + fd = _open(argv[1], O_RDONLY); + if (fd < 0) + { + std::cerr << "Failed to open: " << argv[1] << strerror(errno) << '\n'; + return EXIT_FAILURE; + } + } + + __AFL_INIT(); + + while (__AFL_LOOP(100)) + { + std::vector source(1024 * 1024); + const size_t input_length = _read(fd, source.data(), static_cast(source.size())); + source.resize(input_length); + + try + { + std::vector destination; + charls::jpegls_decoder::decode(source, destination); + } + catch (const charls::jpegls_error&) + { + } + } + + return EXIT_SUCCESS; +} diff -Nru charls-2.1.0+dfsg/include/charls/annotations.h charls-2.2.0+dfsg/include/charls/annotations.h --- charls-2.1.0+dfsg/include/charls/annotations.h 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/include/charls/annotations.h 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,49 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#ifdef _MSC_VER + +#include + +// Note: these macro's are not prefixed with CHARLS_, as these macro's are used for function parameters. +// and long macro's would make the code harder to read. +#define IN_ _In_ +#define IN_OPT_ _In_opt_ +#define IN_Z_ _In_z_ +#define IN_READS_BYTES_(size) _In_reads_bytes_(size) +#define OUT_ _Out_ +#define OUT_OPT_ _Out_opt_ +#define OUT_WRITES_BYTES_(size) _Out_writes_bytes_(size) +#define OUT_WRITES_Z_(size_in_bytes) _Out_writes_z_(size_in_bytes) +#define RETURN_TYPE_SUCCESS_(expr) _Return_type_success_(expr) +#define CHARLS_CHECK_RETURN _Check_return_ +#define CHARLS_RET_MAY_BE_NULL _Ret_maybenull_ + +#else + +#define IN_ +#define IN_OPT_ +#define IN_Z_ +#define IN_READS_BYTES_(size) +#define OUT_ +#define OUT_OPT_ +#define OUT_WRITES_BYTES_(size) +#define OUT_WRITES_Z_(size_in_bytes) +#define RETURN_TYPE_SUCCESS_(expr) +#define CHARLS_CHECK_RETURN +#define CHARLS_RET_MAY_BE_NULL + +#endif + + +#if defined(__clang__) || defined(__GNUC__) + +#define CHARLS_ATTRIBUTE(a) __attribute__(a) + +#else + +#define CHARLS_ATTRIBUTE(a) + +#endif diff -Nru charls-2.1.0+dfsg/include/charls/api_abi.h charls-2.2.0+dfsg/include/charls/api_abi.h --- charls-2.1.0+dfsg/include/charls/api_abi.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/include/charls/api_abi.h 2021-01-10 18:07:25.000000000 +0000 @@ -23,7 +23,7 @@ #endif // Ensure that the exported functions of a 32 bit Windows DLL use the __stdcall convention. -#if defined(_M_IX86) +#if defined(_M_IX86) || defined(__MINGW32__) #define CHARLS_API_CALLING_CONVENTION __stdcall #else #define CHARLS_API_CALLING_CONVENTION @@ -44,27 +44,28 @@ #endif #if defined(__cplusplus) && __cplusplus >= 201703 - #define CHARLS_NO_DISCARD [[nodiscard]] - #else - #define CHARLS_NO_DISCARD - #endif #ifdef __cplusplus #define CHARLS_FINAL final #define CHARLS_NOEXCEPT noexcept +#define CHARLS_C_VOID -#ifdef CHARLS_NO_DEPRECATED_WARNINGS +#ifdef CHARLS_NO_DEPRECATED_WARNING #define CHARLS_DEPRECATED #else -#define CHARLS_DEPRECATED [[deprecated]] +#define CHARLS_DEPRECATED \ + [[deprecated("Functionality will be removed in the next major version, use CHARLS_NO_DEPRECATED_WARNING to suppress " \ + "this deprecation.")]] #endif #else #define CHARLS_FINAL #define CHARLS_NOEXCEPT +#define CHARLS_DEPRECATED +#define CHARLS_C_VOID void #endif diff -Nru charls-2.1.0+dfsg/include/charls/charls.h charls-2.2.0+dfsg/include/charls/charls.h --- charls-2.1.0+dfsg/include/charls/charls.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/include/charls/charls.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,867 +3,32 @@ #pragma once -#include "jpegls_error.h" -#include "version.h" - -#ifdef __cplusplus - -#include -#include - -#else - -#include -#include - -#endif - - -#ifdef __cplusplus - -struct charls_jpegls_decoder; -struct charls_jpegls_encoder; - -extern "C" { - -#else - -typedef struct charls_jpegls_decoder charls_jpegls_decoder; -typedef struct charls_jpegls_encoder charls_jpegls_encoder; - -#endif - -// The following functions define the public C API of the CharLS library. -// The C++ API is defined after the C API. - -/// -/// Creates a JPEG-LS decoder instance, when finished with the instance destroy it with the function charls_jpegls_decoder_destroy. -/// -/// A reference to a new created decoder instance, or a null pointer when the creation fails. -CHARLS_API_IMPORT_EXPORT charls_jpegls_decoder* CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_create(void) CHARLS_NOEXCEPT; - -/// -/// Destroys a JPEG-LS decoder instance created with charls_jpegls_decoder_create and releases all internal resources attached to it. -/// -/// Instance to destroy. If a null pointer is passed as argument, no action occurs. -CHARLS_API_IMPORT_EXPORT void CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_destroy(const charls_jpegls_decoder* decoder) CHARLS_NOEXCEPT; - -/// -/// Set the reference to a source buffer that contains the encoded JPEG-LS byte stream data. -/// This buffer needs to remain valid until the buffer is fully decoded. -/// -/// Reference to the decoder instance. -/// Reference to the start of the source buffer. -/// Size of the source buffer in bytes. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_set_source_buffer(charls_jpegls_decoder* decoder, const void* source_buffer, size_t source_size_bytes) CHARLS_NOEXCEPT; - -/// -/// Tries to read the SPIFF header from the source buffer. -/// If a SPIFF header exists its content will be put into the spiff_header parameter and header_found will be set to 1. -/// Call charls_jpegls_decoder_read_header to read the normal JPEG header afterwards. -/// -/// Reference to the decoder instance. -/// Output argument, will hold the SPIFF header when one could be found. -/// Output argument, will hold 1 if a SPIFF header could be found, otherwise 0. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_read_spiff_header(charls_jpegls_decoder* decoder, charls_spiff_header* spiff_header, int32_t* header_found) CHARLS_NOEXCEPT; - -/// -/// Reads the JPEG-LS header from the JPEG byte stream. After this function is called frame info can be retrieved. -/// -/// Reference to the decoder instance. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_read_header(charls_jpegls_decoder* decoder) CHARLS_NOEXCEPT; - -/// -/// Returns information about the frame stored in the JPEG-LS byte stream. -/// -/// -/// Function should be called after calling the function charls_jpegls_decoder_read_header. -/// -/// Reference to the decoder instance. -/// Output argument, will hold the frame info when the function returns. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_get_frame_info(const charls_jpegls_decoder* decoder, charls_frame_info* frame_info) CHARLS_NOEXCEPT; - -/// -/// Returns the NEAR parameter that was used to encode the scan. A value of 0 means lossless. -/// -/// -/// Function should be called after calling the function charls_jpegls_decoder_read_header. -/// -/// Reference to the decoder instance. -/// The component index for which the NEAR parameter should be retrieved. -/// Reference that will hold the value of the NEAR parameter. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_get_near_lossless(const charls_jpegls_decoder* decoder, int32_t component, int32_t* near_lossless) CHARLS_NOEXCEPT; - -/// -/// Returns the interleave mode that was used to encode the scan(s). -/// -/// -/// Function should be called after calling the function charls_jpegls_decoder_read_header. -/// -/// Reference to the decoder instance. -/// Reference that will hold the value of the interleave mode. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_get_interleave_mode(const charls_jpegls_decoder* decoder, charls_interleave_mode* interleave_mode) CHARLS_NOEXCEPT; - -/// -/// Returns the preset coding parameters used to encode the first scan. -/// -/// -/// Function should be called after calling the function charls_jpegls_decoder_read_header. -/// -/// Reference to the decoder instance. -/// Reserved. Should be set to 0. -/// Reference that will hold the values preset coding parameters. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_get_preset_coding_parameters(const charls_jpegls_decoder* decoder, int32_t reserved, charls_jpegls_pc_parameters* preset_coding_parameters) CHARLS_NOEXCEPT; - -/// -/// Returns the size required for the destination buffer in bytes to hold the decoded pixel data. -/// -/// -/// Function should be called after calling the function charls_jpegls_decoder_read_header. -/// -/// Reference to the decoder instance. -/// Number of bytes to the next line in the buffer, when zero, decoder will compute it. -/// Output argument, will hold the required size when the function returns. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_get_destination_size(const charls_jpegls_decoder* decoder, uint32_t stride, size_t* destination_size_bytes) CHARLS_NOEXCEPT; - -/// -/// Will decode the JPEG-LS byte stream from the source buffer into the destination buffer. -/// -/// -/// Function should be called after calling the function charls_jpegls_decoder_read_header. -/// -/// Reference to the decoder instance. -/// Byte array that holds the encoded bytes when the function returns. -/// Length of the array in bytes. If the array is too small the function will return an error. -/// Number of bytes to the next line in the buffer, when zero, decoder will compute it. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_decode_to_buffer(const charls_jpegls_decoder* decoder, void* destination_buffer, size_t destination_size_bytes, uint32_t stride) CHARLS_NOEXCEPT; - - -/// -/// Creates a JPEG-LS encoder instance, when finished with the instance destroy it with the function charls_jpegls_encoder_destroy. -/// -/// A reference to a new created encoder instance, or a null pointer when the creation fails. -CHARLS_API_IMPORT_EXPORT charls_jpegls_encoder* CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_create(void) CHARLS_NOEXCEPT; - -/// -/// Destroys a JPEG-LS encoder instance created with charls_jpegls_encoder_create and releases all internal resources attached to it. -/// -/// Instance to destroy. If a null pointer is passed as argument, no action occurs. -CHARLS_API_IMPORT_EXPORT void CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_destroy(const charls_jpegls_encoder* encoder) CHARLS_NOEXCEPT; - -/// -/// Configures the frame that needs to be encoded. This information will be written to the Start of Frame segment. -/// -/// Reference to the encoder instance. -/// Information about the frame that needs to be encoded. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_frame_info(charls_jpegls_encoder* encoder, const charls_frame_info* frame_info) CHARLS_NOEXCEPT; - -/// -/// Configures the NEAR parameter the encoder should use. A value of 0 means lossless, 0 is also the default. -/// -/// Reference to the encoder instance. -/// Value of the NEAR parameter. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_near_lossless(charls_jpegls_encoder* encoder, int32_t near_lossless) CHARLS_NOEXCEPT; - -/// -/// Configures the interleave mode the encoder should use. The default is none. -/// The encoder expects the input buffer in the same format as the interleave mode. -/// -/// Reference to the encoder instance. -/// Value of the interleave mode. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_interleave_mode(charls_jpegls_encoder* encoder, charls_interleave_mode interleave_mode) CHARLS_NOEXCEPT; - -/// -/// Configures the preset coding parameters the encoder should use. -/// If not set the encoder will use the default preset coding parameters as defined by the JPEG-LS standard. -/// Only when the coding parameters are different then the default parameters, they will be written to the -/// JPEG-LS stream during the encode phase. -/// -/// Reference to the encoder instance. -/// Reference to the preset coding parameters. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_preset_coding_parameters(charls_jpegls_encoder* encoder, const charls_jpegls_pc_parameters* preset_coding_parameters) CHARLS_NOEXCEPT; - -/// -/// Configures the HP color transformation the encoder should use. -/// If not set the encoder will use no color transformation. -/// Color transformations are a HP extension and not defined by the JPEG-LS standard and can only be set for 3 component encodings. -/// -/// Reference to the encoder instance. -/// The color transformation parameters. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_color_transformation(charls_jpegls_encoder* encoder, charls_color_transformation color_transformation) CHARLS_NOEXCEPT; - -/// -/// Returns the size in bytes, that the encoder expects are needed to hold the encoded image. -/// -/// -/// Size for dynamic extras like SPIFF entries and other tables are not included in this size. -/// -/// Reference to the encoder instance. -/// Reference to the size that will be set when the functions returns. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_get_estimated_destination_size(const charls_jpegls_encoder* encoder, size_t* size_in_bytes) CHARLS_NOEXCEPT; - -/// -/// Set the reference to the destination buffer that will contain the encoded JPEG-LS byte stream data after encoding. -/// This buffer needs to remain valid during the encoding process. -/// -/// Reference to the encoder instance. -/// Reference to the start of the destination buffer. -/// Size of the destination buffer in bytes. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_destination_buffer(charls_jpegls_encoder* encoder, void* destination_buffer, size_t destination_size) CHARLS_NOEXCEPT; - -/// -/// Writes a standard SPIFF header to the destination. The additional values are computed from the current encoder settings. -/// A SPIFF header is optional, but recommended for standalone JPEG-LS files. -/// -/// Reference to the encoder instance. -/// The color space of the image. -/// The resolution units of the next 2 parameters. -/// The vertical resolution. -/// The horizontal resolution. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_write_standard_spiff_header(charls_jpegls_encoder* encoder, - charls_spiff_color_space color_space, - charls_spiff_resolution_units resolution_units, - uint32_t vertical_resolution, - uint32_t horizontal_resolution) CHARLS_NOEXCEPT; - -/// -/// Writes a SPIFF header to the destination. -/// -/// Reference to the encoder instance. -/// Reference to a SPIFF header that will be written to the destination. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_write_spiff_header(charls_jpegls_encoder* encoder, const charls_spiff_header* spiff_header) CHARLS_NOEXCEPT; - -/// -/// Writes a SPIFF directory entry to the destination. -/// -/// -/// Function should be called after writing a SPIFF header. -/// -/// Reference to the encoder instance. -/// The entry tag of the directory entry. -/// The entry data of the directory entry. -/// The size in bytes of the directory entry [0-65528]. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_write_spiff_entry(charls_jpegls_encoder* encoder, uint32_t entry_tag, const void* entry_data, size_t entry_data_size) CHARLS_NOEXCEPT; - -/// -/// Encodes the passed buffer with the source image data to the destination. -/// -/// Reference to the encoder instance. -/// Byte array that holds the image data that needs to be encoded. -/// Length of the array in bytes. -/// -/// The number of bytes from one row of pixels in memory to the next row of pixels in memory. -/// Stride is sometimes called pitch. If padding bytes are present, the stride is wider than the width of the image. -/// -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_encode_from_buffer(charls_jpegls_encoder* encoder, const void* source_buffer, size_t source_size, uint32_t stride) CHARLS_NOEXCEPT; - -/// -/// Returns the size in bytes, that are written to the destination. -/// -/// Reference to the encoder instance. -/// Reference to the size that will be set when the functions returns. -/// The result of the operation: success or a failure code. -CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_get_bytes_written(const charls_jpegls_encoder* encoder, size_t* bytes_written) CHARLS_NOEXCEPT; - - -// Note: The 4 methods below are considered obsolete and will be removed in the next major update. - -/// -/// Encodes a byte array with pixel data to a JPEG-LS encoded (compressed) byte array. -/// -/// This method is considered obsolete and will be removed in the next major update. -/// Byte array that holds the encoded bytes when the function returns. -/// Length of the array in bytes. If the array is too small the function will return an error. -/// This parameter will hold the number of bytes written to the destination byte array. Cannot be NULL. -/// Byte array that holds the pixels that should be encoded. -/// Length of the array in bytes. -/// Parameter object that describes the pixel data and how to encode it. -/// Character array of at least 256 characters or NULL. Hold the error message when a failure occurs, empty otherwise. -CHARLS_API_IMPORT_EXPORT CharlsApiResultType CHARLS_API_CALLING_CONVENTION JpegLsEncode( - void* destination, - size_t destinationLength, - size_t* bytesWritten, - const void* source, - size_t sourceLength, - const struct JlsParameters* params, - char* errorMessage); - -/// -/// Retrieves the JPEG-LS header. This info can be used to pre-allocate the uncompressed output buffer. -/// -/// This method will be removed in the next major update. -/// Byte array that holds the JPEG-LS encoded data of which the header should be extracted. -/// Length of the array in bytes. -/// Parameter object that describes how the pixel data is encoded. -/// Character array of at least 256 characters or NULL. Hold the error message when a failure occurs, empty otherwise. -CHARLS_API_IMPORT_EXPORT CharlsApiResultType CHARLS_API_CALLING_CONVENTION JpegLsReadHeader( - const void* source, - size_t sourceLength, - struct JlsParameters* params, - char* errorMessage); - -/// -/// Encodes a JPEG-LS encoded byte array to uncompressed pixel data byte array. -/// -/// This method will be removed in the next major update. -/// Byte array that holds the uncompressed pixel data bytes when the function returns. -/// Length of the array in bytes. If the array is too small the function will return an error. -/// Byte array that holds the JPEG-LS encoded data that should be decoded. -/// Length of the array in bytes. -/// Parameter object that describes the pixel data and how to decode it. -/// Character array of at least 256 characters or NULL. Hold the error message when a failure occurs, empty otherwise. -CHARLS_API_IMPORT_EXPORT CharlsApiResultType CHARLS_API_CALLING_CONVENTION JpegLsDecode( - void* destination, - size_t destinationLength, - const void* source, - size_t sourceLength, - const struct JlsParameters* params, - char* errorMessage); - -/// This method will be removed in the next major update. -CHARLS_API_IMPORT_EXPORT CharlsApiResultType CHARLS_API_CALLING_CONVENTION JpegLsDecodeRect( - void* destination, - size_t destinationLength, - const void* source, - size_t sourceLength, - struct JlsRect roi, - const struct JlsParameters* params, - char* errorMessage); - -#ifdef __cplusplus - -} // extern "C" - - -namespace charls { - -/// -/// JPEG-LS decoder class that encapsulates the C ABI interface calls and provide a native C++ interface. -/// -class jpegls_decoder final -{ -public: - /// - /// Decodes a JPEG-LS buffer in 1 simple operation. - /// - /// Source container with the JPEG-LS encoded bytes. - /// Destination container that will hold the image data on return. Container will be resized automatically. - /// The maximum output size that may be allocated, default is 94 MiB (enough to decode 8 bit color 8K image). - /// Frame info of the decoded image and the interleave mode. - template - static std::pair decode(const SourceContainer& source, DestinationContainer& destination, const size_t maximum_size_in_bytes = 7680 * 4320 * 3) - { - jpegls_decoder decoder{source}; - - decoder.read_header(); - - const size_t destination_size{decoder.destination_size()}; - if (destination_size > maximum_size_in_bytes) - throw jpegls_error(jpegls_errc::not_enough_memory); - - destination.resize(destination_size / sizeof(ValueType)); - decoder.decode(destination); - - return std::make_pair(decoder.frame_info(), decoder.interleave_mode()); - } - - jpegls_decoder() = default; - - /// - /// Constructs a jpegls_decoder instance. - /// The passed container needs to remain valid until the stream is fully decoded. - /// - /// A STL like container that provides the functions data() and size() and the type value_type. - template - explicit jpegls_decoder(const Container& source_container) - { - source(source_container); - } - - ~jpegls_decoder() = default; - - jpegls_decoder(const jpegls_decoder&) = delete; - jpegls_decoder(jpegls_decoder&&) noexcept = default; - jpegls_decoder& operator=(const jpegls_decoder&) = delete; - jpegls_decoder& operator=(jpegls_decoder&&) noexcept = default; - - /// - /// Set the reference to a source buffer that contains the encoded JPEG-LS byte stream data. - /// This buffer needs to remain valid until the stream is fully decoded. - /// - /// Reference to the start of the source buffer. - /// Size of the source buffer in bytes. - jpegls_decoder& source(const void* source_buffer, const size_t source_size_bytes) - { - check_jpegls_errc(charls_jpegls_decoder_set_source_buffer(decoder_.get(), source_buffer, source_size_bytes)); - return *this; - } - - /// - /// Set the reference to a source container that contains the encoded JPEG-LS byte stream data. - /// This container needs to remain valid until the stream is fully decoded. - /// - /// A STL like container that provides the functions data() and size() and the type value_type. - template - jpegls_decoder& source(const Container& source_container) - { - return source(source_container.data(), source_container.size() * sizeof(ValueType)); - } - - /// - /// Tries to read the SPIFF header from the JPEG-LS stream. - /// If a SPIFF header exists its will be returned otherwise the struct will be filled with default values. - /// The header_found parameter will be set to true if the spiff header could be read. - /// Call read_header to read the normal JPEG header afterwards. - /// - /// Output argument, will hold true if a SPIFF header could be found, otherwise false. - /// The SPIFF header. - CHARLS_NO_DISCARD spiff_header read_spiff_header(bool& header_found) const - { - std::error_code ec; - const spiff_header header{read_spiff_header(header_found, ec)}; - check_jpegls_errc(static_cast(ec.value())); - return header; - } - - /// - /// Tries to read the SPIFF header from the JPEG-LS stream. - /// If a SPIFF header exists its will be returned otherwise the struct will be filled with default values. - /// The header_found parameter will be set to true if the spiff header could be read. - /// Call read_header to read the normal JPEG header afterwards. - /// - /// Output argument, will hold true if a SPIFF header could be found, otherwise false. - /// The out-parameter for error reporting. - /// The SPIFF header. - CHARLS_NO_DISCARD spiff_header read_spiff_header(bool& header_found, std::error_code& ec) const noexcept - { - spiff_header header{}; - int32_t found; - ec = charls_jpegls_decoder_read_spiff_header(decoder_.get(), &header, &found); - header_found = static_cast(found); - return header; - } - - /// - /// Reads the JPEG-LS header from the beginning of the JPEG-LS byte stream or after the SPIFF header. - /// After this function is called frame info and other info can be retrieved. - /// - jpegls_decoder& read_header() - { - check_jpegls_errc(charls_jpegls_decoder_read_header(decoder_.get())); - return *this; - } - - /// - /// Reads the JPEG-LS header from the beginning of the JPEG-LS byte stream or after the SPIFF header. - /// After this function is called frame info and other info can be retrieved. - /// - /// The out-parameter for error reporting. - jpegls_decoder& read_header(std::error_code& ec) noexcept - { - ec = charls_jpegls_decoder_read_header(decoder_.get()); - return *this; - } - - /// - /// Returns information about the frame stored in the JPEG-LS byte stream. - /// Function can be called after read_header. - /// - /// The frame info that describes the image stored in the JPEG-LS byte stream. - CHARLS_NO_DISCARD charls::frame_info frame_info() const - { - charls::frame_info frame_info; - check_jpegls_errc(charls_jpegls_decoder_get_frame_info(decoder_.get(), &frame_info)); - return frame_info; - } - - /// - /// Returns the NEAR parameter that was used to encode the scan. A value of 0 means lossless. - /// - /// The component index for which the NEAR parameter should be retrieved. - /// The value of the NEAR parameter. - CHARLS_NO_DISCARD int32_t near_lossless(int32_t component = 0) const - { - int32_t near_lossless; - check_jpegls_errc(charls_jpegls_decoder_get_near_lossless(decoder_.get(), component, &near_lossless)); - return near_lossless; - } - - /// - /// Returns the interleave mode that was used to encode the scan(s). - /// - /// The value of the interleave mode. - CHARLS_NO_DISCARD charls::interleave_mode interleave_mode() const - { - charls::interleave_mode interleave_mode; - check_jpegls_errc(charls_jpegls_decoder_get_interleave_mode(decoder_.get(), &interleave_mode)); - return interleave_mode; - } - - /// - /// Returns the preset coding parameters used to encode the first scan. - /// - /// The values of the JPEG-LS preset coding parameters. - CHARLS_NO_DISCARD jpegls_pc_parameters preset_coding_parameters() const - { - jpegls_pc_parameters preset_coding_parameters; - check_jpegls_errc(charls_jpegls_decoder_get_preset_coding_parameters(decoder_.get(), 0, &preset_coding_parameters)); - return preset_coding_parameters; - } - - /// - /// Returns the size required for the destination buffer in bytes to hold the decoded pixel data. - /// Function can be read_header. - /// - /// Number of bytes to the next line in the buffer, when zero, decoder will compute it. - /// The required size in bytes of the destination buffer. - CHARLS_NO_DISCARD size_t destination_size(const uint32_t stride = 0) const - { - size_t size_in_bytes; - check_jpegls_errc(charls_jpegls_decoder_get_destination_size(decoder_.get(), stride, &size_in_bytes)); - return size_in_bytes; - } - - /// - /// Will decode the JPEG-LS byte stream set with source into the destination buffer. - /// - /// Byte array that holds the encoded bytes when the function returns. - /// Length of the array in bytes. If the array is too small the function will return an error. - /// Number of bytes to the next line in the buffer, when zero, decoder will compute it. - void decode(void* destination_buffer, const size_t destination_size_bytes, const uint32_t stride = 0) const - { - check_jpegls_errc(charls_jpegls_decoder_decode_to_buffer(decoder_.get(), destination_buffer, destination_size_bytes, stride)); - } - - /// - /// Will decode the JPEG-LS byte stream set with source into the destination container. - /// - /// A STL like container that provides the functions data() and size() and the type value_type. - /// Number of bytes to the next line in the buffer, when zero, decoder will compute it. - template - void decode(Container& destination_container, const uint32_t stride = 0) const - { - decode(destination_container.data(), destination_container.size() * sizeof(ValueType), stride); - } - - /// - /// Will decode the JPEG-LS byte stream set with source and return a container with the decoded data. - /// - /// Number of bytes to the next line in the buffer, when zero, decoder will compute it. - /// Container with the decoded data. - template - auto decode(const uint32_t stride = 0) const - { - Container destination(destination_size() / sizeof(ValueType)); - - decode(destination.data(), destination.size() * sizeof(ValueType), stride); - return destination; - } - -private: - CHARLS_NO_DISCARD static charls_jpegls_decoder* create_decoder() - { - charls_jpegls_decoder* decoder = charls_jpegls_decoder_create(); - if (!decoder) - throw std::bad_alloc(); - - return decoder; - } - - static void destroy_decoder(const charls_jpegls_decoder* decoder) noexcept - { - charls_jpegls_decoder_destroy(decoder); - } - - std::unique_ptr decoder_{create_decoder(), destroy_decoder}; -}; - - -/// -/// JPEG-LS encoder class that encapsulates the C ABI interface calls and provide a native C++ interface. -/// -class jpegls_encoder final -{ -public: - /// - /// Encoded pixel data in 1 simple operation into a JPEG-LS encoded buffer. - /// - /// Source container with the pixel data bytes that need to be encoded. - /// Information about the frame that needs to be encoded. - /// Configures the interleave mode the encoder should use. - /// Container with the JPEG-LS encoded bytes. - template - static auto encode(const Container& source, const charls::frame_info& info, const charls::interleave_mode interleave_mode = charls::interleave_mode::none) - { - jpegls_encoder encoder; - encoder.frame_info(info) - .interleave_mode(interleave_mode); - - Container destination(encoder.estimated_destination_size()); - encoder.destination(destination); - - const size_t bytes_written{encoder.encode(source)}; - destination.resize(bytes_written); - - return destination; - } - - jpegls_encoder() = default; - ~jpegls_encoder() = default; - - jpegls_encoder(const jpegls_encoder&) = delete; - jpegls_encoder(jpegls_encoder&&) noexcept = default; - jpegls_encoder& operator=(const jpegls_encoder&) = delete; - jpegls_encoder& operator=(jpegls_encoder&&) noexcept = default; - - /// - /// Configures the frame that needs to be encoded. - /// This information will be written to the Start of Frame (SOF) segment during the encode phase. - /// - /// Information about the frame that needs to be encoded. - jpegls_encoder& frame_info(const frame_info& frame_info) - { - check_jpegls_errc(charls_jpegls_encoder_set_frame_info(encoder_.get(), &frame_info)); - return *this; - } - - /// - /// Configures the NEAR parameter the encoder should use. A value of 0 means lossless, this is also the default. - /// - /// Value of the NEAR parameter. - jpegls_encoder& near_lossless(const int32_t near_lossless) - { - check_jpegls_errc(charls_jpegls_encoder_set_near_lossless(encoder_.get(), near_lossless)); - return *this; - } - - /// - /// Configures the interleave mode the encoder should use. The default is none. - /// The encoder expects the input buffer in the same format as the interleave mode. - /// - /// Value of the interleave mode. - jpegls_encoder& interleave_mode(const interleave_mode interleave_mode) - { - check_jpegls_errc(charls_jpegls_encoder_set_interleave_mode(encoder_.get(), interleave_mode)); - return *this; - } - - /// - /// Configures the preset coding parameters the encoder should use. - /// If not set the encoder will use the default preset coding parameters as defined by the JPEG-LS standard. - /// Only when the coding parameters are different then the default parameters, they will be written to the - /// JPEG-LS stream during the encode phase. - /// - /// Reference to the preset coding parameters. - jpegls_encoder& preset_coding_parameters(const jpegls_pc_parameters& preset_coding_parameters) - { - check_jpegls_errc(charls_jpegls_encoder_set_preset_coding_parameters(encoder_.get(), &preset_coding_parameters)); - return *this; - } - - /// - /// Configures the HP color transformation the encoder should use. - /// If not set the encoder will use no color transformation. - /// Color transformations are a HP extension and not defined by the JPEG-LS standard and can only be set for 3 component encodings. - /// - /// The color transformation parameters. - jpegls_encoder& color_transformation(const color_transformation color_transformation) - { - check_jpegls_errc(charls_jpegls_encoder_set_color_transformation(encoder_.get(), color_transformation)); - return *this; - } - - /// - /// Returns the size in bytes, that the encoder expects are needed to hold the encoded image. - /// - /// - /// Size for dynamic extras like SPIFF entries and other tables are not included in this size. - /// - /// The estimated size in bytes needed to hold the encoded image. - CHARLS_NO_DISCARD size_t estimated_destination_size() const - { - size_t size_in_bytes; - check_jpegls_errc(charls_jpegls_encoder_get_estimated_destination_size(encoder_.get(), &size_in_bytes)); - return size_in_bytes; - } - - /// - /// Set the reference to the destination buffer that will contain the encoded JPEG-LS byte stream data after encoding. - /// This buffer needs to remain valid during the encoding process. - /// - /// Reference to the start of the destination buffer. - /// Size of the destination buffer in bytes. - jpegls_encoder& destination(void* destination_buffer, const size_t destination_size_bytes) - { - check_jpegls_errc(charls_jpegls_encoder_set_destination_buffer(encoder_.get(), destination_buffer, destination_size_bytes)); - return *this; - } - - /// - /// Set the the container that will contain the encoded JPEG-LS byte stream data after encoding. - /// This container needs to remain valid during the encoding process. - /// - /// The STL like container, that supports the functions data() and size() and the typedef value_type. - template - jpegls_encoder& destination(Container& destination_container) - { - return destination(destination_container.data(), destination_container.size() * sizeof(ValueType)); - } - - /// - /// Writes a standard SPIFF header to the destination. The additional values are computed from the current encoder settings. - /// - /// The color space of the image. - /// The resolution units of the next 2 parameters. - /// The vertical resolution. - /// The horizontal resolution. - jpegls_encoder& write_standard_spiff_header(const spiff_color_space color_space, - const spiff_resolution_units resolution_units = spiff_resolution_units::aspect_ratio, - const uint32_t vertical_resolution = 1, - const uint32_t horizontal_resolution = 1) - { - check_jpegls_errc(charls_jpegls_encoder_write_standard_spiff_header(encoder_.get(), color_space, resolution_units, vertical_resolution, horizontal_resolution)); - return *this; - } - - /// - /// Writes a SPIFF header to the destination. - /// - /// Reference to a SPIFF header that will be written to the destination. - jpegls_encoder& write_spiff_header(const spiff_header& header) - { - check_jpegls_errc(charls_jpegls_encoder_write_spiff_header(encoder_.get(), &header)); - return *this; - } - - /// - /// Writes a SPIFF directory entry to the destination. - /// - /// The entry tag of the directory entry. - /// The entry data of the directory entry. - /// The size in bytes of the directory entry [0-65528]. - template - jpegls_encoder& write_spiff_entry(const IntDerivedType entry_tag, const void* entry_data, const size_t entry_data_size) - { - check_jpegls_errc(charls_jpegls_encoder_write_spiff_entry(encoder_.get(), static_cast(entry_tag), entry_data, entry_data_size)); - return *this; - } - - /// - /// Encodes the passed buffer with the source image data to the destination. - /// - /// Byte array that holds the image data that needs to be encoded. - /// Length of the array in bytes. - /// - /// The number of bytes from one row of pixels in memory to the next row of pixels in memory. - /// Stride is sometimes called pitch. If padding bytes are present, the stride is wider than the width of the image. - /// - /// The number of bytes written to the destination. - size_t encode(const void* source_buffer, const size_t source_size_bytes, const uint32_t stride = 0) const - { - check_jpegls_errc(charls_jpegls_encoder_encode_from_buffer(encoder_.get(), source_buffer, source_size_bytes, stride)); - return bytes_written(); - } - - /// - /// Encodes the passed STL like container with the source image data to the destination. - /// - /// Container that holds the image data that needs to be encoded. - /// - /// The number of bytes from one row of pixels in memory to the next row of pixels in memory. - /// Stride is sometimes called pitch. If padding bytes are present, the stride is wider than the width of the image. - /// - /// The number of bytes written to the destination. - template - size_t encode(const Container& source_container, const uint32_t stride = 0) const - { - return encode(source_container.data(), source_container.size() * sizeof(ValueType), stride); - } - - /// - /// Returns the size in bytes, that are written to the destination. - /// - /// The bytes written. - CHARLS_NO_DISCARD size_t bytes_written() const - { - size_t bytes_written; - check_jpegls_errc(charls_jpegls_encoder_get_bytes_written(encoder_.get(), &bytes_written)); - return bytes_written; - } - -private: - CHARLS_NO_DISCARD static charls_jpegls_encoder* create_encoder() - { - charls_jpegls_encoder* encoder = charls_jpegls_encoder_create(); - if (!encoder) - throw std::bad_alloc(); - - return encoder; - } - - static void destroy_encoder(const charls_jpegls_encoder* encoder) noexcept - { - charls_jpegls_encoder_destroy(encoder); - } - - std::unique_ptr encoder_{create_encoder(), destroy_encoder}; -}; - -} // namespace charls +#include "charls_jpegls_decoder.h" +#include "charls_jpegls_encoder.h" +#include "version.h" -#endif -// Undefine CHARLS macros to prevent global namespace pollution +// Undefine CHARLS macros to prevent global macro namespace pollution #if !defined(CHARLS_LIBRARY_BUILD) #undef CHARLS_API_IMPORT_EXPORT #undef CHARLS_NO_DISCARD -#undef CHARLS_ENUM_DEPRECATED #undef CHARLS_FINAL #undef CHARLS_NOEXCEPT +#undef CHARLS_ATTRIBUTE +#undef CHARLS_DEPRECATED +#undef CHARLS_C_VOID + +#undef IN_ +#undef IN_OPT_ +#undef IN_Z_ +#undef IN_READS_BYTES_ +#undef OUT_ +#undef OUT_OPT_ +#undef OUT_WRITES_BYTES_ +#undef OUT_WRITES_Z_ +#undef RETURN_TYPE_SUCCESS_ +#undef CHARLS_CHECK_RETURN +#undef CHARLS_RET_MAY_BE_NULL + #endif diff -Nru charls-2.1.0+dfsg/include/charls/charls_jpegls_decoder.h charls-2.2.0+dfsg/include/charls/charls_jpegls_decoder.h --- charls-2.1.0+dfsg/include/charls/charls_jpegls_decoder.h 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/include/charls/charls_jpegls_decoder.h 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,541 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include "jpegls_error.h" + +#ifdef __cplusplus +#include +#include +#else +#include +#endif + + +#ifdef __cplusplus +struct charls_jpegls_decoder; +extern "C" { +#else +typedef struct charls_jpegls_decoder charls_jpegls_decoder; +#endif + +// The following functions define the public C API of the CharLS library. +// The C++ API is defined after the C API. + +/// +/// Creates a JPEG-LS decoder instance, when finished with the instance destroy it with the function +/// charls_jpegls_decoder_destroy. +/// +/// A reference to a new created decoder instance, or a null pointer when the creation fails. +CHARLS_CHECK_RETURN CHARLS_RET_MAY_BE_NULL CHARLS_API_IMPORT_EXPORT charls_jpegls_decoder* + CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_create(CHARLS_C_VOID) CHARLS_NOEXCEPT; + +/// +/// Destroys a JPEG-LS decoder instance created with charls_jpegls_decoder_create and releases all internal resources +/// attached to it. +/// +/// Instance to destroy. If a null pointer is passed as argument, no action occurs. +CHARLS_API_IMPORT_EXPORT void CHARLS_API_CALLING_CONVENTION +charls_jpegls_decoder_destroy(IN_OPT_ const charls_jpegls_decoder* decoder) CHARLS_NOEXCEPT; + +/// +/// Set the reference to a source buffer that contains the encoded JPEG-LS byte stream data. +/// This buffer needs to remain valid until the buffer is fully decoded. +/// +/// Reference to the decoder instance. +/// Reference to the start of the source buffer. +/// Size of the source buffer in bytes. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_set_source_buffer( + IN_ charls_jpegls_decoder* decoder, IN_READS_BYTES_(source_size_bytes) const void* source_buffer, + size_t source_size_bytes) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Tries to read the SPIFF header from the source buffer. +/// If a SPIFF header exists its content will be put into the spiff_header parameter and header_found will be set to 1. +/// Call charls_jpegls_decoder_read_header to read the normal JPEG header afterwards. +/// +/// Reference to the decoder instance. +/// Output argument, will hold the SPIFF header when one could be found. +/// Output argument, will hold 1 if a SPIFF header could be found, otherwise 0. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_read_spiff_header( + IN_ charls_jpegls_decoder* decoder, OUT_ charls_spiff_header* spiff_header, OUT_ int32_t* header_found) CHARLS_NOEXCEPT + CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Reads the JPEG-LS header from the JPEG byte stream. After this function is called frame info can be retrieved. +/// +/// Reference to the decoder instance. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION +charls_jpegls_decoder_read_header(IN_ charls_jpegls_decoder* decoder) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Returns information about the frame stored in the JPEG-LS byte stream. +/// +/// +/// Function should be called after calling the function charls_jpegls_decoder_read_header. +/// +/// Reference to the decoder instance. +/// Output argument, will hold the frame info when the function returns. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_frame_info( + IN_ const charls_jpegls_decoder* decoder, OUT_ charls_frame_info* frame_info) CHARLS_NOEXCEPT + CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Returns the NEAR parameter that was used to encode the scan. A value of 0 means lossless. +/// +/// +/// Function should be called after calling the function charls_jpegls_decoder_read_header. +/// +/// Reference to the decoder instance. +/// The component index for which the NEAR parameter should be retrieved. +/// Reference that will hold the value of the NEAR parameter. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_near_lossless( + IN_ const charls_jpegls_decoder* decoder, int32_t component, OUT_ int32_t* near_lossless) CHARLS_NOEXCEPT + CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Returns the interleave mode that was used to encode the scan. +/// +/// +/// Function should be called after calling the function charls_jpegls_decoder_read_header. +/// +/// Reference to the decoder instance. +/// Reference that will hold the value of the interleave mode. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_interleave_mode( + IN_ const charls_jpegls_decoder* decoder, OUT_ charls_interleave_mode* interleave_mode) CHARLS_NOEXCEPT + CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Returns the preset coding parameters used to encode the first scan. +/// +/// +/// Function should be called after calling the function charls_jpegls_decoder_read_header. +/// +/// Reference to the decoder instance. +/// Reserved. Should be set to 0. +/// Reference that will hold the values preset coding parameters. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_preset_coding_parameters( + IN_ const charls_jpegls_decoder* decoder, int32_t reserved, + OUT_ charls_jpegls_pc_parameters* preset_coding_parameters) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Returns the color transformation that was used to encode the scan. +/// +/// +/// Function should be called after calling the function charls_jpegls_decoder_read_header. +/// +/// Reference to the decoder instance. +/// Reference that will hold the value of the color transformation. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_color_transformation( + IN_ const charls_jpegls_decoder* decoder, OUT_ charls_color_transformation* color_transformation) CHARLS_NOEXCEPT + CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Returns the size required for the destination buffer in bytes to hold the decoded pixel data. +/// +/// +/// Function should be called after calling the function charls_jpegls_decoder_read_header. +/// +/// Reference to the decoder instance. +/// Number of bytes to the next line in the buffer, when zero, decoder will compute it. +/// Output argument, will hold the required size when the function returns. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_destination_size( + IN_ const charls_jpegls_decoder* decoder, uint32_t stride, OUT_ size_t* destination_size_bytes) CHARLS_NOEXCEPT + CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Will decode the JPEG-LS byte stream from the source buffer into the destination buffer. +/// +/// +/// Function should be called after calling the function charls_jpegls_decoder_read_header. +/// +/// Reference to the decoder instance. +/// Byte array that holds the encoded bytes when the function returns. +/// +/// Length of the array in bytes. If the array is too small the function will return an error. +/// +/// +/// Number of bytes to the next line in the buffer, when zero, decoder will compute it. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_decode_to_buffer( + IN_ const charls_jpegls_decoder* decoder, OUT_WRITES_BYTES_(destination_size_bytes) void* destination_buffer, + size_t destination_size_bytes, uint32_t stride) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + + +/// +/// Retrieves the JPEG-LS header. This info can be used to pre-allocate the uncompressed output buffer. +/// +/// This method will be removed in the next major update. +/// Byte array that holds the JPEG-LS encoded data of which the header should be extracted. +/// Length of the array in bytes. +/// Parameter object that describes how the pixel data is encoded. +/// +/// Character array of at least 256 characters or NULL. Hold the error message when a failure occurs, empty otherwise. +/// +CHARLS_DEPRECATED +CHARLS_API_IMPORT_EXPORT CharlsApiResultType CHARLS_API_CALLING_CONVENTION +JpegLsReadHeader(IN_READS_BYTES_(source_length) const void* source, size_t source_length, OUT_ struct JlsParameters* params, + OUT_OPT_ char* error_message) CHARLS_ATTRIBUTE((nonnull(1, 3))); + +/// +/// Encodes a JPEG-LS encoded byte array to uncompressed pixel data byte array. +/// +/// This method will be removed in the next major update. +/// Byte array that holds the uncompressed pixel data bytes when the function returns. +/// +/// Length of the array in bytes. If the array is too small the function will return an error. +/// +/// Byte array that holds the JPEG-LS encoded data that should be decoded. +/// Length of the array in bytes. +/// Parameter object that describes the pixel data and how to decode it. +/// +/// Character array of at least 256 characters or NULL. Hold the error message when a failure occurs, empty otherwise. +/// +CHARLS_DEPRECATED +CHARLS_API_IMPORT_EXPORT CharlsApiResultType CHARLS_API_CALLING_CONVENTION +JpegLsDecode(OUT_WRITES_BYTES_(destination_length) void* destination, size_t destination_length, + IN_READS_BYTES_(source_length) const void* source, size_t source_length, + IN_OPT_ const struct JlsParameters* params, OUT_OPT_ char* error_message) CHARLS_ATTRIBUTE((nonnull(1, 3))); + +/// This method will be removed in the next major update. +CHARLS_DEPRECATED +CHARLS_API_IMPORT_EXPORT CharlsApiResultType CHARLS_API_CALLING_CONVENTION +JpegLsDecodeRect(OUT_WRITES_BYTES_(destination_length) void* destination, size_t destination_length, + IN_READS_BYTES_(source_length) const void* source, size_t source_length, struct JlsRect roi, + IN_OPT_ const struct JlsParameters* params, OUT_OPT_ char* error_message) CHARLS_ATTRIBUTE((nonnull(1, 3))); + +#ifdef __cplusplus + +} // extern "C" + +namespace charls { + +/// +/// JPEG-LS decoder class that encapsulates the C ABI interface calls and provide a native C++ interface. +/// +class jpegls_decoder final +{ +public: + /// + /// Decodes a JPEG-LS buffer in 1 simple operation. + /// + /// Source container with the JPEG-LS encoded bytes. + /// Destination container that will hold the image data on return. Container will be resized + /// automatically. The maximum output size that may be allocated, default is + /// 94 MiB (enough to decode 8 bit color 8K image). Frame info of the decoded image and the interleave + /// mode. + template + static std::pair + decode(const SourceContainer& source, DestinationContainer& destination, + const size_t maximum_size_in_bytes = 7680 * 4320 * 3) + { + jpegls_decoder decoder{source, true}; + + const size_t destination_size{decoder.destination_size()}; + if (destination_size > maximum_size_in_bytes) + impl::throw_jpegls_error(jpegls_errc::not_enough_memory); + + destination.resize(destination_size / sizeof(ValueType)); + decoder.decode(destination); + + return std::make_pair(decoder.frame_info(), decoder.interleave_mode()); + } + + jpegls_decoder() = default; + + /// + /// Constructs a jpegls_decoder instance. + /// The passed container needs to remain valid until the stream is fully decoded. + /// + /// + /// A STL like container that provides the functions data() and size() and the type value_type. + /// + /// + /// If true the SPIFF and JPEG header will be directly read from the source. + /// + template + jpegls_decoder(const Container& source_container, const bool parse_header) + { + source(source_container); + if (parse_header) + { + read_spiff_header(); + read_header(); + } + } + + ~jpegls_decoder() = default; + + jpegls_decoder(const jpegls_decoder&) = delete; + jpegls_decoder(jpegls_decoder&&) noexcept = default; + jpegls_decoder& operator=(const jpegls_decoder&) = delete; + jpegls_decoder& operator=(jpegls_decoder&&) noexcept = default; + + /// + /// Set the reference to a source buffer that contains the encoded JPEG-LS byte stream data. + /// This buffer needs to remain valid until the stream is fully decoded. + /// + /// Reference to the start of the source buffer. + /// Size of the source buffer in bytes. + jpegls_decoder& source(IN_READS_BYTES_(source_size_bytes) const void* source_buffer, const size_t source_size_bytes) + { + check_jpegls_errc(charls_jpegls_decoder_set_source_buffer(decoder_.get(), source_buffer, source_size_bytes)); + return *this; + } + + /// + /// Set the reference to a source container that contains the encoded JPEG-LS byte stream data. + /// This container needs to remain valid until the stream is fully decoded. + /// + /// + /// A STL like container that provides the functions data() and size() and the type value_type. + /// + template + jpegls_decoder& source(const Container& source_container) + { + return source(source_container.data(), source_container.size() * sizeof(ValueType)); + } + + /// + /// Tries to read the SPIFF header from the JPEG-LS stream. + /// If a SPIFF header exists its will be returned otherwise the struct will be filled with default values. + /// The header_found parameter will be set to true if the spiff header could be read. + /// Call read_header to read the normal JPEG header afterwards. + /// + /// True if a valid SPIFF header could be found. + bool read_spiff_header() + { + std::error_code ec; + read_spiff_header(ec); + check_jpegls_errc(static_cast(ec.value())); + return spiff_header_has_value_; + } + + /// + /// Tries to read the SPIFF header from the JPEG-LS stream. + /// If a SPIFF header exists its will be returned otherwise the struct will be filled with default values. + /// The header_found parameter will be set to true if the spiff header could be read. + /// Call read_header to read the normal JPEG header afterwards. + /// + /// The out-parameter for error reporting. + /// True if a valid SPIFF header could be found. + bool read_spiff_header(std::error_code& ec) noexcept + { + int32_t found; + ec = charls_jpegls_decoder_read_spiff_header(decoder_.get(), &spiff_header_, &found); + spiff_header_has_value_ = found != 0; + return spiff_header_has_value_; + } + + /// + /// Reads the JPEG-LS header from the beginning of the JPEG-LS byte stream or after the SPIFF header. + /// After this function is called frame info and other info can be retrieved. + /// + jpegls_decoder& read_header() + { + std::error_code ec; + read_header(ec); + check_jpegls_errc(static_cast(ec.value())); + return *this; + } + + /// + /// Reads the JPEG-LS header from the beginning of the JPEG-LS byte stream or after the SPIFF header. + /// After this function is called frame info and other info can be retrieved. + /// + /// The out-parameter for error reporting. + jpegls_decoder& read_header(OUT_ std::error_code& ec) noexcept + { + ec = charls_jpegls_decoder_read_header(decoder_.get()); + if (ec == jpegls_errc::success) + { + ec = charls_jpegls_decoder_get_frame_info(decoder_.get(), &frame_info_); + } + return *this; + } + + /// + /// Returns true if a valid SPIFF header was found. + /// + /// True of false, depending if a SPIFF header was found. + CHARLS_NO_DISCARD bool spiff_header_has_value() const noexcept + { + return spiff_header_has_value_; + } + + /// + /// Returns the SPIFF header, if read and found. + /// Function can be called after read_spiff_header and spiff_header_has_value. + /// + /// The SPIFF header. + CHARLS_NO_DISCARD const charls::spiff_header& spiff_header() const& noexcept + { + return spiff_header_; + } + + /// + /// Returns the SPIFF header, if read and found. + /// Function can be called after read_spiff_header and spiff_header_has_value. + /// + /// The SPIFF header. + CHARLS_NO_DISCARD charls::spiff_header spiff_header() const&& noexcept + { + return spiff_header_; + } + + /// + /// Returns information about the frame stored in the JPEG-LS byte stream. + /// Function can be called after read_header. + /// + /// The frame info that describes the image stored in the JPEG-LS byte stream. + CHARLS_NO_DISCARD const charls::frame_info& frame_info() const& noexcept + { + return frame_info_; + } + + /// + /// Returns information about the frame stored in the JPEG-LS byte stream. + /// Function can be called after read_header. + /// + /// The frame info that describes the image stored in the JPEG-LS byte stream. + CHARLS_NO_DISCARD charls::frame_info frame_info() const&& noexcept + { + return frame_info_; + } + + /// + /// Returns the NEAR parameter that was used to encode the scan. A value of 0 means lossless. + /// + /// The component index for which the NEAR parameter should be retrieved. + /// The value of the NEAR parameter. + CHARLS_NO_DISCARD int32_t near_lossless(const int32_t component = 0) const + { + int32_t near_lossless; + check_jpegls_errc(charls_jpegls_decoder_get_near_lossless(decoder_.get(), component, &near_lossless)); + return near_lossless; + } + + /// + /// Returns the interleave mode that was used to encode the scan. + /// + /// The value of the interleave mode. + CHARLS_NO_DISCARD charls::interleave_mode interleave_mode() const + { + charls::interleave_mode interleave_mode; + check_jpegls_errc(charls_jpegls_decoder_get_interleave_mode(decoder_.get(), &interleave_mode)); + return interleave_mode; + } + + /// + /// Returns the preset coding parameters used to encode the first scan. + /// + /// The values of the JPEG-LS preset coding parameters. + CHARLS_NO_DISCARD jpegls_pc_parameters preset_coding_parameters() const + { + jpegls_pc_parameters preset_coding_parameters; + check_jpegls_errc(charls_jpegls_decoder_get_preset_coding_parameters(decoder_.get(), 0, &preset_coding_parameters)); + return preset_coding_parameters; + } + + /// + /// Returns the HP color transformation that was used to encode the scan. + /// + /// The value of the color transformation. + CHARLS_NO_DISCARD charls::color_transformation color_transformation() const + { + charls::color_transformation color_transformation; + check_jpegls_errc(charls_jpegls_decoder_get_color_transformation(decoder_.get(), &color_transformation)); + return color_transformation; + } + + /// + /// Returns the size required for the destination buffer in bytes to hold the decoded pixel data. + /// Function can be read_header. + /// + /// Number of bytes to the next line in the buffer, when zero, decoder will compute it. + /// The required size in bytes of the destination buffer. + CHARLS_NO_DISCARD size_t destination_size(const uint32_t stride = 0) const + { + size_t size_in_bytes; + check_jpegls_errc(charls_jpegls_decoder_get_destination_size(decoder_.get(), stride, &size_in_bytes)); + return size_in_bytes; + } + + /// + /// Will decode the JPEG-LS byte stream set with source into the destination buffer. + /// + /// Byte array that holds the encoded bytes when the function returns. + /// + /// Length of the array in bytes. If the array is too small the function will return an error. + /// + /// Number of bytes to the next line in the buffer, when zero, decoder will compute it. + void decode(OUT_WRITES_BYTES_(destination_size_bytes) void* destination_buffer, const size_t destination_size_bytes, + const uint32_t stride = 0) const + { + check_jpegls_errc( + charls_jpegls_decoder_decode_to_buffer(decoder_.get(), destination_buffer, destination_size_bytes, stride)); + } + + /// + /// Will decode the JPEG-LS byte stream set with source into the destination container. + /// + /// + /// A STL like container that provides the functions data() and size() and the type value_type. + /// + /// Number of bytes to the next line in the buffer, when zero, decoder will compute it. + template + void decode(OUT_ Container& destination_container, const uint32_t stride = 0) const + { + decode(destination_container.data(), destination_container.size() * sizeof(ValueType), stride); + } + + /// + /// Will decode the JPEG-LS byte stream set with source and return a container with the decoded data. + /// + /// Number of bytes to the next line in the buffer, when zero, decoder will compute it. + /// Container with the decoded data. + template + CHARLS_NO_DISCARD auto decode(const uint32_t stride = 0) const + { + Container destination(destination_size() / sizeof(ValueType)); + + decode(destination.data(), destination.size() * sizeof(ValueType), stride); + return destination; + } + +private: + CHARLS_NO_DISCARD static charls_jpegls_decoder* create_decoder() + { + charls_jpegls_decoder* decoder{charls_jpegls_decoder_create()}; + if (!decoder) + throw std::bad_alloc(); + + return decoder; + } + + static void destroy_decoder(IN_OPT_ const charls_jpegls_decoder* decoder) noexcept + { + charls_jpegls_decoder_destroy(decoder); + } + + std::unique_ptr decoder_{create_decoder(), + &destroy_decoder}; + bool spiff_header_has_value_{}; + charls::spiff_header spiff_header_{}; + charls::frame_info frame_info_{}; +}; + +} // namespace charls + +#endif diff -Nru charls-2.1.0+dfsg/include/charls/charls_jpegls_encoder.h charls-2.2.0+dfsg/include/charls/charls_jpegls_encoder.h --- charls-2.1.0+dfsg/include/charls/charls_jpegls_encoder.h 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/include/charls/charls_jpegls_encoder.h 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,459 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include "jpegls_error.h" + +#ifdef __cplusplus +#include +#else +#include +#endif + +#ifdef __cplusplus +struct charls_jpegls_encoder; +extern "C" { +#else +typedef struct charls_jpegls_encoder charls_jpegls_encoder; +#endif + +// The following functions define the public C API of the CharLS library. +// The C++ API is defined after the C API. + +/// +/// Creates a JPEG-LS encoder instance, when finished with the instance destroy it with the function +/// charls_jpegls_encoder_destroy. +/// +/// A reference to a new created encoder instance, or a null pointer when the creation fails. +CHARLS_CHECK_RETURN CHARLS_RET_MAY_BE_NULL CHARLS_API_IMPORT_EXPORT charls_jpegls_encoder* + CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_create(CHARLS_C_VOID) CHARLS_NOEXCEPT; + +/// +/// Destroys a JPEG-LS encoder instance created with charls_jpegls_encoder_create and releases all internal resources +/// attached to it. +/// +/// Instance to destroy. If a null pointer is passed as argument, no action occurs. +CHARLS_API_IMPORT_EXPORT void CHARLS_API_CALLING_CONVENTION +charls_jpegls_encoder_destroy(IN_OPT_ const charls_jpegls_encoder* encoder) CHARLS_NOEXCEPT; + +/// +/// Configures the frame that needs to be encoded. This information will be written to the Start of Frame segment. +/// +/// Reference to the encoder instance. +/// Information about the frame that needs to be encoded. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_frame_info( + IN_ charls_jpegls_encoder* encoder, IN_ const charls_frame_info* frame_info) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Configures the NEAR parameter the encoder should use. A value of 0 means lossless, 0 is also the default. +/// +/// Reference to the encoder instance. +/// Value of the NEAR parameter. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_near_lossless( + IN_ charls_jpegls_encoder* encoder, int32_t near_lossless) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Configures the interleave mode the encoder should use. The default is none. +/// The encoder expects the input buffer in the same format as the interleave mode. +/// +/// Reference to the encoder instance. +/// Value of the interleave mode. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_interleave_mode( + IN_ charls_jpegls_encoder* encoder, charls_interleave_mode interleave_mode) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Configures the preset coding parameters the encoder should use. +/// If not set the encoder will use the default preset coding parameters as defined by the JPEG-LS standard. +/// Only when the coding parameters are different then the default parameters, they will be written to the +/// JPEG-LS stream during the encode phase. +/// +/// Reference to the encoder instance. +/// Reference to the preset coding parameters. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_preset_coding_parameters( + IN_ charls_jpegls_encoder* encoder, IN_ const charls_jpegls_pc_parameters* preset_coding_parameters) CHARLS_NOEXCEPT + CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Configures the HP color transformation the encoder should use. +/// If not set the encoder will use no color transformation. +/// Color transformations are a HP extension and not defined by the JPEG-LS standard and can only be set for 3 component +/// encodings. +/// +/// Reference to the encoder instance. +/// The color transformation parameters. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_color_transformation( + IN_ charls_jpegls_encoder* encoder, charls_color_transformation color_transformation) CHARLS_NOEXCEPT + CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Returns the size in bytes, that the encoder expects are needed to hold the encoded image. +/// +/// +/// Size for dynamic extras like SPIFF entries and other tables are not included in this size. +/// +/// Reference to the encoder instance. +/// Reference to the size that will be set when the functions returns. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION +charls_jpegls_encoder_get_estimated_destination_size(IN_ const charls_jpegls_encoder* encoder, + OUT_ size_t* size_in_bytes) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Set the reference to the destination buffer that will contain the encoded JPEG-LS byte stream data after encoding. +/// This buffer needs to remain valid during the encoding process. +/// +/// Reference to the encoder instance. +/// Reference to the start of the destination buffer. +/// Size of the destination buffer in bytes. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_destination_buffer( + IN_ charls_jpegls_encoder* encoder, OUT_WRITES_BYTES_(destination_size_bytes) void* destination_buffer, + size_t destination_size_bytes) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Writes a standard SPIFF header to the destination. The additional values are computed from the current encoder settings. +/// A SPIFF header is optional, but recommended for standalone JPEG-LS files. +/// +/// Reference to the encoder instance. +/// The color space of the image. +/// The resolution units of the next 2 parameters. +/// The vertical resolution. +/// The horizontal resolution. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_write_standard_spiff_header( + IN_ charls_jpegls_encoder* encoder, charls_spiff_color_space color_space, charls_spiff_resolution_units resolution_units, + uint32_t vertical_resolution, uint32_t horizontal_resolution) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Writes a SPIFF header to the destination. +/// +/// Reference to the encoder instance. +/// Reference to a SPIFF header that will be written to the destination. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_write_spiff_header( + IN_ charls_jpegls_encoder* encoder, IN_ const charls_spiff_header* spiff_header) CHARLS_NOEXCEPT + CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Writes a SPIFF directory entry to the destination. +/// +/// +/// Function should be called after writing a SPIFF header. +/// +/// Reference to the encoder instance. +/// The entry tag of the directory entry. +/// The entry data of the directory entry. +/// The size in bytes of the directory entry [0-65528]. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_write_spiff_entry( + IN_ charls_jpegls_encoder* encoder, uint32_t entry_tag, IN_READS_BYTES_(entry_data_size_bytes) const void* entry_data, + size_t entry_data_size_bytes) CHARLS_NOEXCEPT; + +/// +/// Encodes the passed buffer with the source image data to the destination. +/// +/// Reference to the encoder instance. +/// Byte array that holds the image data that needs to be encoded. +/// Length of the array in bytes. +/// +/// The number of bytes from one row of pixels in memory to the next row of pixels in memory. +/// Stride is sometimes called pitch. If padding bytes are present, the stride is wider than the width of the image. +/// +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_encode_from_buffer( + IN_ charls_jpegls_encoder* encoder, IN_READS_BYTES_(source_size_bytes) const void* source_buffer, + size_t source_size_bytes, uint32_t stride) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + +/// +/// Returns the size in bytes, that are written to the destination. +/// +/// Reference to the encoder instance. +/// Reference to the size that will be set when the functions returns. +/// The result of the operation: success or a failure code. +CHARLS_API_IMPORT_EXPORT charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_get_bytes_written( + IN_ const charls_jpegls_encoder* encoder, OUT_ size_t* bytes_written) CHARLS_NOEXCEPT CHARLS_ATTRIBUTE((nonnull)); + + +// Note: The 4 methods below are considered obsolete and will be removed in the next major update. + +/// +/// Encodes a byte array with pixel data to a JPEG-LS encoded (compressed) byte array. +/// +/// This method is considered obsolete and will be removed in the next major update. +/// Byte array that holds the encoded bytes when the function returns. +/// +/// Length of the array in bytes. If the array is too small the function will return an error. +/// +/// +/// This parameter will hold the number of bytes written to the destination byte array. Cannot be NULL. +/// +/// Byte array that holds the pixels that should be encoded. +/// Length of the array in bytes. +/// Parameter object that describes the pixel data and how to encode it. +/// +/// Character array of at least 256 characters or NULL. Hold the error message when a failure occurs, empty otherwise. +/// +CHARLS_DEPRECATED +CHARLS_API_IMPORT_EXPORT CharlsApiResultType CHARLS_API_CALLING_CONVENTION +JpegLsEncode(OUT_WRITES_BYTES_(destination_length) void* destination, size_t destination_length, OUT_ size_t* bytes_written, + IN_READS_BYTES_(source_length) const void* source, size_t source_length, IN_ const struct JlsParameters* params, + OUT_OPT_ char* error_message) CHARLS_ATTRIBUTE((nonnull(1, 3, 4, 6))); + +#ifdef __cplusplus + +} // extern "C" + +namespace charls { + +/// +/// JPEG-LS encoder class that encapsulates the C ABI interface calls and provide a native C++ interface. +/// +class jpegls_encoder final +{ +public: + /// + /// Encoded pixel data in 1 simple operation into a JPEG-LS encoded buffer. + /// + /// Source container with the pixel data bytes that need to be encoded. + /// Information about the frame that needs to be encoded. + /// Configures the interleave mode the encoder should use. + /// Container with the JPEG-LS encoded bytes. + template + static auto encode(const Container& source, const charls::frame_info& info, + const charls::interleave_mode interleave_mode = charls::interleave_mode::none) + { + jpegls_encoder encoder; + encoder.frame_info(info).interleave_mode(interleave_mode); + + Container destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + return destination; + } + + jpegls_encoder() = default; + ~jpegls_encoder() = default; + + jpegls_encoder(const jpegls_encoder&) = delete; + jpegls_encoder(jpegls_encoder&&) noexcept = default; + jpegls_encoder& operator=(const jpegls_encoder&) = delete; + jpegls_encoder& operator=(jpegls_encoder&&) noexcept = default; + + /// + /// Configures the frame that needs to be encoded. + /// This information will be written to the Start of Frame (SOF) segment during the encode phase. + /// + /// Information about the frame that needs to be encoded. + jpegls_encoder& frame_info(const frame_info& frame_info) + { + check_jpegls_errc(charls_jpegls_encoder_set_frame_info(encoder_.get(), &frame_info)); + return *this; + } + + /// + /// Configures the NEAR parameter the encoder should use. A value of 0 means lossless, this is also the default. + /// + /// Value of the NEAR parameter. + jpegls_encoder& near_lossless(const int32_t near_lossless) + { + check_jpegls_errc(charls_jpegls_encoder_set_near_lossless(encoder_.get(), near_lossless)); + return *this; + } + + /// + /// Configures the interleave mode the encoder should use. The default is none. + /// The encoder expects the input buffer in the same format as the interleave mode. + /// + /// Value of the interleave mode. + jpegls_encoder& interleave_mode(const interleave_mode interleave_mode) + { + check_jpegls_errc(charls_jpegls_encoder_set_interleave_mode(encoder_.get(), interleave_mode)); + return *this; + } + + /// + /// Configures the preset coding parameters the encoder should use. + /// If not set the encoder will use the default preset coding parameters as defined by the JPEG-LS standard. + /// Only when the coding parameters are different then the default parameters, they will be written to the + /// JPEG-LS stream during the encode phase. + /// + /// Reference to the preset coding parameters. + jpegls_encoder& preset_coding_parameters(const jpegls_pc_parameters& preset_coding_parameters) + { + check_jpegls_errc(charls_jpegls_encoder_set_preset_coding_parameters(encoder_.get(), &preset_coding_parameters)); + return *this; + } + + /// + /// Configures the HP color transformation the encoder should use. + /// If not set the encoder will use no color transformation. + /// Color transformations are a HP extension and not defined by the JPEG-LS standard + /// and can only be set for 3 component encodings. + /// + /// The color transformation parameters. + jpegls_encoder& color_transformation(const color_transformation color_transformation) + { + check_jpegls_errc(charls_jpegls_encoder_set_color_transformation(encoder_.get(), color_transformation)); + return *this; + } + + /// + /// Returns the size in bytes, that the encoder expects are needed to hold the encoded image. + /// + /// + /// Size for dynamic extras like SPIFF entries and other tables are not included in this size. + /// + /// The estimated size in bytes needed to hold the encoded image. + CHARLS_NO_DISCARD size_t estimated_destination_size() const + { + size_t size_in_bytes; + check_jpegls_errc(charls_jpegls_encoder_get_estimated_destination_size(encoder_.get(), &size_in_bytes)); + return size_in_bytes; + } + + /// + /// Set the reference to the destination buffer that will contain the encoded JPEG-LS byte stream data after encoding. + /// This buffer needs to remain valid during the encoding process. + /// + /// Reference to the start of the destination buffer. + /// Size of the destination buffer in bytes. + jpegls_encoder& destination(OUT_WRITES_BYTES_(destination_size_bytes) void* destination_buffer, + const size_t destination_size_bytes) + { + check_jpegls_errc( + charls_jpegls_encoder_set_destination_buffer(encoder_.get(), destination_buffer, destination_size_bytes)); + return *this; + } + + /// + /// Set the the container that will contain the encoded JPEG-LS byte stream data after encoding. + /// This container needs to remain valid during the encoding process. + /// + /// + /// The STL like container, that supports the functions data() and size() and the typedef value_type. + /// + template + jpegls_encoder& destination(OUT_ Container& destination_container) + { + return destination(destination_container.data(), destination_container.size() * sizeof(ValueType)); + } + + /// + /// Writes a standard SPIFF header to the destination. The additional values are computed from the current encoder + /// settings. + /// + /// The color space of the image. + /// The resolution units of the next 2 parameters. + /// The vertical resolution. + /// The horizontal resolution. + jpegls_encoder& + write_standard_spiff_header(const spiff_color_space color_space, + const spiff_resolution_units resolution_units = spiff_resolution_units::aspect_ratio, + const uint32_t vertical_resolution = 1, const uint32_t horizontal_resolution = 1) + { + check_jpegls_errc(charls_jpegls_encoder_write_standard_spiff_header(encoder_.get(), color_space, resolution_units, + vertical_resolution, horizontal_resolution)); + return *this; + } + + /// + /// Writes a SPIFF header to the destination. + /// + /// Reference to a SPIFF header that will be written to the destination. + jpegls_encoder& write_spiff_header(const spiff_header& header) + { + check_jpegls_errc(charls_jpegls_encoder_write_spiff_header(encoder_.get(), &header)); + return *this; + } + + /// + /// Writes a SPIFF directory entry to the destination. + /// + /// The entry tag of the directory entry. + /// The entry data of the directory entry. + /// The size in bytes of the directory entry [0-65528]. + template + jpegls_encoder& write_spiff_entry(const IntDerivedType entry_tag, + IN_READS_BYTES_(entry_data_size_bytes) const void* entry_data, + const size_t entry_data_size_bytes) + { + check_jpegls_errc(charls_jpegls_encoder_write_spiff_entry(encoder_.get(), static_cast(entry_tag), + entry_data, entry_data_size_bytes)); + return *this; + } + + /// + /// Encodes the passed buffer with the source image data to the destination. + /// + /// Byte array that holds the image data that needs to be encoded. + /// Length of the array in bytes. + /// + /// The number of bytes from one row of pixels in memory to the next row of pixels in memory. + /// Stride is sometimes called pitch. If padding bytes are present, the stride is wider than the width of the image. + /// + /// The number of bytes written to the destination. + size_t encode(IN_READS_BYTES_(source_size_bytes) const void* source_buffer, const size_t source_size_bytes, + const uint32_t stride = 0) const + { + check_jpegls_errc( + charls_jpegls_encoder_encode_from_buffer(encoder_.get(), source_buffer, source_size_bytes, stride)); + return bytes_written(); + } + + /// + /// Encodes the passed STL like container with the source image data to the destination. + /// + /// Container that holds the image data that needs to be encoded. + /// + /// The number of bytes from one row of pixels in memory to the next row of pixels in memory. + /// Stride is sometimes called pitch. If padding bytes are present, the stride is wider than the width of the image. + /// + /// The number of bytes written to the destination. + template + size_t encode(const Container& source_container, const uint32_t stride = 0) const + { + return encode(source_container.data(), source_container.size() * sizeof(ValueType), stride); + } + + /// + /// Returns the size in bytes, that are written to the destination. + /// + /// The bytes written. + CHARLS_NO_DISCARD size_t bytes_written() const + { + size_t bytes_written; + check_jpegls_errc(charls_jpegls_encoder_get_bytes_written(encoder_.get(), &bytes_written)); + return bytes_written; + } + +private: + CHARLS_NO_DISCARD static charls_jpegls_encoder* create_encoder() + { + charls_jpegls_encoder* encoder{charls_jpegls_encoder_create()}; + if (!encoder) + throw std::bad_alloc(); + + return encoder; + } + + static void destroy_encoder(IN_OPT_ const charls_jpegls_encoder* encoder) noexcept + { + charls_jpegls_encoder_destroy(encoder); + } + + std::unique_ptr encoder_{create_encoder(), + &destroy_encoder}; +}; + +} // namespace charls + +#endif diff -Nru charls-2.1.0+dfsg/include/charls/charls_legacy.h charls-2.2.0+dfsg/include/charls/charls_legacy.h --- charls-2.1.0+dfsg/include/charls/charls_legacy.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/include/charls/charls_legacy.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -#pragma once - -#include "public_types.h" - -#include - -// -// ByteStreamInfo & FromByteArray helper function -// -// ByteStreamInfo describes the stream: either set rawStream to a valid stream, or rawData/count, not both. -// it's possible to decode to memory streams, but using rawData will always be faster. -struct ByteStreamInfo final -{ - std::basic_streambuf* rawStream; - uint8_t* rawData; - std::size_t count; -}; - - -inline ByteStreamInfo FromByteArray(void* bytes, std::size_t count) noexcept -{ - return {nullptr, static_cast(bytes), count}; -} - - -inline ByteStreamInfo FromByteArrayConst(const void* bytes, std::size_t count) noexcept -{ - return FromByteArray(const_cast(bytes), count); -} - -CHARLS_API_IMPORT_EXPORT charls::jpegls_errc JpegLsEncodeStream(ByteStreamInfo destination, size_t& bytesWritten, ByteStreamInfo source, const JlsParameters& params); -CHARLS_API_IMPORT_EXPORT charls::jpegls_errc JpegLsDecodeStream(ByteStreamInfo destination, ByteStreamInfo source, const JlsParameters* params); -CHARLS_API_IMPORT_EXPORT charls::jpegls_errc JpegLsReadHeaderStream(ByteStreamInfo source, JlsParameters* params); diff -Nru charls-2.1.0+dfsg/include/charls/jpegls_error.h charls-2.2.0+dfsg/include/charls/jpegls_error.h --- charls-2.1.0+dfsg/include/charls/jpegls_error.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/include/charls/jpegls_error.h 2021-01-10 18:07:25.000000000 +0000 @@ -6,13 +6,10 @@ #include "public_types.h" #ifdef __cplusplus - -#include - extern "C" { +CHARLS_API_IMPORT_EXPORT const std::error_category* CHARLS_API_CALLING_CONVENTION charls_get_jpegls_category(); #endif -CHARLS_API_IMPORT_EXPORT const void* CHARLS_API_CALLING_CONVENTION charls_get_jpegls_category(void); CHARLS_API_IMPORT_EXPORT const char* CHARLS_API_CALLING_CONVENTION charls_get_error_message(charls_jpegls_errc error_value); #ifdef __cplusplus @@ -22,7 +19,7 @@ CHARLS_NO_DISCARD inline const std::error_category& jpegls_category() noexcept { - return *static_cast(charls_get_jpegls_category()); + return *(charls_get_jpegls_category()); } CHARLS_NO_DISCARD inline std::error_code make_error_code(jpegls_errc error_value) noexcept @@ -37,13 +34,11 @@ class jpegls_error final : public std::system_error { public: - explicit jpegls_error(std::error_code ec) : - system_error{ec} + explicit jpegls_error(const std::error_code ec) : system_error{ec} { } - explicit jpegls_error(jpegls_errc error_value) : - system_error{error_value} + explicit jpegls_error(const jpegls_errc error_value) : system_error{error_value} { } }; @@ -60,6 +55,8 @@ #define CHARLS_NO_INLINE #endif +// Not inlined by design, as this code path is the exceptional case. +// It will help to allow the compiler to inline other functions. [[noreturn]] inline CHARLS_NO_INLINE void throw_jpegls_error(const jpegls_errc error_value) { throw jpegls_error(error_value); @@ -73,7 +70,7 @@ { if (error_value != jpegls_errc::success) { - impl::throw_jpegls_error(error_value); // not inlined by design, as this code path is the exceptional case. + impl::throw_jpegls_error(error_value); } } diff -Nru charls-2.1.0+dfsg/include/charls/public_types.h charls-2.2.0+dfsg/include/charls/public_types.h --- charls-2.1.0+dfsg/include/charls/public_types.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/include/charls/public_types.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,6 +3,7 @@ #pragma once +#include "annotations.h" #include "api_abi.h" #ifdef __cplusplus @@ -21,6 +22,7 @@ // The following enum values are for C applications, for C++ the enums are defined after these definitions. // For the documentation, see the C++ enums definitions. +RETURN_TYPE_SUCCESS_(return == 0) enum charls_jpegls_errc { CHARLS_JPEGLS_ERRC_SUCCESS = 0, @@ -39,7 +41,7 @@ CHARLS_JPEGLS_ERRC_NOT_ENOUGH_MEMORY = 13, CHARLS_JPEGLS_ERRC_UNEXPECTED_FAILURE = 14, CHARLS_JPEGLS_ERRC_START_OF_IMAGE_MARKER_NOT_FOUND = 15, - CHARLS_JPEGLS_ERRC_START_OF_FRAME_MARKER_NOT_FOUND = 16, + CHARLS_JPEGLS_ERRC_UNEXPECTED_MARKER_FOUND = 16, CHARLS_JPEGLS_ERRC_INVALID_MARKER_SEGMENT_SIZE = 17, CHARLS_JPEGLS_ERRC_DUPLICATE_START_OF_IMAGE_MARKER = 18, CHARLS_JPEGLS_ERRC_DUPLICATE_START_OF_FRAME_MARKER = 19, @@ -54,7 +56,7 @@ CHARLS_JPEGLS_ERRC_INVALID_ARGUMENT_BITS_PER_SAMPLE = 103, CHARLS_JPEGLS_ERRC_INVALID_ARGUMENT_INTERLEAVE_MODE = 104, CHARLS_JPEGLS_ERRC_INVALID_ARGUMENT_NEAR_LOSSLESS = 105, - CHARLS_JPEGLS_ERRC_INVALID_ARGUMENT_PC_PARAMETERS = 106, + CHARLS_JPEGLS_ERRC_INVALID_ARGUMENT_JPEGLS_PC_PARAMETERS = 106, CHARLS_JPEGLS_ERRC_INVALID_ARGUMENT_SPIFF_ENTRY_SIZE = 110, CHARLS_JPEGLS_ERRC_INVALID_ARGUMENT_COLOR_TRANSFORMATION = 111, CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_WIDTH = 200, @@ -62,6 +64,8 @@ CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_COMPONENT_COUNT = 202, CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_BITS_PER_SAMPLE = 203, CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_INTERLEAVE_MODE = 204, + CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_NEAR_LOSSLESS = 205, + CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_JPEGLS_PC_PARAMETERS = 206 }; enum charls_interleave_mode @@ -143,14 +147,15 @@ }; #ifdef __cplusplus -} -} +} // namespace impl +} // namespace charls namespace charls { /// /// Defines the result values that are returned by the CharLS API functions. /// +RETURN_TYPE_SUCCESS_(return == 0) enum class jpegls_errc { /// @@ -224,7 +229,8 @@ not_enough_memory = impl::CHARLS_JPEGLS_ERRC_NOT_ENOUGH_MEMORY, /// - /// This error is returned when the implementation encountered a failure it did not expect. No guarantees can be given for the state after this error. + /// This error is returned when the implementation encountered a failure it did not expect. No guarantees can be given + /// for the state after this error. /// unexpected_failure = impl::CHARLS_JPEGLS_ERRC_UNEXPECTED_FAILURE, @@ -234,9 +240,9 @@ start_of_image_marker_not_found = impl::CHARLS_JPEGLS_ERRC_START_OF_IMAGE_MARKER_NOT_FOUND, /// - /// This error is returned when the SOF JPEG marker is not found before the SOS marker. + /// This error is returned when a JPEG marker is found that is not valid for the current state. /// - start_of_frame_marker_not_found = impl::CHARLS_JPEGLS_ERRC_START_OF_FRAME_MARKER_NOT_FOUND, + unexpected_marker_found = impl::CHARLS_JPEGLS_ERRC_UNEXPECTED_MARKER_FOUND, /// /// This error is returned when the segment size of a marker segment is invalid. @@ -271,7 +277,8 @@ /// /// This error is returned when the stream contains an unsupported type parameter in the JPEG-LS segment. /// - jpegls_preset_extended_parameter_type_not_supported = impl::CHARLS_JPEGLS_ERRC_JPEGLS_PRESET_EXTENDED_PARAMETER_TYPE_NOT_SUPPORTED, + jpegls_preset_extended_parameter_type_not_supported = + impl::CHARLS_JPEGLS_ERRC_JPEGLS_PRESET_EXTENDED_PARAMETER_TYPE_NOT_SUPPORTED, /// /// This error is returned when the stream contains a SPIFF header but not an SPIFF end-of-directory entry. @@ -312,7 +319,7 @@ /// The argument for the JPEG-LS preset coding parameters is not valid, see ISO/IEC 14495-1, /// C.2.4.1.1, Table C.1 for the ranges of valid values. /// - invalid_argument_pc_parameters = impl::CHARLS_JPEGLS_ERRC_INVALID_ARGUMENT_PC_PARAMETERS, + invalid_argument_jpegls_pc_parameters = impl::CHARLS_JPEGLS_ERRC_INVALID_ARGUMENT_JPEGLS_PC_PARAMETERS, /// /// The argument for the entry size parameter is outside the range [0, 65528]. @@ -340,7 +347,8 @@ invalid_parameter_component_count = impl::CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_COMPONENT_COUNT, /// - /// This error is returned when the stream contains a bits per sample (sample precision) parameter outside the range [2,16] + /// This error is returned when the stream contains a bits per sample (sample precision) parameter outside the range + /// [2,16] /// invalid_parameter_bits_per_sample = impl::CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_BITS_PER_SAMPLE, @@ -349,10 +357,22 @@ /// invalid_parameter_interleave_mode = impl::CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_INTERLEAVE_MODE, - // Legacy enumerator names, will be removed in next major release. Not tagged with [[deprecated]] as that is a C++17 extension. + /// + /// This error is returned when the stream contains a near-lossless (NEAR) parameter outside the range [0, min(255, + /// MAXVAL/2)] + /// + invalid_parameter_near_lossless = impl::CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_NEAR_LOSSLESS, + + /// + /// This error is returned when the stream contains an invalid JPEG-LS preset coding parameters segment. + /// + invalid_parameter_jpegls_pc_parameters = impl::CHARLS_JPEGLS_ERRC_INVALID_PARAMETER_JPEGLS_PC_PARAMETERS, + + // Legacy enumerator names, will be removed in next major release. Not tagged with [[deprecated]] as that is a C++17 + // extension. OK = success, InvalidJlsParameters = invalid_argument, - ParameterValueNotSupported = invalid_encoded_data, + ParameterValueNotSupported = parameter_value_not_supported, UncompressedBufferTooSmall = destination_buffer_too_small, CompressedBufferTooSmall = source_buffer_too_small, InvalidCompressedData = invalid_encoded_data, @@ -385,7 +405,8 @@ /// sample = impl::CHARLS_INTERLEAVE_MODE_SAMPLE, - // Legacy enumerator names, will be removed in next major release. Not tagged with [[deprecated]] as that is a C++17 extension. + // Legacy enumerator names, will be removed in next major release. Not tagged with [[deprecated]] as that is a C++17 + // extension. None = none, Line = line, Sample = sample @@ -393,9 +414,9 @@ /// /// Defines color space transformations as defined and implemented by the JPEG-LS library of HP Labs. -/// These color space transformation decrease the correlation between the 3 color components, resulting in better encoding ratio. -/// These options are only implemented for backwards compatibility and NOT part of the JPEG-LS standard. -/// The JPEG-LS ISO/IEC 14495-1:1999 standard provides no capabilities to transport which color space transformation was used. +/// These color space transformation decrease the correlation between the 3 color components, resulting in better encoding +/// ratio. These options are only implemented for backwards compatibility and NOT part of the JPEG-LS standard. The JPEG-LS +/// ISO/IEC 14495-1:1999 standard provides no capabilities to transport which color space transformation was used. /// enum class color_transformation { @@ -428,7 +449,8 @@ /// hp3 = impl::CHARLS_COLOR_TRANSFORMATION_HP3, - // Legacy enumerator names, will be removed in next major release. Not tagged with [[deprecated]] as that is a C++17 extension. + // Legacy enumerator names, will be removed in next major release. Not tagged with [[deprecated]] as that is a C++17 + // extension. None = none, HP1 = hp1, HP2 = hp2, @@ -436,7 +458,8 @@ }; /// -/// Defines the Application profile identifier options that can be used in a SPIFF header v2, as defined in ISO/IEC 10918-3, F.1.2 +/// Defines the Application profile identifier options that can be used in a SPIFF header v2, as defined in ISO/IEC 10918-3, +/// F.1.2 /// enum class spiff_profile_id : int32_t { @@ -497,7 +520,8 @@ ycbcr_itu_bt_601_1_video = impl::CHARLS_SPIFF_COLOR_SPACE_YCBCR_ITU_BT_601_1_VIDEO, /// - /// Grayscale – This is a single component sample with interpretation as grayscale value, 0 is minimum, 2bps -1 is maximum. + /// Grayscale – This is a single component sample with interpretation as grayscale value, 0 is minimum, 2bps -1 is + /// maximum. /// grayscale = impl::CHARLS_SPIFF_COLOR_SPACE_GRAYSCALE, @@ -614,7 +638,8 @@ transfer_characteristics = impl::CHARLS_SPIFF_ENTRY_TAG_TRANSFER_CHARACTERISTICS, /// - /// This entry specifies component registration, the spatial positioning of samples within components relative to the samples of other components. + /// This entry specifies component registration, the spatial positioning of samples within components relative to the + /// samples of other components. /// component_registration = impl::CHARLS_SPIFF_ENTRY_TAG_COMPONENT_REGISTRATION, @@ -654,7 +679,8 @@ creator_identification = impl::CHARLS_SPIFF_ENTRY_TAG_CREATOR_IDENTIFICATION, /// - /// The presence of this entry, indicates that the image’s owner has retained copyright protection and usage rights for the image. + /// The presence of this entry, indicates that the image’s owner has retained copyright protection and usage rights for + /// the image. /// protection_indicator = impl::CHARLS_SPIFF_ENTRY_TAG_PROTECTION_INDICATOR, @@ -754,16 +780,16 @@ /// struct charls_spiff_header CHARLS_FINAL { - charls_spiff_profile_id profile_id; // P: Application profile, type I.8 - int32_t component_count; // NC: Number of color components, range [1, 255], type I.8 - uint32_t height; // HEIGHT: Number of lines in image, range [1, 4294967295], type I.32 - uint32_t width; // WIDTH: Number of samples per line, range [1, 4294967295], type I.32 - charls_spiff_color_space color_space; // S: Color space used by image data, type is I.8 - int32_t bits_per_sample; // BPS: Number of bits per sample, range (1, 2, 4, 8, 12, 16), type is I.8 + charls_spiff_profile_id profile_id; // P: Application profile, type I.8 + int32_t component_count; // NC: Number of color components, range [1, 255], type I.8 + uint32_t height; // HEIGHT: Number of lines in image, range [1, 4294967295], type I.32 + uint32_t width; // WIDTH: Number of samples per line, range [1, 4294967295], type I.32 + charls_spiff_color_space color_space; // S: Color space used by image data, type is I.8 + int32_t bits_per_sample; // BPS: Number of bits per sample, range (1, 2, 4, 8, 12, 16), type is I.8 charls_spiff_compression_type compression_type; // C: Type of data compression used, type is I.8 charls_spiff_resolution_units resolution_units; // R: Type of resolution units, type is I.8 - uint32_t vertical_resolution; // VRES: Vertical resolution, range [1, 4294967295], type can be F or I.32 - uint32_t horizontal_resolution; // HRES: Horizontal resolution, range [1, 4294967295], type can be F or I.32 + uint32_t vertical_resolution; // VRES: Vertical resolution, range [1, 4294967295], type can be F or I.32 + uint32_t horizontal_resolution; // HRES: Horizontal resolution, range [1, 4294967295], type can be F or I.32 }; @@ -942,8 +968,9 @@ /// /// The number of valid bits per sample to encode. - /// Valid range 2 - 16. When greater than 8, pixels are assumed to stored as two bytes per sample, otherwise one byte per sample is assumed. - /// This parameter is called "Sample precision" in the JPEG-LS standard, often also called "Bit Depth". + /// Valid range 2 - 16. When greater than 8, pixels are assumed to stored as two bytes per sample, otherwise one byte per + /// sample is assumed. This parameter is called "Sample precision" in the JPEG-LS standard, often also called "Bit + /// Depth". /// int32_t bitsPerSample; diff -Nru charls-2.1.0+dfsg/include/charls/version.h charls-2.2.0+dfsg/include/charls/version.h --- charls-2.1.0+dfsg/include/charls/version.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/include/charls/version.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,28 +3,25 @@ #pragma once +#include "annotations.h" #include "api_abi.h" -#define CHARLS_VERSION_MAJOR 2 -#define CHARLS_VERSION_MINOR 1 -#define CHARLS_VERSION_PATCH 0 - #ifdef __cplusplus - #include - extern "C" { - #else - #include - #endif + +#define CHARLS_VERSION_MAJOR 2 +#define CHARLS_VERSION_MINOR 2 +#define CHARLS_VERSION_PATCH 0 + /// /// Returns the version of CharLS in the semver format "major.minor.patch" or "major.minor.patch-pre_release" /// -CHARLS_API_IMPORT_EXPORT const char* CHARLS_API_CALLING_CONVENTION charls_get_version_string(void) CHARLS_NOEXCEPT; +CHARLS_API_IMPORT_EXPORT const char* CHARLS_API_CALLING_CONVENTION charls_get_version_string(CHARLS_C_VOID); /// /// Returns the version of CharLS in its numerical format. @@ -32,7 +29,8 @@ /// Reference to the major number, may be NULL/nullptr when this info is not needed. /// Reference to the minor number, may be NULL/nullptr when this info is not needed. /// Reference to the patch number, may be NULL/nullptr when this info is not needed. -CHARLS_API_IMPORT_EXPORT void CHARLS_API_CALLING_CONVENTION charls_get_version_number(int32_t* major, int32_t* minor, int32_t* patch) CHARLS_NOEXCEPT; +CHARLS_API_IMPORT_EXPORT void CHARLS_API_CALLING_CONVENTION +charls_get_version_number(OUT_OPT_ int32_t* major, OUT_OPT_ int32_t* minor, OUT_OPT_ int32_t* patch); #ifdef __cplusplus } diff -Nru charls-2.1.0+dfsg/LICENSE.md charls-2.2.0+dfsg/LICENSE.md --- charls-2.1.0+dfsg/LICENSE.md 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/LICENSE.md 2021-01-10 18:07:25.000000000 +0000 @@ -1,7 +1,7 @@ BSD 3-Clause License -Copyright (c) 2007, Jan de Vaan \ Victor Derks -All rights reserved. +Copyright (c) 2007, Jan de Vaan and Victor Derks +All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -26,4 +26,4 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -Nru charls-2.1.0+dfsg/README.md charls-2.2.0+dfsg/README.md --- charls-2.1.0+dfsg/README.md 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/README.md 2021-01-10 18:07:25.000000000 +0000 @@ -6,6 +6,7 @@ [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://raw.githubusercontent.com/team-charls/charls/master/LICENSE.md) [![Build status](https://ci.appveyor.com/api/projects/status/yq0naf3v2m8nfa8r/branch/master?svg=true)](https://ci.appveyor.com/project/vbaderks/charls/branch/master) [![Build Status](https://travis-ci.org/team-charls/charls.svg?branch=master)](https://travis-ci.org/team-charls/charls) +[![Build Status](https://dev.azure.com/team-charls/charls/_apis/build/status/team-charls.charls?branchName=master)](https://dev.azure.com/team-charls/charls/_build/latest?definitionId=2&branchName=master) [![Vcpkg package](https://repology.org/badge/version-for-repo/vcpkg/charls.svg)](https://repology.org/metapackage/charls) CharLS is a C++ implementation of the JPEG-LS standard for lossless and near-lossless image compression and decompression. @@ -14,8 +15,8 @@ ## Features * C++14 library implementation with a binary C interface for maximum interoperability. -* Supports Windows, Linux and Solaris in 32 bit and 64 bit. -* Includes an adapter assembly for .NET based languages. +* Supports Windows, Linux and macOS on x86, x64, arm, arm64 and ppc64le. +* Adapters for .NET, JavaScript (WebAssembly) and Python applications available. * Excellent compression and decompression performance. ## About JPEG-LS @@ -42,7 +43,7 @@ or in multiple scans, but not use a mix of these in one file. * No support for oversize image dimension. Maximum supported image dimensions are [1, 65535] by [1, 65535]. * No support for JPEG-LS mapping tables. -* No support Point transform. +* No support for point transform. Point transform is a lossly encoding mechanism and not used in lossless scenarios. #### Note about JPEG-LS part 2 @@ -61,7 +62,7 @@ PS> vcpkg install charls charls:x64-windows ``` -With [vcpkg](https://github.com/Microsoft/vcpkg) on Linux or MacOS +With [vcpkg](https://github.com/Microsoft/vcpkg) on Linux or macOS ```bash ~/$ ./vcpkg install charls @@ -80,9 +81,15 @@ Before any major breaking change in the API and/or ABI a branch will be created from master to freeze that snapshot as LTS branch. +## Related Projects + +* [CharLS.Native .NET](https://github.com/team-charls/charls-native-dotnet) - a .NET 5.0 adapter assembly for CharLS +* [JPEG-LS WIC codec](https://github.com/team-charls/jpegls-wic-codec) - Windows Imaging Component (WIC) codec for JPEG-LS .jls files +* [charls-js](https://github.com/chafey/charls-js) - WebAssembly build of CharLS, [Demo](https://chafey.github.io/charls-js/test/browser/index.html) + ## Users & Acknowledgements -CharLS is being used by [GDCM DICOM toolkit](http://sourceforge.net/projects/gdcm/), thanks for [Mathieu Malaterre](http://sourceforge.net/users/malat) for getting CharLS started on Linux. Kato Kanryu wrote an initial version of the color transforms and the DIB output format code, for an [irfanview](http://www.irfanview.com) plugin using CharLS. Thanks to Uli Schlachter, CharLS now finally runs correctly on big-endian architectures like Sun SPARC. +CharLS is being used by [GDCM DICOM toolkit](http://sourceforge.net/projects/gdcm/), thanks for [Mathieu Malaterre](http://sourceforge.net/users/malat) for getting CharLS started on Linux. Kato Kanryu wrote an initial version of the color transforms and the DIB output format code, for an [irfanview](http://www.irfanview.com) plugin using CharLS. ## Legal diff -Nru charls-2.1.0+dfsg/samples/convert/Convert.csproj charls-2.2.0+dfsg/samples/convert/Convert.csproj --- charls-2.1.0+dfsg/samples/convert/Convert.csproj 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert/Convert.csproj 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ - - - - Debug - x86 - 8.0.30703 - 2.0 - {19337FF7-ADE8-4D18-8998-6B03C8B6A630} - Exe - Properties - Convert - Convert - v4.0 - Client - 512 - - - x86 - true - full - false - ..\..\..\Win32\Debug\ - DEBUG;TRACE - prompt - - - x86 - pdbonly - true - bin\Release\ - TRACE - prompt - - - true - bin\x86\Checked\ - DEBUG;TRACE - full - x86 - ..\..\..\Win32\Debug\Convert.exe.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - - - - - - - - - - - - - - - - - - - - {4EDED401-1B63-4989-BF3F-2875B6C9203D} - CharLSNet - - - - - \ No newline at end of file diff -Nru charls-2.1.0+dfsg/samples/convert/Program.cs charls-2.2.0+dfsg/samples/convert/Program.cs --- charls-2.1.0+dfsg/samples/convert/Program.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert/Program.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System; -using System.IO; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using CharLS; - -namespace Convert -{ - internal class Program - { - private const int Success = 0; - private const int Failure = 1; - - private static int Main(string[] args) - { - // This sample demonstrates how to convert 8 bit monochrome images and 24 bit color images to a .jls - // The input path should be a absolute path to a file format .NET can read (.bmp, .png, etc). - string inputPath; - if (!TryParseArguments(args, out inputPath)) - { - Console.WriteLine("Usage: Converter "); - return Failure; - } - - try - { - // Load the image file and get the first frame. - var decoder = BitmapDecoder.Create(new Uri(inputPath, UriKind.Absolute), - BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); - - var frame = decoder.Frames[0]; - int componentCount; - if (!TryGetComponentCount(frame.Format, out componentCount)) - { - Console.WriteLine("Input has format: {0}, which is not supported", frame.Format); - return Failure; - } - - var uncompressedPixels = new byte[frame.PixelWidth * frame.PixelHeight * componentCount]; - frame.CopyPixels(uncompressedPixels, frame.PixelWidth * componentCount, 0); - - // Prepare the 'info' metadata that describes the pixels in the byte buffer. - var info = new JpegLSMetadataInfo(frame.PixelWidth, frame.PixelHeight, 8, componentCount); - if (componentCount == 3) - { - info.InterleaveMode = JpegLSInterleaveMode.Line; - - // PixelFormat is Bgr24. CharLS expects RGB byte stream. - // By enabling this CharLS will transform input before decoding. - info.OutputBgr = true; - } - - // Compress. - var compressedPixels = JpegLSCodec.Compress(info, uncompressedPixels, true); - - Save(compressedPixels.Array, compressedPixels.Count, GetOutputPath(inputPath)); - } - catch (FileNotFoundException e) - { - Console.WriteLine("Error: " + e.Message); - } - - return Success; - } - - private static bool TryParseArguments(string[] args, out string inputPath) - { - inputPath = string.Empty; - - if (args.Length != 1) - return false; - - inputPath = args[0]; - return true; - } - - private static bool TryGetComponentCount(PixelFormat format, out int componentCount) - { - // For this sample: only RGB color and 8 bit monochrome images are supported. - if (format == PixelFormats.Bgr24) - { - componentCount = 3; - } - else if (format == PixelFormats.Gray8) - { - componentCount = 1; - } - else - { - componentCount = 0; - } - - return componentCount != 0; - } - - private static string GetOutputPath(string inputPath) - { - return Path.ChangeExtension(inputPath, ".jls"); - } - - private static void Save(byte[] pixels, int count, string path) - { - using (var output = new FileStream(path, FileMode.OpenOrCreate)) - { - output.Write(pixels, 0, count); - } - } - } -} diff -Nru charls-2.1.0+dfsg/samples/convert/Properties/AssemblyInfo.cs charls-2.2.0+dfsg/samples/convert/Properties/AssemblyInfo.cs --- charls-2.1.0+dfsg/samples/convert/Properties/AssemblyInfo.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert/Properties/AssemblyInfo.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Convert")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Convert")] -[assembly: AssemblyCopyright("Copyright (c) Team CharLS")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("8cb88f0e-291d-49ce-9f9d-20e2cbb7f300")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff -Nru charls-2.1.0+dfsg/samples/convert.c/app.manifest charls-2.2.0+dfsg/samples/convert.c/app.manifest --- charls-2.1.0+dfsg/samples/convert.c/app.manifest 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert.c/app.manifest 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,21 @@ + + + + + + + + + + + + true" + + + UTF-8 + + + SegmentHeap + + + \ No newline at end of file diff -Nru charls-2.1.0+dfsg/samples/convert.c/CMakeLists.txt charls-2.2.0+dfsg/samples/convert.c/CMakeLists.txt --- charls-2.1.0+dfsg/samples/convert.c/CMakeLists.txt 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert.c/CMakeLists.txt 2021-01-10 18:07:25.000000000 +0000 @@ -8,6 +8,11 @@ main.c ) +if(WIN32 AND MSVC_VERSION GREATER_EQUAL 1920) + # Only add the manifest file when building a Windows app + target_sources(convert-c PRIVATE app.manifest) +endif() + target_link_libraries(convert-c PRIVATE charls) if(MSVC) Binary files /tmp/tmptxyqr8/iDZBmIvU8B/charls-2.1.0+dfsg/samples/convert.c/lena512.bmp and /tmp/tmptxyqr8/i0J_6ju0mM/charls-2.2.0+dfsg/samples/convert.c/lena512.bmp differ diff -Nru charls-2.1.0+dfsg/samples/convert.c/main.c charls-2.2.0+dfsg/samples/convert.c/main.c --- charls-2.1.0+dfsg/samples/convert.c/main.c 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert.c/main.c 2021-01-10 18:07:25.000000000 +0000 @@ -4,12 +4,15 @@ #include #include +// ReSharper disable once CppUnusedIncludeDirective #include #include #include #include #include +#include +const static size_t bytes_per_rgb_pixel = 3; typedef struct { @@ -31,7 +34,7 @@ { uint32_t header_size; // the size of this header (40 bytes) uint32_t width; // the bitmap width in pixels - uint32_t height; // the bitmap height in pixels + int32_t height; // the bitmap height in pixels uint16_t number_planes; // the number of color planes being used. Must be set to 1 uint16_t depth; // the number of bits per pixel,which is the color depth of the image. // Typical values are 1, 4, 8, 16, 24 and 32. @@ -41,7 +44,8 @@ uint32_t horizontal_resolution; // the horizontal resolution of the image. (pixel per meter) uint32_t vertical_resolution; // the vertical resolution of the image. (pixel per meter) uint32_t number_colors; // the number of colors in the color palette, or 0 to default to 2^depth. - uint32_t number_important_colors; // the number of important colors used, or 0 when every color is important generally ignored. + uint32_t number_important_colors; // the number of important colors used, or 0 when every color is important generally + // ignored. } bmp_dib_header_t; @@ -53,8 +57,7 @@ return fread(&header->magic, sizeof header->magic, 1, fp) && fread(&header->file_size, sizeof header->file_size, 1, fp) && fread(&header->reserved, sizeof header->reserved, 1, fp) && - fread(&header->offset, sizeof header->offset, 1, fp) && - header->magic[0] == 0x42 && header->magic[1] == 0x4D; + fread(&header->offset, sizeof header->offset, 1, fp) && header->magic[0] == 0x42 && header->magic[1] == 0x4D; } @@ -63,34 +66,50 @@ assert(fp); assert(header); - return fread(&header->header_size, sizeof header->header_size, 1, fp) && - fread(&header->width, sizeof header->width, 1, fp) && - fread(&header->height, sizeof header->height, 1, fp) && + return fread(&header->header_size, sizeof header->header_size, 1, fp) && header->header_size >= 40 && + fread(&header->width, sizeof header->width, 1, fp) && fread(&header->height, sizeof header->height, 1, fp) && fread(&header->number_planes, sizeof header->number_planes, 1, fp) && fread(&header->depth, sizeof header->depth, 1, fp) && - fread(&header->compress_type, sizeof header->compress_type, 1, fp); + fread(&header->compress_type, sizeof header->compress_type, 1, fp) && + fread(&header->bmp_byte_size, sizeof header->bmp_byte_size, 1, fp) && + fread(&header->horizontal_resolution, sizeof header->horizontal_resolution, 1, fp) && + fread(&header->vertical_resolution, sizeof header->vertical_resolution, 1, fp) && + fread(&header->number_colors, sizeof header->number_colors, 1, fp) && + fread(&header->number_important_colors, sizeof header->number_important_colors, 1, fp); } -static void* bmp_read_pixel_data(FILE* fp, uint32_t offset, const bmp_dib_header_t* header, size_t* buffer_size) +static void* bmp_read_pixel_data(FILE* fp, const uint32_t offset, const bmp_dib_header_t* dib_header, size_t* stride) { assert(fp); - assert(header); - assert(buffer_size); + assert(dib_header); + assert(stride); + assert(dib_header->compress_type == 0); + assert(dib_header->depth == 24); - if (fseek(fp, offset, SEEK_SET)) + if (fseek(fp, (long)offset, SEEK_SET)) return NULL; - *buffer_size = (size_t)header->height * header->width * 3; - void* buffer = malloc(*buffer_size); - if (buffer && fread(buffer, *buffer_size, 1, fp)) - return buffer; + // The BMP format requires that the size of each row is rounded up to a multiple of 4 bytes by padding. + *stride = ((dib_header->width * bytes_per_rgb_pixel) + 3) / 4 * 4; - free(buffer); - return NULL; + const size_t buffer_size = (size_t)dib_header->height * *stride; + void* buffer = malloc(buffer_size); + if (!buffer) + return NULL; + + if (fread(buffer, buffer_size, 1, fp) != 1) + { + free(buffer); + return NULL; + } + + return buffer; } -static void* handle_encoder_failure(charls_jpegls_errc error, const char* step, charls_jpegls_encoder* encoder, void* buffer) + +static void* handle_encoder_failure(const charls_jpegls_errc error, const char* step, charls_jpegls_encoder* encoder, + void* buffer) { printf("Failed to %s: %i, %s\n", step, error, charls_get_error_message(error)); charls_jpegls_encoder_destroy(encoder); @@ -99,10 +118,78 @@ } -static void* encode_bmp_to_jpegls(const void* pixel_data, size_t pixel_data_size, const bmp_dib_header_t* header, int near_lossless, size_t* bytes_written) +static void convert_bgr_to_rgb(uint8_t* triplet_buffer, const size_t width, const size_t height, const size_t stride) +{ + for (size_t line = 0; line < height; ++line) + { + const size_t line_start = line * stride; + for (size_t pixel = 0; pixel < width; ++pixel) + { + const size_t column = pixel * bytes_per_rgb_pixel; + + const size_t a = line_start + column; + const size_t b = line_start + column + 2; + + const uint8_t temp = triplet_buffer[a]; + triplet_buffer[a] = triplet_buffer[b]; + triplet_buffer[b] = temp; + } + } +} + + +static void triplet_to_planar(const uint8_t* triplet_buffer, uint8_t* planar_buffer, const size_t width, const size_t height, + const size_t stride) { - assert(header->depth == 24); // This function only supports 24-bit BMP pixel data. - assert(header->compress_type == 0); // Data needs to be stored by pixel as RGB. + const size_t byte_count_plane = width * height; + size_t plane_column = 0; + + for (size_t line = 0; line < height; ++line) + { + const size_t line_start = line * stride; + for (size_t pixel = 0; pixel < width; ++pixel) + { + const size_t column = line_start + pixel * bytes_per_rgb_pixel; + + planar_buffer[plane_column] = triplet_buffer[column]; + planar_buffer[plane_column + 1 * byte_count_plane] = triplet_buffer[column + 1]; + planar_buffer[plane_column + 2 * byte_count_plane] = triplet_buffer[column + 2]; + ++plane_column; + } + } +} + + +static bool convert_bottom_up_to_top_down(uint8_t* triplet_buffer, const size_t width, const size_t height, + const size_t stride) +{ + const size_t row_length = width * bytes_per_rgb_pixel; + void* temp_row = malloc(row_length); + if (!temp_row) + return false; + + for (size_t i = 0; i < height / 2; ++i) + { + memcpy(temp_row, &triplet_buffer[i * stride], row_length); + const size_t bottom_row = height - i - 1; + memcpy(&triplet_buffer[i * stride], &triplet_buffer[bottom_row * stride], row_length); + memcpy(&triplet_buffer[bottom_row * stride], temp_row, row_length); + } + + free(temp_row); + + return true; +} + + +static void* encode_bmp_to_jpegls(const void* pixel_data, const size_t stride, const bmp_dib_header_t* header, + const charls_interleave_mode interleave_mode, const int near_lossless, + size_t* bytes_written) +{ + assert(header->depth == 24 && "This function only supports 24-bit BMP pixel data."); + assert(header->compress_type == 0 && "Data needs to be stored by pixel as RGB."); + assert(header->width > 0 && "0 width not supported, may cause 0 byte malloc"); + assert(header->height > 0 && "0 and negative height not supported, may cause 0 byte malloc"); charls_jpegls_encoder* encoder = charls_jpegls_encoder_create(); if (!encoder) @@ -113,23 +200,23 @@ charls_frame_info frame_info = {.bits_per_sample = 8, .component_count = 3}; frame_info.width = header->width; - frame_info.height = header->height; + frame_info.height = (uint32_t)header->height; charls_jpegls_errc error = charls_jpegls_encoder_set_frame_info(encoder, &frame_info); if (error) { return handle_encoder_failure(error, "set frame_info", encoder, NULL); } - error = charls_jpegls_encoder_set_near_lossless(encoder, near_lossless); + error = charls_jpegls_encoder_set_interleave_mode(encoder, interleave_mode); if (error) { - return handle_encoder_failure(error, "set near lossless", encoder, NULL); + return handle_encoder_failure(error, "set near interleave mode", encoder, NULL); } - error = charls_jpegls_encoder_set_interleave_mode(encoder, CHARLS_INTERLEAVE_MODE_SAMPLE); + error = charls_jpegls_encoder_set_near_lossless(encoder, near_lossless); if (error) { - return handle_encoder_failure(error, "set near interleave mode", encoder, NULL); + return handle_encoder_failure(error, "set near lossless", encoder, NULL); } size_t encoded_buffer_size; @@ -140,22 +227,56 @@ } void* encoded_buffer = malloc(encoded_buffer_size); + if (!encoded_buffer) + { + return handle_encoder_failure(error, "malloc failed", encoder, NULL); + } + error = charls_jpegls_encoder_set_destination_buffer(encoder, encoded_buffer, encoded_buffer_size); if (error) { return handle_encoder_failure(error, "set destination buffer", encoder, encoded_buffer); } - error = charls_jpegls_encoder_write_standard_spiff_header(encoder, - CHARLS_SPIFF_COLOR_SPACE_RGB, - CHARLS_SPIFF_RESOLUTION_UNITS_DOTS_PER_CENTIMETER, - header->vertical_resolution / 100, header->vertical_resolution / 100); + // The resolution in BMP files is often 0 to indicate that no resolution has been defined. + // The SPIFF header specification requires however that VRES and HRES are never 0. + // The ISO 10918-3 recommendation for these cases is to define that the pixels should be interpreted as a square. + if (header->vertical_resolution < 100 || header->horizontal_resolution < 100) + { + error = charls_jpegls_encoder_write_standard_spiff_header(encoder, CHARLS_SPIFF_COLOR_SPACE_RGB, + CHARLS_SPIFF_RESOLUTION_UNITS_ASPECT_RATIO, 1, 1); + } + else + { + error = charls_jpegls_encoder_write_standard_spiff_header( + encoder, CHARLS_SPIFF_COLOR_SPACE_RGB, CHARLS_SPIFF_RESOLUTION_UNITS_DOTS_PER_CENTIMETER, + header->vertical_resolution / 100, header->horizontal_resolution / 100); + } + if (error) { return handle_encoder_failure(error, "write_standard_spiff_header", encoder, encoded_buffer); } - error = charls_jpegls_encoder_encode_from_buffer(encoder, pixel_data, pixel_data_size, 0); + if (interleave_mode == CHARLS_INTERLEAVE_MODE_NONE) + { + const size_t pixel_data_size = (size_t)header->height * header->width * bytes_per_rgb_pixel; + void* planar_pixel_data = malloc(pixel_data_size); + if (!planar_pixel_data) + { + return handle_encoder_failure(CHARLS_JPEGLS_ERRC_NOT_ENOUGH_MEMORY, "malloc", encoder, encoded_buffer); + } + + triplet_to_planar(pixel_data, planar_pixel_data, header->width, (size_t)header->height, stride); + error = charls_jpegls_encoder_encode_from_buffer(encoder, planar_pixel_data, pixel_data_size, 0); + free(planar_pixel_data); + } + else + { + const size_t pixel_data_size = (size_t)header->height * stride; + error = charls_jpegls_encoder_encode_from_buffer(encoder, pixel_data, pixel_data_size, (uint32_t)stride); + } + if (error) { return handle_encoder_failure(error, "encode", encoder, encoded_buffer); @@ -173,7 +294,7 @@ } -static bool save_jpegls_file(const char* filename, const void* buffer, size_t buffer_size) +static bool save_jpegls_file(const char* filename, const void* buffer, const size_t buffer_size) { assert(filename); assert(buffer); @@ -191,29 +312,80 @@ } -int main(int argc, char* argv[]) +typedef struct +{ + const char* input_filename; + const char* output_filename; + charls_interleave_mode interleave_mode; + int near_lossless; +} options_t; + + +static bool parse_command_line_options(const int argc, char* argv[], options_t* options) { if (argc < 3) { - printf("Usage: [near_lossless, default=0 (lossless)]\n"); - return EXIT_FAILURE; + printf("Usage: [interleave-mode (none, line or sample), default = none] " + "[near-lossless, default=0 (lossless)]\n"); + return false; } - int near_lossless = 0; + options->input_filename = argv[1]; + options->output_filename = argv[2]; + if (argc > 3) { - near_lossless = strtol(argv[3], NULL, 10); - if (near_lossless < 0 || near_lossless > 255) + if (strcmp(argv[3], "none") == 0) { - printf("Argument near_lossless needs to be in the range [0,255]\n"); - return EXIT_FAILURE; + options->interleave_mode = CHARLS_INTERLEAVE_MODE_NONE; + } + else if (strcmp(argv[3], "line") == 0) + { + options->interleave_mode = CHARLS_INTERLEAVE_MODE_LINE; } + else if (strcmp(argv[3], "sample") == 0) + { + options->interleave_mode = CHARLS_INTERLEAVE_MODE_SAMPLE; + } + else + { + printf("Argument interleave-mode needs to be: none, line or sample\n"); + return false; + } + } + else + { + options->interleave_mode = CHARLS_INTERLEAVE_MODE_NONE; } - FILE* input_stream = fopen(argv[1], "rb"); + if (argc > 4) + { + options->near_lossless = (int)strtol(argv[4], NULL, 10); + if (options->near_lossless < 0 || options->near_lossless > 255) + { + printf("Argument near-lossless needs to be in the range [0,255]\n"); + return false; + } + } + else + { + options->near_lossless = 0; + } + + return true; +} + + +int main(const int argc, char* argv[]) +{ + options_t options; + if (!parse_command_line_options(argc, argv, &options)) + return EXIT_FAILURE; + + FILE* input_stream = fopen(options.input_filename, "rb"); if (!input_stream) { - printf("Failed to open file: %s, errno: %d\n", argv[1], errno); + printf("Failed to open file: %s, errno: %d\n", options.input_filename, errno); return EXIT_FAILURE; } @@ -233,8 +405,15 @@ return EXIT_FAILURE; } - size_t buffer_size; - void* pixel_data = bmp_read_pixel_data(input_stream, header.offset, &dib_header, &buffer_size); + if (dib_header.width == 0 || dib_header.height == 0) + { + printf("Can only process an image that is 1 x 1 or bigger"); + fclose(input_stream); + return EXIT_FAILURE; + } + + size_t stride; + void* pixel_data = bmp_read_pixel_data(input_stream, header.offset, &dib_header, &stride); fclose(input_stream); if (!pixel_data) @@ -243,19 +422,39 @@ return EXIT_FAILURE; } + // Pixels in the BMP file format are stored bottom up (when the height parameter is positive), JPEG-LS requires top down. + if (dib_header.height > 0) + { + if (!convert_bottom_up_to_top_down(pixel_data, dib_header.width, (size_t)dib_header.height, stride)) + { + printf("Failed to convert the pixels from bottom up to top down\n"); + return EXIT_FAILURE; + } + } + else + { + dib_header.height = abs(dib_header.height); + } + + // Pixels in the BMP file format are stored as BGR. JPEG-LS (SPIFF header) only supports the RGB color model. + // Note: without the optional SPIFF header no color information is stored in the JPEG-LS file and the common assumption + // is RGB. + convert_bgr_to_rgb(pixel_data, dib_header.width, (size_t)dib_header.height, stride); + size_t encoded_size; - void* encoded_data = encode_bmp_to_jpegls(pixel_data, buffer_size, &dib_header, near_lossless, &encoded_size); + void* encoded_data = + encode_bmp_to_jpegls(pixel_data, stride, &dib_header, options.interleave_mode, options.near_lossless, &encoded_size); free(pixel_data); if (!encoded_data) return EXIT_FAILURE; // error already printed. - const bool result = save_jpegls_file(argv[2], encoded_data, encoded_size); + const bool result = save_jpegls_file(options.output_filename, encoded_data, encoded_size); free(encoded_data); if (!result) { - printf("Failed to write encoded data to the file: %s\n", argv[2]); + printf("Failed to write encoded data to the file: %s\n", options.output_filename); return EXIT_FAILURE; } return EXIT_SUCCESS; -} \ No newline at end of file +} diff -Nru charls-2.1.0+dfsg/samples/convert.cpp/app.manifest charls-2.2.0+dfsg/samples/convert.cpp/app.manifest --- charls-2.1.0+dfsg/samples/convert.cpp/app.manifest 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert.cpp/app.manifest 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,21 @@ + + + + + + + + + + + + true" + + + UTF-8 + + + SegmentHeap + + + \ No newline at end of file diff -Nru charls-2.1.0+dfsg/samples/convert.cpp/bmp_image.h charls-2.2.0+dfsg/samples/convert.cpp/bmp_image.h --- charls-2.1.0+dfsg/samples/convert.cpp/bmp_image.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert.cpp/bmp_image.h 2021-01-10 18:07:25.000000000 +0000 @@ -12,7 +12,7 @@ public: struct bmp_header final { - uint8_t magic[2]; // the magic number used to identify the BMP file: + uint16_t magic; // the magic number used to identify the BMP file: // 0x42 0x4D (Hex code points for B and M). // The following entries are possible: // BM - Windows 3.1x, 95, NT, ... etc @@ -30,7 +30,7 @@ { uint32_t header_size; // the size of this header (40 bytes) uint32_t width; // the bitmap width in pixels - uint32_t height; // the bitmap height in pixels + int32_t height; // the bitmap height in pixels (if negative image is top-down) uint16_t number_planes; // the number of color planes being used. Must be set to 1 uint16_t depth; // the number of bits per pixel,which is the color depth of the image. // Typical values are 1, 4, 8, 16, 24 and 32. @@ -40,7 +40,8 @@ uint32_t horizontal_resolution; // the horizontal resolution of the image. (pixel per meter) uint32_t vertical_resolution; // the vertical resolution of the image. (pixel per meter) uint32_t number_colors; // the number of colors in the color palette, or 0 to default to 2^depth. - uint32_t number_important_colors; // the number of important colors used, or 0 when every color is important generally ignored. + uint32_t number_important_colors; // the number of important colors used, or 0 when every color is important + // generally ignored. }; explicit bmp_image(const char* filename) @@ -50,14 +51,26 @@ input.open(filename, std::ios_base::in | std::ios_base::binary); header = read_bmp_header(input); - if (header.magic[0] != 0x42 || header.magic[1] != 0x4D) + if (header.magic != 0x4D42) throw std::istream::failure("Missing BMP identifier"); dib_header = read_dib_header(input); + if (dib_header.header_size < 40 || dib_header.compress_type != 0 || dib_header.depth != 24) + throw std::istream::failure("Can only read uncompressed 24 bits BMP files"); + + if (dib_header.width == 0 || dib_header.height == 0) + throw std::istream::failure("Can only process an image that is 1 x 1 or bigger"); + + // The BMP format requires that the size of each row is rounded up to a multiple of 4 bytes by padding. + constexpr int bytes_per_pixel{3}; + stride = ((dib_header.width * bytes_per_pixel) + 3) / 4 * 4; + + pixel_data = read_pixel_data(input, header.offset, dib_header.height, stride); } bmp_header header; bmp_dib_header dib_header; + uint32_t stride{}; std::vector pixel_data; private: @@ -65,10 +78,10 @@ { bmp_header result{}; - input.read(reinterpret_cast(&result.magic), sizeof result.magic); - input.read(reinterpret_cast(&result.file_size), sizeof result.file_size); - input.read(reinterpret_cast(&result.reserved), sizeof result.reserved); - input.read(reinterpret_cast(&result.offset), sizeof result.offset); + read(input, result.magic); + read(input, result.file_size); + read(input, result.reserved); + read(input, result.offset); return result; } @@ -77,13 +90,35 @@ { bmp_dib_header result{}; - input.read(reinterpret_cast(&result.header_size), sizeof result.header_size); - input.read(reinterpret_cast(&result.width), sizeof result.width); - input.read(reinterpret_cast(&result.height), sizeof result.height); - input.read(reinterpret_cast(&result.number_planes), sizeof result.number_planes); - input.read(reinterpret_cast(&result.depth), sizeof result.depth); - input.read(reinterpret_cast(&result.compress_type), sizeof result.compress_type); + read(input, result.header_size); + read(input, result.width); + read(input, result.height); + read(input, result.number_planes); + read(input, result.depth); + read(input, result.compress_type); + read(input, result.bmp_byte_size); + read(input, result.horizontal_resolution); + read(input, result.vertical_resolution); + read(input, result.number_colors); + read(input, result.number_important_colors); return result; } + + static std::vector read_pixel_data(std::istream& input, const uint32_t offset, const int32_t height, + const uint32_t stride) + { + input.seekg(offset); + + std::vector pixel_data(static_cast(height) * stride); + input.read(reinterpret_cast(pixel_data.data()), static_cast(pixel_data.size())); + + return pixel_data; + } + + template + static void read(std::istream& input, T& value) + { + input.read(reinterpret_cast(&value), sizeof value); + } }; diff -Nru charls-2.1.0+dfsg/samples/convert.cpp/CMakeLists.txt charls-2.2.0+dfsg/samples/convert.cpp/CMakeLists.txt --- charls-2.1.0+dfsg/samples/convert.cpp/CMakeLists.txt 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert.cpp/CMakeLists.txt 2021-01-10 18:07:25.000000000 +0000 @@ -10,6 +10,11 @@ main.cpp ) +if(WIN32 AND MSVC_VERSION GREATER_EQUAL 1920) + # Only add the manifest file when building a Windows app + target_sources(convert-cpp PRIVATE app.manifest) +endif() + set_target_properties(convert-cpp PROPERTIES CXX_VISIBILITY_PRESET hidden) target_link_libraries(convert-cpp PRIVATE charls) \ No newline at end of file diff -Nru charls-2.1.0+dfsg/samples/convert.cpp/main.cpp charls-2.2.0+dfsg/samples/convert.cpp/main.cpp --- charls-2.1.0+dfsg/samples/convert.cpp/main.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert.cpp/main.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -8,84 +8,222 @@ #include #include +#include #include #include namespace { -std::vector encode_bmp_image_to_jpegls(const bmp_image& image, int near_lossless) +constexpr size_t bytes_per_rgb_pixel{3}; + +void convert_bgr_to_rgb(std::vector& triplet_buffer, const size_t width, const size_t height, + const size_t stride) noexcept +{ + for (size_t line{}; line < height; ++line) + { + const auto line_start{line * stride}; + for (size_t pixel{}; pixel < width; ++pixel) + { + const auto column{pixel * bytes_per_rgb_pixel}; + std::swap(triplet_buffer[line_start + column], triplet_buffer[line_start + column + 2]); + } + } +} + +std::vector triplet_to_planar(const std::vector& buffer, const size_t width, const size_t height, + const size_t stride) +{ + std::vector result(bytes_per_rgb_pixel * width * height); + const size_t byte_count_plane{width * height}; + + size_t plane_column{}; + for (size_t line{}; line < height; ++line) + { + const auto line_start{line * stride}; + for (size_t pixel{}; pixel < width; ++pixel) + { + const auto column{line_start + pixel * bytes_per_rgb_pixel}; + result[plane_column] = buffer[column]; + result[plane_column + 1 * byte_count_plane] = buffer[column + 1]; + result[plane_column + 2 * byte_count_plane] = buffer[column + 2]; + ++plane_column; + } + } + + return result; +} + +void convert_bottom_up_to_top_down(uint8_t* triplet_buffer, const size_t width, const size_t height, const size_t stride) +{ + const size_t row_length{width * bytes_per_rgb_pixel}; + std::vector temp_row(row_length); + + for (size_t i{}; i < height / 2; ++i) + { + memcpy(temp_row.data(), &triplet_buffer[i * stride], row_length); + const size_t bottom_row{height - i - 1}; + memcpy(&triplet_buffer[i * stride], &triplet_buffer[bottom_row * stride], row_length); + memcpy(&triplet_buffer[bottom_row * stride], temp_row.data(), row_length); + } +} + +std::vector encode_bmp_image_to_jpegls(const bmp_image& image, const charls::interleave_mode interleave_mode, + const int near_lossless) { assert(image.dib_header.depth == 24); // This function only supports 24-bit BMP pixel data. assert(image.dib_header.compress_type == 0); // Data needs to be stored by pixel as RGB. charls::jpegls_encoder encoder; - encoder.frame_info({image.dib_header.width, image.dib_header.height, 8, 3}) + encoder.frame_info({image.dib_header.width, static_cast(image.dib_header.height), 8, bytes_per_rgb_pixel}) + .interleave_mode(interleave_mode) .near_lossless(near_lossless); std::vector buffer(encoder.estimated_destination_size()); encoder.destination(buffer); - encoder.write_standard_spiff_header(charls::spiff_color_space::rgb, - charls::spiff_resolution_units::dots_per_centimeter, - image.dib_header.vertical_resolution / 100, - image.dib_header.horizontal_resolution / 100); + // The resolution in BMP files is often 0 to indicate that no resolution has been defined. + // The SPIFF header specification requires however that VRES and HRES are never 0. + // The ISO 10918-3 recommendation for these cases is to define that the pixels should be interpreted as a square. + if (image.dib_header.vertical_resolution < 100 || image.dib_header.horizontal_resolution < 100) + { + encoder.write_standard_spiff_header(charls::spiff_color_space::rgb, charls::spiff_resolution_units::aspect_ratio, 1, + 1); + } + else + { + encoder.write_standard_spiff_header( + charls::spiff_color_space::rgb, charls::spiff_resolution_units::dots_per_centimeter, + image.dib_header.vertical_resolution / 100, image.dib_header.horizontal_resolution / 100); + } - const size_t encoded_size = encoder.encode(image.pixel_data); + size_t encoded_size; + if (interleave_mode == charls::interleave_mode::none) + { + const auto planar_pixel_data{triplet_to_planar(image.pixel_data, image.dib_header.width, + static_cast(image.dib_header.height), image.stride)}; + encoded_size = encoder.encode(planar_pixel_data); + } + else + { + encoded_size = encoder.encode(image.pixel_data, image.stride); + } buffer.resize(encoded_size); return buffer; } -void save_buffer_to_file(const void* buffer, size_t buffer_size, const char* filename) +void save_buffer_to_file(const void* buffer, const size_t buffer_size, const char* filename) { assert(filename); assert(buffer); assert(buffer_size); std::ofstream output(filename, std::ios::binary); - output.write(static_cast(buffer), buffer_size); + output.exceptions(std::ios::failbit | std::ios::badbit); + + output.write(static_cast(buffer), static_cast(buffer_size)); } void log_failure(const char* message) noexcept { try { - std::cerr << message << "\n"; + std::cerr << message << "\n"; } catch (...) { + // Catch and ignore all exceptions,to ensure a noexcept log function (but warn in debug builds) assert(false); } } +struct options final +{ + const char* input_filename; + const char* output_filename; + charls::interleave_mode interleave_mode; + int near_lossless; + + options(const int argc, char** argv) + { + if (argc < 3) + { + throw std::runtime_error("Usage: [interleave-mode (none, line, or sample), " + "default = none] [near-lossless, default = 0 (lossless)]\n"); + } + + input_filename = argv[1]; + output_filename = argv[2]; + + if (argc > 3) + { + interleave_mode = string_to_interleave_mode(argv[3]); + } + else + { + interleave_mode = charls::interleave_mode::none; + } + + if (argc > 4) + { + near_lossless = static_cast(strtol(argv[4], nullptr, 10)); + if (near_lossless < 0 || near_lossless > 255) + throw std::runtime_error("Argument near_lossless needs to be in the range [0,255]\n"); + } + else + { + near_lossless = 0; + } + } + +private: + static charls::interleave_mode string_to_interleave_mode(const char* argument) + { + if (strcmp(argument, "none") == 0) + return charls::interleave_mode::none; + + if (strcmp(argument, "line") == 0) + return charls::interleave_mode::line; + + if (strcmp(argument, "sample") == 0) + return charls::interleave_mode::sample; + + throw std::runtime_error("Argument interleave-mode needs to be: none, line or sample\n"); + } +}; + } // namespace -int main(const int argc, char const* const argv[]) +int main(const int argc, char** argv) { try { std::ios::sync_with_stdio(false); + const options options{argc, argv}; - if (argc < 3) + bmp_image bmp_image{options.input_filename}; + + // Pixels in the BMP file format are stored bottom up (when the height parameter is positive), JPEG-LS requires top + // down. + if (bmp_image.dib_header.height > 0) { - log_failure("Usage: [near-lossless-value, default=0 (lossless)]"); - return EXIT_FAILURE; + convert_bottom_up_to_top_down(bmp_image.pixel_data.data(), bmp_image.dib_header.width, + static_cast(bmp_image.dib_header.height), bmp_image.stride); } - - int near_lossless{}; - if (argc > 3) + else { - near_lossless = static_cast(strtol(argv[3], nullptr, 10)); - if (near_lossless < 0 || near_lossless > 255) - { - log_failure("near-lossless-value needs to be in the range [0,255]"); - return EXIT_FAILURE; - } + bmp_image.dib_header.height = std::abs(bmp_image.dib_header.height); } - auto encoded_buffer = encode_bmp_image_to_jpegls(bmp_image{argv[1]}, near_lossless); - save_buffer_to_file(encoded_buffer.data(), encoded_buffer.size(), argv[2]); + // Pixels in the BMP file format are stored as BGR. JPEG-LS (SPIFF header) only supports the RGB color model. + // Note: without the optional SPIFF header no color information is stored in the JPEG-LS file and the common + // assumption is RGB. + convert_bgr_to_rgb(bmp_image.pixel_data, bmp_image.dib_header.width, + static_cast(bmp_image.dib_header.height), bmp_image.stride); + + auto encoded_buffer{encode_bmp_image_to_jpegls(bmp_image, options.interleave_mode, options.near_lossless)}; + save_buffer_to_file(encoded_buffer.data(), encoded_buffer.size(), options.output_filename); return EXIT_SUCCESS; } diff -Nru charls-2.1.0+dfsg/samples/convert.cpp/pch.h charls-2.2.0+dfsg/samples/convert.cpp/pch.h --- charls-2.1.0+dfsg/samples/convert.cpp/pch.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/convert.cpp/pch.h 2021-01-10 18:07:25.000000000 +0000 @@ -6,5 +6,6 @@ #include #include +#include #include #include diff -Nru charls-2.1.0+dfsg/samples/viewer/App.config charls-2.2.0+dfsg/samples/viewer/App.config --- charls-2.1.0+dfsg/samples/viewer/App.config 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/App.config 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ - - - - - - diff -Nru charls-2.1.0+dfsg/samples/viewer/App.xaml charls-2.2.0+dfsg/samples/viewer/App.xaml --- charls-2.1.0+dfsg/samples/viewer/App.xaml 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/App.xaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ - - - - - diff -Nru charls-2.1.0+dfsg/samples/viewer/App.xaml.cs charls-2.2.0+dfsg/samples/viewer/App.xaml.cs --- charls-2.1.0+dfsg/samples/viewer/App.xaml.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/App.xaml.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System.Windows; - -namespace Viewer -{ - /// - /// Interaction logic for App.xaml - /// - public class App : Application - { - } -} diff -Nru charls-2.1.0+dfsg/samples/viewer/MainWindow.xaml charls-2.2.0+dfsg/samples/viewer/MainWindow.xaml --- charls-2.1.0+dfsg/samples/viewer/MainWindow.xaml 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/MainWindow.xaml 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - diff -Nru charls-2.1.0+dfsg/samples/viewer/MainWindow.xaml.cs charls-2.2.0+dfsg/samples/viewer/MainWindow.xaml.cs --- charls-2.1.0+dfsg/samples/viewer/MainWindow.xaml.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/MainWindow.xaml.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -using System.IO; -using System.Windows; -using CharLS; -using Microsoft.Win32; - -namespace Viewer -{ - /// - /// Interaction logic for MainWindow.xaml - /// - public partial class MainWindow - { - // Cache the File dialog to allow it to remember the last used directory. - private OpenFileDialog openFileDialog; - - public MainWindow() - { - InitializeComponent(); - } - - private void buttonBrowse_Click(object sender, RoutedEventArgs e) - { - if (openFileDialog == null) - { - openFileDialog = new OpenFileDialog - { - InitialDirectory = "c:\\", - Filter = "JPEG-LS files (*.jls)|*.jls|All files (*.*)|*.*" - }; - } - - if (!openFileDialog.ShowDialog().Value) - return; - - textBoxPath.Text = openFileDialog.FileName; - } - - private void buttonView_Click(object sender, RoutedEventArgs e) - { - try - { - using (var stream = new FileStream(textBoxPath.Text, FileMode.Open, FileAccess.Read)) - { - image.Source = new JpegLSBitmapDecoder(stream).Frames[0]; - } - } - catch (FileFormatException error) - { - MessageBox.Show("Error: " + error.Message, Title, MessageBoxButton.OK, MessageBoxImage.Error); - } - } - } -} diff -Nru charls-2.1.0+dfsg/samples/viewer/Properties/AssemblyInfo.cs charls-2.2.0+dfsg/samples/viewer/Properties/AssemblyInfo.cs --- charls-2.1.0+dfsg/samples/viewer/Properties/AssemblyInfo.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/Properties/AssemblyInfo.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; -using System.Windows; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Viewer_VS2013")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Viewer_VS2013")] -[assembly: AssemblyCopyright("Copyright (c) Team CharLS")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -//In order to begin building localizable applications, set -//CultureYouAreCodingWith in your .csproj file -//inside a . For example, if you are using US english -//in your source files, set the to en-US. Then uncomment -//the NeutralResourceLanguage attribute below. Update the "en-US" in -//the line below to match the UICulture setting in the project file. - -//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] - - -[assembly: ThemeInfo( - ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located - //(used if a resource is not found in the page, - // or application resource dictionaries) - ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located - //(used if a resource is not found in the page, - // app, or any theme specific resource dictionaries) -)] - - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff -Nru charls-2.1.0+dfsg/samples/viewer/Properties/Resources.Designer.cs charls-2.2.0+dfsg/samples/viewer/Properties/Resources.Designer.cs --- charls-2.1.0+dfsg/samples/viewer/Properties/Resources.Designer.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/Properties/Resources.Designer.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Viewer.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Viewer.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - } -} diff -Nru charls-2.1.0+dfsg/samples/viewer/Properties/Resources.resx charls-2.2.0+dfsg/samples/viewer/Properties/Resources.resx --- charls-2.1.0+dfsg/samples/viewer/Properties/Resources.resx 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/Properties/Resources.resx 1970-01-01 00:00:00.000000000 +0000 @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff -Nru charls-2.1.0+dfsg/samples/viewer/Properties/Settings.Designer.cs charls-2.2.0+dfsg/samples/viewer/Properties/Settings.Designer.cs --- charls-2.1.0+dfsg/samples/viewer/Properties/Settings.Designer.cs 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/Properties/Settings.Designer.cs 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Viewer.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - } -} diff -Nru charls-2.1.0+dfsg/samples/viewer/Properties/Settings.settings charls-2.2.0+dfsg/samples/viewer/Properties/Settings.settings --- charls-2.1.0+dfsg/samples/viewer/Properties/Settings.settings 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/Properties/Settings.settings 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff -Nru charls-2.1.0+dfsg/samples/viewer/Viewer_VS2015.csproj charls-2.2.0+dfsg/samples/viewer/Viewer_VS2015.csproj --- charls-2.1.0+dfsg/samples/viewer/Viewer_VS2015.csproj 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/samples/viewer/Viewer_VS2015.csproj 1970-01-01 00:00:00.000000000 +0000 @@ -1,354 +0,0 @@ - - - - - - Debug - AnyCPU - {62B47821-5105-49A2-A157-5FAE1AEA0686} - WinExe - Properties - Viewer - Viewer - v4.6.1 - 512 - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 4 - - 1 - - - true - DEBUG;TRACE - true - full - x64 - prompt - MinimumRecommendedRules.ruleset - true - True - False - True - False - False - False - True - True - False - False - False - True - True - False - False - False - True - False - True - True - False - False - - - - - - - - True - False - Full - DoNotBuild - 0 - - - TRACE - true - true - pdbonly - x64 - prompt - MinimumRecommendedRules.ruleset - true - True - False - True - False - False - False - True - True - False - False - False - True - True - False - False - False - True - False - True - True - False - False - - - - - - - - True - False - ReleaseRequires - DoNotBuild - 0 - - - true - DEBUG;TRACE - true - full - x86 - prompt - MinimumRecommendedRules.ruleset - true - True - False - True - False - False - False - True - True - False - False - False - True - True - False - False - False - True - False - True - True - False - False - - - - - - - - True - False - Full - DoNotBuild - 0 - - - TRACE - true - true - pdbonly - x86 - prompt - MinimumRecommendedRules.ruleset - true - True - False - True - False - False - False - True - True - False - False - False - True - True - False - False - False - True - False - True - True - False - False - - - - - - - - True - False - ReleaseRequires - DoNotBuild - 0 - - - true - DEBUG;TRACE - true - full - x64 - prompt - MinimumRecommendedRules.ruleset - true - True - False - True - False - False - True - True - True - False - False - False - True - True - False - False - False - True - False - True - True - False - False - - - - - - - - True - False - Full - DoNotBuild - 0 - - - true - DEBUG;TRACE - true - full - x86 - prompt - MinimumRecommendedRules.ruleset - true - True - False - True - False - False - True - True - True - False - False - False - True - True - False - False - False - True - False - True - True - False - False - - - - - - - - True - False - Full - DoNotBuild - 0 - - - - - - - - - - - - - MSBuild:Compile - Designer - - - MSBuild:Compile - Designer - - - App.xaml - Code - - - MainWindow.xaml - Code - - - - - Code - - - True - True - Resources.resx - - - True - Settings.settings - True - - - ResXFileCodeGenerator - Resources.Designer.cs - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - - Designer - - - - - {4eded401-1b63-4989-bf3f-2875b6c9203d} - CharLSNet_VS2015 - - - - - \ No newline at end of file diff -Nru charls-2.1.0+dfsg/SECURITY.md charls-2.2.0+dfsg/SECURITY.md --- charls-2.1.0+dfsg/SECURITY.md 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/SECURITY.md 2021-01-10 18:07:25.000000000 +0000 @@ -4,9 +4,10 @@ | Version | Supported | | ------- | ------------------ | -| 2.1.0 | :white_check_mark: | +| 2.2.0 | :white_check_mark: | +| 2.1.0 | :x: | | 2.0.0 | :x: | -| 1.1.0 | :white_check_mark: | +| 1.1.0 | :x: | | < 1.1.0 | :x: | ## Reporting a Vulnerability diff -Nru charls-2.1.0+dfsg/src/byte_span.h charls-2.2.0+dfsg/src/byte_span.h --- charls-2.1.0+dfsg/src/byte_span.h 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/src/byte_span.h 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,24 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include + +/// Simplified span class for C++14. +struct byte_span final +{ + byte_span() = default; + + byte_span(void* data_arg, const size_t size_arg) noexcept : data{static_cast(data_arg)}, size{size_arg} + { + } + + byte_span(const void* data_arg, const size_t size_arg) noexcept : + data{static_cast(const_cast(data_arg))}, size{size_arg} + { + } + + uint8_t* data{}; + size_t size{}; +}; diff -Nru charls-2.1.0+dfsg/src/charls.def charls-2.2.0+dfsg/src/charls.def --- charls-2.1.0+dfsg/src/charls.def 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/charls.def 2021-01-10 18:07:25.000000000 +0000 @@ -1,9 +1,18 @@ +; Copyright (c) Team CharLS. +; SPDX-License-Identifier: BSD-3-Clause + +; In x86 DLL builds, C functions can be exported with __declspec(dllexport) but will +; have different names depending on the calling convention. The __stdcall uses by default a +; decoration with @ and the number of bytes in the parameter list. +; The legacy functions have however always been exported with undecorated names, to ensure +; a stable ABI, explicit define the legacy exported functions here. +; Note 1: new functions are exported with the default naming decoration as this will +; ensure incorrect linkage detection at runtime +; Note 2: a x64 DLL build doesn't require a .def files as only 1 calling convention is used in x64 mode. + LIBRARY EXPORTS JpegLsEncode JpegLsDecode JpegLsDecodeRect - JpegLsReadHeader - JpegLsEncodeStream - JpegLsDecodeStream - JpegLsReadHeaderStream \ No newline at end of file + JpegLsReadHeader \ No newline at end of file diff -Nru charls-2.1.0+dfsg/src/charls_jpegls_decoder.cpp charls-2.2.0+dfsg/src/charls_jpegls_decoder.cpp --- charls-2.1.0+dfsg/src/charls_jpegls_decoder.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/charls_jpegls_decoder.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -1,40 +1,40 @@ // Copyright (c) Team CharLS. // SPDX-License-Identifier: BSD-3-Clause -#include +#include #include "jpeg_stream_reader.h" #include "util.h" -#include #include #include using std::unique_ptr; using namespace charls; +using impl::throw_jpegls_error; struct charls_jpegls_decoder final { - void source(const void* source_buffer, size_t source_size_bytes) + void source(IN_READS_BYTES_(source_size_bytes) const void* source_buffer, const size_t source_size_bytes) + CHARLS_ATTRIBUTE((nonnull)) { if (state_ != state::initial) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); source_buffer_ = source_buffer; size_ = source_size_bytes; - ByteStreamInfo source{FromByteArrayConst(source_buffer_, size_)}; - reader_ = std::make_unique(source); + reader_ = std::make_unique(byte_span{source_buffer, source_size_bytes}); state_ = state::source_set; } - bool read_header(spiff_header* spiff_header) + bool read_header(OUT_ spiff_header* spiff_header) { if (state_ != state::source_set) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); bool spiff_header_found{}; - reader_->ReadHeader(spiff_header, &spiff_header_found); + reader_->read_header(spiff_header, &spiff_header_found); state_ = spiff_header_found ? state::spiff_header_read : state::spiff_header_not_found; return spiff_header_found; @@ -43,105 +43,101 @@ void read_header() { if (state_ == state::initial || state_ >= state::header_read) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); if (state_ != state::spiff_header_not_found) { - reader_->ReadHeader(); + reader_->read_header(); } - reader_->ReadStartOfScan(true); + reader_->read_start_of_scan(); state_ = state::header_read; } charls::frame_info frame_info() const { if (state_ < state::header_read) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); - const auto& metadata = reader_->GetMetadata(); - return {static_cast(metadata.width), static_cast(metadata.height), metadata.bitsPerSample, metadata.components}; + return reader_->frame_info(); } - int32_t near_lossless(int32_t /*component*/) const + int32_t near_lossless(int32_t /*component*/ = 0) const { if (state_ < state::header_read) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); // Note: The JPEG-LS standard allows to define different NEAR parameter for every scan. - const auto& metadata = reader_->GetMetadata(); - return metadata.allowedLossyError; + return reader_->parameters().near_lossless; } charls::interleave_mode interleave_mode() const { if (state_ < state::header_read) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); // Note: The JPEG-LS standard allows to define different interleave modes for every scan. // CharLS doesn't support mixed interleave modes, first scan determines the mode. - const auto& metadata = reader_->GetMetadata(); - return metadata.interleaveMode; + return reader_->parameters().interleave_mode; + } + + charls::color_transformation color_transformation() const + { + if (state_ < state::header_read) + throw_jpegls_error(jpegls_errc::invalid_operation); + + return reader_->parameters().transformation; } const jpegls_pc_parameters& preset_coding_parameters() const { if (state_ < state::header_read) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); - return reader_->GetCustomPreset(); + return reader_->preset_coding_parameters(); } - size_t destination_size(const uint32_t stride) const + size_t destination_size(const size_t stride) const { const charls::frame_info info{frame_info()}; if (stride == 0) { - return static_cast(info.width) * info.height * info.component_count * (info.bits_per_sample <= 8 ? 1 : 2); + return static_cast(info.component_count) * info.height * info.width * + bit_to_byte_count(info.bits_per_sample); } switch (interleave_mode()) { - case interleave_mode::none: - return static_cast(stride) * info.height * info.component_count; + case charls::interleave_mode::none: + return stride * info.component_count * info.height; - case interleave_mode::line: - case interleave_mode::sample: - return static_cast(stride) * info.height; + case charls::interleave_mode::line: + case charls::interleave_mode::sample: + return stride * info.height; } ASSERT(false); return 0; } - void decode(void* destination_buffer, size_t destination_size_bytes, uint32_t stride) const + void decode(OUT_WRITES_BYTES_(destination_size_bytes) void* destination_buffer, const size_t destination_size_bytes, + const size_t stride) const CHARLS_ATTRIBUTE((nonnull)) { if (state_ != state::header_read) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); - if (stride != 0) - { - reader_->GetMetadata().stride = static_cast(stride); - } - - const ByteStreamInfo destination = FromByteArray(destination_buffer, destination_size_bytes); - reader_->Read(destination); - } - - void output_bgr(char value) const noexcept - { - reader_->SetOutputBgr(value); + reader_->read({destination_buffer, destination_size_bytes}, stride); } - const JlsParameters& metadata() const noexcept + void output_bgr(const bool value) const noexcept { - return reader_->GetMetadata(); + reader_->output_bgr(value); } void region(const JlsRect& rect) const noexcept { - reader_->SetRect(rect); + reader_->rect(rect); } private: @@ -152,11 +148,11 @@ spiff_header_read, spiff_header_not_found, header_read, - completed, + completed }; state state_{}; - unique_ptr reader_; + unique_ptr reader_; const void* source_buffer_{}; size_t size_{}; }; @@ -164,34 +160,26 @@ extern "C" { -#if defined _MSC_VER && _MSC_VER < 1917 -#pragma warning(disable : 26447) // Function is declared 'noexcept' but calls function '' which may throw exceptions (f.6). [false warning in VS 2017] -#endif - -charls_jpegls_decoder* CHARLS_API_CALLING_CONVENTION +CHARLS_CHECK_RETURN CHARLS_RET_MAY_BE_NULL charls_jpegls_decoder* CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_create() noexcept { - MSVC_WARNING_SUPPRESS(26402 26409) // don't use new and delete + scoped object and move - return new (std::nothrow) charls_jpegls_decoder; - MSVC_WARNING_UNSUPPRESS() + MSVC_WARNING_SUPPRESS_NEXT_LINE(26402 26409) // don't use new and delete + scoped object and move + return new (std::nothrow) charls_jpegls_decoder; // NOLINT(cppcoreguidelines-owning-memory) } -void CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_destroy(const charls_jpegls_decoder* decoder) noexcept +void CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_destroy(IN_OPT_ const charls_jpegls_decoder* decoder) noexcept { - MSVC_WARNING_SUPPRESS(26401 26409) // don't use new and delete + non-owner. - delete decoder; - MSVC_WARNING_UNSUPPRESS() + MSVC_WARNING_SUPPRESS_NEXT_LINE(26401 26409) // don't use new and delete + non-owner. + delete decoder; // NOLINT(cppcoreguidelines-owning-memory) } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_set_source_buffer(charls_jpegls_decoder* decoder, const void* source_buffer, size_t source_size_bytes) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_set_source_buffer(IN_ charls_jpegls_decoder* decoder, + IN_READS_BYTES_(source_size_bytes) + const void* source_buffer, + const size_t source_size_bytes) noexcept try { - if (!decoder || !source_buffer) - return jpegls_errc::invalid_argument; - - decoder->source(source_buffer, source_size_bytes); + check_pointer(decoder)->source(check_pointer(source_buffer), source_size_bytes); return jpegls_errc::success; } catch (...) @@ -199,14 +187,11 @@ return to_jpegls_errc(); } -charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_read_spiff_header(charls_jpegls_decoder* const decoder, charls_spiff_header* spiff_header, int32_t* header_found) noexcept +charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_read_spiff_header( + IN_ charls_jpegls_decoder* const decoder, OUT_ charls_spiff_header* spiff_header, OUT_ int32_t* header_found) noexcept try { - if (!decoder || !spiff_header || !header_found) - return jpegls_errc::invalid_argument; - - *header_found = decoder->read_header(spiff_header); + *check_pointer(header_found) = static_cast(check_pointer(decoder)->read_header(check_pointer(spiff_header))); return jpegls_errc::success; } catch (...) @@ -215,13 +200,10 @@ } jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_read_header(charls_jpegls_decoder* const decoder) noexcept +charls_jpegls_decoder_read_header(IN_ charls_jpegls_decoder* const decoder) noexcept try { - if (!decoder) - return jpegls_errc::invalid_argument; - - decoder->read_header(); + check_pointer(decoder)->read_header(); return jpegls_errc::success; } catch (...) @@ -229,14 +211,11 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_get_frame_info(const charls_jpegls_decoder* const decoder, charls_frame_info* frame_info) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_frame_info( + IN_ const charls_jpegls_decoder* const decoder, OUT_ charls_frame_info* frame_info) noexcept try { - if (!decoder || !frame_info) - return jpegls_errc::invalid_argument; - - *frame_info = decoder->frame_info(); + *check_pointer(frame_info) = check_pointer(decoder)->frame_info(); return jpegls_errc::success; } catch (...) @@ -244,14 +223,11 @@ return to_jpegls_errc(); } -charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_get_near_lossless(const charls_jpegls_decoder* decoder, int32_t component, int32_t* near_lossless) noexcept +charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_near_lossless( + IN_ const charls_jpegls_decoder* decoder, const int32_t component, OUT_ int32_t* near_lossless) noexcept try { - if (!decoder || !near_lossless) - return jpegls_errc::invalid_argument; - - *near_lossless = decoder->near_lossless(component); + *check_pointer(near_lossless) = check_pointer(decoder)->near_lossless(component); return jpegls_errc::success; } catch (...) @@ -259,14 +235,11 @@ return to_jpegls_errc(); } -charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_get_interleave_mode(const charls_jpegls_decoder* decoder, charls_interleave_mode* interleave_mode) noexcept +charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_interleave_mode( + IN_ const charls_jpegls_decoder* decoder, OUT_ charls_interleave_mode* interleave_mode) noexcept try { - if (!decoder || !interleave_mode) - return jpegls_errc::invalid_argument; - - *interleave_mode = decoder->interleave_mode(); + *check_pointer(interleave_mode) = check_pointer(decoder)->interleave_mode(); return jpegls_errc::success; } catch (...) @@ -275,13 +248,11 @@ } charls_jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_get_preset_coding_parameters(const charls_jpegls_decoder* decoder, int32_t /*reserved*/, charls_jpegls_pc_parameters* preset_coding_parameters) noexcept +charls_jpegls_decoder_get_preset_coding_parameters(IN_ const charls_jpegls_decoder* decoder, const int32_t /*reserved*/, + OUT_ charls_jpegls_pc_parameters* preset_coding_parameters) noexcept try { - if (!decoder || !preset_coding_parameters) - return jpegls_errc::invalid_argument; - - *preset_coding_parameters = decoder->preset_coding_parameters(); + *check_pointer(preset_coding_parameters) = check_pointer(decoder)->preset_coding_parameters(); return jpegls_errc::success; } catch (...) @@ -289,14 +260,11 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_get_destination_size(const struct charls_jpegls_decoder* decoder, const uint32_t stride, size_t* destination_size_bytes) noexcept +charls_jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_color_transformation( + IN_ const charls_jpegls_decoder* decoder, OUT_ charls_color_transformation* color_transformation) noexcept try { - if (!decoder || !destination_size_bytes) - return jpegls_errc::invalid_argument; - - *destination_size_bytes = decoder->destination_size(stride); + *check_pointer(color_transformation) = check_pointer(decoder)->color_transformation(); return jpegls_errc::success; } catch (...) @@ -304,14 +272,11 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_decoder_decode_to_buffer(const charls_jpegls_decoder* decoder, void* destination_buffer, size_t destination_size_bytes, uint32_t stride) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_get_destination_size( + IN_ const charls_jpegls_decoder* decoder, const uint32_t stride, OUT_ size_t* destination_size_bytes) noexcept try { - if (!decoder || !destination_buffer) - return jpegls_errc::invalid_argument; - - decoder->decode(destination_buffer, destination_size_bytes, stride); + *check_pointer(destination_size_bytes) = check_pointer(decoder)->destination_size(stride); return jpegls_errc::success; } catch (...) @@ -319,19 +284,39 @@ return to_jpegls_errc(); } - -jpegls_errc CHARLS_API_CALLING_CONVENTION -JpegLsReadHeader(const void* source, size_t sourceLength, JlsParameters* params, char* errorMessage) +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_decoder_decode_to_buffer( + IN_ const charls_jpegls_decoder* decoder, OUT_WRITES_BYTES_(destination_size_bytes) void* destination_buffer, + const size_t destination_size_bytes, const uint32_t stride) noexcept try { - if (!source || !params) - return jpegls_errc::invalid_argument; + check_pointer(decoder)->decode(check_pointer(destination_buffer), destination_size_bytes, stride); + return jpegls_errc::success; +} +catch (...) +{ + return to_jpegls_errc(); +} +jpegls_errc CHARLS_API_CALLING_CONVENTION JpegLsReadHeader(IN_READS_BYTES_(source_length) const void* source, + const size_t source_length, OUT_ JlsParameters* params, + OUT_OPT_ char* error_message) +try +{ charls_jpegls_decoder decoder; - decoder.source(source, sourceLength); + decoder.source(check_pointer(source), source_length); decoder.read_header(); - *params = decoder.metadata(); + *check_pointer(params) = JlsParameters{}; + const frame_info info{decoder.frame_info()}; + params->height = static_cast(info.height); + params->width = static_cast(info.width); + params->bitsPerSample = info.bits_per_sample; + params->components = info.component_count; + params->interleaveMode = decoder.interleave_mode(); + params->allowedLossyError = decoder.near_lossless(); + params->colorTransformation = decoder.color_transformation(); + const int32_t component_count{params->interleaveMode == interleave_mode::none ? 1 : params->components}; + params->stride = params->width * component_count * static_cast(bit_to_byte_count(params->bitsPerSample)); const auto& preset{decoder.preset_coding_parameters()}; params->custom.MaximumSampleValue = preset.maximum_sample_value; @@ -340,70 +325,69 @@ params->custom.Threshold3 = preset.threshold3; params->custom.ResetValue = preset.reset_value; - clear_error_message(errorMessage); + clear_error_message(error_message); return jpegls_errc::success; } catch (...) { - return set_error_message(to_jpegls_errc(), errorMessage); + return set_error_message(to_jpegls_errc(), error_message); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -JpegLsDecode(void* destination, size_t destinationLength, const void* source, size_t sourceLength, - const struct JlsParameters* params, char* errorMessage) +jpegls_errc CHARLS_API_CALLING_CONVENTION JpegLsDecode(OUT_WRITES_BYTES_(destination_length) void* destination, + const size_t destination_length, + IN_READS_BYTES_(source_length) const void* source, + const size_t source_length, + IN_OPT_ const struct JlsParameters* params, + OUT_OPT_ char* error_message) try { - if (!destination || !source) - return jpegls_errc::invalid_argument; - charls_jpegls_decoder decoder; - decoder.source(source, sourceLength); + decoder.source(check_pointer(source), source_length); decoder.read_header(); int32_t stride{}; if (params) { - decoder.output_bgr(params->outputBgr); + decoder.output_bgr(params->outputBgr != 0); stride = params->stride; } - decoder.decode(destination, destinationLength, static_cast(stride)); + decoder.decode(check_pointer(destination), destination_length, static_cast(stride)); - clear_error_message(errorMessage); + clear_error_message(error_message); return jpegls_errc::success; } catch (...) { - return set_error_message(to_jpegls_errc(), errorMessage); + return set_error_message(to_jpegls_errc(), error_message); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -JpegLsDecodeRect(void* destination, size_t destinationLength, const void* source, size_t sourceLength, - JlsRect roi, const JlsParameters* params, char* errorMessage) +jpegls_errc CHARLS_API_CALLING_CONVENTION JpegLsDecodeRect(OUT_WRITES_BYTES_(destination_length) void* destination, + const size_t destination_length, + IN_READS_BYTES_(source_length) const void* source, + const size_t source_length, const JlsRect roi, + IN_OPT_ const JlsParameters* params, OUT_OPT_ char* error_message) try { - if (!destination || !source) - return jpegls_errc::invalid_argument; - charls_jpegls_decoder decoder; - decoder.source(source, sourceLength); + decoder.source(check_pointer(source), source_length); decoder.read_header(); int32_t stride{}; if (params) { - decoder.output_bgr(params->outputBgr); + decoder.output_bgr(params->outputBgr != 0); stride = params->stride; } decoder.region(roi); - decoder.decode(destination, destinationLength, static_cast(stride)); + decoder.decode(check_pointer(destination), destination_length, static_cast(stride)); - clear_error_message(errorMessage); + clear_error_message(error_message); return jpegls_errc::success; } catch (...) { - return set_error_message(to_jpegls_errc(), errorMessage); + return set_error_message(to_jpegls_errc(), error_message); } } diff -Nru charls-2.1.0+dfsg/src/charls_jpegls_encoder.cpp charls-2.2.0+dfsg/src/charls_jpegls_encoder.cpp --- charls-2.1.0+dfsg/src/charls_jpegls_encoder.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/charls_jpegls_encoder.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -1,7 +1,7 @@ // Copyright (c) Team CharLS. // SPDX-License-Identifier: BSD-3-Clause -#include +#include #include "encoder_strategy.h" #include "jls_codec_factory.h" @@ -9,38 +9,36 @@ #include "jpegls_preset_coding_parameters.h" #include "util.h" -#include #include using namespace charls; +using impl::throw_jpegls_error; using std::unique_ptr; struct charls_jpegls_encoder final { - charls_jpegls_encoder() = default; - - void destination(void* destination, const size_t size) + void destination(const byte_span destination) { if (state_ != state::initial) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); - writer_.UpdateDestination(destination, size); + writer_.destination(destination); state_ = state::destination_set; } void frame_info(const charls_frame_info& frame_info) { if (frame_info.width < 1 || frame_info.width > maximum_width) - throw jpegls_error{jpegls_errc::invalid_argument_width}; + throw_jpegls_error(jpegls_errc::invalid_argument_width); if (frame_info.height < 1 || frame_info.height > maximum_height) - throw jpegls_error{jpegls_errc::invalid_argument_height}; + throw_jpegls_error(jpegls_errc::invalid_argument_height); - if (frame_info.bits_per_sample < MinimumBitsPerSample || frame_info.bits_per_sample > MaximumBitsPerSample) - throw jpegls_error{jpegls_errc::invalid_argument_bits_per_sample}; + if (frame_info.bits_per_sample < minimum_bits_per_sample || frame_info.bits_per_sample > maximum_bits_per_sample) + throw_jpegls_error(jpegls_errc::invalid_argument_bits_per_sample); - if (frame_info.component_count < 1 || frame_info.component_count > MaximumComponentCount) - throw jpegls_error{jpegls_errc::invalid_argument_component_count}; + if (frame_info.component_count < 1 || frame_info.component_count > maximum_component_count) + throw_jpegls_error(jpegls_errc::invalid_argument_component_count); frame_info_ = frame_info; } @@ -48,7 +46,7 @@ void interleave_mode(const charls::interleave_mode interleave_mode) { if (interleave_mode < charls::interleave_mode::none || interleave_mode > charls::interleave_mode::sample) - throw jpegls_error{jpegls_errc::invalid_argument_interleave_mode}; + throw_jpegls_error(jpegls_errc::invalid_argument_interleave_mode); interleave_mode_ = interleave_mode; } @@ -56,7 +54,7 @@ void near_lossless(const int32_t near_lossless) { if (near_lossless < 0 || near_lossless > maximum_near_lossless) - throw jpegls_error{jpegls_errc::invalid_argument_near_lossless}; + throw_jpegls_error(jpegls_errc::invalid_argument_near_lossless); near_lossless_ = near_lossless; } @@ -64,15 +62,16 @@ void preset_coding_parameters(const jpegls_pc_parameters& preset_coding_parameters) { if (!is_valid(preset_coding_parameters, UINT16_MAX, near_lossless_)) - throw jpegls_error{jpegls_errc::invalid_argument_pc_parameters}; + throw_jpegls_error(jpegls_errc::invalid_argument_jpegls_pc_parameters); preset_coding_parameters_ = preset_coding_parameters; } - void color_transformation(const color_transformation color_transformation) + void color_transformation(const charls::color_transformation color_transformation) { - if (color_transformation < charls::color_transformation::none || color_transformation > charls::color_transformation::hp3) - throw jpegls_error{jpegls_errc::invalid_argument_color_transformation}; + if (color_transformation < charls::color_transformation::none || + color_transformation > charls::color_transformation::hp3) + throw_jpegls_error(jpegls_errc::invalid_argument_color_transformation); color_transformation_ = color_transformation; } @@ -80,125 +79,124 @@ size_t estimated_destination_size() const { if (!is_frame_info_configured()) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); - return static_cast(frame_info_.width) * frame_info_.height * - frame_info_.component_count * (frame_info_.bits_per_sample < 9 ? 1 : 2) + - 1024 + spiff_header_size_in_bytes; + return static_cast(frame_info_.component_count) * frame_info_.width * frame_info_.height * + bit_to_byte_count(frame_info_.bits_per_sample) + + 1024 + spiff_header_size_in_bytes; } void write_spiff_header(const spiff_header& spiff_header) { if (spiff_header.height == 0) - throw jpegls_error{jpegls_errc::invalid_argument_height}; + throw_jpegls_error(jpegls_errc::invalid_argument_height); if (spiff_header.width == 0) - throw jpegls_error{jpegls_errc::invalid_argument_width}; + throw_jpegls_error(jpegls_errc::invalid_argument_width); if (state_ != state::destination_set) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); - writer_.WriteStartOfImage(); - writer_.WriteSpiffHeaderSegment(spiff_header); + writer_.write_start_of_image(); + writer_.write_spiff_header_segment(spiff_header); state_ = state::spiff_header; } - void write_standard_spiff_header(spiff_color_space color_space, const spiff_resolution_units resolution_units, const uint32_t vertical_resolution, const uint32_t horizontal_resolution) + void write_standard_spiff_header(const spiff_color_space color_space, const spiff_resolution_units resolution_units, + const uint32_t vertical_resolution, const uint32_t horizontal_resolution) { if (!is_frame_info_configured()) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); - write_spiff_header({spiff_profile_id::none, - frame_info_.component_count, - frame_info_.height, - frame_info_.width, - color_space, - frame_info_.bits_per_sample, - spiff_compression_type::jpeg_ls, - resolution_units, - vertical_resolution, - horizontal_resolution}); + write_spiff_header({spiff_profile_id::none, frame_info_.component_count, frame_info_.height, frame_info_.width, + color_space, frame_info_.bits_per_sample, spiff_compression_type::jpeg_ls, resolution_units, + vertical_resolution, horizontal_resolution}); } - void write_spiff_entry(const uint32_t entry_tag, const void* entry_data, const size_t entry_data_size) + void write_spiff_entry(const uint32_t entry_tag, IN_READS_BYTES_(entry_data_size_bytes) const void* entry_data, + const size_t entry_data_size_bytes) { if (entry_tag == spiff_end_of_directory_entry_type) - throw jpegls_error{jpegls_errc::invalid_argument}; + throw_jpegls_error(jpegls_errc::invalid_argument); - if (entry_data_size > 65528) - throw jpegls_error{jpegls_errc::invalid_argument_spiff_entry_size}; + if (entry_data_size_bytes > 65528) + throw_jpegls_error(jpegls_errc::invalid_argument_spiff_entry_size); if (state_ != state::spiff_header) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); - writer_.WriteSpiffDirectoryEntry(entry_tag, entry_data, entry_data_size); + writer_.write_spiff_directory_entry(entry_tag, entry_data, entry_data_size_bytes); } - void encode(const void* source, const size_t source_size, uint32_t stride) + void encode(byte_span source, size_t stride) { if (!is_frame_info_configured() || state_ == state::initial) - throw jpegls_error{jpegls_errc::invalid_operation}; + throw_jpegls_error(jpegls_errc::invalid_operation); if (stride == 0) { - stride = frame_info_.width * ((frame_info_.bits_per_sample + 7) / 8); + stride = static_cast(frame_info_.width) * bit_to_byte_count(frame_info_.bits_per_sample); if (interleave_mode_ != charls::interleave_mode::none) { - stride *= frame_info_.component_count; + stride *= static_cast(frame_info_.component_count); } } if (state_ == state::spiff_header) { - writer_.WriteSpiffEndOfDirectoryEntry(); + writer_.write_spiff_end_of_directory_entry(); } else { - writer_.WriteStartOfImage(); + writer_.write_start_of_image(); } - writer_.WriteStartOfFrameSegment(frame_info_.width, frame_info_.height, frame_info_.bits_per_sample, frame_info_.component_count); + writer_.write_start_of_frame_segment(frame_info_.width, frame_info_.height, frame_info_.bits_per_sample, + frame_info_.component_count); if (color_transformation_ != charls::color_transformation::none) { - writer_.WriteColorTransformSegment(color_transformation_); + if (!(frame_info_.bits_per_sample == 8 || frame_info_.bits_per_sample == 16)) + throw_jpegls_error(jpegls_errc::bit_depth_for_transform_not_supported); + + writer_.write_color_transform_segment(color_transformation_); } if (!is_default(preset_coding_parameters_)) { - writer_.WriteJpegLSPresetParametersSegment(preset_coding_parameters_); + writer_.write_jpegls_preset_parameters_segment(preset_coding_parameters_); } else if (frame_info_.bits_per_sample > 12) { - const jpegls_pc_parameters preset = compute_default((1 << frame_info_.bits_per_sample) - 1, near_lossless_); - writer_.WriteJpegLSPresetParametersSegment(preset); + const jpegls_pc_parameters preset{compute_default( + static_cast(calculate_maximum_sample_value(frame_info_.bits_per_sample)), near_lossless_)}; + writer_.write_jpegls_preset_parameters_segment(preset); } - ByteStreamInfo sourceInfo = FromByteArrayConst(source, source_size); if (interleave_mode_ == charls::interleave_mode::none) { - const int32_t byteCountComponent = frame_info_.width * frame_info_.height * ((frame_info_.bits_per_sample + 7) / 8); - for (int32_t component = 0; component < frame_info_.component_count; ++component) + const size_t byte_count_component{stride * frame_info_.height}; + for (int32_t component{}; component < frame_info_.component_count; ++component) { - writer_.WriteStartOfScanSegment(1, near_lossless_, interleave_mode_); - encode_scan(sourceInfo, stride, 1); + writer_.write_start_of_scan_segment(1, near_lossless_, interleave_mode_); + encode_scan(source, stride, 1); - // Synchronize the source stream (EncodeScan works on a local copy) - SkipBytes(sourceInfo, byteCountComponent); + // Synchronize the source stream (encode_scan works on a local copy) + skip_bytes(source, byte_count_component); } } else { - writer_.WriteStartOfScanSegment(frame_info_.component_count, near_lossless_, interleave_mode_); - encode_scan(sourceInfo, stride, frame_info_.component_count); + writer_.write_start_of_scan_segment(frame_info_.component_count, near_lossless_, interleave_mode_); + encode_scan(source, stride, frame_info_.component_count); } - writer_.WriteEndOfImage(); + writer_.write_end_of_image(); } size_t bytes_written() const noexcept { - return writer_.GetBytesWritten(); + return writer_.bytes_written(); } private: @@ -207,7 +205,7 @@ initial, destination_set, spiff_header, - completed, + completed }; bool is_frame_info_configured() const noexcept @@ -215,24 +213,18 @@ return frame_info_.width != 0; } - void encode_scan(ByteStreamInfo source, const uint32_t stride, const int32_t component_count) + void encode_scan(const byte_span source, const size_t stride, const int32_t component_count) { - JlsParameters info{}; - info.components = component_count; - info.bitsPerSample = frame_info_.bits_per_sample; - info.height = frame_info_.height; - info.width = frame_info_.width; - info.stride = stride; - info.interleaveMode = interleave_mode_; - info.allowedLossyError = near_lossless_; - - auto codec = JlsCodecFactory().CreateCodec(info, preset_coding_parameters_); - unique_ptr processLine(codec->CreateProcess(source)); - ByteStreamInfo destination{writer_.OutputStream()}; - const size_t bytesWritten = codec->EncodeScan(move(processLine), destination); + const charls::frame_info frame_info{frame_info_.width, frame_info_.height, frame_info_.bits_per_sample, + component_count}; + + auto codec{jls_codec_factory().create_codec( + frame_info, {near_lossless_, interleave_mode_, color_transformation_, false}, preset_coding_parameters_)}; + unique_ptr process_line(codec->create_process_line(source, stride)); + const size_t bytes_written{codec->encode_scan(move(process_line), writer_.remaining_destination())}; - // Synchronize the destination encapsulated in the writer (EncodeScan works on a local copy) - writer_.Seek(bytesWritten); + // Synchronize the destination encapsulated in the writer (encode_scan works on a local copy) + writer_.seek(bytes_written); } charls_frame_info frame_info_{}; @@ -240,40 +232,31 @@ charls::interleave_mode interleave_mode_{}; charls::color_transformation color_transformation_{}; state state_{}; - JpegStreamWriter writer_; + jpeg_stream_writer writer_; jpegls_pc_parameters preset_coding_parameters_{}; }; extern "C" { -#if defined _MSC_VER && _MSC_VER < 1917 -#pragma warning(disable : 26447) // Function is declared 'noexcept' but calls function '' which may throw exceptions (f.6). [false warning in VS 2017] -#endif - -charls_jpegls_encoder* CHARLS_API_CALLING_CONVENTION +CHARLS_CHECK_RETURN CHARLS_RET_MAY_BE_NULL charls_jpegls_encoder* CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_create() noexcept { - MSVC_WARNING_SUPPRESS(26402 26409) // don't use new and delete + scoped object and move - return new (std::nothrow) charls_jpegls_encoder; - MSVC_WARNING_UNSUPPRESS() + MSVC_WARNING_SUPPRESS_NEXT_LINE(26402 26409) // don't use new and delete + scoped object and move + return new (std::nothrow) charls_jpegls_encoder; // NOLINT(cppcoreguidelines-owning-memory) } -void CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_destroy(const charls_jpegls_encoder* encoder) noexcept +void CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_destroy(IN_OPT_ const charls_jpegls_encoder* encoder) noexcept { - MSVC_WARNING_SUPPRESS(26401 26409) // don't use new and delete + non-owner. - delete encoder; - MSVC_WARNING_UNSUPPRESS() + MSVC_WARNING_SUPPRESS_NEXT_LINE(26401 26409) // don't use new and delete + non-owner. + delete encoder; // NOLINT(cppcoreguidelines-owning-memory) } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_destination_buffer(charls_jpegls_encoder* encoder, void* destination_buffer, size_t destination_size) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_destination_buffer( + IN_ charls_jpegls_encoder* encoder, OUT_WRITES_BYTES_(destination_size_bytes) void* destination_buffer, + const size_t destination_size_bytes) noexcept try { - if (!encoder || !destination_buffer) - return jpegls_errc::invalid_argument; - - encoder->destination(destination_buffer, destination_size); + check_pointer(encoder)->destination({check_pointer(destination_buffer), destination_size_bytes}); return jpegls_errc::success; } catch (...) @@ -282,13 +265,10 @@ } jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_frame_info(charls_jpegls_encoder* encoder, const charls_frame_info* frame_info) noexcept +charls_jpegls_encoder_set_frame_info(IN_ charls_jpegls_encoder* encoder, IN_ const charls_frame_info* frame_info) noexcept try { - if (!encoder || !frame_info) - return jpegls_errc::invalid_argument; - - encoder->frame_info(*frame_info); + check_pointer(encoder)->frame_info(*check_pointer(frame_info)); return jpegls_errc::success; } catch (...) @@ -296,14 +276,11 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_near_lossless(charls_jpegls_encoder* encoder, int32_t near_lossless) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_near_lossless(IN_ charls_jpegls_encoder* encoder, + const int32_t near_lossless) noexcept try { - if (!encoder) - return jpegls_errc::invalid_argument; - - encoder->near_lossless(near_lossless); + check_pointer(encoder)->near_lossless(near_lossless); return jpegls_errc::success; } catch (...) @@ -311,14 +288,11 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_interleave_mode(charls_jpegls_encoder* encoder, charls_interleave_mode interleave_mode) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_interleave_mode( + IN_ charls_jpegls_encoder* encoder, const charls_interleave_mode interleave_mode) noexcept try { - if (!encoder) - return jpegls_errc::invalid_argument; - - encoder->interleave_mode(interleave_mode); + check_pointer(encoder)->interleave_mode(interleave_mode); return jpegls_errc::success; } catch (...) @@ -326,14 +300,11 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_preset_coding_parameters(charls_jpegls_encoder* encoder, const charls_jpegls_pc_parameters* preset_coding_parameters) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_preset_coding_parameters( + IN_ charls_jpegls_encoder* encoder, IN_ const charls_jpegls_pc_parameters* preset_coding_parameters) noexcept try { - if (!encoder || !preset_coding_parameters) - return jpegls_errc::invalid_argument; - - encoder->preset_coding_parameters(*preset_coding_parameters); + check_pointer(encoder)->preset_coding_parameters(*check_pointer(preset_coding_parameters)); return jpegls_errc::success; } catch (...) @@ -341,14 +312,11 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_set_color_transformation(charls_jpegls_encoder* encoder, charls_color_transformation color_transformation) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_set_color_transformation( + IN_ charls_jpegls_encoder* encoder, const charls_color_transformation color_transformation) noexcept try { - if (!encoder) - return jpegls_errc::invalid_argument; - - encoder->color_transformation(color_transformation); + check_pointer(encoder)->color_transformation(color_transformation); return jpegls_errc::success; } catch (...) @@ -356,14 +324,11 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_get_estimated_destination_size(const charls_jpegls_encoder* encoder, size_t* size_in_bytes) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_get_estimated_destination_size( + IN_ const charls_jpegls_encoder* encoder, OUT_ size_t* size_in_bytes) noexcept try { - if (!encoder || !size_in_bytes) - return jpegls_errc::invalid_argument; - - *size_in_bytes = encoder->estimated_destination_size(); + *check_pointer(size_in_bytes) = check_pointer(encoder)->estimated_destination_size(); return jpegls_errc::success; } catch (...) @@ -371,24 +336,26 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_get_bytes_written(const charls_jpegls_encoder* encoder, size_t* bytes_written) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_get_bytes_written(IN_ const charls_jpegls_encoder* encoder, + OUT_ size_t* bytes_written) noexcept +try { - if (!encoder || !bytes_written) - return jpegls_errc::invalid_argument; - - *bytes_written = encoder->bytes_written(); + *check_pointer(bytes_written) = check_pointer(encoder)->bytes_written(); return jpegls_errc::success; } +catch (...) +{ + return to_jpegls_errc(); +} -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_encode_from_buffer(charls_jpegls_encoder* encoder, const void* source_buffer, const size_t source_size, const uint32_t stride) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_encode_from_buffer(IN_ charls_jpegls_encoder* encoder, + IN_READS_BYTES_(source_size_bytes) + const void* source_buffer, + const size_t source_size_bytes, + const uint32_t stride) noexcept try { - if (!encoder || !source_buffer) - return jpegls_errc::invalid_argument; - - encoder->encode(source_buffer, source_size, stride); + check_pointer(encoder)->encode({check_pointer(source_buffer), source_size_bytes}, stride); return jpegls_errc::success; } catch (...) @@ -396,14 +363,11 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_write_spiff_header(charls_jpegls_encoder* encoder, const charls_spiff_header* spiff_header) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_write_spiff_header( + IN_ charls_jpegls_encoder* encoder, IN_ const charls_spiff_header* spiff_header) noexcept try { - if (!encoder || !spiff_header) - return jpegls_errc::invalid_argument; - - encoder->write_spiff_header(*spiff_header); + check_pointer(encoder)->write_spiff_header(*check_pointer(spiff_header)); return jpegls_errc::success; } catch (...) @@ -411,15 +375,14 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_write_standard_spiff_header(charls_jpegls_encoder* encoder, const charls_spiff_color_space color_space, - const charls_spiff_resolution_units resolution_units, const uint32_t vertical_resolution, const uint32_t horizontal_resolution) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_write_standard_spiff_header( + IN_ charls_jpegls_encoder* encoder, const charls_spiff_color_space color_space, + const charls_spiff_resolution_units resolution_units, const uint32_t vertical_resolution, + const uint32_t horizontal_resolution) noexcept try { - if (!encoder) - return jpegls_errc::invalid_argument; - - encoder->write_standard_spiff_header(color_space, resolution_units, vertical_resolution, horizontal_resolution); + check_pointer(encoder)->write_standard_spiff_header(color_space, resolution_units, vertical_resolution, + horizontal_resolution); return jpegls_errc::success; } catch (...) @@ -427,14 +390,15 @@ return to_jpegls_errc(); } -jpegls_errc CHARLS_API_CALLING_CONVENTION -charls_jpegls_encoder_write_spiff_entry(charls_jpegls_encoder* encoder, const uint32_t entry_tag, const void* entry_data, const size_t entry_data_size) noexcept +jpegls_errc CHARLS_API_CALLING_CONVENTION charls_jpegls_encoder_write_spiff_entry( + IN_ charls_jpegls_encoder* encoder, const uint32_t entry_tag, + IN_READS_BYTES_(entry_data_size_bytes) const void* entry_data, const size_t entry_data_size_bytes) noexcept try { - if (!encoder || (!entry_data && entry_data_size != 0)) + if (!entry_data && entry_data_size_bytes != 0) return jpegls_errc::invalid_argument; - encoder->write_spiff_entry(entry_tag, entry_data, entry_data_size); + check_pointer(encoder)->write_spiff_entry(entry_tag, entry_data, entry_data_size_bytes); return jpegls_errc::success; } catch (...) @@ -443,30 +407,35 @@ } -jpegls_errc CHARLS_API_CALLING_CONVENTION -JpegLsEncode(void* destination, size_t destinationLength, size_t* bytesWritten, const void* source, size_t sourceLength, const struct JlsParameters* params, char* errorMessage) +jpegls_errc CHARLS_API_CALLING_CONVENTION JpegLsEncode(OUT_WRITES_BYTES_(destination_length) void* destination, + const size_t destination_length, OUT_ size_t* bytes_written, + IN_READS_BYTES_(source_length) const void* source, + const size_t source_length, IN_ const struct JlsParameters* params, + OUT_OPT_ char* error_message) try { - if (!destination || !bytesWritten || !source || !params || params->jfif.version) + if (check_pointer(params)->jfif.version != 0) return jpegls_errc::invalid_argument; charls_jpegls_encoder encoder; - encoder.destination(destination, destinationLength); - encoder.frame_info({static_cast(params->width), static_cast(params->height), params->bitsPerSample, params->components}); + encoder.destination({check_pointer(destination), destination_length}); encoder.near_lossless(params->allowedLossyError); + + encoder.frame_info({static_cast(params->width), static_cast(params->height), params->bitsPerSample, + params->components}); encoder.interleave_mode(params->interleaveMode); encoder.color_transformation(params->colorTransformation); - const auto& pc = params->custom; + const auto& pc{params->custom}; encoder.preset_coding_parameters({pc.MaximumSampleValue, pc.Threshold1, pc.Threshold2, pc.Threshold3, pc.ResetValue}); - encoder.encode(source, sourceLength, params->stride); - *bytesWritten = encoder.bytes_written(); + encoder.encode({check_pointer(source), source_length}, static_cast(params->stride)); + *check_pointer(bytes_written) = encoder.bytes_written(); - clear_error_message(errorMessage); + clear_error_message(error_message); return jpegls_errc::success; } catch (...) { - return set_error_message(to_jpegls_errc(), errorMessage); + return set_error_message(to_jpegls_errc(), error_message); } } Binary files /tmp/tmptxyqr8/iDZBmIvU8B/charls-2.1.0+dfsg/src/charls.rc and /tmp/tmptxyqr8/i0J_6ju0mM/charls-2.2.0+dfsg/src/charls.rc differ diff -Nru charls-2.1.0+dfsg/src/charls-template.pc charls-2.2.0+dfsg/src/charls-template.pc --- charls-2.1.0+dfsg/src/charls-template.pc 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/src/charls-template.pc 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: CharLS +Description: JPEG-LS (ISO/IEC 14495-1 / ITU-T.87) library implementation. +Version: @PROJECT_VERSION@ +URL: https://github.com/team-charls/charls/ +Cflags: -I${includedir} +Libs: -L${libdir} -lcharls diff -Nru charls-2.1.0+dfsg/src/CMakeLists.txt charls-2.2.0+dfsg/src/CMakeLists.txt --- charls-2.1.0+dfsg/src/CMakeLists.txt 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/CMakeLists.txt 2021-01-10 18:07:25.000000000 +0000 @@ -5,7 +5,7 @@ add_library(charls "") target_include_directories(charls PUBLIC - $ + $ $) if(NOT BUILD_SHARED_LIBS) @@ -20,30 +20,48 @@ endif() endif() +# Disable C++ run-time type info (dynamic cast + typeid). CharLS doesn't use +# this functionality and disabling it will result in a smaller image. +# Don't do this for gcc and clang (normally done with -fno-rtti) as it will cause +# UBSAN warnings as jpegls_category is derived from std::error_category +# If the base class was built with RTTI and has virtual functions, the derived class +# also needs to be build with RTTI. +if(MSVC) + target_compile_options(charls PRIVATE /GR-) +endif() + set_target_properties(charls PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) target_compile_definitions(charls PRIVATE CHARLS_LIBRARY_BUILD) -set(CHARLS_PUBLIC_HEADERS - "${CMAKE_CURRENT_SOURCE_DIR}/include/charls/api_abi.h" - "${CMAKE_CURRENT_SOURCE_DIR}/include/charls/charls.h" - "${CMAKE_CURRENT_SOURCE_DIR}/include/charls/charls_legacy.h" - "${CMAKE_CURRENT_SOURCE_DIR}/include/charls/jpegls_error.h" - "${CMAKE_CURRENT_SOURCE_DIR}/include/charls/public_types.h" - "${CMAKE_CURRENT_SOURCE_DIR}/include/charls/version.h" +set(HEADERS + "include/charls/api_abi.h" + "include/charls/annotations.h" + "include/charls/charls.h" + "include/charls/charls_jpegls_decoder.h" + "include/charls/charls_jpegls_encoder.h" + "include/charls/jpegls_error.h" + "include/charls/public_types.h" + "include/charls/version.h" ) +foreach(header HEADERS) + list(APPEND CHARLS_PUBLIC_HEADER $) + list(APPEND CHARLS_PUBLIC_HEADER $) +endforeach() set_target_properties(charls PROPERTIES CXX_VISIBILITY_PRESET hidden) -set_property(TARGET charls PROPERTY PUBLIC_HEADER ${CHARLS_PUBLIC_HEADERS}) +set_property(TARGET charls PROPERTY PUBLIC_HEADER ${HEADERS}) target_sources(charls PUBLIC ${CHARLS_PUBLIC_HEADERS} PRIVATE + "${CMAKE_CURRENT_LIST_DIR}/byte_span.h" "${CMAKE_CURRENT_LIST_DIR}/charls_jpegls_decoder.cpp" "${CMAKE_CURRENT_LIST_DIR}/charls_jpegls_encoder.cpp" + "${CMAKE_CURRENT_LIST_DIR}/coding_parameters.h" "${CMAKE_CURRENT_LIST_DIR}/color_transform.h" "${CMAKE_CURRENT_LIST_DIR}/constants.h" "${CMAKE_CURRENT_LIST_DIR}/context.h" @@ -51,7 +69,6 @@ "${CMAKE_CURRENT_LIST_DIR}/decoder_strategy.h" "${CMAKE_CURRENT_LIST_DIR}/default_traits.h" "${CMAKE_CURRENT_LIST_DIR}/encoder_strategy.h" - "${CMAKE_CURRENT_LIST_DIR}/interface.cpp" "${CMAKE_CURRENT_LIST_DIR}/jls_codec_factory.h" "${CMAKE_CURRENT_LIST_DIR}/jpegls_error.cpp" "${CMAKE_CURRENT_LIST_DIR}/jpegls.cpp" @@ -66,17 +83,33 @@ "${CMAKE_CURRENT_LIST_DIR}/scan.h" "${CMAKE_CURRENT_LIST_DIR}/util.h" "${CMAKE_CURRENT_LIST_DIR}/version.cpp" - "${CMAKE_CURRENT_LIST_DIR}/charls.def" - "${CMAKE_CURRENT_LIST_DIR}/charls.rc" ) +if(WIN32 AND BUILD_SHARED_LIBS) + # Only add the Win32 resource script file when building a DLL + target_sources(charls PRIVATE "${CMAKE_CURRENT_LIST_DIR}/charls.rc") + + # Only add the definition file when building a x86 DLL with MSVC + # The definition file is needed to ensure the legacy functions are exported with the correct name + if(MSVC AND CMAKE_SIZEOF_VOID_P EQUAL 4) + target_sources(charls PRIVATE "${CMAKE_CURRENT_LIST_DIR}/charls.def") + endif() +endif() + if(CHARLS_INSTALL) include(GNUInstallDirs) - install(TARGETS charls + install(TARGETS charls EXPORT charls_targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/charls ) + + # Configure and copy the pkg-config configuration file. + # These type of configuration file can make it easier to detect if charls is installed. + CONFIGURE_FILE("${CMAKE_CURRENT_LIST_DIR}/charls-template.pc" "charls.pc" @ONLY) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/charls.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + + install (EXPORT charls_targets FILE charlsConfig.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/charls) endif() diff -Nru charls-2.1.0+dfsg/src/coding_parameters.h charls-2.2.0+dfsg/src/coding_parameters.h --- charls-2.1.0+dfsg/src/coding_parameters.h 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/src/coding_parameters.h 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,18 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +#include + +namespace charls { + +struct coding_parameters final +{ + int32_t near_lossless; + charls::interleave_mode interleave_mode; + color_transformation transformation; + bool output_bgr; +}; + +} // namespace charls diff -Nru charls-2.1.0+dfsg/src/color_transform.h charls-2.2.0+dfsg/src/color_transform.h --- charls-2.1.0+dfsg/src/color_transform.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/color_transform.h 2021-01-10 18:07:25.000000000 +0000 @@ -12,50 +12,50 @@ // Color transforms work best for computer generated images, but are outside the official JPEG-LS specifications. template -struct TransformNoneImpl +struct transform_none_impl { static_assert(std::is_integral::value, "Integral required."); using size_type = T; - FORCE_INLINE Triplet operator()(int v1, int v2, int v3) const noexcept + FORCE_INLINE triplet operator()(const int v1, const int v2, const int v3) const noexcept { - return Triplet(v1, v2, v3); + return triplet(v1, v2, v3); } }; template -struct TransformNone final : TransformNoneImpl +struct transform_none final : transform_none_impl { static_assert(std::is_integral::value, "Integral required."); - using Inverse = TransformNoneImpl; + using inverse = transform_none_impl; }; template -struct TransformHp1 final +struct transform_hp1 final { static_assert(std::is_integral::value, "Integral required."); using size_type = T; - struct Inverse final + struct inverse final { - explicit Inverse(const TransformHp1&) noexcept + explicit inverse(const transform_hp1&) noexcept { } - FORCE_INLINE Triplet operator()(int v1, int v2, int v3) const noexcept + FORCE_INLINE triplet operator()(const int v1, const int v2, const int v3) const noexcept { - return Triplet(v1 + v2 - Range / 2, v2, v3 + v2 - Range / 2); + return triplet(v1 + v2 - Range / 2, v2, v3 + v2 - Range / 2); } }; - FORCE_INLINE Triplet operator()(int red, int green, int blue) const noexcept + FORCE_INLINE triplet operator()(const int red, const int green, const int blue) const noexcept { - Triplet hp1; + triplet hp1; hp1.v2 = static_cast(green); hp1.v1 = static_cast(red - green + Range / 2); hp1.v3 = static_cast(blue - green + Range / 2); @@ -68,21 +68,21 @@ template -struct TransformHp2 final +struct transform_hp2 final { static_assert(std::is_integral::value, "Integral required."); using size_type = T; - struct Inverse final + struct inverse final { - explicit Inverse(const TransformHp2&) noexcept + explicit inverse(const transform_hp2&) noexcept { } - FORCE_INLINE Triplet operator()(int v1, int v2, int v3) const noexcept + FORCE_INLINE triplet operator()(const int v1, const int v2, const int v3) const noexcept { - Triplet rgb; + triplet rgb; rgb.R = static_cast(v1 + v2 - Range / 2); // new R rgb.G = static_cast(v2); // new G rgb.B = static_cast(v3 + ((rgb.R + rgb.G) >> 1) - Range / 2); // new B @@ -90,9 +90,9 @@ } }; - FORCE_INLINE Triplet operator()(int red, int green, int blue) const noexcept + FORCE_INLINE triplet operator()(const int red, const int green, const int blue) const noexcept { - return Triplet(red - green + Range / 2, green, blue - ((red + green) >> 1) - Range / 2); + return triplet(red - green + Range / 2, green, blue - ((red + green) >> 1) - Range / 2); } private: @@ -101,32 +101,32 @@ template -struct TransformHp3 final +struct transform_hp3 final { static_assert(std::is_integral::value, "Integral required."); using size_type = T; - struct Inverse final + struct inverse final { - explicit Inverse(const TransformHp3&) noexcept + explicit inverse(const transform_hp3&) noexcept { } - FORCE_INLINE Triplet operator()(int v1, int v2, int v3) const noexcept + FORCE_INLINE triplet operator()(const int v1, const int v2, const int v3) const noexcept { - const int G = v1 - ((v3 + v2) >> 2) + Range / 4; - Triplet rgb; - rgb.R = static_cast(v3 + G - Range / 2); // new R - rgb.G = static_cast(G); // new G - rgb.B = static_cast(v2 + G - Range / 2); // new B + const int g = v1 - ((v3 + v2) >> 2) + Range / 4; + triplet rgb; + rgb.R = static_cast(v3 + g - Range / 2); // new R + rgb.G = static_cast(g); // new G + rgb.B = static_cast(v2 + g - Range / 2); // new B return rgb; } }; - FORCE_INLINE Triplet operator()(int red, int green, int blue) const noexcept + FORCE_INLINE triplet operator()(const int red, const int green, const int blue) const noexcept { - Triplet hp3; + triplet hp3; hp3.v2 = static_cast(blue - green + Range / 2); hp3.v3 = static_cast(red - green + Range / 2); hp3.v1 = static_cast(green + ((hp3.v2 + hp3.v3) >> 2)) - Range / 4; @@ -137,59 +137,4 @@ static constexpr size_t Range = 1 << (sizeof(T) * 8); }; - -// Transform class that shifts bits towards the high bit when bit count is not 8 or 16 -// needed to make the HP color transformations work correctly. -template -struct TransformShifted final -{ - using size_type = typename Transform::size_type; - - struct Inverse final - { - explicit Inverse(const TransformShifted& transform) noexcept : - shift_{transform.shift_}, - inverseTransform_{transform.colorTransform_} - { - } - - FORCE_INLINE Triplet operator()(int v1, int v2, int v3) noexcept - { - const Triplet result = inverseTransform_(v1 << shift_, v2 << shift_, v3 << shift_); - return Triplet(result.R >> shift_, result.G >> shift_, result.B >> shift_); - } - - FORCE_INLINE Quad operator()(int v1, int v2, int v3, int v4) - { - Triplet result = inverseTransform_(v1 << shift_, v2 << shift_, v3 << shift_); - return Quad(result.R >> shift_, result.G >> shift_, result.B >> shift_, v4); - } - - private: - int shift_; - typename Transform::Inverse inverseTransform_; - }; - - explicit TransformShifted(int shift) noexcept : - shift_{shift} - { - } - - FORCE_INLINE Triplet operator()(int red, int green, int blue) noexcept - { - const Triplet result = colorTransform_(red << shift_, green << shift_, blue << shift_); - return Triplet(result.R >> shift_, result.G >> shift_, result.B >> shift_); - } - - FORCE_INLINE Quad operator()(int red, int green, int blue, int alpha) - { - Triplet result = colorTransform_(red << shift_, green << shift_, blue << shift_); - return Quad(result.R >> shift_, result.G >> shift_, result.B >> shift_, alpha); - } - -private: - int shift_; - Transform colorTransform_; -}; - } // namespace charls diff -Nru charls-2.1.0+dfsg/src/constants.h charls-2.2.0+dfsg/src/constants.h --- charls-2.1.0+dfsg/src/constants.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/constants.h 2021-01-10 18:07:25.000000000 +0000 @@ -4,38 +4,42 @@ #pragma once #include +#include namespace charls { // Default threshold values for JPEG-LS statistical modeling as defined in ISO/IEC 14495-1, Table C.3 // for the case MAXVAL = 255 and NEAR = 0. // Can be overridden at compression time, however this is rarely done. -constexpr int DefaultThreshold1 = 3; // BASIC_T1 -constexpr int DefaultThreshold2 = 7; // BASIC_T2 -constexpr int DefaultThreshold3 = 21; // BASIC_T3 - -constexpr int DefaultResetValue = 64; // Default RESET value as defined in ISO/IEC 14495-1, table C.2 - -constexpr int maximum_width = 65535; -constexpr int maximum_height = 65535; -constexpr int MaximumComponentCount = 255; -constexpr int MinimumBitsPerSample = 2; -constexpr int MaximumBitsPerSample = 16; -constexpr int maximum_near_lossless = 255; +constexpr int default_threshold1{3}; // BASIC_T1 +constexpr int default_threshold2{7}; // BASIC_T2 +constexpr int default_threshold3{21}; // BASIC_T3 + +constexpr int default_reset_value{64}; // Default RESET value as defined in ISO/IEC 14495-1, table C.2 + +constexpr int maximum_width{65535}; +constexpr int maximum_height{65535}; +constexpr int maximum_component_count{255}; +constexpr int minimum_bits_per_sample{2}; +constexpr int maximum_bits_per_sample{16}; +constexpr int maximum_near_lossless{255}; -constexpr int MaximumNearLossless(const int maximumSampleValue) noexcept +constexpr int max_k_value{16}; // This is an implementation limit (theoretical limit is 32) + +constexpr int compute_maximum_near_lossless(const int maximum_sample_value) noexcept { - return std::min(255, maximumSampleValue / 2); // As defined by ISO/IEC 14495-1, C.2.3 + return std::min(255, maximum_sample_value / 2); // As defined by ISO/IEC 14495-1, C.2.3 } -// ISO/IEC 14495-1, section 4.8.1 defines the SPIFF version numbers to be used for the SPIFF header in combination with JPEG-LS. -constexpr uint8_t spiff_major_revision_number = 2; -constexpr uint8_t spiff_minor_revision_number = 0; +// ISO/IEC 14495-1, section 4.8.1 defines the SPIFF version numbers to be used for the SPIFF header in combination with +// JPEG-LS. +constexpr uint8_t spiff_major_revision_number{2}; +constexpr uint8_t spiff_minor_revision_number{}; -constexpr uint8_t spiff_end_of_directory_entry_type = 1; +constexpr uint8_t spiff_end_of_directory_entry_type{1}; // The size of a SPIFF header when serialized to a JPEG byte stream. -constexpr size_t spiff_header_size_in_bytes = 34; +constexpr size_t spiff_header_size_in_bytes{34}; } // namespace charls diff -Nru charls-2.1.0+dfsg/src/context.h charls-2.2.0+dfsg/src/context.h --- charls-2.1.0+dfsg/src/context.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/context.h 2021-01-10 18:07:25.000000000 +0000 @@ -11,41 +11,42 @@ namespace charls { // Purpose: a JPEG-LS context with it's current statistics. -struct JlsContext final +struct jls_context final { int32_t A{}; int32_t B{}; int16_t C{}; int16_t N{1}; - JlsContext() = default; + jls_context() = default; - explicit JlsContext(int32_t a) noexcept : - A{a} + explicit jls_context(const int32_t a) noexcept : A{a} { } - FORCE_INLINE int32_t GetErrorCorrection(int32_t k) const noexcept + FORCE_INLINE int32_t get_error_correction(const int32_t k) const noexcept { if (k != 0) return 0; - return BitWiseSign(2 * B + N - 1); + return bit_wise_sign(2 * B + N - 1); } - FORCE_INLINE void UpdateVariables(int32_t errorValue, int32_t NEAR, int32_t NRESET) noexcept + FORCE_INLINE void update_variables(const int32_t error_value, const int32_t near_lossless, + const int32_t reset_threshold) { ASSERT(N != 0); // For performance work on copies of A,B,N (compiler will use registers). - int a = A + std::abs(errorValue); - int b = B + errorValue * (2 * NEAR + 1); - int n = N; + int a{A + std::abs(error_value)}; + int b{B + error_value * (2 * near_lossless + 1)}; + int n{N}; + + constexpr int limit{65536 * 256}; + if (a >= limit || std::abs(b) >= limit) + impl::throw_jpegls_error(jpegls_errc::invalid_encoded_data); - ASSERT(a < 65536 * 256); - ASSERT(std::abs(b) < 65536 * 256); - - if (n == NRESET) + if (n == reset_threshold) { a = a >> 1; b = b >> 1; @@ -79,22 +80,20 @@ ASSERT(N != 0); } - FORCE_INLINE int32_t GetGolomb() const noexcept + /// + /// Computes the Golomb coding parameter using the algorithm as defined in ISO/IEC 14495-1, code segment A.10 + /// Original algorithm is: for (k = 0; (N[Q] << k) < A[Q]; k++) + /// + FORCE_INLINE int32_t get_golomb_coding_parameter() const { - const int32_t nTest = N; - const int32_t aTest = A; - - if (nTest >= aTest) return 0; - if (nTest << 1 >= aTest) return 1; - if (nTest << 2 >= aTest) return 2; - if (nTest << 3 >= aTest) return 3; - if (nTest << 4 >= aTest) return 4; - - int32_t k = 5; - for (; nTest << k < aTest; ++k) + int32_t k{0}; + for (; N << k < A && k < max_k_value; ++k) { - ASSERT(k <= 32); } + + if (k == max_k_value) + impl::throw_jpegls_error(jpegls_errc::invalid_encoded_data); + return k; } }; diff -Nru charls-2.1.0+dfsg/src/context_run_mode.h charls-2.2.0+dfsg/src/context_run_mode.h --- charls-2.1.0+dfsg/src/context_run_mode.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/context_run_mode.h 2021-01-10 18:07:25.000000000 +0000 @@ -12,87 +12,88 @@ // Implements statistical modeling for the run mode context. // Computes model dependent parameters like the Golomb code lengths -struct CContextRunMode final +struct context_run_mode final { - // Note: members are sorted based on their size. - int32_t A{}; - int32_t nRItype_{}; - uint8_t nReset_{}; - uint8_t N{}; - uint8_t Nn{}; + int32_t run_interruption_type{}; - CContextRunMode() = default; + context_run_mode() = default; - CContextRunMode(int32_t a, int32_t nRItype, int32_t nReset) noexcept : - A{a}, - nRItype_{nRItype}, - nReset_{static_cast(nReset)}, - N{1} + context_run_mode(const int32_t arg_run_interruption_type, const int32_t a, const int32_t reset_threshold) noexcept : + run_interruption_type{arg_run_interruption_type}, + a_{a}, + reset_threshold_{static_cast(reset_threshold)}, + n_{1} { } - FORCE_INLINE int32_t GetGolomb() const noexcept + FORCE_INLINE int32_t get_golomb_code() const noexcept { - const int32_t TEMP = A + (N >> 1) * nRItype_; - int32_t nTest = N; - int32_t k = 0; - for (; nTest < TEMP; ++k) + const int32_t temp{a_ + (n_ >> 1) * run_interruption_type}; + int32_t n_test{n_}; + int32_t k{}; + for (; n_test < temp; ++k) { - nTest <<= 1; + n_test <<= 1; ASSERT(k <= 32); } return k; } - void UpdateVariables(int32_t errorValue, int32_t EMErrval) noexcept + void update_variables(const int32_t error_value, const int32_t e_mapped_error_value) noexcept { - if (errorValue < 0) + if (error_value < 0) { - Nn = Nn + 1; + nn_ = nn_ + 1U; } - A = A + ((EMErrval + 1 - nRItype_) >> 1); - if (N == nReset_) + a_ = a_ + ((e_mapped_error_value + 1 - run_interruption_type) >> 1); + if (n_ == reset_threshold_) { - A = A >> 1; - N = N >> 1; - Nn = Nn >> 1; + a_ = a_ >> 1; + n_ = n_ >> 1; + nn_ = nn_ >> 1; } - N = N + 1; + n_ = n_ + 1; } - FORCE_INLINE int32_t ComputeErrVal(int32_t temp, int32_t k) const noexcept + FORCE_INLINE int32_t compute_error_value(const int32_t temp, const int32_t k) const noexcept { const bool map = temp & 1; - const int32_t errorValueAbs = (temp + static_cast(map)) / 2; + const int32_t error_value_abs{(temp + static_cast(map)) / 2}; - if ((k != 0 || (2 * Nn >= N)) == map) + if ((k != 0 || (2 * nn_ >= n_)) == map) { - ASSERT(map == ComputeMap(-errorValueAbs, k)); - return -errorValueAbs; + ASSERT(map == compute_map(-error_value_abs, k)); + return -error_value_abs; } - ASSERT(map == ComputeMap(errorValueAbs, k)); - return errorValueAbs; + ASSERT(map == compute_map(error_value_abs, k)); + return error_value_abs; } - bool ComputeMap(int32_t errorValue, int32_t k) const noexcept + bool compute_map(const int32_t error_value, const int32_t k) const noexcept { - if (k == 0 && errorValue > 0 && 2 * Nn < N) + if (k == 0 && error_value > 0 && 2 * nn_ < n_) return true; - if (errorValue < 0 && 2 * Nn >= N) + if (error_value < 0 && 2 * nn_ >= n_) return true; - if (errorValue < 0 && k != 0) + if (error_value < 0 && k != 0) return true; return false; } - FORCE_INLINE bool ComputeMapNegativeE(int32_t k) const noexcept + FORCE_INLINE bool compute_map_negative_e(const int32_t k) const noexcept { - return k != 0 || 2 * Nn >= N; + return k != 0 || 2 * nn_ >= n_; } + +private: + int32_t a_{}; + uint8_t reset_threshold_{}; + uint8_t n_{}; + uint8_t nn_{}; }; } // namespace charls diff -Nru charls-2.1.0+dfsg/src/decoder_strategy.h charls-2.2.0+dfsg/src/decoder_strategy.h --- charls-2.1.0+dfsg/src/decoder_strategy.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/decoder_strategy.h 2021-01-10 18:07:25.000000000 +0000 @@ -5,305 +5,265 @@ #include -#include "util.h" -#include "process_line.h" #include "jpeg_marker_code.h" +#include "process_line.h" +#include "util.h" -#include #include +#include namespace charls { -// Purpose: Implements encoding to stream of bits. In encoding mode JpegLsCodec inherits from EncoderStrategy -class DecoderStrategy +// Purpose: Implements encoding to stream of bits. In encoding mode jls_codec inherits from decoder_strategy +class decoder_strategy { public: - explicit DecoderStrategy(const JlsParameters& params) : - params_{params} + explicit decoder_strategy(const frame_info& frame, const coding_parameters& parameters) noexcept : + frame_info_{frame}, parameters_{parameters} { } - virtual ~DecoderStrategy() = default; + virtual ~decoder_strategy() = default; - DecoderStrategy(const DecoderStrategy&) = delete; - DecoderStrategy(DecoderStrategy&&) = delete; - DecoderStrategy& operator=(const DecoderStrategy&) = delete; - DecoderStrategy& operator=(DecoderStrategy&&) = delete; - - virtual std::unique_ptr CreateProcess(ByteStreamInfo rawStreamInfo) = 0; - virtual void SetPresets(const jpegls_pc_parameters& preset_coding_parameters) = 0; - virtual void DecodeScan(std::unique_ptr outputData, const JlsRect& size, ByteStreamInfo& compressedData) = 0; - - void Init(ByteStreamInfo& compressedStream) - { - validBits_ = 0; - readCache_ = 0; - - if (compressedStream.rawStream) - { - buffer_.resize(40000); - position_ = buffer_.data(); - endPosition_ = position_; - byteStream_ = compressedStream.rawStream; - AddBytesFromStream(); - } - else - { - byteStream_ = nullptr; - position_ = compressedStream.rawData; - endPosition_ = position_ + compressedStream.count; - } + decoder_strategy(const decoder_strategy&) = delete; + decoder_strategy(decoder_strategy&&) = delete; + decoder_strategy& operator=(const decoder_strategy&) = delete; + decoder_strategy& operator=(decoder_strategy&&) = delete; - nextFFPosition_ = FindNextFF(); - MakeValid(); - } + virtual std::unique_ptr create_process_line(byte_span destination, size_t stride) = 0; + virtual void set_presets(const jpegls_pc_parameters& preset_coding_parameters) = 0; + virtual void decode_scan(std::unique_ptr output_data, const JlsRect& size, byte_span& compressed_data) = 0; - void AddBytesFromStream() + void initialize(const byte_span source) { - if (!byteStream_ || byteStream_->sgetc() == std::char_traits::eof()) - return; - - const auto count = endPosition_ - position_; - - if (count > 64) - return; - - for (std::size_t i = 0; i < static_cast < std::size_t>(count); ++i) - { - buffer_[i] = position_[i]; - } - const auto offset = buffer_.data() - position_; + valid_bits_ = 0; + read_cache_ = 0; - position_ += offset; - endPosition_ += offset; - nextFFPosition_ += offset; + position_ = source.data; + end_position_ = position_ + source.size; - const std::streamsize readBytes = byteStream_->sgetn(reinterpret_cast(endPosition_), - static_cast(buffer_.size()) - count); - endPosition_ += readBytes; + next_ff_position_ = find_next_ff(); + make_valid(); } - FORCE_INLINE void Skip(int32_t length) noexcept + FORCE_INLINE void skip(const int32_t length) noexcept { - validBits_ -= length; - readCache_ = readCache_ << length; + valid_bits_ -= length; + read_cache_ = read_cache_ << length; } - static void OnLineBegin(int32_t /*cpixel*/, void* /*ptypeBuffer*/, int32_t /*pixelStride*/) noexcept + static void on_line_begin(const size_t /*pixel_count*/, void* /*source*/, size_t /*pixel_stride*/) noexcept { } - void OnLineEnd(int32_t pixelCount, const void* ptypeBuffer, int32_t pixelStride) const + void on_line_end(const size_t pixel_count, const void* source, const size_t pixel_stride) const { - processLine_->NewLineDecoded(ptypeBuffer, pixelCount, pixelStride); + process_line_->new_line_decoded(source, pixel_count, pixel_stride); } - void EndScan() + void end_scan() { - if (*position_ != JpegMarkerStartByte) + if (*position_ != jpeg_marker_start_byte) { - ReadBit(); + read_bit(); - if (*position_ != JpegMarkerStartByte) - throw jpegls_error{jpegls_errc::too_much_encoded_data}; + if (*position_ != jpeg_marker_start_byte) + impl::throw_jpegls_error(jpegls_errc::too_much_encoded_data); } - if (readCache_ != 0) - throw jpegls_error{jpegls_errc::too_much_encoded_data}; + if (read_cache_ != 0) + impl::throw_jpegls_error(jpegls_errc::too_much_encoded_data); } - FORCE_INLINE bool OptimizedRead() noexcept + FORCE_INLINE bool optimized_read() noexcept { // Easy & fast: if there is no 0xFF byte in sight, we can read without bit stuffing - if (position_ < nextFFPosition_ - (sizeof(bufType)-1)) + if (position_ < next_ff_position_ - (sizeof(bufType) - 1)) { - readCache_ |= FromBigEndian::Read(position_) >> validBits_; - const int bytesToRead = (bufType_bit_count - validBits_) >> 3; - position_ += bytesToRead; - validBits_ += bytesToRead * 8; - ASSERT(validBits_ >= bufType_bit_count - 8); + read_cache_ |= from_big_endian::read(position_) >> valid_bits_; + const int bytes_to_read{(bufType_bit_count - valid_bits_) >> 3}; + position_ += bytes_to_read; + valid_bits_ += bytes_to_read * 8; + ASSERT(valid_bits_ >= bufType_bit_count - 8); return true; } return false; } - void MakeValid() + void make_valid() { - ASSERT(validBits_ <= bufType_bit_count - 8); + ASSERT(valid_bits_ <= bufType_bit_count - 8); - if (OptimizedRead()) + if (optimized_read()) return; - AddBytesFromStream(); - do { - if (position_ >= endPosition_) + if (position_ >= end_position_) { - if (validBits_ <= 0) - throw jpegls_error{jpegls_errc::invalid_encoded_data}; + if (valid_bits_ <= 0) + impl::throw_jpegls_error(jpegls_errc::invalid_encoded_data); return; } - const bufType valueNew = position_[0]; + const bufType value_new{position_[0]}; - if (valueNew == JpegMarkerStartByte) + if (value_new == jpeg_marker_start_byte) { // JPEG bit stream rule: no FF may be followed by 0x80 or higher - if (position_ == endPosition_ - 1 || (position_[1] & 0x80) != 0) + if (position_ == end_position_ - 1 || (position_[1] & 0x80) != 0) { - if (validBits_ <= 0) - throw jpegls_error{jpegls_errc::invalid_encoded_data}; + if (valid_bits_ <= 0) + impl::throw_jpegls_error(jpegls_errc::invalid_encoded_data); return; } } - readCache_ |= valueNew << (bufType_bit_count - 8 - validBits_); + read_cache_ |= value_new << (bufType_bit_count - 8 - valid_bits_); position_ += 1; - validBits_ += 8; + valid_bits_ += 8; - if (valueNew == JpegMarkerStartByte) + if (value_new == jpeg_marker_start_byte) { - validBits_--; + --valid_bits_; } - } - while (validBits_ < bufType_bit_count - 8); + } while (valid_bits_ < bufType_bit_count - 8); - nextFFPosition_ = FindNextFF(); + next_ff_position_ = find_next_ff(); } - uint8_t* FindNextFF() const noexcept + uint8_t* find_next_ff() const noexcept { - auto positionNextFF = position_; + auto* position_next_ff{position_}; - while (positionNextFF < endPosition_) + while (position_next_ff < end_position_) { - if (*positionNextFF == JpegMarkerStartByte) + if (*position_next_ff == jpeg_marker_start_byte) break; - positionNextFF++; + ++position_next_ff; } - return positionNextFF; + return position_next_ff; } - uint8_t* GetCurBytePos() const noexcept + uint8_t* get_cur_byte_pos() const noexcept { - int32_t validBits = validBits_; - uint8_t* compressedBytes = position_; + int32_t valid_bits{valid_bits_}; + uint8_t* compressed_bytes{position_}; for (;;) { - const int32_t lastBitsCount = compressedBytes[-1] == JpegMarkerStartByte ? 7 : 8; + const int32_t last_bits_count{compressed_bytes[-1] == jpeg_marker_start_byte ? 7 : 8}; - if (validBits < lastBitsCount) - return compressedBytes; + if (valid_bits < last_bits_count) + return compressed_bytes; - validBits -= lastBitsCount; - compressedBytes--; + valid_bits -= last_bits_count; + --compressed_bytes; } } - FORCE_INLINE int32_t ReadValue(int32_t length) + FORCE_INLINE int32_t read_value(const int32_t length) { - if (validBits_ < length) + if (valid_bits_ < length) { - MakeValid(); - if (validBits_ < length) - throw jpegls_error{jpegls_errc::invalid_encoded_data}; + make_valid(); + if (valid_bits_ < length) + impl::throw_jpegls_error(jpegls_errc::invalid_encoded_data); } - ASSERT(length != 0 && length <= validBits_); + ASSERT(length != 0 && length <= valid_bits_); ASSERT(length < 32); - const auto result = static_cast(readCache_ >> (bufType_bit_count - length)); - Skip(length); + const auto result = static_cast(read_cache_ >> (bufType_bit_count - length)); + skip(length); return result; } - FORCE_INLINE int32_t PeekByte() + FORCE_INLINE int32_t peek_byte() { - if (validBits_ < 8) + if (valid_bits_ < 8) { - MakeValid(); + make_valid(); } - return static_cast(readCache_ >> (bufType_bit_count - 8)); + return static_cast(read_cache_ >> (bufType_bit_count - 8)); } - FORCE_INLINE bool ReadBit() + FORCE_INLINE bool read_bit() { - if (validBits_ <= 0) + if (valid_bits_ <= 0) { - MakeValid(); + make_valid(); } - const bool bSet = (readCache_ & (static_cast(1) << (bufType_bit_count - 1))) != 0; - Skip(1); - return bSet; + const bool set = (read_cache_ & (static_cast(1) << (bufType_bit_count - 1))) != 0; + skip(1); + return set; } - FORCE_INLINE int32_t Peek0Bits() + FORCE_INLINE int32_t peek_0_bits() { - if (validBits_ < 16) + if (valid_bits_ < 16) { - MakeValid(); + make_valid(); } - bufType valTest = readCache_; + bufType val_test = read_cache_; - for (int32_t count = 0; count < 16; count++) + for (int32_t count{}; count < 16; ++count) { - if ((valTest & (static_cast(1) << (bufType_bit_count - 1))) != 0) + if ((val_test & (static_cast(1) << (bufType_bit_count - 1))) != 0) return count; - valTest <<= 1; + val_test <<= 1; } return -1; } - FORCE_INLINE int32_t ReadHighBits() + FORCE_INLINE int32_t read_high_bits() { - const int32_t count = Peek0Bits(); + const int32_t count{peek_0_bits()}; if (count >= 0) { - Skip(count + 1); + skip(count + 1); return count; } - Skip(15); + skip(15); - for (int32_t highBitsCount = 15; ; highBitsCount++) + for (int32_t high_bits_count{15};; ++high_bits_count) { - if (ReadBit()) - return highBitsCount; + if (read_bit()) + return high_bits_count; } } - int32_t ReadLongValue(int32_t length) + int32_t read_long_value(const int32_t length) { if (length <= 24) - return ReadValue(length); + return read_value(length); - return (ReadValue(length - 24) << 24) + ReadValue(24); + return (read_value(length - 24) << 24) + read_value(24); } protected: - JlsParameters params_; - std::unique_ptr processLine_; + frame_info frame_info_; + coding_parameters parameters_; + std::unique_ptr process_line_; private: - using bufType = std::size_t; + using bufType = size_t; static constexpr auto bufType_bit_count = static_cast(sizeof(bufType) * 8); std::vector buffer_; - std::basic_streambuf* byteStream_{}; // decoding - bufType readCache_{}; - int32_t validBits_{}; + bufType read_cache_{}; + int32_t valid_bits_{}; uint8_t* position_{}; - uint8_t* nextFFPosition_{}; - uint8_t* endPosition_{}; + uint8_t* next_ff_position_{}; + uint8_t* end_position_{}; }; } // namespace charls diff -Nru charls-2.1.0+dfsg/src/default_traits.h charls-2.2.0+dfsg/src/default_traits.h --- charls-2.1.0+dfsg/src/default_traits.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/default_traits.h 2021-01-10 18:07:25.000000000 +0000 @@ -6,7 +6,6 @@ #include "constants.h" #include "util.h" -#include #include #include #include @@ -16,138 +15,127 @@ // This traits class is used to initialize a coder/decoder. // The coder/decoder also delegates some functions to the traits class. // This is to allow the traits class to replace the default implementation here with optimized specific implementations. -// This is done for lossless coding/decoding: see losslesstraits.h +// This is done for lossless coding/decoding: see lossless_traits.h namespace charls { -template -struct DefaultTraits final +template +struct default_traits final { - using SAMPLE = sample; - using PIXEL = pixel; + using sample_type = SampleType; + using pixel_type = PixelType; - int32_t MAXVAL; - const int32_t RANGE; - const int32_t NEAR; - const int32_t qbpp; - const int32_t bpp; - const int32_t LIMIT; - const int32_t RESET; + int32_t maximum_sample_value; + const int32_t near_lossless; + const int32_t range; + const int32_t quantized_bits_per_pixel; + const int32_t bits_per_pixel; + const int32_t limit; + const int32_t reset_threshold; - DefaultTraits(int32_t max, int32_t near, int32_t reset = DefaultResetValue) noexcept : - MAXVAL{max}, - RANGE{(max + 2 * near) / (2 * near + 1) + 1}, - NEAR{near}, - qbpp{log_2(RANGE)}, - bpp{log_2(max)}, - LIMIT{2 * (bpp + std::max(8, bpp))}, - RESET{reset} + default_traits(const int32_t arg_maximum_sample_value, const int32_t arg_near_lossless, + const int32_t reset = default_reset_value) noexcept : + maximum_sample_value{arg_maximum_sample_value}, + near_lossless{arg_near_lossless}, + range{compute_range_parameter(maximum_sample_value, near_lossless)}, + quantized_bits_per_pixel{log_2(range)}, + bits_per_pixel{log_2(maximum_sample_value)}, + limit{compute_limit_parameter(bits_per_pixel)}, + reset_threshold{reset} { } - DefaultTraits(const DefaultTraits& other) noexcept : - MAXVAL{other.MAXVAL}, - RANGE{other.RANGE}, - NEAR{other.NEAR}, - qbpp{other.qbpp}, - bpp{other.bpp}, - LIMIT{other.LIMIT}, - RESET{other.RESET} - { - } - - DefaultTraits() = delete; - DefaultTraits(DefaultTraits&&) noexcept = default; - ~DefaultTraits() = default; - DefaultTraits& operator=(const DefaultTraits&) = delete; - DefaultTraits& operator=(DefaultTraits&&) = delete; + default_traits() = delete; + default_traits(const default_traits&) noexcept = default; + default_traits(default_traits&&) noexcept = default; + ~default_traits() = default; + default_traits& operator=(const default_traits&) = delete; + default_traits& operator=(default_traits&&) = delete; - FORCE_INLINE int32_t ComputeErrVal(int32_t e) const noexcept + FORCE_INLINE int32_t compute_error_value(const int32_t e) const noexcept { - return ModuloRange(Quantize(e)); + return modulo_range(quantize(e)); } - FORCE_INLINE SAMPLE ComputeReconstructedSample(int32_t Px, int32_t ErrVal) const noexcept + FORCE_INLINE SampleType compute_reconstructed_sample(const int32_t predicted_value, + const int32_t error_value) const noexcept { - return FixReconstructedValue(Px + DeQuantize(ErrVal)); + return fix_reconstructed_value(predicted_value + dequantize(error_value)); } - FORCE_INLINE bool IsNear(int32_t lhs, int32_t rhs) const noexcept + FORCE_INLINE bool is_near(const int32_t lhs, const int32_t rhs) const noexcept { - return std::abs(lhs - rhs) <= NEAR; + return std::abs(lhs - rhs) <= near_lossless; } - bool IsNear(Triplet lhs, Triplet rhs) const noexcept + bool is_near(const triplet lhs, const triplet rhs) const noexcept { - return std::abs(lhs.v1 - rhs.v1) <= NEAR && - std::abs(lhs.v2 - rhs.v2) <= NEAR && - std::abs(lhs.v3 - rhs.v3) <= NEAR; + return std::abs(lhs.v1 - rhs.v1) <= near_lossless && std::abs(lhs.v2 - rhs.v2) <= near_lossless && + std::abs(lhs.v3 - rhs.v3) <= near_lossless; } - bool IsNear(Quad lhs, Quad rhs) const noexcept + bool is_near(const quad lhs, const quad rhs) const noexcept { - return std::abs(lhs.v1 - rhs.v1) <= NEAR && - std::abs(lhs.v2 - rhs.v2) <= NEAR && - std::abs(lhs.v3 - rhs.v3) <= NEAR && - std::abs(lhs.v4 - rhs.v4) <= NEAR; + return std::abs(lhs.v1 - rhs.v1) <= near_lossless && std::abs(lhs.v2 - rhs.v2) <= near_lossless && + std::abs(lhs.v3 - rhs.v3) <= near_lossless && std::abs(lhs.v4 - rhs.v4) <= near_lossless; } - FORCE_INLINE int32_t CorrectPrediction(int32_t Pxc) const noexcept + FORCE_INLINE int32_t correct_prediction(const int32_t predicted) const noexcept { - if ((Pxc & MAXVAL) == Pxc) - return Pxc; + if ((predicted & maximum_sample_value) == predicted) + return predicted; - return (~(Pxc >> (int32_t_bit_count - 1))) & MAXVAL; + return (~(predicted >> (int32_t_bit_count - 1))) & maximum_sample_value; } /// /// Returns the value of errorValue modulo RANGE. ITU.T.87, A.4.5 (code segment A.9) /// This ensures the error is reduced to the range (-⌊RANGE/2⌋ .. ⌈RANGE/2⌉-1) /// - FORCE_INLINE int32_t ModuloRange(int32_t errorValue) const noexcept + FORCE_INLINE int32_t modulo_range(int32_t error_value) const noexcept { - ASSERT(std::abs(errorValue) <= RANGE); + ASSERT(std::abs(error_value) <= range); - if (errorValue < 0) + if (error_value < 0) { - errorValue += RANGE; + error_value += range; } - if (errorValue >= (RANGE + 1) / 2) + if (error_value >= (range + 1) / 2) { - errorValue -= RANGE; + error_value -= range; } - ASSERT(-RANGE / 2 <= errorValue && errorValue <= ((RANGE + 1) / 2) - 1); - return errorValue; + ASSERT(-range / 2 <= error_value && error_value <= ((range + 1) / 2) - 1); + return error_value; } private: - int32_t Quantize(int32_t errorValue) const noexcept + int32_t quantize(const int32_t error_value) const noexcept { - if (errorValue > 0) - return (errorValue + NEAR) / (2 * NEAR + 1); + if (error_value > 0) + return (error_value + near_lossless) / (2 * near_lossless + 1); - return -(NEAR - errorValue) / (2 * NEAR + 1); + return -(near_lossless - error_value) / (2 * near_lossless + 1); } - FORCE_INLINE int32_t DeQuantize(int32_t ErrorValue) const noexcept + FORCE_INLINE int32_t dequantize(const int32_t error_value) const noexcept { - return ErrorValue * (2 * NEAR + 1); + return error_value * (2 * near_lossless + 1); } - FORCE_INLINE SAMPLE FixReconstructedValue(int32_t value) const noexcept + FORCE_INLINE SampleType fix_reconstructed_value(int32_t value) const noexcept { - if (value < -NEAR) + if (value < -near_lossless) { - value = value + RANGE * (2 * NEAR + 1); + value = value + range * (2 * near_lossless + 1); } - else if (value > MAXVAL + NEAR) + else if (value > maximum_sample_value + near_lossless) { - value = value - RANGE * (2 * NEAR + 1); + value = value - range * (2 * near_lossless + 1); } - return static_cast(CorrectPrediction(value)); + return static_cast(correct_prediction(value)); } }; diff -Nru charls-2.1.0+dfsg/src/encoder_strategy.h charls-2.2.0+dfsg/src/encoder_strategy.h --- charls-2.1.0+dfsg/src/encoder_strategy.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/encoder_strategy.h 2021-01-10 18:07:25.000000000 +0000 @@ -8,182 +8,154 @@ namespace charls { -// Purpose: Implements encoding to stream of bits. In encoding mode JpegLsCodec inherits from EncoderStrategy -class EncoderStrategy +// Purpose: Implements encoding to stream of bits. In encoding mode jls_codec inherits from encoder_strategy +class encoder_strategy { public: - explicit EncoderStrategy(const JlsParameters& params) : - params_{params} + explicit encoder_strategy(const frame_info& frame, const coding_parameters& parameters) noexcept : + frame_info_{frame}, parameters_{parameters} { } - virtual ~EncoderStrategy() = default; + virtual ~encoder_strategy() = default; - EncoderStrategy(const EncoderStrategy&) = delete; - EncoderStrategy(EncoderStrategy&&) = delete; - EncoderStrategy& operator=(const EncoderStrategy&) = delete; - EncoderStrategy& operator=(EncoderStrategy&&) = delete; + encoder_strategy(const encoder_strategy&) = delete; + encoder_strategy(encoder_strategy&&) = delete; + encoder_strategy& operator=(const encoder_strategy&) = delete; + encoder_strategy& operator=(encoder_strategy&&) = delete; - virtual std::unique_ptr CreateProcess(ByteStreamInfo rawStreamInfo) = 0; - virtual void SetPresets(const jpegls_pc_parameters& preset_coding_parameters) = 0; - virtual std::size_t EncodeScan(std::unique_ptr rawData, ByteStreamInfo& compressedData) = 0; + virtual std::unique_ptr create_process_line(byte_span stream_info, size_t stride) = 0; + virtual void set_presets(const jpegls_pc_parameters& preset_coding_parameters) = 0; + virtual size_t encode_scan(std::unique_ptr raw_data, byte_span destination) = 0; - int32_t PeekByte(); + int32_t peek_byte(); - void OnLineBegin(int32_t cpixel, void* ptypeBuffer, int32_t pixelStride) const + void on_line_begin(const size_t pixel_count, void* destination, const size_t pixel_stride) const { - processLine_->NewLineRequested(ptypeBuffer, cpixel, pixelStride); + process_line_->new_line_requested(destination, pixel_count, pixel_stride); } - static void OnLineEnd(int32_t /*cpixel*/, void* /*ptypeBuffer*/, int32_t /*pixelStride*/) noexcept + static void on_line_end(size_t /*pixel_count*/, void* /*destination*/, size_t /*pixel_stride*/) noexcept { } protected: - void Init(ByteStreamInfo& compressedStream) + void initialize(const byte_span destination) noexcept { - freeBitCount_ = sizeof(bitBuffer_) * 8; - bitBuffer_ = 0; + free_bit_count_ = sizeof(bit_buffer_) * 8; + bit_buffer_ = 0; - if (compressedStream.rawStream) - { - compressedStream_ = compressedStream.rawStream; - buffer_.resize(4000); - position_ = buffer_.data(); - compressedLength_ = buffer_.size(); - } - else - { - position_ = compressedStream.rawData; - compressedLength_ = compressedStream.count; - } + position_ = destination.data; + compressed_length_ = destination.size; } - void AppendToBitStream(int32_t bits, int32_t bitCount) + void append_to_bit_stream(const uint32_t bits, const int32_t bit_count) { - ASSERT(bitCount < 32 && bitCount >= 0); - ASSERT((!decoder_) || (bitCount == 0 && bits == 0) || (decoder_->ReadLongValue(bitCount) == bits)); + ASSERT(bit_count < 32 && bit_count >= 0); + ASSERT((!decoder_) || (bit_count == 0 && bits == 0) || + (static_cast(decoder_->read_long_value(bit_count)) == bits)); #ifndef NDEBUG - const int mask = (1U << (bitCount)) - 1; + const uint32_t mask{(1U << bit_count) - 1U}; ASSERT((bits | mask) == mask); // Not used bits must be set to zero. #endif - freeBitCount_ -= bitCount; - if (freeBitCount_ >= 0) + free_bit_count_ -= bit_count; + if (free_bit_count_ >= 0) { - bitBuffer_ |= bits << freeBitCount_; + bit_buffer_ |= bits << free_bit_count_; } else { // Add as much bits in the remaining space as possible and flush. - bitBuffer_ |= bits >> -freeBitCount_; - Flush(); + bit_buffer_ |= bits >> -free_bit_count_; + flush(); // A second flush may be required if extra marker detect bits were needed and not all bits could be written. - if (freeBitCount_ < 0) + if (free_bit_count_ < 0) { - bitBuffer_ |= bits >> -freeBitCount_; - Flush(); + bit_buffer_ |= bits >> -free_bit_count_; + flush(); } - ASSERT(freeBitCount_ >= 0); - bitBuffer_ |= bits << freeBitCount_; + ASSERT(free_bit_count_ >= 0); + bit_buffer_ |= bits << free_bit_count_; } } - void EndScan() + void end_scan() { - Flush(); + flush(); // if a 0xff was written, Flush() will force one unset bit anyway - if (isFFWritten_) - AppendToBitStream(0, (freeBitCount_ - 1) % 8); - else - AppendToBitStream(0, freeBitCount_ % 8); - - Flush(); - ASSERT(freeBitCount_ == 0x20); - - if (compressedStream_) + if (is_ff_written_) { - OverFlow(); + append_to_bit_stream(0, (free_bit_count_ - 1) % 8); } - } - void OverFlow() - { - if (!compressedStream_) - throw jpegls_error{jpegls_errc::destination_buffer_too_small}; - - const std::size_t bytesCount = position_ - buffer_.data(); - const auto bytesWritten = static_cast(compressedStream_->sputn(reinterpret_cast(buffer_.data()), position_ - buffer_.data())); - - if (bytesWritten != bytesCount) - throw jpegls_error{jpegls_errc::destination_buffer_too_small}; - - position_ = buffer_.data(); - compressedLength_ = buffer_.size(); + flush(); + ASSERT(free_bit_count_ == 32); } - void Flush() + void flush() { - if (compressedLength_ < 4) - { - OverFlow(); - } + if (compressed_length_ < 4) + impl::throw_jpegls_error(jpegls_errc::destination_buffer_too_small); - for (int i = 0; i < 4; ++i) + for (int i{}; i < 4; ++i) { - if (freeBitCount_ >= 32) + if (free_bit_count_ >= 32) + { + free_bit_count_ = 32; break; + } - if (isFFWritten_) + if (is_ff_written_) { // JPEG-LS requirement (T.87, A.1) to detect markers: after a xFF value a single 0 bit needs to be inserted. - *position_ = static_cast(bitBuffer_ >> 25); - bitBuffer_ = bitBuffer_ << 7; - freeBitCount_ += 7; + *position_ = static_cast(bit_buffer_ >> 25); + bit_buffer_ = bit_buffer_ << 7; + free_bit_count_ += 7; } else { - *position_ = static_cast(bitBuffer_ >> 24); - bitBuffer_ = bitBuffer_ << 8; - freeBitCount_ += 8; + *position_ = static_cast(bit_buffer_ >> 24); + bit_buffer_ = bit_buffer_ << 8; + free_bit_count_ += 8; } - isFFWritten_ = *position_ == JpegMarkerStartByte; - position_++; - compressedLength_--; - bytesWritten_++; + is_ff_written_ = *position_ == jpeg_marker_start_byte; + ++position_; + --compressed_length_; + ++bytes_written_; } } - std::size_t GetLength() const noexcept + size_t get_length() const noexcept { - return bytesWritten_ - (freeBitCount_ - 32) / 8; + return bytes_written_ - (static_cast(free_bit_count_) - 32U) / 8U; } - FORCE_INLINE void AppendOnesToBitStream(int32_t length) + FORCE_INLINE void append_ones_to_bit_stream(const int32_t length) { - AppendToBitStream((1 << length) - 1, length); + append_to_bit_stream((1U << length) - 1U, length); } - std::unique_ptr decoder_; - JlsParameters params_; - std::unique_ptr processLine_; + frame_info frame_info_; + coding_parameters parameters_; + std::unique_ptr decoder_; + std::unique_ptr process_line_; private: - unsigned int bitBuffer_{}; - int32_t freeBitCount_{sizeof bitBuffer_ * 8}; - std::size_t compressedLength_{}; + unsigned int bit_buffer_{}; + int32_t free_bit_count_{sizeof bit_buffer_ * 8}; + size_t compressed_length_{}; // encoding uint8_t* position_{}; - bool isFFWritten_{}; - std::size_t bytesWritten_{}; + bool is_ff_written_{}; + size_t bytes_written_{}; std::vector buffer_; - std::basic_streambuf* compressedStream_{}; }; } // namespace charls diff -Nru charls-2.1.0+dfsg/src/interface.cpp charls-2.2.0+dfsg/src/interface.cpp --- charls-2.1.0+dfsg/src/interface.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/interface.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,194 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -#include "jpeg_stream_reader.h" -#include "jpeg_stream_writer.h" -#include "jpegls_preset_coding_parameters.h" -#include "encoder_strategy.h" -#include "jls_codec_factory.h" -#include "util.h" -#include "constants.h" - -using namespace charls; - -namespace { - -void VerifyInput(const ByteStreamInfo& destination, const JlsParameters& parameters) -{ - if (!destination.rawStream && !destination.rawData) - throw jpegls_error{jpegls_errc::invalid_operation}; - - if (parameters.bitsPerSample < MinimumBitsPerSample || parameters.bitsPerSample > MaximumBitsPerSample) - throw jpegls_error{jpegls_errc::invalid_argument_bits_per_sample}; - - if (!(parameters.interleaveMode == interleave_mode::none || parameters.interleaveMode == interleave_mode::sample || parameters.interleaveMode == interleave_mode::line)) - throw jpegls_error{jpegls_errc::invalid_argument_interleave_mode}; - - if (parameters.components < 1 || parameters.components > MaximumComponentCount) - throw jpegls_error{jpegls_errc::invalid_argument_component_count}; - - if (destination.rawData && - destination.count < static_cast(parameters.height) * parameters.width * parameters.components * (parameters.bitsPerSample > 8 ? 2 : 1)) - throw jpegls_error{jpegls_errc::destination_buffer_too_small}; - - switch (parameters.components) - { - case 3: - case 4: - break; - default: - if (parameters.interleaveMode != interleave_mode::none) - throw jpegls_error{jpegls_errc::invalid_argument_interleave_mode}; - break; - } -} - -void EncodeScan(const JlsParameters& params, int componentCount, ByteStreamInfo source, JpegStreamWriter& writer) -{ - JlsParameters info{params}; - info.components = componentCount; - - const jpegls_pc_parameters preset_coding_parameters{ - info.custom.MaximumSampleValue, - info.custom.Threshold1, - info.custom.Threshold2, - info.custom.Threshold3, - info.custom.ResetValue, - }; - - auto codec = JlsCodecFactory().CreateCodec(info, preset_coding_parameters); - std::unique_ptr processLine(codec->CreateProcess(source)); - ByteStreamInfo destination{writer.OutputStream()}; - const size_t bytesWritten = codec->EncodeScan(move(processLine), destination); - - // Synchronize the destination encapsulated in the writer (EncodeScan works on a local copy) - writer.Seek(bytesWritten); -} - -} // namespace - - -jpegls_errc JpegLsEncodeStream(ByteStreamInfo destination, size_t& bytesWritten, - ByteStreamInfo source, const JlsParameters& params) -{ - if (params.width < 1 || params.width > 65535) - return jpegls_errc::invalid_argument_width; - - if (params.height < 1 || params.height > 65535) - return jpegls_errc::invalid_argument_height; - - try - { - VerifyInput(source, params); - - JlsParameters info{params}; - if (info.stride == 0) - { - info.stride = info.width * ((info.bitsPerSample + 7) / 8); - if (info.interleaveMode != interleave_mode::none) - { - info.stride *= info.components; - } - } - - JpegStreamWriter writer{destination}; - - writer.WriteStartOfImage(); - - writer.WriteStartOfFrameSegment(info.width, info.height, info.bitsPerSample, info.components); - - if (info.colorTransformation != color_transformation::none) - { - writer.WriteColorTransformSegment(info.colorTransformation); - } - - const jpegls_pc_parameters preset_coding_parameters{ - info.custom.MaximumSampleValue, - info.custom.Threshold1, - info.custom.Threshold2, - info.custom.Threshold3, - info.custom.ResetValue, - }; - - if (!is_default(preset_coding_parameters)) - { - writer.WriteJpegLSPresetParametersSegment(preset_coding_parameters); - } - else if (info.bitsPerSample > 12) - { - const auto default_preset_coding_parameters{compute_default((1 << info.bitsPerSample) - 1, info.allowedLossyError)}; - writer.WriteJpegLSPresetParametersSegment(default_preset_coding_parameters); - } - - if (info.interleaveMode == interleave_mode::none) - { - const int32_t byteCountComponent = info.width * info.height * ((info.bitsPerSample + 7) / 8); - for (int32_t component = 0; component < info.components; ++component) - { - writer.WriteStartOfScanSegment(1, info.allowedLossyError, info.interleaveMode); - EncodeScan(info, 1, source, writer); - - // Synchronize the source stream (EncodeScan works on a local copy) - SkipBytes(source, byteCountComponent); - } - } - else - { - writer.WriteStartOfScanSegment(info.components, info.allowedLossyError, info.interleaveMode); - EncodeScan(info, info.components, source, writer); - } - - writer.WriteEndOfImage(); - - bytesWritten = writer.GetBytesWritten(); - - return jpegls_errc::success; - } - catch (...) - { - return to_jpegls_errc(); - } -} - - -jpegls_errc JpegLsDecodeStream(ByteStreamInfo destination, ByteStreamInfo source, const JlsParameters* params) -{ - try - { - JpegStreamReader reader{source}; - - reader.ReadHeader(); - reader.ReadStartOfScan(true); - - if (params) - { - reader.SetInfo(*params); - } - - reader.Read(destination); - - return jpegls_errc::success; - } - catch (...) - { - return to_jpegls_errc(); - } -} - - -jpegls_errc JpegLsReadHeaderStream(ByteStreamInfo source, JlsParameters* params) -{ - try - { - JpegStreamReader reader{source}; - reader.ReadHeader(); - reader.ReadStartOfScan(true); - *params = reader.GetMetadata(); - - return jpegls_errc::success; - } - catch (...) - { - return to_jpegls_errc(); - } -} diff -Nru charls-2.1.0+dfsg/src/jls_codec_factory.h charls-2.2.0+dfsg/src/jls_codec_factory.h --- charls-2.1.0+dfsg/src/jls_codec_factory.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/jls_codec_factory.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,22 +3,28 @@ #pragma once -#include +#include "coding_parameters.h" #include -struct JlsParameters; namespace charls { +class decoder_strategy; +class encoder_strategy; + template -class JlsCodecFactory final +class jls_codec_factory final { public: - std::unique_ptr CreateCodec(const JlsParameters& params, const jpegls_pc_parameters& preset_coding_parameters); + std::unique_ptr create_codec(const frame_info& frame, const coding_parameters& parameters, + const jpegls_pc_parameters& preset_coding_parameters); private: - std::unique_ptr CreateOptimizedCodec(const JlsParameters& params); + std::unique_ptr try_create_optimized_codec(const frame_info& frame, const coding_parameters& parameters); }; +extern template class jls_codec_factory; +extern template class jls_codec_factory; + } // namespace charls diff -Nru charls-2.1.0+dfsg/src/jpegls.cpp charls-2.2.0+dfsg/src/jpegls.cpp --- charls-2.1.0+dfsg/src/jpegls.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/jpegls.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -1,145 +1,163 @@ // Copyright (c) Team CharLS. // SPDX-License-Identifier: BSD-3-Clause -#include "decoder_strategy.h" #include "default_traits.h" #include "encoder_strategy.h" #include "jls_codec_factory.h" -#include "jpeg_stream_reader.h" #include "jpegls_preset_coding_parameters.h" -#include "lookup_table.h" #include "lossless_traits.h" +#include "scan.h" #include "util.h" +#include #include -// As defined in the JPEG-LS standard - -// used to determine how large runs should be encoded at a time. -const int J[32] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - -#include "scan.h" +namespace charls { +using std::array; using std::make_unique; using std::unique_ptr; using std::vector; -using namespace charls; namespace { -signed char QuantizeGradientOrg(const jpegls_pc_parameters& preset, int32_t near_lossless, int32_t Di) noexcept +// See JPEG-LS standard ISO/IEC 14495-1, A.3.3, golomb_code Segment A.4 +int8_t quantize_gradient_org(const jpegls_pc_parameters& preset, const int32_t di) noexcept { - if (Di <= -preset.threshold3) return -4; - if (Di <= -preset.threshold2) return -3; - if (Di <= -preset.threshold1) return -2; - if (Di < -near_lossless) return -1; - if (Di <= near_lossless) return 0; - if (Di < preset.threshold1) return 1; - if (Di < preset.threshold2) return 2; - if (Di < preset.threshold3) return 3; + constexpr int32_t near_lossless{}; + + if (di <= -preset.threshold3) + return -4; + if (di <= -preset.threshold2) + return -3; + if (di <= -preset.threshold1) + return -2; + if (di < -near_lossless) + return -1; + if (di <= near_lossless) + return 0; + if (di < preset.threshold1) + return 1; + if (di < preset.threshold2) + return 2; + if (di < preset.threshold3) + return 3; return 4; } -vector CreateQLutLossless(int32_t bitCount) +vector create_quantize_lut_lossless(const int32_t bit_count) { - const jpegls_pc_parameters preset{compute_default((1U << static_cast(bitCount)) - 1, 0)}; - const int32_t range = preset.maximum_sample_value + 1; + const jpegls_pc_parameters preset{compute_default((1 << static_cast(bit_count)) - 1, 0)}; + const int32_t range{preset.maximum_sample_value + 1}; - vector lut(static_cast(range) * 2); - - for (int32_t diff = -range; diff < range; diff++) + vector lut(static_cast(range) * 2); + for (size_t i{}; i < lut.size(); ++i) { - lut[static_cast(range) + diff] = QuantizeGradientOrg(preset, 0, diff); + lut[i] = quantize_gradient_org(preset, static_cast(i) - range); } + return lut; } template -unique_ptr create_codec(const Traits& traits, const JlsParameters& params) +unique_ptr make_codec(const Traits& traits, const frame_info& frame_info, const coding_parameters& parameters) { - return make_unique>(traits, params); + return make_unique>(traits, frame_info, parameters); } } // namespace -namespace charls { - // Lookup tables to replace code with lookup tables. // To avoid threading issues, all tables are created when the program is loaded. // Lookup table: decode symbols that are smaller or equal to 8 bit (16 tables for each value of k) -CTable decodingTables[16] = {InitTable(0), InitTable(1), InitTable(2), InitTable(3), - InitTable(4), InitTable(5), InitTable(6), InitTable(7), - InitTable(8), InitTable(9), InitTable(10), InitTable(11), - InitTable(12), InitTable(13), InitTable(14), InitTable(15)}; +// NOLINTNEXTLINE(clang-diagnostic-global-constructors) +const array decoding_tables{ + initialize_table(0), initialize_table(1), initialize_table(2), initialize_table(3), + initialize_table(4), initialize_table(5), initialize_table(6), initialize_table(7), + initialize_table(8), initialize_table(9), initialize_table(10), initialize_table(11), + initialize_table(12), initialize_table(13), initialize_table(14), initialize_table(15)}; // Lookup tables: sample differences to bin indexes. -vector rgquant8Ll = CreateQLutLossless(8); -vector rgquant10Ll = CreateQLutLossless(10); -vector rgquant12Ll = CreateQLutLossless(12); -vector rgquant16Ll = CreateQLutLossless(16); +// NOLINTNEXTLINE(clang-diagnostic-global-constructors) +const vector quantization_lut_lossless_8{create_quantize_lut_lossless(8)}; + +// NOLINTNEXTLINE(clang-diagnostic-global-constructors) +const vector quantization_lut_lossless_10{create_quantize_lut_lossless(10)}; + +// NOLINTNEXTLINE(clang-diagnostic-global-constructors) +const vector quantization_lut_lossless_12{create_quantize_lut_lossless(12)}; + +// NOLINTNEXTLINE(clang-diagnostic-global-constructors) +const vector quantization_lut_lossless_16{create_quantize_lut_lossless(16)}; template -unique_ptr JlsCodecFactory::CreateCodec(const JlsParameters& params, const jpegls_pc_parameters& preset_coding_parameters) +unique_ptr jls_codec_factory::create_codec(const frame_info& frame, const coding_parameters& parameters, + const jpegls_pc_parameters& preset_coding_parameters) { unique_ptr codec; - if (preset_coding_parameters.reset_value == 0 || preset_coding_parameters.reset_value == DefaultResetValue) + if (preset_coding_parameters.reset_value == 0 || preset_coding_parameters.reset_value == default_reset_value) { - codec = CreateOptimizedCodec(params); + codec = try_create_optimized_codec(frame, parameters); } if (!codec) { - if (params.bitsPerSample <= 8) + if (frame.bits_per_sample <= 8) { - DefaultTraits traits((1 << params.bitsPerSample) - 1, params.allowedLossyError, preset_coding_parameters.reset_value); - traits.MAXVAL = preset_coding_parameters.maximum_sample_value; - codec = make_unique, Strategy>>(traits, params); + default_traits traits( + static_cast(calculate_maximum_sample_value(frame.bits_per_sample)), parameters.near_lossless, + preset_coding_parameters.reset_value); + traits.maximum_sample_value = preset_coding_parameters.maximum_sample_value; + codec = make_unique, Strategy>>(traits, frame, parameters); } else { - DefaultTraits traits((1 << params.bitsPerSample) - 1, params.allowedLossyError, preset_coding_parameters.reset_value); - traits.MAXVAL = preset_coding_parameters.maximum_sample_value; - codec = make_unique, Strategy>>(traits, params); + default_traits traits( + static_cast(calculate_maximum_sample_value(frame.bits_per_sample)), parameters.near_lossless, + preset_coding_parameters.reset_value); + traits.maximum_sample_value = preset_coding_parameters.maximum_sample_value; + codec = make_unique, Strategy>>(traits, frame, parameters); } } - codec->SetPresets(preset_coding_parameters); + codec->set_presets(preset_coding_parameters); return codec; } template -unique_ptr JlsCodecFactory::CreateOptimizedCodec(const JlsParameters& params) +unique_ptr jls_codec_factory::try_create_optimized_codec(const frame_info& frame, + const coding_parameters& parameters) { - if (params.interleaveMode == interleave_mode::sample && params.components != 3 && params.components != 4) + if (parameters.interleave_mode == interleave_mode::sample && frame.component_count != 3 && frame.component_count != 4) return nullptr; #ifndef DISABLE_SPECIALIZATIONS // optimized lossless versions common formats - if (params.allowedLossyError == 0) + if (parameters.near_lossless == 0) { - if (params.interleaveMode == interleave_mode::sample) + if (parameters.interleave_mode == interleave_mode::sample) { - if (params.components == 3 && params.bitsPerSample == 8) - return create_codec(LosslessTraits, 8>(), params); - if (params.components == 4 && params.bitsPerSample == 8) - return create_codec(LosslessTraits, 8>(), params); + if (frame.component_count == 3 && frame.bits_per_sample == 8) + return make_codec(lossless_traits, 8>(), frame, parameters); + if (frame.component_count == 4 && frame.bits_per_sample == 8) + return make_codec(lossless_traits, 8>(), frame, parameters); } else { - switch (params.bitsPerSample) + switch (frame.bits_per_sample) { case 8: - return create_codec(LosslessTraits(), params); + return make_codec(lossless_traits(), frame, parameters); case 12: - return create_codec(LosslessTraits(), params); + return make_codec(lossless_traits(), frame, parameters); case 16: - return create_codec(LosslessTraits(), params); + return make_codec(lossless_traits(), frame, parameters); default: break; } @@ -148,37 +166,51 @@ #endif - const int maxval = (1u << static_cast(params.bitsPerSample)) - 1; + const auto maxval = static_cast(calculate_maximum_sample_value(frame.bits_per_sample)); - if (params.bitsPerSample <= 8) + if (frame.bits_per_sample <= 8) { - if (params.interleaveMode == interleave_mode::sample) + if (parameters.interleave_mode == interleave_mode::sample) { - if (params.components == 3) - return create_codec(DefaultTraits>(maxval, params.allowedLossyError), params); - if (params.components == 4) - return create_codec(DefaultTraits>(maxval, params.allowedLossyError), params); + if (frame.component_count == 3) + { + return make_codec(default_traits>(maxval, parameters.near_lossless), + frame, parameters); + } + + if (frame.component_count == 4) + { + return make_codec(default_traits>(maxval, parameters.near_lossless), frame, + parameters); + } } - return create_codec(DefaultTraits((1u << params.bitsPerSample) - 1, params.allowedLossyError), params); + return make_codec(default_traits(maxval, parameters.near_lossless), frame, parameters); } - if (params.bitsPerSample <= 16) + if (frame.bits_per_sample <= 16) { - if (params.interleaveMode == interleave_mode::sample) + if (parameters.interleave_mode == interleave_mode::sample) { - if (params.components == 3) - return create_codec(DefaultTraits>(maxval, params.allowedLossyError), params); - if (params.components == 4) - return create_codec(DefaultTraits>(maxval, params.allowedLossyError), params); + if (frame.component_count == 3) + { + return make_codec(default_traits>(maxval, parameters.near_lossless), + frame, parameters); + } + + if (frame.component_count == 4) + { + return make_codec(default_traits>(maxval, parameters.near_lossless), + frame, parameters); + } } - return create_codec(DefaultTraits(maxval, params.allowedLossyError), params); + return make_codec(default_traits(maxval, parameters.near_lossless), frame, parameters); } return nullptr; } -template class JlsCodecFactory; -template class JlsCodecFactory; +template class jls_codec_factory; +template class jls_codec_factory; } // namespace charls diff -Nru charls-2.1.0+dfsg/src/jpegls_error.cpp charls-2.2.0+dfsg/src/jpegls_error.cpp --- charls-2.1.0+dfsg/src/jpegls_error.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/jpegls_error.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -3,11 +3,12 @@ #include -using std::string; -using std::error_category; +#include namespace charls { +using std::error_category; + class jpegls_category final : public error_category { public: @@ -16,23 +17,23 @@ return "charls::jpegls"; } - string message(int error_value) const override + std::string message(int error_value) const override { return charls_get_error_message(static_cast(error_value)); } }; -} +} // namespace charls using namespace charls; -const void* CHARLS_API_CALLING_CONVENTION charls_get_jpegls_category() +const error_category* CHARLS_API_CALLING_CONVENTION charls_get_jpegls_category() { static class jpegls_category instance; return &instance; } -const char* CHARLS_API_CALLING_CONVENTION charls_get_error_message(jpegls_errc error_value) +const char* CHARLS_API_CALLING_CONVENTION charls_get_error_message(const charls_jpegls_errc error_value) { switch (error_value) { @@ -64,16 +65,17 @@ return "The argument for the entry size parameter is outside the range [0, 65528]"; case jpegls_errc::invalid_argument_color_transformation: - return "The argument for the color component is not (None, Hp1, Hp2, Hp3) or invalid in combination with component count"; + return "The argument for the color component is not (None, Hp1, Hp2, Hp3) or invalid in combination with component " + "count"; - case jpegls_errc::invalid_argument_pc_parameters: + case jpegls_errc::invalid_argument_jpegls_pc_parameters: return "The argument for the JPEG-LS preset coding parameters is not valid"; case jpegls_errc::start_of_image_marker_not_found: return "Invalid JPEG-LS stream, first JPEG marker is not a Start Of Image (SOI) marker"; - case jpegls_errc::start_of_frame_marker_not_found: - return "Invalid JPEG-LS stream, Start Of Frame (SOF) marker not found before the SOS marker"; + case jpegls_errc::unexpected_marker_found: + return "Invalid JPEG-LS stream, unexpected marker found"; case jpegls_errc::invalid_marker_segment_size: return "Invalid JPEG-LS stream, segment size of a marker segment is invalid"; @@ -94,7 +96,8 @@ return "Invalid JPEG-LS stream, JPEG-LS preset parameters segment contains an invalid type"; case jpegls_errc::jpegls_preset_extended_parameter_type_not_supported: - return "Unsupported JPEG-LS stream, JPEG-LS preset parameters segment contains an JPEG-LS Extended (ISO/IEC 14495-2) type"; + return "Unsupported JPEG-LS stream, JPEG-LS preset parameters segment contains an JPEG-LS Extended (ISO/IEC " + "14495-2) type"; case jpegls_errc::missing_end_of_spiff_directory: return "Invalid JPEG-LS stream, SPIFF header without End Of Directory (EOD) entry"; @@ -139,7 +142,7 @@ return "No memory could be allocated for an internal buffer"; case jpegls_errc::unexpected_failure: - return "An unexpected internal failure occured"; + return "An unexpected internal failure occurred"; case jpegls_errc::invalid_parameter_width: return "Invalid JPEG-LS stream, the width (Number of samples per line) is already defined"; @@ -152,6 +155,12 @@ case jpegls_errc::invalid_parameter_interleave_mode: return "Invalid JPEG-LS stream, interleave mode is outside the range [0, 2] or conflicts with component count"; + + case jpegls_errc::invalid_parameter_near_lossless: + return "Invalid JPEG-LS stream, near-lossless is outside the range [0, min(255, MAXVAL/2)]"; + + case jpegls_errc::invalid_parameter_jpegls_pc_parameters: + return "Invalid JPEG-LS stream, JPEG-LS preset coding parameters segment contains invalid values"; } return "Unknown"; diff -Nru charls-2.1.0+dfsg/src/jpegls_preset_coding_parameters.h charls-2.2.0+dfsg/src/jpegls_preset_coding_parameters.h --- charls-2.1.0+dfsg/src/jpegls_preset_coding_parameters.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/jpegls_preset_coding_parameters.h 2021-01-10 18:07:25.000000000 +0000 @@ -14,9 +14,9 @@ namespace charls { /// Clamping function as defined by ISO/IEC 14495-1, Figure C.3 -inline int32_t clamp(const int32_t i, const int32_t j, const int32_t maximumSampleValue) noexcept +inline int32_t clamp(const int32_t i, const int32_t j, const int32_t maximum_sample_value) noexcept { - if (i > maximumSampleValue || i < j) + if (i > maximum_sample_value || i < j) return j; return i; @@ -26,32 +26,30 @@ inline jpegls_pc_parameters compute_default(const int32_t maximum_sample_value, const int32_t near_lossless) noexcept { ASSERT(maximum_sample_value <= UINT16_MAX); - ASSERT(near_lossless >= 0 && near_lossless <= MaximumNearLossless(maximum_sample_value)); + ASSERT(near_lossless >= 0 && near_lossless <= compute_maximum_near_lossless(maximum_sample_value)); if (maximum_sample_value >= 128) { - const int32_t factor = (std::min(maximum_sample_value, 4095) + 128) / 256; - const int threshold1 = clamp(factor * (DefaultThreshold1 - 2) + 2 + 3 * near_lossless, near_lossless + 1, maximum_sample_value); - const int threshold2 = clamp(factor * (DefaultThreshold2 - 3) + 3 + 5 * near_lossless, threshold1, maximum_sample_value); //-V537 - - return { - maximum_sample_value, - threshold1, - threshold2, - clamp(factor * (DefaultThreshold3 - 4) + 4 + 7 * near_lossless, threshold2, maximum_sample_value), - DefaultResetValue}; + const int32_t factor{(std::min(maximum_sample_value, 4095) + 128) / 256}; + const int threshold1{ + clamp(factor * (default_threshold1 - 2) + 2 + 3 * near_lossless, near_lossless + 1, maximum_sample_value)}; + const int threshold2{ + clamp(factor * (default_threshold2 - 3) + 3 + 5 * near_lossless, threshold1, maximum_sample_value)}; + + return {maximum_sample_value, threshold1, threshold2, + clamp(factor * (default_threshold3 - 4) + 4 + 7 * near_lossless, threshold2, maximum_sample_value), + default_reset_value}; } - const int32_t factor = 256 / (maximum_sample_value + 1); - const int threshold1 = clamp(std::max(2, DefaultThreshold1 / factor + 3 * near_lossless), near_lossless + 1, maximum_sample_value); - const int threshold2 = clamp(std::max(3, DefaultThreshold2 / factor + 5 * near_lossless), threshold1, maximum_sample_value); - - return { - maximum_sample_value, - threshold1, - threshold2, - clamp(std::max(4, DefaultThreshold3 / factor + 7 * near_lossless), threshold2, maximum_sample_value), - DefaultResetValue}; + const int32_t factor{256 / (maximum_sample_value + 1)}; + const int threshold1{ + clamp(std::max(2, default_threshold1 / factor + 3 * near_lossless), near_lossless + 1, maximum_sample_value)}; + const int threshold2{ + clamp(std::max(3, default_threshold2 / factor + 5 * near_lossless), threshold1, maximum_sample_value)}; + + return {maximum_sample_value, threshold1, threshold2, + clamp(std::max(4, default_threshold3 / factor + 7 * near_lossless), threshold2, maximum_sample_value), + default_reset_value}; } @@ -76,28 +74,35 @@ } -inline bool is_valid(const jpegls_pc_parameters& pc_parameters, const int32_t maximum_component_value, const int32_t near_lossless) noexcept +inline bool is_valid(const jpegls_pc_parameters& pc_parameters, const int32_t maximum_component_value, + const int32_t near_lossless) noexcept { ASSERT(maximum_component_value <= UINT16_MAX); // ISO/IEC 14495-1, C.2.4.1.1, Table C.1 defines the valid JPEG-LS preset coding parameters values. - if (pc_parameters.maximum_sample_value != 0 && (pc_parameters.maximum_sample_value < 1 || pc_parameters.maximum_sample_value > maximum_component_value)) + if (pc_parameters.maximum_sample_value != 0 && + (pc_parameters.maximum_sample_value < 1 || pc_parameters.maximum_sample_value > maximum_component_value)) return false; - const int32_t maximum_sample_value = pc_parameters.maximum_sample_value != 0 ? pc_parameters.maximum_sample_value : maximum_component_value; - if (pc_parameters.threshold1 != 0 && (pc_parameters.threshold1 < near_lossless + 1 || pc_parameters.threshold1 > maximum_sample_value)) + const int32_t maximum_sample_value{pc_parameters.maximum_sample_value != 0 ? pc_parameters.maximum_sample_value + : maximum_component_value}; + if (pc_parameters.threshold1 != 0 && + (pc_parameters.threshold1 < near_lossless + 1 || pc_parameters.threshold1 > maximum_sample_value)) return false; const jpegls_pc_parameters default_parameters{compute_default(maximum_sample_value, near_lossless)}; - const int32_t threshold1 = pc_parameters.threshold1 != 0 ? pc_parameters.threshold1 : default_parameters.threshold1; - if (pc_parameters.threshold2 != 0 && (pc_parameters.threshold2 < threshold1 || pc_parameters.threshold2 > maximum_sample_value)) + const int32_t threshold1{pc_parameters.threshold1 != 0 ? pc_parameters.threshold1 : default_parameters.threshold1}; + if (pc_parameters.threshold2 != 0 && + (pc_parameters.threshold2 < threshold1 || pc_parameters.threshold2 > maximum_sample_value)) return false; - const int32_t threshold2 = pc_parameters.threshold2 != 0 ? pc_parameters.threshold2 : default_parameters.threshold2; - if (pc_parameters.threshold3 != 0 && (pc_parameters.threshold3 < threshold2 || pc_parameters.threshold3 > maximum_sample_value)) + const int32_t threshold2{pc_parameters.threshold2 != 0 ? pc_parameters.threshold2 : default_parameters.threshold2}; + if (pc_parameters.threshold3 != 0 && + (pc_parameters.threshold3 < threshold2 || pc_parameters.threshold3 > maximum_sample_value)) return false; - if (pc_parameters.reset_value != 0 && (pc_parameters.reset_value < 3 || pc_parameters.reset_value > std::max(255, maximum_sample_value))) + if (pc_parameters.reset_value != 0 && + (pc_parameters.reset_value < 3 || pc_parameters.reset_value > std::max(255, maximum_sample_value))) return false; return true; diff -Nru charls-2.1.0+dfsg/src/jpegls_preset_parameters_type.h charls-2.2.0+dfsg/src/jpegls_preset_parameters_type.h --- charls-2.1.0+dfsg/src/jpegls_preset_parameters_type.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/jpegls_preset_parameters_type.h 2021-01-10 18:07:25.000000000 +0000 @@ -7,20 +7,23 @@ namespace charls { -enum class JpegLSPresetParametersType : uint8_t +enum class jpegls_preset_parameters_type : uint8_t { - PresetCodingParameters = 0x1, // JPEG-LS Baseline (ISO/IEC 14495-1): Preset coding parameters. - MappingTableSpecification = 0x2, // JPEG-LS Baseline (ISO/IEC 14495-1): Mapping table specification. - MappingTableContinuation = 0x3, // JPEG-LS Baseline (ISO/IEC 14495-1): Mapping table continuation. - ExtendedWidthAndHeight = 0x4, // JPEG-LS Baseline (ISO/IEC 14495-1): X and Y parameters greater than 16 bits are defined. - CodingMethodSpecification = 0x5, // JPEG-LS Extended (ISO/IEC 14495-2): Coding method specification. - NearLosslessErrorReSpecification = 0x6, // JPEG-LS Extended (ISO/IEC 14495-2): NEAR value re-specification. - VisuallyOrientedQuantizationSpecification = 0x7, // JPEG-LS Extended (ISO/IEC 14495-2): Visually oriented quantization specification. - ExtendedPredictionSpecification = 0x8, // JPEG-LS Extended (ISO/IEC 14495-2): Extended prediction specification. - StartOfFixedLengthCoding = 0x9, // JPEG-LS Extended (ISO/IEC 14495-2): Specification of the start of fixed length coding. - EndOfFixedLengthCoding = 0xA, // JPEG-LS Extended (ISO/IEC 14495-2): Specification of the end of fixed length coding. - ExtendedPresetCodingParameters = 0xC, // JPEG-LS Extended (ISO/IEC 14495-2): JPEG-LS preset coding parameters. - InverseColorTransformSpecification = 0xD // JPEG-LS Extended (ISO/IEC 14495-2): Inverse color transform specification. + preset_coding_parameters = 0x1, // JPEG-LS Baseline (ISO/IEC 14495-1): Preset coding parameters. + mapping_table_specification = 0x2, // JPEG-LS Baseline (ISO/IEC 14495-1): Mapping table specification. + mapping_table_continuation = 0x3, // JPEG-LS Baseline (ISO/IEC 14495-1): Mapping table continuation. + extended_width_and_height = + 0x4, // JPEG-LS Baseline (ISO/IEC 14495-1): X and Y parameters greater than 16 bits are defined. + coding_method_specification = 0x5, // JPEG-LS Extended (ISO/IEC 14495-2): Coding method specification. + near_lossless_error_re_specification = 0x6, // JPEG-LS Extended (ISO/IEC 14495-2): NEAR value re-specification. + visually_oriented_quantization_specification = + 0x7, // JPEG-LS Extended (ISO/IEC 14495-2): Visually oriented quantization specification. + extended_prediction_specification = 0x8, // JPEG-LS Extended (ISO/IEC 14495-2): Extended prediction specification. + start_of_fixed_length_coding = + 0x9, // JPEG-LS Extended (ISO/IEC 14495-2): Specification of the start of fixed length coding. + end_of_fixed_length_coding = 0xA, // JPEG-LS Extended (ISO/IEC 14495-2): Specification of the end of fixed length coding. + extended_preset_coding_parameters = 0xC, // JPEG-LS Extended (ISO/IEC 14495-2): JPEG-LS preset coding parameters. + inverse_color_transform_specification = 0xD // JPEG-LS Extended (ISO/IEC 14495-2): inverse color transform specification. }; -} +} // namespace charls diff -Nru charls-2.1.0+dfsg/src/jpeg_marker_code.h charls-2.2.0+dfsg/src/jpeg_marker_code.h --- charls-2.1.0+dfsg/src/jpeg_marker_code.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/jpeg_marker_code.h 2021-01-10 18:07:25.000000000 +0000 @@ -12,51 +12,53 @@ // 0x00, 0x01, 0xFE, 0xC0-0xDF are defined in ISO/IEC 10918-1, ITU T.81 // 0xF0 - 0xF6 are defined in ISO/IEC 10918-3 | ITU T.84: JPEG extensions // 0xF7 - 0xF8 are defined in ISO/IEC 14495-1 | ITU T.87: JPEG LS baseline -// 0xF9 is defined in ISO/IEC 14495-2 | ITU T.870: JPEG LS extensions +// 0xF9 is defined in ISO/IEC 14495-2 | ITU T.870: JPEG LS extensions // 0x4F - 0x6F, 0x90 - 0x93 are defined in ISO/IEC 15444-1: JPEG 2000 -constexpr uint8_t JpegMarkerStartByte = 0xFF; +constexpr uint8_t jpeg_marker_start_byte{0xFF}; -enum class JpegMarkerCode : uint8_t +enum class jpeg_marker_code : uint8_t { - StartOfImage = 0xD8, // SOI: Marks the start of an image. - EndOfImage = 0xD9, // EOI: Marks the end of an image. - StartOfScan = 0xDA, // SOS: Marks the start of scan. + start_of_image = 0xD8, // SOI: Marks the start of an image. + end_of_image = 0xD9, // EOI: Marks the end of an image. + start_of_scan = 0xDA, // SOS: Marks the start of scan. // The following markers are defined in ISO/IEC 10918-1 | ITU T.81. - StartOfFrameBaselineJpeg = 0xC0, // SOF_0: Marks the start of a baseline jpeg encoded frame. - StartOfFrameExtendedSequential = 0xC1, // SOF_1: Marks the start of a extended sequential Huffman encoded frame. - StartOfFrameProgressive = 0xC2, // SOF_2: Marks the start of a progressive Huffman encoded frame. - StartOfFrameLossless = 0xC3, // SOF_3: Marks the start of a lossless Huffman encoded frame. - StartOfFrameDifferentialSequential = 0xC5, // SOF_5: Marks the start of a differential sequential Huffman encoded frame. - StartOfFrameDifferentialProgressive = 0xC6, // SOF_6: Marks the start of a differential progressive Huffman encoded frame. - StartOfFrameDifferentialLossless = 0xC7, // SOF_7: Marks the start of a differential lossless Huffman encoded frame. - StartOfFrameExtendedArithmetic = 0xC9, // SOF_9: Marks the start of a extended sequential arithmetic encoded frame. - StartOfFrameProgressiveArithmetic = 0xCA, // SOF_10: Marks the start of a progressive arithmetic encoded frame. - StartOfFrameLosslessArithmetic = 0xCB, // SOF_11: Marks the start of a lossless arithmetic encoded frame. + start_of_frame_baseline_jpeg = 0xC0, // SOF_0: Marks the start of a baseline jpeg encoded frame. + start_of_frame_extended_sequential = 0xC1, // SOF_1: Marks the start of a extended sequential Huffman encoded frame. + start_of_frame_progressive = 0xC2, // SOF_2: Marks the start of a progressive Huffman encoded frame. + start_of_frame_lossless = 0xC3, // SOF_3: Marks the start of a lossless Huffman encoded frame. + start_of_frame_differential_sequential = + 0xC5, // SOF_5: Marks the start of a differential sequential Huffman encoded frame. + start_of_frame_differential_progressive = + 0xC6, // SOF_6: Marks the start of a differential progressive Huffman encoded frame. + start_of_frame_differential_lossless = 0xC7, // SOF_7: Marks the start of a differential lossless Huffman encoded frame. + start_of_frame_extended_arithmetic = 0xC9, // SOF_9: Marks the start of a extended sequential arithmetic encoded frame. + start_of_frame_progressive_arithmetic = 0xCA, // SOF_10: Marks the start of a progressive arithmetic encoded frame. + start_of_frame_lossless_arithmetic = 0xCB, // SOF_11: Marks the start of a lossless arithmetic encoded frame. // The following markers are defined in ISO/IEC 14495-1 | ITU T.87. - StartOfFrameJpegLS = 0xF7, // SOF_55: Marks the start of a JPEG-LS encoded frame. - JpegLSPresetParameters = 0xF8, // LSE: Marks the start of a JPEG-LS preset parameters segment. - StartOfFrameJpegLSExtended = 0xF9, // SOF_57: Marks the start of a JPEG-LS extended (ISO/IEC 14495-2) encoded frame. - - ApplicationData0 = 0xE0, // APP0: Application data 0: used for JFIF header. - ApplicationData1 = 0xE1, // APP1: Application data 1: used for EXIF or XMP header. - ApplicationData2 = 0xE2, // APP2: Application data 2: used for ICC profile. - ApplicationData3 = 0xE3, // APP3: Application data 3: used for meta info - ApplicationData4 = 0xE4, // APP4: Application data 4. - ApplicationData5 = 0xE5, // APP5: Application data 5. - ApplicationData6 = 0xE6, // APP6: Application data 6. - ApplicationData7 = 0xE7, // APP7: Application data 7: used for HP color-space info. - ApplicationData8 = 0xE8, // APP8: Application data 8: used for HP color-transformation info or SPIFF header. - ApplicationData9 = 0xE9, // APP9: Application data 9. - ApplicationData10 = 0xEA, // APP10: Application data 10. - ApplicationData11 = 0xEB, // APP11: Application data 11. - ApplicationData12 = 0xEC, // APP12: Application data 12: used for Picture info. - ApplicationData13 = 0xEE, // APP13: Application data 13: used by PhotoShop IRB - ApplicationData14 = 0xED, // APP14: Application data 14: used by Adobe - ApplicationData15 = 0xEF, // APP15: Application data 15. - Comment = 0xFE // COM: Comment block. + start_of_frame_jpegls = 0xF7, // SOF_55: Marks the start of a JPEG-LS encoded frame. + jpegls_preset_parameters = 0xF8, // LSE: Marks the start of a JPEG-LS preset parameters segment. + start_of_frame_jpegls_extended = 0xF9, // SOF_57: Marks the start of a JPEG-LS extended (ISO/IEC 14495-2) encoded frame. + + application_data0 = 0xE0, // APP0: Application data 0: used for JFIF header. + application_data1 = 0xE1, // APP1: Application data 1: used for EXIF or XMP header. + application_data2 = 0xE2, // APP2: Application data 2: used for ICC profile. + application_data3 = 0xE3, // APP3: Application data 3: used for meta info + application_data4 = 0xE4, // APP4: Application data 4. + application_data5 = 0xE5, // APP5: Application data 5. + application_data6 = 0xE6, // APP6: Application data 6. + application_data7 = 0xE7, // APP7: Application data 7: used for HP color-space info. + application_data8 = 0xE8, // APP8: Application data 8: used for HP color-transformation info or SPIFF header. + application_data9 = 0xE9, // APP9: Application data 9. + application_data10 = 0xEA, // APP10: Application data 10. + application_data11 = 0xEB, // APP11: Application data 11. + application_data12 = 0xEC, // APP12: Application data 12: used for Picture info. + application_data13 = 0xEE, // APP13: Application data 13: used by PhotoShop IRB + application_data14 = 0xED, // APP14: Application data 14: used by Adobe + application_data15 = 0xEF, // APP15: Application data 15. + comment = 0xFE // COM: Comment block. }; } // namespace charls diff -Nru charls-2.1.0+dfsg/src/jpeg_stream_reader.cpp charls-2.2.0+dfsg/src/jpeg_stream_reader.cpp --- charls-2.1.0+dfsg/src/jpeg_stream_reader.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/jpeg_stream_reader.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -5,139 +5,131 @@ #include "constants.h" #include "decoder_strategy.h" -#include "encoder_strategy.h" #include "jls_codec_factory.h" #include "jpeg_marker_code.h" #include "jpegls_preset_parameters_type.h" #include "util.h" #include -#include #include +namespace charls { + +using impl::throw_jpegls_error; using std::find; -using std::vector; using std::unique_ptr; -using namespace charls; - -namespace { - -void CheckParameterCoherent(const JlsParameters& params) -{ - switch (params.components) - { - case 4: - case 3: - break; - default: - if (params.interleaveMode != interleave_mode::none) - throw jpegls_error{jpegls_errc::parameter_value_not_supported}; - - break; - } -} - -} // namespace - -namespace charls { +using std::vector; -JpegStreamReader::JpegStreamReader(ByteStreamInfo byteStreamInfo) noexcept : - byteStream_{byteStreamInfo} +jpeg_stream_reader::jpeg_stream_reader(const byte_span source) noexcept : source_{source} { } -void JpegStreamReader::Read(ByteStreamInfo rawPixels) +void jpeg_stream_reader::read(byte_span source, size_t stride) { ASSERT(state_ == state::bit_stream_section); - CheckParameterCoherent(params_); + check_parameter_coherent(); if (rect_.Width <= 0) { - rect_.Width = params_.width; - rect_.Height = params_.height; + rect_.Width = static_cast(frame_info_.width); + rect_.Height = static_cast(frame_info_.height); } - const int64_t bytesPerPlane = static_cast(rect_.Width) * rect_.Height * ((params_.bitsPerSample + 7) / 8); + if (stride == 0) + { + const uint32_t width{rect_.Width != 0 ? static_cast(rect_.Width) : frame_info_.width}; + const uint32_t component_count{ + parameters_.interleave_mode == interleave_mode::none ? 1U : static_cast(frame_info_.component_count)}; + stride = + static_cast(component_count) * width * ((static_cast(frame_info_.bits_per_sample) + 7U) / 8U); + } - if (rawPixels.rawData && static_cast(rawPixels.count) < bytesPerPlane * params_.components) - throw jpegls_error{jpegls_errc::destination_buffer_too_small}; + const int64_t bytes_per_plane{static_cast(rect_.Width) * rect_.Height * + bit_to_byte_count(frame_info_.bits_per_sample)}; - int componentIndex{}; - while (componentIndex < params_.components) + if (static_cast(source.size) < bytes_per_plane * frame_info_.component_count) + throw_jpegls_error(jpegls_errc::destination_buffer_too_small); + + int component_index{}; + while (component_index < frame_info_.component_count) { if (state_ == state::scan_section) { - ReadStartOfScan(componentIndex == 0); + read_next_start_of_scan(); } - unique_ptr codec = JlsCodecFactory().CreateCodec(params_, preset_coding_parameters_); - unique_ptr processLine(codec->CreateProcess(rawPixels)); - codec->DecodeScan(move(processLine), rect_, byteStream_); - SkipBytes(rawPixels, static_cast(bytesPerPlane)); + unique_ptr codec{ + jls_codec_factory().create_codec(frame_info_, parameters_, preset_coding_parameters_)}; + unique_ptr process_line(codec->create_process_line(source, stride)); + codec->decode_scan(move(process_line), rect_, source_); + skip_bytes(source, static_cast(bytes_per_plane)); state_ = state::scan_section; - if (params_.interleaveMode != interleave_mode::none) + if (parameters_.interleave_mode != interleave_mode::none) return; - componentIndex++; + component_index++; } } -void JpegStreamReader::ReadNBytes(std::vector& destination, int byteCount) +void jpeg_stream_reader::read_bytes(std::vector& destination, const int byte_count) { - for (int i = 0; i < byteCount; ++i) + for (int i{}; i < byte_count; ++i) { - destination.push_back(static_cast(ReadByte())); + destination.push_back(static_cast(read_byte())); } } -void JpegStreamReader::ReadHeader(spiff_header* header, bool* spiff_header_found) +void jpeg_stream_reader::read_header(spiff_header* header, bool* spiff_header_found) { ASSERT(state_ != state::scan_section); if (state_ == state::before_start_of_image) { - if (ReadNextMarkerCode() != JpegMarkerCode::StartOfImage) - throw jpegls_error{jpegls_errc::start_of_image_marker_not_found}; + if (read_next_marker_code() != jpeg_marker_code::start_of_image) + throw_jpegls_error(jpegls_errc::start_of_image_marker_not_found); state_ = state::header_section; } for (;;) { - const JpegMarkerCode markerCode = ReadNextMarkerCode(); - ValidateMarkerCode(markerCode); + const jpeg_marker_code marker_code = read_next_marker_code(); + validate_marker_code(marker_code); - if (markerCode == JpegMarkerCode::StartOfScan) + if (marker_code == jpeg_marker_code::start_of_scan) { + if (!is_maximum_sample_value_valid()) + throw_jpegls_error(jpegls_errc::invalid_parameter_jpegls_pc_parameters); + state_ = state::scan_section; return; } - const int32_t segmentSize = ReadSegmentSize(); - int bytesRead; + const int32_t segment_size{read_segment_size()}; + int bytes_read; switch (state_) { case state::spiff_header_section: - bytesRead = ReadSpiffDirectoryEntry(markerCode, segmentSize - 2) + 2; + bytes_read = read_spiff_directory_entry(marker_code, segment_size - 2) + 2; break; default: - bytesRead = ReadMarkerSegment(markerCode, segmentSize - 2, header, spiff_header_found) + 2; + bytes_read = read_marker_segment(marker_code, segment_size - 2, header, spiff_header_found) + 2; break; } - const int paddingToRead = segmentSize - bytesRead; - if (paddingToRead < 0) - throw jpegls_error{jpegls_errc::invalid_marker_segment_size}; + const int padding_to_read{segment_size - bytes_read}; + if (padding_to_read < 0) + throw_jpegls_error(jpegls_errc::invalid_marker_segment_size); - for (int i = 0; i < paddingToRead; ++i) + for (int i{}; i < padding_to_read; ++i) { - ReadByte(); + read_byte(); } if (state_ == state::header_section && spiff_header_found && *spiff_header_found) @@ -149,108 +141,149 @@ } -JpegMarkerCode JpegStreamReader::ReadNextMarkerCode() +void jpeg_stream_reader::read_next_start_of_scan() +{ + ASSERT(state_ == state::scan_section); + + for (;;) + { + const jpeg_marker_code marker_code{read_next_marker_code()}; + validate_marker_code(marker_code); + + if (marker_code == jpeg_marker_code::start_of_scan) + { + read_start_of_scan(); + return; + } + + const int32_t segment_size{read_segment_size()}; + const int bytes_read{read_marker_segment(marker_code, segment_size - 2) + 2}; + + const int padding_to_read{segment_size - bytes_read}; + if (padding_to_read < 0) + throw_jpegls_error(jpegls_errc::invalid_marker_segment_size); + + for (int i{}; i < padding_to_read; ++i) + { + read_byte(); + } + } +} + + +jpeg_marker_code jpeg_stream_reader::read_next_marker_code() { - auto byte = ReadByte(); - if (byte != JpegMarkerStartByte) - throw jpegls_error{jpegls_errc::jpeg_marker_start_byte_not_found}; + auto byte{read_byte()}; + if (byte != jpeg_marker_start_byte) + throw_jpegls_error(jpegls_errc::jpeg_marker_start_byte_not_found); // Read all preceding 0xFF fill values until a non 0xFF value has been found. (see T.81, B.1.1.2) do { - byte = ReadByte(); - } while (byte == JpegMarkerStartByte); + byte = read_byte(); + } while (byte == jpeg_marker_start_byte); - return static_cast(byte); + return static_cast(byte); } -void JpegStreamReader::ValidateMarkerCode(JpegMarkerCode markerCode) +void jpeg_stream_reader::validate_marker_code(const jpeg_marker_code marker_code) const { // ISO/IEC 14495-1, C.1.1. defines the following markers as valid for a JPEG-LS byte stream: // SOF55, LSE, SOI, EOI, SOS, DNL, DRI, RSTm, APPn and COM. // All other markers shall not be present. - switch (markerCode) + switch (marker_code) { - case JpegMarkerCode::StartOfFrameJpegLS: - case JpegMarkerCode::JpegLSPresetParameters: - case JpegMarkerCode::StartOfScan: - case JpegMarkerCode::Comment: - case JpegMarkerCode::ApplicationData0: - case JpegMarkerCode::ApplicationData1: - case JpegMarkerCode::ApplicationData2: - case JpegMarkerCode::ApplicationData3: - case JpegMarkerCode::ApplicationData4: - case JpegMarkerCode::ApplicationData5: - case JpegMarkerCode::ApplicationData6: - case JpegMarkerCode::ApplicationData7: - case JpegMarkerCode::ApplicationData8: - case JpegMarkerCode::ApplicationData9: - case JpegMarkerCode::ApplicationData10: - case JpegMarkerCode::ApplicationData11: - case JpegMarkerCode::ApplicationData12: - case JpegMarkerCode::ApplicationData13: - case JpegMarkerCode::ApplicationData14: - case JpegMarkerCode::ApplicationData15: + case jpeg_marker_code::start_of_scan: + if (state_ != state::scan_section) + throw_jpegls_error(jpegls_errc::unexpected_marker_found); + + return; + + case jpeg_marker_code::start_of_frame_jpegls: + if (state_ == state::scan_section) + throw_jpegls_error(jpegls_errc::duplicate_start_of_frame_marker); + + return; + + case jpeg_marker_code::jpegls_preset_parameters: + case jpeg_marker_code::comment: + case jpeg_marker_code::application_data0: + case jpeg_marker_code::application_data1: + case jpeg_marker_code::application_data2: + case jpeg_marker_code::application_data3: + case jpeg_marker_code::application_data4: + case jpeg_marker_code::application_data5: + case jpeg_marker_code::application_data6: + case jpeg_marker_code::application_data7: + case jpeg_marker_code::application_data8: + case jpeg_marker_code::application_data9: + case jpeg_marker_code::application_data10: + case jpeg_marker_code::application_data11: + case jpeg_marker_code::application_data12: + case jpeg_marker_code::application_data13: + case jpeg_marker_code::application_data14: + case jpeg_marker_code::application_data15: return; // Check explicit for one of the other common JPEG encodings. - case JpegMarkerCode::StartOfFrameBaselineJpeg: - case JpegMarkerCode::StartOfFrameExtendedSequential: - case JpegMarkerCode::StartOfFrameProgressive: - case JpegMarkerCode::StartOfFrameLossless: - case JpegMarkerCode::StartOfFrameDifferentialSequential: - case JpegMarkerCode::StartOfFrameDifferentialProgressive: - case JpegMarkerCode::StartOfFrameDifferentialLossless: - case JpegMarkerCode::StartOfFrameExtendedArithmetic: - case JpegMarkerCode::StartOfFrameProgressiveArithmetic: - case JpegMarkerCode::StartOfFrameLosslessArithmetic: - case JpegMarkerCode::StartOfFrameJpegLSExtended: - throw jpegls_error{jpegls_errc::encoding_not_supported}; - - case JpegMarkerCode::StartOfImage: - throw jpegls_error{jpegls_errc::duplicate_start_of_image_marker}; - - case JpegMarkerCode::EndOfImage: - throw jpegls_error{jpegls_errc::unexpected_end_of_image_marker}; - } - - throw jpegls_error{jpegls_errc::unknown_jpeg_marker_found}; + case jpeg_marker_code::start_of_frame_baseline_jpeg: + case jpeg_marker_code::start_of_frame_extended_sequential: + case jpeg_marker_code::start_of_frame_progressive: + case jpeg_marker_code::start_of_frame_lossless: + case jpeg_marker_code::start_of_frame_differential_sequential: + case jpeg_marker_code::start_of_frame_differential_progressive: + case jpeg_marker_code::start_of_frame_differential_lossless: + case jpeg_marker_code::start_of_frame_extended_arithmetic: + case jpeg_marker_code::start_of_frame_progressive_arithmetic: + case jpeg_marker_code::start_of_frame_lossless_arithmetic: + case jpeg_marker_code::start_of_frame_jpegls_extended: + throw_jpegls_error(jpegls_errc::encoding_not_supported); + + case jpeg_marker_code::start_of_image: + throw_jpegls_error(jpegls_errc::duplicate_start_of_image_marker); + + case jpeg_marker_code::end_of_image: + throw_jpegls_error(jpegls_errc::unexpected_end_of_image_marker); + } + + throw_jpegls_error(jpegls_errc::unknown_jpeg_marker_found); } -int JpegStreamReader::ReadMarkerSegment(JpegMarkerCode markerCode, int32_t segmentSize, spiff_header* header, bool* spiff_header_found) -{ - switch (markerCode) - { - case JpegMarkerCode::StartOfFrameJpegLS: - return ReadStartOfFrameSegment(segmentSize); - - case JpegMarkerCode::Comment: - return ReadComment(); - - case JpegMarkerCode::JpegLSPresetParameters: - return ReadPresetParametersSegment(segmentSize); - - case JpegMarkerCode::ApplicationData0: - case JpegMarkerCode::ApplicationData1: - case JpegMarkerCode::ApplicationData2: - case JpegMarkerCode::ApplicationData3: - case JpegMarkerCode::ApplicationData4: - case JpegMarkerCode::ApplicationData5: - case JpegMarkerCode::ApplicationData6: - case JpegMarkerCode::ApplicationData7: - case JpegMarkerCode::ApplicationData9: - case JpegMarkerCode::ApplicationData10: - case JpegMarkerCode::ApplicationData11: - case JpegMarkerCode::ApplicationData12: - case JpegMarkerCode::ApplicationData13: - case JpegMarkerCode::ApplicationData14: - case JpegMarkerCode::ApplicationData15: +int jpeg_stream_reader::read_marker_segment(const jpeg_marker_code marker_code, const int32_t segment_size, + spiff_header* header, bool* spiff_header_found) +{ + switch (marker_code) + { + case jpeg_marker_code::start_of_frame_jpegls: + return read_start_of_frame_segment(segment_size); + + case jpeg_marker_code::comment: + return read_comment(); + + case jpeg_marker_code::jpegls_preset_parameters: + return read_preset_parameters_segment(segment_size); + + case jpeg_marker_code::application_data0: + case jpeg_marker_code::application_data1: + case jpeg_marker_code::application_data2: + case jpeg_marker_code::application_data3: + case jpeg_marker_code::application_data4: + case jpeg_marker_code::application_data5: + case jpeg_marker_code::application_data6: + case jpeg_marker_code::application_data7: + case jpeg_marker_code::application_data9: + case jpeg_marker_code::application_data10: + case jpeg_marker_code::application_data11: + case jpeg_marker_code::application_data12: + case jpeg_marker_code::application_data13: + case jpeg_marker_code::application_data14: + case jpeg_marker_code::application_data15: return 0; - case JpegMarkerCode::ApplicationData8: - return TryReadApplicationData8Segment(segmentSize, header, spiff_header_found); + case jpeg_marker_code::application_data8: + return try_read_application_data8_segment(segment_size, header, spiff_header_found); // Other tags not supported (among which DNL DRI) default: @@ -259,16 +292,16 @@ } } -int JpegStreamReader::ReadSpiffDirectoryEntry(JpegMarkerCode markerCode, int32_t segmentSize) +int jpeg_stream_reader::read_spiff_directory_entry(const jpeg_marker_code marker_code, const int32_t segment_size) { - if (markerCode != JpegMarkerCode::ApplicationData8) - throw jpegls_error{jpegls_errc::missing_end_of_spiff_directory}; + if (marker_code != jpeg_marker_code::application_data8) + throw_jpegls_error(jpegls_errc::missing_end_of_spiff_directory); - if (segmentSize < 4) - throw jpegls_error{jpegls_errc::invalid_marker_segment_size}; + if (segment_size < 4) + throw_jpegls_error(jpegls_errc::invalid_marker_segment_size); - const uint32_t spiffDirectoryType = ReadUInt32(); - if (spiffDirectoryType == spiff_end_of_directory_entry_type) + const uint32_t spiff_directory_type{read_uint32()}; + if (spiff_directory_type == spiff_end_of_directory_entry_type) { state_ = state::image_section; } @@ -276,189 +309,180 @@ return 4; } -int JpegStreamReader::ReadStartOfFrameSegment(int32_t segmentSize) +int jpeg_stream_reader::read_start_of_frame_segment(const int32_t segment_size) { // A JPEG-LS Start of Frame (SOF) segment is documented in ISO/IEC 14495-1, C.2.2 // This section references ISO/IEC 10918-1, B.2.2, which defines the normal JPEG SOF, // with some modifications. - if (segmentSize < 6) - throw jpegls_error{jpegls_errc::invalid_marker_segment_size}; + if (segment_size < 6) + throw_jpegls_error(jpegls_errc::invalid_marker_segment_size); - params_.bitsPerSample = ReadByte(); - if (params_.bitsPerSample < MinimumBitsPerSample || params_.bitsPerSample > MaximumBitsPerSample) - throw jpegls_error{jpegls_errc::invalid_parameter_bits_per_sample}; + frame_info_.bits_per_sample = read_byte(); + if (frame_info_.bits_per_sample < minimum_bits_per_sample || frame_info_.bits_per_sample > maximum_bits_per_sample) + throw_jpegls_error(jpegls_errc::invalid_parameter_bits_per_sample); - params_.height = ReadUInt16(); - if (params_.height < 1) - throw jpegls_error{jpegls_errc::parameter_value_not_supported}; + frame_info_.height = read_uint16(); + if (frame_info_.height < 1) + throw_jpegls_error(jpegls_errc::parameter_value_not_supported); - params_.width = ReadUInt16(); - if (params_.width < 1) - throw jpegls_error{jpegls_errc::parameter_value_not_supported}; + frame_info_.width = read_uint16(); + if (frame_info_.width < 1) + throw_jpegls_error(jpegls_errc::parameter_value_not_supported); - params_.components = ReadByte(); - if (params_.components < 1) - throw jpegls_error{jpegls_errc::invalid_parameter_component_count}; + frame_info_.component_count = read_byte(); + if (frame_info_.component_count < 1) + throw_jpegls_error(jpegls_errc::invalid_parameter_component_count); - if (segmentSize != 6 + (params_.components * 3)) - throw jpegls_error{jpegls_errc::invalid_marker_segment_size}; + if (segment_size != 6 + (frame_info_.component_count * 3)) + throw_jpegls_error(jpegls_errc::invalid_marker_segment_size); - for (auto i = 0; i < params_.components; ++i) + for (int32_t i{}; i < frame_info_.component_count; ++i) { // Component specification parameters - AddComponent(ReadByte()); // Ci = Component identifier - const uint8_t horizontalVerticalSamplingFactor = ReadByte(); // Hi + Vi = Horizontal sampling factor + Vertical sampling factor - if (horizontalVerticalSamplingFactor != 0x11) - throw jpegls_error{jpegls_errc::parameter_value_not_supported}; + add_component(read_byte()); // Ci = Component identifier + const uint8_t horizontal_vertical_sampling_factor{ + read_byte()}; // Hi + Vi = Horizontal sampling factor + Vertical sampling factor + if (horizontal_vertical_sampling_factor != 0x11) + throw_jpegls_error(jpegls_errc::parameter_value_not_supported); - SkipByte(); // Tqi = Quantization table destination selector (reserved for JPEG-LS, should be set to 0) + skip_byte(); // Tqi = Quantization table destination selector (reserved for JPEG-LS, should be set to 0) } - return segmentSize; + state_ = state::scan_section; + + return segment_size; } -int JpegStreamReader::ReadComment() noexcept +int jpeg_stream_reader::read_comment() noexcept { return 0; } -int JpegStreamReader::ReadPresetParametersSegment(int32_t segmentSize) +int jpeg_stream_reader::read_preset_parameters_segment(const int32_t segment_size) { - if (segmentSize < 1) - throw jpegls_error{jpegls_errc::invalid_marker_segment_size}; - - const auto type = static_cast(ReadByte()); + if (segment_size < 1) + throw_jpegls_error(jpegls_errc::invalid_marker_segment_size); + const auto type{static_cast(read_byte())}; switch (type) { - case JpegLSPresetParametersType::PresetCodingParameters: - { - constexpr int32_t CodingParameterSegmentSize = 11; - if (segmentSize != CodingParameterSegmentSize) - throw jpegls_error{jpegls_errc::invalid_marker_segment_size}; + case jpegls_preset_parameters_type::preset_coding_parameters: { + constexpr int32_t coding_parameter_segment_size = 11; + if (segment_size != coding_parameter_segment_size) + throw_jpegls_error(jpegls_errc::invalid_marker_segment_size); - preset_coding_parameters_.maximum_sample_value = ReadUInt16(); - preset_coding_parameters_.threshold1 = ReadUInt16(); - preset_coding_parameters_.threshold2 = ReadUInt16(); - preset_coding_parameters_.threshold3 = ReadUInt16(); - preset_coding_parameters_.reset_value = ReadUInt16(); + preset_coding_parameters_.maximum_sample_value = read_uint16(); + preset_coding_parameters_.threshold1 = read_uint16(); + preset_coding_parameters_.threshold2 = read_uint16(); + preset_coding_parameters_.threshold3 = read_uint16(); + preset_coding_parameters_.reset_value = read_uint16(); - return CodingParameterSegmentSize; + return coding_parameter_segment_size; } - case JpegLSPresetParametersType::MappingTableSpecification: - case JpegLSPresetParametersType::MappingTableContinuation: - case JpegLSPresetParametersType::ExtendedWidthAndHeight: - throw jpegls_error{jpegls_errc::parameter_value_not_supported}; + case jpegls_preset_parameters_type::mapping_table_specification: + case jpegls_preset_parameters_type::mapping_table_continuation: + case jpegls_preset_parameters_type::extended_width_and_height: + throw_jpegls_error(jpegls_errc::parameter_value_not_supported); - case JpegLSPresetParametersType::CodingMethodSpecification: - case JpegLSPresetParametersType::NearLosslessErrorReSpecification: - case JpegLSPresetParametersType::VisuallyOrientedQuantizationSpecification: - case JpegLSPresetParametersType::ExtendedPredictionSpecification: - case JpegLSPresetParametersType::StartOfFixedLengthCoding: - case JpegLSPresetParametersType::EndOfFixedLengthCoding: - case JpegLSPresetParametersType::ExtendedPresetCodingParameters: - case JpegLSPresetParametersType::InverseColorTransformSpecification: - throw jpegls_error{jpegls_errc::jpegls_preset_extended_parameter_type_not_supported}; + case jpegls_preset_parameters_type::coding_method_specification: + case jpegls_preset_parameters_type::near_lossless_error_re_specification: + case jpegls_preset_parameters_type::visually_oriented_quantization_specification: + case jpegls_preset_parameters_type::extended_prediction_specification: + case jpegls_preset_parameters_type::start_of_fixed_length_coding: + case jpegls_preset_parameters_type::end_of_fixed_length_coding: + case jpegls_preset_parameters_type::extended_preset_coding_parameters: + case jpegls_preset_parameters_type::inverse_color_transform_specification: + throw_jpegls_error(jpegls_errc::jpegls_preset_extended_parameter_type_not_supported); } - throw jpegls_error{jpegls_errc::invalid_jpegls_preset_parameter_type}; + throw_jpegls_error(jpegls_errc::invalid_jpegls_preset_parameter_type); } -void JpegStreamReader::ReadStartOfScan(bool firstComponent) +void jpeg_stream_reader::read_start_of_scan() { - if (!firstComponent) - { - const JpegMarkerCode markerCode = ReadNextMarkerCode(); - if (markerCode != JpegMarkerCode::StartOfScan) - throw jpegls_error{jpegls_errc::invalid_encoded_data}; - } + const int32_t segment_size{read_segment_size()}; + if (segment_size < 3) + throw_jpegls_error(jpegls_errc::invalid_marker_segment_size); - const int32_t segmentSize = ReadSegmentSize(); - if (segmentSize < 6) - throw jpegls_error{jpegls_errc::invalid_marker_segment_size}; + const int component_count_in_scan{read_byte()}; + if (component_count_in_scan != 1 && component_count_in_scan != frame_info_.component_count) + throw_jpegls_error(jpegls_errc::parameter_value_not_supported); - const int componentCountInScan = ReadByte(); - if (componentCountInScan != 1 && componentCountInScan != params_.components) - throw jpegls_error{jpegls_errc::parameter_value_not_supported}; + if (segment_size != 6 + (2 * component_count_in_scan)) + throw_jpegls_error(jpegls_errc::invalid_marker_segment_size); - if (segmentSize < 6 + (2 * componentCountInScan)) - throw jpegls_error{jpegls_errc::invalid_marker_segment_size}; - - for (int i = 0; i < componentCountInScan; ++i) + for (int i{}; i < component_count_in_scan; ++i) { - ReadByte(); // Read Scan component selector - ReadByte(); // Read Mapping table selector + read_byte(); // Read Scan component selector + const uint8_t mapping_table_selector{read_byte()}; + if (mapping_table_selector != 0) + throw_jpegls_error(jpegls_errc::parameter_value_not_supported); } - params_.allowedLossyError = ReadByte(); // Read NEAR parameter - params_.interleaveMode = static_cast(ReadByte()); // Read ILV parameter - if (!(params_.interleaveMode == interleave_mode::none || params_.interleaveMode == interleave_mode::line || params_.interleaveMode == interleave_mode::sample)) - throw jpegls_error{jpegls_errc::invalid_parameter_interleave_mode}; + parameters_.near_lossless = read_byte(); // Read NEAR parameter + if (parameters_.near_lossless > compute_maximum_near_lossless(static_cast(maximum_sample_value()))) + throw_jpegls_error(jpegls_errc::invalid_parameter_near_lossless); - if ((ReadByte() & 0xF) != 0) // Read Ah (no meaning) and Al (point transform). - throw jpegls_error{jpegls_errc::parameter_value_not_supported}; + const auto mode{static_cast(read_byte())}; // Read ILV parameter + if (!(mode == interleave_mode::none || mode == interleave_mode::line || mode == interleave_mode::sample)) + throw_jpegls_error(jpegls_errc::invalid_parameter_interleave_mode); + parameters_.interleave_mode = mode; - if (params_.stride == 0) - { - const int width = rect_.Width != 0 ? rect_.Width : params_.width; - const int components = params_.interleaveMode == interleave_mode::none ? 1 : params_.components; - params_.stride = components * width * ((params_.bitsPerSample + 7) / 8); - } + if ((read_byte() & 0xFU) != 0) // Read Ah (no meaning) and Al (point transform). + throw_jpegls_error(jpegls_errc::parameter_value_not_supported); state_ = state::bit_stream_section; } -uint8_t JpegStreamReader::ReadByte() +uint8_t jpeg_stream_reader::read_byte() { - if (byteStream_.rawStream) - return static_cast(byteStream_.rawStream->sbumpc()); + if (source_.size == 0) + throw_jpegls_error(jpegls_errc::source_buffer_too_small); - if (byteStream_.count == 0) - throw jpegls_error{jpegls_errc::source_buffer_too_small}; - - const uint8_t value = byteStream_.rawData[0]; - SkipBytes(byteStream_, 1); + const uint8_t value{source_.data[0]}; + skip_bytes(source_, 1); return value; } -void JpegStreamReader::SkipByte() +void jpeg_stream_reader::skip_byte() { - static_cast(ReadByte()); + static_cast(read_byte()); } -int JpegStreamReader::ReadUInt16() +uint16_t jpeg_stream_reader::read_uint16() { - const int i = ReadByte() * 256; - return i + ReadByte(); + const uint16_t value = read_byte() * 256U; + return static_cast(value + read_byte()); } -uint32_t JpegStreamReader::ReadUInt32() +uint32_t jpeg_stream_reader::read_uint32() { - uint32_t value = ReadUInt16(); - value = value << 16; - value += ReadUInt16(); + uint32_t value{read_uint16()}; + value = value << 16U; + value += read_uint16(); return value; } -int32_t JpegStreamReader::ReadSegmentSize() +int32_t jpeg_stream_reader::read_segment_size() { - const int32_t segmentSize = ReadUInt16(); - if (segmentSize < 2) - throw jpegls_error{jpegls_errc::invalid_marker_segment_size}; + const int32_t segment_size{read_uint16()}; + if (segment_size < 2) + throw_jpegls_error(jpegls_errc::invalid_marker_segment_size); - return segmentSize; + return segment_size; } -int JpegStreamReader::TryReadApplicationData8Segment(int32_t segmentSize, spiff_header* header, bool* spiff_header_found) +int jpeg_stream_reader::try_read_application_data8_segment(const int32_t segment_size, spiff_header* header, + bool* spiff_header_found) { if (spiff_header_found) { @@ -466,39 +490,39 @@ *spiff_header_found = false; } - if (segmentSize == 5) - return TryReadHPColorTransformSegment(); + if (segment_size == 5) + return try_read_hp_color_transform_segment(); - if (header && spiff_header_found && segmentSize >= 30) - return TryReadSpiffHeaderSegment(header, *spiff_header_found); + if (header && spiff_header_found && segment_size >= 30) + return try_read_spiff_header_segment(*header, *spiff_header_found); return 0; } -int JpegStreamReader::TryReadHPColorTransformSegment() +int jpeg_stream_reader::try_read_hp_color_transform_segment() { - vector sourceTag; - ReadNBytes(sourceTag, 4); - if (strncmp(sourceTag.data(), "mrfx", 4) != 0) // mrfx = xfrm (in big endian) = colorXFoRM + vector source_tag; + read_bytes(source_tag, 4); + if (strncmp(source_tag.data(), "mrfx", 4) != 0) // mrfx = xfrm (in big endian) = colorXFoRM return 4; - const auto colorTransformation = ReadByte(); - switch (colorTransformation) + const auto transformation{read_byte()}; + switch (transformation) { case static_cast(color_transformation::none): case static_cast(color_transformation::hp1): case static_cast(color_transformation::hp2): case static_cast(color_transformation::hp3): - params_.colorTransformation = static_cast(colorTransformation); + parameters_.transformation = static_cast(transformation); return 5; case 4: // RgbAsYuvLossy (The standard lossy RGB to YCbCr transform used in JPEG.) case 5: // Matrix (transformation is controlled using a matrix that is also stored in the segment. - throw jpegls_error{jpegls_errc::color_transform_not_supported}; + throw_jpegls_error(jpegls_errc::color_transform_not_supported); default: - throw jpegls_error{jpegls_errc::invalid_encoded_data}; + throw_jpegls_error(jpegls_errc::invalid_encoded_data); } } @@ -506,49 +530,92 @@ EnumType enum_cast(uint8_t value) { if (value < static_cast(Low)) - throw jpegls_error{jpegls_errc::invalid_encoded_data}; + throw_jpegls_error(jpegls_errc::invalid_encoded_data); if (value > static_cast(High)) - throw jpegls_error{jpegls_errc::invalid_encoded_data}; + throw_jpegls_error(jpegls_errc::invalid_encoded_data); return static_cast(value); } -int JpegStreamReader::TryReadSpiffHeaderSegment(spiff_header* header, bool& spiff_header_found) +int jpeg_stream_reader::try_read_spiff_header_segment(OUT_ spiff_header& header, OUT_ bool& spiff_header_found) { - vector sourceTag; - ReadNBytes(sourceTag, 6); - if (strncmp(sourceTag.data(), "SPIFF", 6) != 0) + vector source_tag; + read_bytes(source_tag, 6); + if (strncmp(source_tag.data(), "SPIFF\0", 6) != 0) + { + header = {}; + spiff_header_found = false; return 6; + } - const auto high_version = ReadByte(); + const auto high_version{read_byte()}; if (high_version > spiff_major_revision_number) + { + header = {}; + spiff_header_found = false; return 7; // Treat unknown versions as if the SPIFF header doesn't exists. + } - SkipByte(); // low version + skip_byte(); // low version - header->profile_id = static_cast(ReadByte()); - header->component_count = ReadByte(); - header->height = ReadUInt32(); - header->width = ReadUInt32(); - header->color_space = static_cast(ReadByte()); - header->bits_per_sample = ReadByte(); - header->compression_type = static_cast(ReadByte()); - header->resolution_units = static_cast(ReadByte()); - header->vertical_resolution = ReadUInt32(); - header->horizontal_resolution = ReadUInt32(); + header.profile_id = static_cast(read_byte()); + header.component_count = read_byte(); + header.height = read_uint32(); + header.width = read_uint32(); + header.color_space = static_cast(read_byte()); + header.bits_per_sample = read_byte(); + header.compression_type = static_cast(read_byte()); + header.resolution_units = static_cast(read_byte()); + header.vertical_resolution = read_uint32(); + header.horizontal_resolution = read_uint32(); spiff_header_found = true; return 30; } -void JpegStreamReader::AddComponent(uint8_t componentId) +void jpeg_stream_reader::add_component(const uint8_t component_id) { - if (find(componentIds_.cbegin(), componentIds_.cend(), componentId) != componentIds_.cend()) - throw jpegls_error{jpegls_errc::duplicate_component_id_in_sof_segment}; + if (find(component_ids_.cbegin(), component_ids_.cend(), component_id) != component_ids_.cend()) + throw_jpegls_error(jpegls_errc::duplicate_component_id_in_sof_segment); + + component_ids_.push_back(component_id); +} + + +void jpeg_stream_reader::check_parameter_coherent() const +{ + switch (frame_info_.component_count) + { + case 4: + case 3: + break; + default: + if (parameters_.interleave_mode != interleave_mode::none) + throw_jpegls_error(jpegls_errc::parameter_value_not_supported); + + break; + } +} + + +bool jpeg_stream_reader::is_maximum_sample_value_valid() const noexcept +{ + return preset_coding_parameters_.maximum_sample_value == 0 || + static_cast(preset_coding_parameters_.maximum_sample_value) <= + calculate_maximum_sample_value(frame_info_.bits_per_sample); +} + + +uint32_t jpeg_stream_reader::maximum_sample_value() const noexcept +{ + ASSERT(is_maximum_sample_value_valid()); + + if (preset_coding_parameters_.maximum_sample_value != 0) + return static_cast(preset_coding_parameters_.maximum_sample_value); - componentIds_.push_back(componentId); + return calculate_maximum_sample_value(frame_info_.bits_per_sample); } diff -Nru charls-2.1.0+dfsg/src/jpeg_stream_reader.h charls-2.2.0+dfsg/src/jpeg_stream_reader.h --- charls-2.1.0+dfsg/src/jpeg_stream_reader.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/jpeg_stream_reader.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,72 +3,85 @@ #pragma once -#include #include +#include "byte_span.h" +#include "coding_parameters.h" + #include #include namespace charls { -enum class JpegMarkerCode : uint8_t; +enum class jpeg_marker_code : uint8_t; // Purpose: minimal implementation to read a JPEG byte stream. -class JpegStreamReader final +class jpeg_stream_reader final { public: - explicit JpegStreamReader(ByteStreamInfo byteStreamInfo) noexcept; + explicit jpeg_stream_reader(byte_span source) noexcept; + ~jpeg_stream_reader() = default; + + jpeg_stream_reader(const jpeg_stream_reader&) = delete; + jpeg_stream_reader& operator=(const jpeg_stream_reader&) = delete; + jpeg_stream_reader(jpeg_stream_reader&&) = default; + jpeg_stream_reader& operator=(jpeg_stream_reader&&) = default; - JlsParameters& GetMetadata() noexcept + const charls::frame_info& frame_info() const noexcept { - return params_; + return frame_info_; } - const jpegls_pc_parameters& GetCustomPreset() const noexcept + const coding_parameters& parameters() const noexcept { - return preset_coding_parameters_; + return parameters_; } - void Read(ByteStreamInfo rawPixels); - void ReadHeader(spiff_header* header = nullptr, bool* spiff_header_found = nullptr); - - void SetInfo(const JlsParameters& params) noexcept + const jpegls_pc_parameters& preset_coding_parameters() const noexcept { - params_ = params; + return preset_coding_parameters_; } - void SetOutputBgr(char value) noexcept + void read(byte_span source, size_t stride); + void read_header(spiff_header* header = nullptr, bool* spiff_header_found = nullptr); + + void output_bgr(const bool value) noexcept { - params_.outputBgr = value; + parameters_.output_bgr = value; } - void SetRect(const JlsRect& rect) noexcept + void rect(const JlsRect& rect) noexcept { rect_ = rect; } - void ReadStartOfScan(bool firstComponent); - uint8_t ReadByte(); + void read_start_of_scan(); + uint8_t read_byte(); private: - void SkipByte(); - int ReadUInt16(); - uint32_t ReadUInt32(); - int32_t ReadSegmentSize(); - void ReadNBytes(std::vector& destination, int byteCount); - JpegMarkerCode ReadNextMarkerCode(); - static void ValidateMarkerCode(JpegMarkerCode markerCode); - - int ReadMarkerSegment(JpegMarkerCode markerCode, int32_t segmentSize, spiff_header* header, bool* spiff_header_found); - int ReadSpiffDirectoryEntry(JpegMarkerCode markerCode, int32_t segmentSize); - int ReadStartOfFrameSegment(int32_t segmentSize); - static int ReadComment() noexcept; - int ReadPresetParametersSegment(int32_t segmentSize); - int TryReadApplicationData8Segment(int32_t segmentSize, spiff_header* header, bool* spiff_header_found); - int TryReadSpiffHeaderSegment(spiff_header* header, bool& spiff_header_found); - - int TryReadHPColorTransformSegment(); - void AddComponent(uint8_t componentId); + void skip_byte(); + uint16_t read_uint16(); + uint32_t read_uint32(); + int32_t read_segment_size(); + void read_bytes(std::vector& destination, int byte_count); + void read_next_start_of_scan(); + jpeg_marker_code read_next_marker_code(); + void validate_marker_code(jpeg_marker_code marker_code) const; + + int read_marker_segment(jpeg_marker_code marker_code, int32_t segment_size, spiff_header* header = nullptr, + bool* spiff_header_found = nullptr); + int read_spiff_directory_entry(jpeg_marker_code marker_code, int32_t segment_size); + int read_start_of_frame_segment(int32_t segment_size); + static int read_comment() noexcept; + int read_preset_parameters_segment(int32_t segment_size); + int try_read_application_data8_segment(int32_t segment_size, spiff_header* header, bool* spiff_header_found); + int try_read_spiff_header_segment(OUT_ spiff_header& header, OUT_ bool& spiff_header_found); + + int try_read_hp_color_transform_segment(); + void add_component(uint8_t component_id); + void check_parameter_coherent() const; + bool is_maximum_sample_value_valid() const noexcept; + uint32_t maximum_sample_value() const noexcept; enum class state { @@ -81,11 +94,12 @@ bit_stream_section }; - ByteStreamInfo byteStream_; - JlsParameters params_{}; + byte_span source_; + charls::frame_info frame_info_{}; + coding_parameters parameters_{}; jpegls_pc_parameters preset_coding_parameters_{}; JlsRect rect_{}; - std::vector componentIds_; + std::vector component_ids_; state state_{}; }; diff -Nru charls-2.1.0+dfsg/src/jpeg_stream_writer.cpp charls-2.2.0+dfsg/src/jpeg_stream_writer.cpp --- charls-2.1.0+dfsg/src/jpeg_stream_writer.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/jpeg_stream_writer.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -5,168 +5,169 @@ #include "constants.h" #include "jpeg_marker_code.h" -#include "jpeg_stream_reader.h" #include "jpegls_preset_parameters_type.h" #include "util.h" #include -#include -#include - -using std::array; -using std::vector; namespace charls { -JpegStreamWriter::JpegStreamWriter(const ByteStreamInfo& destination) noexcept : - destination_{destination} +using std::array; + +jpeg_stream_writer::jpeg_stream_writer(const byte_span destination) noexcept : destination_{destination} { } -void JpegStreamWriter::WriteStartOfImage() +void jpeg_stream_writer::write_start_of_image() { - WriteMarker(JpegMarkerCode::StartOfImage); + write_segment_without_data(jpeg_marker_code::start_of_image); } -void JpegStreamWriter::WriteEndOfImage() +void jpeg_stream_writer::write_end_of_image() { - WriteMarker(JpegMarkerCode::EndOfImage); + write_segment_without_data(jpeg_marker_code::end_of_image); } -void JpegStreamWriter::WriteSpiffHeaderSegment(const spiff_header& header) +void jpeg_stream_writer::write_spiff_header_segment(const spiff_header& header) { ASSERT(header.height > 0); ASSERT(header.width > 0); - // Create a JPEG APP8 segment in Still Picture Interchange File Format (SPIFF), v2.0 - vector segment{'S', 'P', 'I', 'F', 'F', '\0'}; - segment.push_back(spiff_major_revision_number); - segment.push_back(spiff_minor_revision_number); - segment.push_back(static_cast(header.profile_id)); - segment.push_back(static_cast(header.component_count)); - push_back(segment, header.height); - push_back(segment, header.width); - segment.push_back(static_cast(header.color_space)); - segment.push_back(static_cast(header.bits_per_sample)); - segment.push_back(static_cast(header.compression_type)); - segment.push_back(static_cast(header.resolution_units)); - push_back(segment, header.vertical_resolution); - push_back(segment, header.horizontal_resolution); + static constexpr array spiff_magic_id{'S', 'P', 'I', 'F', 'F', '\0'}; - WriteSegment(JpegMarkerCode::ApplicationData8, segment.data(), segment.size()); + // Create a JPEG APP8 segment in Still Picture Interchange File Format (SPIFF), v2.0 + write_segment_header(jpeg_marker_code::application_data8, 30); + write_bytes(spiff_magic_id.data(), spiff_magic_id.size()); + write_uint8(spiff_major_revision_number); + write_uint8(spiff_minor_revision_number); + write_uint8(static_cast(header.profile_id)); + write_uint8(static_cast(header.component_count)); + write_uint32(header.height); + write_uint32(header.width); + write_uint8(static_cast(header.color_space)); + write_uint8(static_cast(header.bits_per_sample)); + write_uint8(static_cast(header.compression_type)); + write_uint8(static_cast(header.resolution_units)); + write_uint32(header.vertical_resolution); + write_uint32(header.horizontal_resolution); } -void JpegStreamWriter::WriteSpiffDirectoryEntry(uint32_t entry_tag, const void* entry_data, size_t entry_data_size) -{ - WriteMarker(JpegMarkerCode::ApplicationData8); - WriteUInt16(static_cast(sizeof(uint16_t) + sizeof(uint32_t) + entry_data_size)); - WriteUInt32(entry_tag); - WriteBytes(entry_data, entry_data_size); +void jpeg_stream_writer::write_spiff_directory_entry(const uint32_t entry_tag, + IN_READS_BYTES_(entry_data_size_bytes) const void* entry_data, + const size_t entry_data_size_bytes) +{ + write_segment_header(jpeg_marker_code::application_data8, sizeof(uint32_t) + entry_data_size_bytes); + write_uint32(entry_tag); + write_bytes(entry_data, entry_data_size_bytes); } -void JpegStreamWriter::WriteSpiffEndOfDirectoryEntry() +void jpeg_stream_writer::write_spiff_end_of_directory_entry() { // Note: ISO/IEC 10918-3, Annex F.2.2.3 documents that the EOD entry segment should have a length of 8 // but only 6 data bytes. This approach allows to wrap existing bit streams\encoders with a SPIFF header. // In this implementation the SOI marker is added as data bytes to simplify the design. + static constexpr array spiff_end_of_directory{ + 0, 0, 0, spiff_end_of_directory_entry_type, 0xFF, static_cast(charls::jpeg_marker_code::start_of_image)}; - array segment{0, 0, 0, spiff_end_of_directory_entry_type, 0xFF, static_cast(JpegMarkerCode::StartOfImage)}; - WriteSegment(JpegMarkerCode::ApplicationData8, segment.data(), segment.size()); + write_segment_header(jpeg_marker_code::application_data8, spiff_end_of_directory.size()); + write_bytes(spiff_end_of_directory.data(), spiff_end_of_directory.size()); } -void JpegStreamWriter::WriteStartOfFrameSegment(int width, int height, int bitsPerSample, int componentCount) +void jpeg_stream_writer::write_start_of_frame_segment(const uint32_t width, const uint32_t height, + const int32_t bits_per_sample, const int32_t component_count) { - ASSERT(width >= 0 && width <= UINT16_MAX); - ASSERT(height >= 0 && height <= UINT16_MAX); - ASSERT(bitsPerSample >= MinimumBitsPerSample && bitsPerSample <= MaximumBitsPerSample); - ASSERT(componentCount > 0 && componentCount <= UINT8_MAX); + ASSERT(width <= UINT16_MAX); + ASSERT(height <= UINT16_MAX); + ASSERT(bits_per_sample >= minimum_bits_per_sample && bits_per_sample <= maximum_bits_per_sample); + ASSERT(component_count > 0 && component_count <= UINT8_MAX); // Create a Frame Header as defined in ISO/IEC 14495-1, C.2.2 and T.81, B.2.2 - vector segment; - segment.push_back(static_cast(bitsPerSample)); // P = Sample precision - push_back(segment, static_cast(height)); // Y = Number of lines - push_back(segment, static_cast(width)); // X = Number of samples per line + const size_t data_size{6 + (static_cast(component_count) * 3)}; + write_segment_header(jpeg_marker_code::start_of_frame_jpegls, data_size); + write_uint8(static_cast(bits_per_sample)); // P = Sample precision + write_uint16(static_cast(height)); // Y = Number of lines + write_uint16(static_cast(width)); // X = Number of samples per line // Components - segment.push_back(static_cast(componentCount)); // Nf = Number of image components in frame + write_uint8(static_cast(component_count)); // Nf = Number of image components in frame // Use by default 1 as the start component identifier to remain compatible with the // code sample of ISO/IEC 14495-1, H.4 and the JPEG-LS ISO conformance sample files. - for (auto componentId = 1; componentId <= componentCount; ++componentId) + for (auto component_id{1}; component_id <= component_count; ++component_id) { // Component Specification parameters - segment.push_back(static_cast(componentId)); // Ci = Component identifier - segment.push_back(0x11); // Hi + Vi = Horizontal sampling factor + Vertical sampling factor - segment.push_back(0); // Tqi = Quantization table destination selector (reserved for JPEG-LS, should be set to 0) + write_uint8(static_cast(component_id)); // Ci = Component identifier + write_uint8(0x11); // Hi + Vi = Horizontal sampling factor + Vertical sampling factor + write_uint8(0); // Tqi = Quantization table destination selector (reserved for JPEG-LS, should be set to 0) } - - WriteSegment(JpegMarkerCode::StartOfFrameJpegLS, segment.data(), segment.size()); } -void JpegStreamWriter::WriteColorTransformSegment(const color_transformation transformation) +void jpeg_stream_writer::write_color_transform_segment(const color_transformation transformation) { array segment{'m', 'r', 'f', 'x', static_cast(transformation)}; - WriteSegment(JpegMarkerCode::ApplicationData8, segment.data(), segment.size()); + + write_segment_header(jpeg_marker_code::application_data8, segment.size()); + write_bytes(segment.data(), segment.size()); } -void JpegStreamWriter::WriteJpegLSPresetParametersSegment(const jpegls_pc_parameters& preset_coding_parameters) +void jpeg_stream_writer::write_jpegls_preset_parameters_segment(const jpegls_pc_parameters& preset_coding_parameters) { - vector segment; - - segment.push_back(static_cast(JpegLSPresetParametersType::PresetCodingParameters)); - - push_back(segment, static_cast(preset_coding_parameters.maximum_sample_value)); - push_back(segment, static_cast(preset_coding_parameters.threshold1)); - push_back(segment, static_cast(preset_coding_parameters.threshold2)); - push_back(segment, static_cast(preset_coding_parameters.threshold3)); - push_back(segment, static_cast(preset_coding_parameters.reset_value)); - - WriteSegment(JpegMarkerCode::JpegLSPresetParameters, segment.data(), segment.size()); + write_segment_header(jpeg_marker_code::jpegls_preset_parameters, 1 + 5 * sizeof(uint16_t)); + write_uint8(to_underlying_type(jpegls_preset_parameters_type::preset_coding_parameters)); + write_uint16(preset_coding_parameters.maximum_sample_value); + write_uint16(preset_coding_parameters.threshold1); + write_uint16(preset_coding_parameters.threshold2); + write_uint16(preset_coding_parameters.threshold3); + write_uint16(preset_coding_parameters.reset_value); } -void JpegStreamWriter::WriteStartOfScanSegment(int componentCount, int allowedLossyError, interleave_mode interleaveMode) +void jpeg_stream_writer::write_start_of_scan_segment(const int32_t component_count, const int32_t near_lossless, + const interleave_mode interleave_mode) { - ASSERT(componentCount > 0 && componentCount <= UINT8_MAX); - ASSERT(allowedLossyError >= 0 && allowedLossyError <= UINT8_MAX); - ASSERT(interleaveMode == interleave_mode::none || - interleaveMode == interleave_mode::line || - interleaveMode == interleave_mode::sample); + ASSERT(component_count > 0 && component_count <= UINT8_MAX); + ASSERT(near_lossless >= 0 && near_lossless <= UINT8_MAX); + ASSERT(interleave_mode == interleave_mode::none || interleave_mode == interleave_mode::line || + interleave_mode == interleave_mode::sample); // Create a Scan Header as defined in T.87, C.2.3 and T.81, B.2.3 - vector segment; + write_segment_header(jpeg_marker_code::start_of_scan, 1 + (static_cast(component_count) * 2) + 3); + write_uint8(static_cast(component_count)); - segment.push_back(static_cast(componentCount)); - for (auto i = 0; i < componentCount; ++i) + for (int32_t i{}; i < component_count; ++i) { - segment.push_back(static_cast(componentId_)); - componentId_++; - segment.push_back(0); // Mapping table selector (0 = no table) + write_uint8(component_id_); + write_uint8(0); // Mapping table selector (0 = no table) + ++component_id_; } - segment.push_back(static_cast(allowedLossyError)); // NEAR parameter - segment.push_back(static_cast(interleaveMode)); // ILV parameter - segment.push_back(0); // transformation - - WriteSegment(JpegMarkerCode::StartOfScan, segment.data(), segment.size()); + write_uint8(static_cast(near_lossless)); // NEAR parameter + write_uint8(static_cast(interleave_mode)); // ILV parameter + write_uint8(0); // transformation } -void JpegStreamWriter::WriteSegment(JpegMarkerCode markerCode, const void* data, size_t dataSize) +void jpeg_stream_writer::write_segment_header(const jpeg_marker_code marker_code, const size_t data_size) { - ASSERT(dataSize <= UINT16_MAX - sizeof(uint16_t)); + constexpr size_t marker_code_size{2}; + constexpr size_t segment_length_size{sizeof(uint16_t)}; + + ASSERT(data_size <= UINT16_MAX - segment_length_size); + + const size_t total_segment_size{marker_code_size + segment_length_size + data_size}; + if (byte_offset_ + total_segment_size > destination_.size) + impl::throw_jpegls_error(jpegls_errc::destination_buffer_too_small); - WriteMarker(markerCode); - WriteUInt16(static_cast(dataSize + sizeof(uint16_t))); - WriteBytes(data, dataSize); + write_marker(marker_code); + write_uint16(static_cast(data_size + segment_length_size)); } } // namespace charls diff -Nru charls-2.1.0+dfsg/src/jpeg_stream_writer.h charls-2.2.0+dfsg/src/jpeg_stream_writer.h --- charls-2.1.0+dfsg/src/jpeg_stream_writer.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/jpeg_stream_writer.h 2021-01-10 18:07:25.000000000 +0000 @@ -4,167 +4,169 @@ #pragma once #include -#include +#include "byte_span.h" #include "jpeg_marker_code.h" - -#include +#include "util.h" namespace charls { -enum class JpegMarkerCode : uint8_t; - // Purpose: 'Writer' class that can generate JPEG-LS file streams. -class JpegStreamWriter final +class jpeg_stream_writer final { public: - JpegStreamWriter() = default; - explicit JpegStreamWriter(const ByteStreamInfo& destination) noexcept; + jpeg_stream_writer() = default; + explicit jpeg_stream_writer(byte_span destination) noexcept; + ~jpeg_stream_writer() = default; + + jpeg_stream_writer(const jpeg_stream_writer&) = delete; + jpeg_stream_writer& operator=(const jpeg_stream_writer&) = delete; + jpeg_stream_writer(jpeg_stream_writer&&) = default; + jpeg_stream_writer& operator=(jpeg_stream_writer&&) = default; - void WriteStartOfImage(); + void write_start_of_image(); /// /// Write a JPEG SPIFF (APP8 + spiff) segment. /// This segment is documented in ISO/IEC 10918-3, Annex F. /// /// Header info to write into the SPIFF segment. - void WriteSpiffHeaderSegment(const spiff_header& header); + void write_spiff_header_segment(const spiff_header& header); - void WriteSpiffDirectoryEntry(uint32_t entry_tag, const void* entry_data, size_t entry_data_size); + void write_spiff_directory_entry(uint32_t entry_tag, IN_READS_BYTES_(entry_data_size_bytes) const void* entry_data, + size_t entry_data_size_bytes); /// /// Write a JPEG SPIFF end of directory (APP8) segment. /// This segment is documented in ISO/IEC 10918-3, Annex F. /// - void WriteSpiffEndOfDirectoryEntry(); + void write_spiff_end_of_directory_entry(); /// /// Writes a HP color transformation (APP8) segment. /// /// Color transformation to put into the segment. - void WriteColorTransformSegment(color_transformation transformation); + void write_color_transform_segment(color_transformation transformation); /// /// Writes a JPEG-LS preset parameters (LSE) segment. /// /// Parameters to write into the JPEG-LS preset segment. - void WriteJpegLSPresetParametersSegment(const jpegls_pc_parameters& preset_coding_parameters); + void write_jpegls_preset_parameters_segment(const jpegls_pc_parameters& preset_coding_parameters); /// /// Writes a JPEG-LS Start Of Frame (SOF-55) segment. /// /// The width of the frame. /// The height of the frame. - /// The bits per sample. - /// The component count. - void WriteStartOfFrameSegment(int width, int height, int bitsPerSample, int componentCount); + /// The bits per sample. + /// The component count. + void write_start_of_frame_segment(uint32_t width, uint32_t height, int32_t bits_per_sample, int32_t component_count); /// /// Writes a JPEG-LS Start Of Scan (SOS) segment. /// - /// The number of components in the scan segment. Can only be > 1 when the components are interleaved. - /// The allowed lossy error. 0 means lossless. - /// The interleave mode of the components. - void WriteStartOfScanSegment(int componentCount, int allowedLossyError, interleave_mode interleaveMode); + /// + /// The number of components in the scan segment. Can only be > 1 when the components are interleaved. + /// + /// The allowed lossy error. 0 means lossless. + /// The interleave mode of the components. + void write_start_of_scan_segment(int32_t component_count, int32_t near_lossless, interleave_mode interleave_mode); - void WriteEndOfImage(); + void write_end_of_image(); - std::size_t GetBytesWritten() const noexcept + size_t bytes_written() const noexcept { - return byteOffset_; + return byte_offset_; } - std::size_t GetLength() const noexcept + byte_span remaining_destination() const noexcept { - return destination_.count - byteOffset_; + return {destination_.data + byte_offset_, destination_.size - byte_offset_}; } - ByteStreamInfo OutputStream() const noexcept + void seek(const size_t byte_count) noexcept { - ByteStreamInfo data = destination_; - data.count -= byteOffset_; - data.rawData += byteOffset_; - return data; + ASSERT(byte_offset_ + byte_count <= destination_.size); + byte_offset_ += byte_count; } - void Seek(std::size_t byteCount) noexcept + void destination(const byte_span destination) noexcept { - if (destination_.rawStream) - return; + destination_ = destination; + } - byteOffset_ += byteCount; +private: + void write_segment_header(jpeg_marker_code marker_code, size_t data_size); + + void write_uint8(const uint8_t value) noexcept + { + ASSERT(byte_offset_ + sizeof(uint8_t) <= destination_.size); + destination_.data[byte_offset_++] = value; } - void UpdateDestination(void* destination_buffer, size_t destination_size) noexcept + void write_uint16(const uint16_t value) noexcept { - destination_.rawData = static_cast(destination_buffer); - destination_.count = destination_size; + write_uint(value); } -private: - uint8_t* GetPos() const noexcept + void write_uint16(const int32_t value) noexcept { - return destination_.rawData + byteOffset_; + ASSERT(value >= 0 && value <= UINT16_MAX); + write_uint16(static_cast(value)); } - void WriteSegment(JpegMarkerCode markerCode, const void* data, size_t dataSize); + void write_uint32(const uint32_t value) noexcept + { + write_uint(value); + } - void WriteByte(uint8_t value) + template + void write_uint(const UnsignedIntType value) noexcept { - if (destination_.rawStream) - { - destination_.rawStream->sputc(static_cast(value)); - } - else - { - if (byteOffset_ >= destination_.count) - throw jpegls_error{jpegls_errc::destination_buffer_too_small}; + ASSERT(byte_offset_ + sizeof(UnsignedIntType) <= destination_.size); - destination_.rawData[byteOffset_++] = value; - } + // Use write_bytes to write to the unaligned byte array. + // The compiler will perform the correct optimization when the target platform support unaligned writes. + const UnsignedIntType big_endian_value{endian_swap(value)}; + write_bytes(&big_endian_value, sizeof big_endian_value); } - void WriteBytes(const std::vector& bytes) + void write_bytes(IN_READS_BYTES_(size) const void* data, const size_t size) noexcept { - for (std::size_t i = 0; i < bytes.size(); ++i) - { - WriteByte(bytes[i]); - } + ASSERT(byte_offset_ + size <= destination_.size); + memcpy(destination_.data + byte_offset_, data, size); + byte_offset_ += size; } - void WriteBytes(const void* data, const size_t dataSize) + void write_marker(const jpeg_marker_code marker_code) noexcept { - const auto bytes = static_cast(data); - - for (std::size_t i = 0; i < dataSize; ++i) - { - WriteByte(bytes[i]); - } + write_uint8(jpeg_marker_start_byte); + write_uint8(static_cast(marker_code)); } - void WriteUInt16(uint16_t value) + void write_segment_without_data(const jpeg_marker_code marker_code) { - WriteByte(static_cast(value / 0x100)); - WriteByte(static_cast(value % 0x100)); + if (byte_offset_ + 2 > destination_.size) + impl::throw_jpegls_error(jpegls_errc::destination_buffer_too_small); + + write_uint8(jpeg_marker_start_byte); + write_uint8(static_cast(marker_code)); } - void WriteUInt32(uint32_t value) + static constexpr uint32_t endian_swap(const uint32_t value) noexcept { - WriteByte(static_cast(value >> 24)); - WriteByte(static_cast(value >> 16)); - WriteByte(static_cast(value >> 8)); - WriteByte(static_cast(value)); + return value >> 24 | (value & 0x00FF0000) >> 8 | (value & 0x0000FF00) << 8 | value << 24; } - void WriteMarker(JpegMarkerCode markerCode) + static constexpr uint16_t endian_swap(const uint16_t value) noexcept { - WriteByte(JpegMarkerStartByte); - WriteByte(static_cast(markerCode)); + return static_cast(value >> 8 | value << 8); } - ByteStreamInfo destination_{}; - std::size_t byteOffset_{}; - int8_t componentId_{1}; + byte_span destination_{}; + size_t byte_offset_{}; + uint8_t component_id_{1}; }; } // namespace charls diff -Nru charls-2.1.0+dfsg/src/lookup_table.h charls-2.2.0+dfsg/src/lookup_table.h --- charls-2.1.0+dfsg/src/lookup_table.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/lookup_table.h 2021-01-10 18:07:25.000000000 +0000 @@ -11,55 +11,54 @@ namespace charls { // Tables for fast decoding of short Golomb Codes. -struct Code final +struct golomb_code final { - Code() = default; + golomb_code() = default; - Code(int32_t value, int32_t length) noexcept : - value_{value}, - length_{length} + golomb_code(const int32_t value, const uint32_t length) noexcept : value_{value}, length_{length} { } - int32_t GetValue() const noexcept + int32_t value() const noexcept { return value_; } - int32_t GetLength() const noexcept + uint32_t length() const noexcept { return length_; } +private: int32_t value_{}; - int32_t length_{}; + uint32_t length_{}; }; -class CTable final +class golomb_code_table final { public: - static constexpr size_t byte_bit_count = 8; + static constexpr size_t byte_bit_count{8}; - void AddEntry(const uint8_t value, const Code c) noexcept + void add_entry(const uint8_t value, const golomb_code c) noexcept { - const int32_t length = c.GetLength(); + const uint32_t length{c.length()}; ASSERT(static_cast(length) <= byte_bit_count); - for (size_t i = 0; i < static_cast(1U) << (byte_bit_count - length); ++i) + for (size_t i{}; i < static_cast(1U) << (byte_bit_count - length); ++i) { - ASSERT(types_[(static_cast(value) << (byte_bit_count - length)) + i].GetLength() == 0); + ASSERT(types_[(static_cast(value) << (byte_bit_count - length)) + i].length() == 0); types_[(static_cast(value) << (byte_bit_count - length)) + i] = c; } } - FORCE_INLINE const Code& Get(const int32_t value) const noexcept + FORCE_INLINE const golomb_code& get(const uint32_t value) const noexcept { return types_[value]; } private: - std::array types_; + std::array types_; }; } // namespace charls diff -Nru charls-2.1.0+dfsg/src/lossless_traits.h charls-2.2.0+dfsg/src/lossless_traits.h --- charls-2.1.0+dfsg/src/lossless_traits.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/lossless_traits.h 2021-01-10 18:07:25.000000000 +0000 @@ -4,6 +4,8 @@ #pragma once #include "constants.h" +#include "util.h" + #include namespace charls { @@ -11,148 +13,147 @@ // Optimized trait classes for lossless compression of 8 bit color and 8/16 bit monochrome images. // This class assumes MaximumSampleValue correspond to a whole number of bits, and no custom ResetValue is set when encoding. // The point of this is to have the most optimized code for the most common and most demanding scenario. -template -struct LosslessTraitsImpl +template +struct lossless_traits_impl { - using SAMPLE = sample; + using sample_type = SampleType; - enum - { - NEAR = 0, - bpp = bitsPerPixel, - qbpp = bitsPerPixel, - RANGE = (1 << bpp), - MAXVAL = (1 << bpp) - 1, - LIMIT = 2 * (bitsPerPixel + std::max(8, bitsPerPixel)), - RESET = DefaultResetValue - }; + static constexpr int32_t maximum_sample_value{(1U << BitsPerPixel) - 1}; + static constexpr int32_t near_lossless{}; + static constexpr int32_t quantized_bits_per_pixel{BitsPerPixel}; + static constexpr int32_t range{compute_range_parameter(maximum_sample_value, near_lossless)}; + static constexpr int32_t bits_per_pixel{BitsPerPixel}; + static constexpr int32_t limit{compute_limit_parameter(BitsPerPixel)}; + static constexpr int32_t reset_threshold{default_reset_value}; - FORCE_INLINE constexpr static int32_t ComputeErrVal(int32_t d) noexcept + FORCE_INLINE constexpr static int32_t compute_error_value(const int32_t d) noexcept { - return ModuloRange(d); + return modulo_range(d); } - FORCE_INLINE constexpr static bool IsNear(int32_t lhs, int32_t rhs) noexcept + FORCE_INLINE constexpr static bool is_near(const int32_t lhs, const int32_t rhs) noexcept { return lhs == rhs; } -// The following optimization is implementation-dependent (works on x86 and ARM, see charlstest). -#if defined(__clang__) - __attribute__((no_sanitize("shift"))) -#endif FORCE_INLINE constexpr static int32_t - ModuloRange(int32_t errorValue) noexcept + modulo_range(const int32_t error_value) noexcept { - return static_cast(errorValue << (int32_t_bit_count - bpp)) >> (int32_t_bit_count - bpp); + return (static_cast(static_cast(error_value) << (int32_t_bit_count - bits_per_pixel))) >> + (int32_t_bit_count - bits_per_pixel); } - FORCE_INLINE static SAMPLE ComputeReconstructedSample(int32_t Px, int32_t ErrVal) noexcept + FORCE_INLINE static SampleType compute_reconstructed_sample(const int32_t predicted_value, + const int32_t error_value) noexcept { - return static_cast(MAXVAL & (Px + ErrVal)); + return static_cast(maximum_sample_value & (predicted_value + error_value)); } - FORCE_INLINE static int32_t CorrectPrediction(int32_t Pxc) noexcept + FORCE_INLINE static int32_t correct_prediction(const int32_t predicted) noexcept { - if ((Pxc & MAXVAL) == Pxc) - return Pxc; + if ((predicted & maximum_sample_value) == predicted) + return predicted; - return (~(Pxc >> (int32_t_bit_count - 1))) & MAXVAL; + return (~(predicted >> (int32_t_bit_count - 1))) & maximum_sample_value; } }; -template -struct LosslessTraits final : LosslessTraitsImpl +template +struct lossless_traits final : lossless_traits_impl { - using PIXEL = T; + using pixel_type = PixelType; }; template<> -struct LosslessTraits final : LosslessTraitsImpl +struct lossless_traits final : lossless_traits_impl { - using PIXEL = SAMPLE; + using pixel_type = sample_type; - FORCE_INLINE constexpr static signed char ModRange(int32_t errorValue) noexcept + FORCE_INLINE constexpr static signed char mod_range(const int32_t error_value) noexcept { - return static_cast(errorValue); + return static_cast(error_value); } - FORCE_INLINE constexpr static int32_t ComputeErrVal(int32_t d) noexcept + FORCE_INLINE constexpr static int32_t compute_error_value(const int32_t d) noexcept { return static_cast(d); } - FORCE_INLINE constexpr static uint8_t ComputeReconstructedSample(int32_t Px, int32_t ErrVal) noexcept + FORCE_INLINE constexpr static uint8_t compute_reconstructed_sample(const int32_t predicted_value, + const int32_t error_value) noexcept { - return static_cast(Px + ErrVal); + return static_cast(predicted_value + error_value); } }; template<> -struct LosslessTraits final : LosslessTraitsImpl +struct lossless_traits final : lossless_traits_impl { - using PIXEL = SAMPLE; + using pixel_type = sample_type; - FORCE_INLINE constexpr static short ModRange(int32_t errorValue) noexcept + FORCE_INLINE constexpr static short mod_range(const int32_t error_value) noexcept { - return static_cast(errorValue); + return static_cast(error_value); } - FORCE_INLINE constexpr static int32_t ComputeErrVal(int32_t d) noexcept + FORCE_INLINE constexpr static int32_t compute_error_value(const int32_t d) noexcept { return static_cast(d); } - FORCE_INLINE constexpr static SAMPLE ComputeReconstructedSample(int32_t Px, int32_t errorValue) noexcept + FORCE_INLINE constexpr static sample_type compute_reconstructed_sample(const int32_t predicted_value, + const int32_t error_value) noexcept { - return static_cast(Px + errorValue); + return static_cast(predicted_value + error_value); } }; -template -struct LosslessTraits, bpp> final : LosslessTraitsImpl +template +struct lossless_traits, BitsPerPixel> final : lossless_traits_impl { - using PIXEL = Triplet; + using pixel_type = triplet; - FORCE_INLINE constexpr static bool IsNear(int32_t lhs, int32_t rhs) noexcept + FORCE_INLINE constexpr static bool is_near(const int32_t lhs, const int32_t rhs) noexcept { return lhs == rhs; } - FORCE_INLINE static bool IsNear(PIXEL lhs, PIXEL rhs) noexcept + FORCE_INLINE static bool is_near(pixel_type lhs, pixel_type rhs) noexcept { return lhs == rhs; } - FORCE_INLINE static T ComputeReconstructedSample(int32_t Px, int32_t errorValue) noexcept + FORCE_INLINE static PixelType compute_reconstructed_sample(const int32_t predicted_value, + const int32_t error_value) noexcept { - return static_cast(Px + errorValue); + return static_cast(predicted_value + error_value); } }; -template -struct LosslessTraits, bpp> final : LosslessTraitsImpl +template +struct lossless_traits, BitsPerPixel> final : lossless_traits_impl { - using PIXEL = Quad; + using pixel_type = quad; - FORCE_INLINE constexpr static bool IsNear(int32_t lhs, int32_t rhs) noexcept + FORCE_INLINE constexpr static bool is_near(const int32_t lhs, const int32_t rhs) noexcept { return lhs == rhs; } - FORCE_INLINE static bool IsNear(PIXEL lhs, PIXEL rhs) noexcept + FORCE_INLINE static bool is_near(pixel_type lhs, pixel_type rhs) noexcept { return lhs == rhs; } - FORCE_INLINE static T ComputeReconstructedSample(int32_t Px, int32_t errorValue) noexcept + FORCE_INLINE static PixelType compute_reconstructed_sample(const int32_t predicted_value, + const int32_t error_value) noexcept { - return static_cast(Px + errorValue); + return static_cast(predicted_value + error_value); } }; diff -Nru charls-2.1.0+dfsg/src/process_line.h charls-2.2.0+dfsg/src/process_line.h --- charls-2.1.0+dfsg/src/process_line.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/process_line.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,9 +3,7 @@ #pragma once -#include -#include - +#include "coding_parameters.h" #include "util.h" #include @@ -25,346 +23,386 @@ namespace charls { -class ProcessLine +class process_line { public: - virtual ~ProcessLine() = default; + virtual ~process_line() = default; - ProcessLine(const ProcessLine&) = delete; - ProcessLine(ProcessLine&&) = delete; - ProcessLine& operator=(const ProcessLine&) = delete; - ProcessLine& operator=(ProcessLine&&) = delete; + process_line(const process_line&) = delete; + process_line(process_line&&) = delete; + process_line& operator=(const process_line&) = delete; + process_line& operator=(process_line&&) = delete; - virtual void NewLineDecoded(const void* pSrc, int pixelCount, int sourceStride) = 0; - virtual void NewLineRequested(void* pDest, int pixelCount, int destStride) = 0; + virtual void new_line_decoded(const void* source, size_t pixel_count, size_t source_stride) = 0; + virtual void new_line_requested(void* destination, size_t pixel_count, size_t destination_stride) = 0; protected: - ProcessLine() = default; + process_line() = default; }; -class PostProcessSingleComponent final : public ProcessLine +class post_process_single_component final : public process_line { public: - PostProcessSingleComponent(void* rawData, const uint32_t stride, const size_t bytesPerPixel) noexcept : - rawData_{static_cast(rawData)}, - bytesPerPixel_{bytesPerPixel}, - bytesPerLine_{stride} + post_process_single_component(void* raw_data, const size_t stride, const size_t bytes_per_pixel) noexcept : + raw_data_{static_cast(raw_data)}, bytes_per_pixel_{bytes_per_pixel}, stride_{stride} { + ASSERT(bytes_per_pixel == sizeof(uint8_t) || bytes_per_pixel == sizeof(uint16_t)); } - void NewLineRequested(void* destination, int pixelCount, int /*byteStride*/) noexcept(false) override + void new_line_requested(void* destination, const size_t pixel_count, + size_t /* destination_stride */) noexcept(false) override { - std::memcpy(destination, rawData_, pixelCount * bytesPerPixel_); - rawData_ += bytesPerLine_; + memcpy(destination, raw_data_, pixel_count * bytes_per_pixel_); + raw_data_ += stride_; } - void NewLineDecoded(const void* source, int pixelCount, int /*sourceStride*/) noexcept(false) override + void new_line_decoded(const void* source, const size_t pixel_count, size_t /* source_stride */) noexcept(false) override { - std::memcpy(rawData_, source, pixelCount * bytesPerPixel_); - rawData_ += bytesPerLine_; + memcpy(raw_data_, source, pixel_count * bytes_per_pixel_); + raw_data_ += stride_; } private: - uint8_t* rawData_; - size_t bytesPerPixel_; - size_t bytesPerLine_; + uint8_t* raw_data_; + size_t bytes_per_pixel_; + size_t stride_; }; -inline void ByteSwap(void* data, int count) -{ - if (static_cast(count) & 1U) - throw jpegls_error{jpegls_errc::invalid_encoded_data}; - - const auto data32 = static_cast(data); - for (auto i = 0; i < count / 4; i++) - { - const auto value = data32[i]; - data32[i] = ((value >> 8U) & 0x00FF00FFU) | ((value & 0x00FF00FFU) << 8U); - } - - const auto data8 = static_cast(data); - if ((count % 4) != 0) - { - std::swap(data8[count - 2], data8[count - 1]); - } -} - -class PostProcessSingleStream final : public ProcessLine +class post_process_single_component_masked final : public process_line { public: - PostProcessSingleStream(std::basic_streambuf* rawData, uint32_t stride, size_t bytesPerPixel) noexcept : - rawData_{rawData}, - bytesPerPixel_{bytesPerPixel}, - bytesPerLine_{stride} + post_process_single_component_masked(void* raw_data, const size_t stride, const size_t bytes_per_pixel, + const uint32_t bits_per_pixel) noexcept : + raw_data_{raw_data}, + bytes_per_pixel_{bytes_per_pixel}, + stride_{stride}, + mask_{(1U << bits_per_pixel) - 1U}, + single_byte_pixel_{bytes_per_pixel_ == sizeof(uint8_t)} { + ASSERT(bytes_per_pixel == sizeof(uint8_t) || bytes_per_pixel == sizeof(uint16_t)); } - void NewLineRequested(void* destination, int pixelCount, int /*destStride*/) override + void new_line_requested(void* destination, const size_t pixel_count, + size_t /* destination_stride */) noexcept(false) override { - auto bytesToRead = static_cast(static_cast(pixelCount) * bytesPerPixel_); - while (bytesToRead != 0) + if (single_byte_pixel_) { - const auto bytesRead = rawData_->sgetn(static_cast(destination), bytesToRead); - if (bytesRead == 0) - throw jpegls_error{jpegls_errc::destination_buffer_too_small}; - - bytesToRead = bytesToRead - bytesRead; + const auto* pixel_source{static_cast(raw_data_)}; + auto* pixel_destination{static_cast(destination)}; + for (size_t i{}; i < pixel_count; ++i) + { + pixel_destination[i] = pixel_source[i] & mask_; + } } - - if (bytesPerPixel_ == 2) + else { - ByteSwap(static_cast(destination), 2 * pixelCount); + const auto* pixel_source{static_cast(raw_data_)}; + auto* pixel_destination{static_cast(destination)}; + for (size_t i{}; i < pixel_count; ++i) + { + pixel_destination[i] = pixel_source[i] & mask_; + } } - if (bytesPerLine_ - pixelCount * bytesPerPixel_ > 0) - { - rawData_->pubseekoff(static_cast(bytesPerLine_ - bytesToRead), std::ios_base::cur); - } + raw_data_ = static_cast(raw_data_) + stride_; } - void NewLineDecoded(const void* source, int pixelCount, int /*sourceStride*/) override + void new_line_decoded(const void* source, const size_t pixel_count, size_t /* source_stride */) noexcept(false) override { - const auto bytesToWrite = pixelCount * bytesPerPixel_; - const auto bytesWritten = static_cast(rawData_->sputn(static_cast(source), static_cast(bytesToWrite))); - if (bytesWritten != bytesToWrite) - throw jpegls_error{jpegls_errc::destination_buffer_too_small}; + memcpy(raw_data_, source, pixel_count * bytes_per_pixel_); + raw_data_ = static_cast(raw_data_) + stride_; } private: - std::basic_streambuf* rawData_; - size_t bytesPerPixel_; - size_t bytesPerLine_; + void* raw_data_; + size_t bytes_per_pixel_; + size_t stride_; + uint32_t mask_; + bool single_byte_pixel_; }; -template -void TransformLineToQuad(const T* ptypeInput, int32_t pixelStrideIn, Quad* byteBuffer, int32_t pixelStride, TRANSFORM& transform) noexcept -{ - const int cpixel = std::min(pixelStride, pixelStrideIn); - Quad* ptypeBuffer = byteBuffer; - - for (auto x = 0; x < cpixel; ++x) - { - const Quad pixel(transform(ptypeInput[x], ptypeInput[x + pixelStrideIn], ptypeInput[x + 2 * pixelStrideIn]), ptypeInput[x + 3 * pixelStrideIn]); - ptypeBuffer[x] = pixel; +template +void transform_line_to_quad(const PixelType* source, const size_t pixel_stride_in, quad* destination, + const size_t pixel_stride, + Transform& transform) noexcept +{ + const auto pixel_count{std::min(pixel_stride, pixel_stride_in)}; + + for (size_t i{}; i < pixel_count; ++i) + { + const quad pixel(transform(source[i], source[i + pixel_stride_in], source[i + 2 * pixel_stride_in]), + source[i + 3 * pixel_stride_in]); + destination[i] = pixel; } } -template -void TransformQuadToLine(const Quad* byteInput, int32_t pixelStrideIn, T* ptypeBuffer, int32_t pixelStride, TRANSFORM& transform) noexcept -{ - const auto cpixel = std::min(pixelStride, pixelStrideIn); - const Quad* ptypeBufferIn = byteInput; +template +void transform_quad_to_line(const quad* source, const size_t pixel_stride_in, PixelType* destination, + const size_t pixel_stride, + Transform& transform) noexcept +{ + const auto pixel_count{std::min(pixel_stride, pixel_stride_in)}; + + for (size_t i{}; i < pixel_count; ++i) + { + const quad color{source[i]}; + const quad color_transformed(transform(color.v1, color.v2, color.v3), color.v4); + + destination[i] = color_transformed.v1; + destination[i + pixel_stride] = color_transformed.v2; + destination[i + 2 * pixel_stride] = color_transformed.v3; + destination[i + 3 * pixel_stride] = color_transformed.v4; + } +} - for (auto x = 0; x < cpixel; ++x) - { - const Quad color = ptypeBufferIn[x]; - const Quad colorTransformed(transform(color.v1, color.v2, color.v3), color.v4); - ptypeBuffer[x] = colorTransformed.v1; - ptypeBuffer[x + pixelStride] = colorTransformed.v2; - ptypeBuffer[x + 2 * pixelStride] = colorTransformed.v3; - ptypeBuffer[x + 3 * pixelStride] = colorTransformed.v4; +template +void transform_quad_to_line(const quad* source, const size_t pixel_stride_in, PixelType* destination, + const size_t pixel_stride, Transform& transform, const uint32_t mask) noexcept +{ + const auto pixel_count{std::min(pixel_stride, pixel_stride_in)}; + + for (size_t i{}; i < pixel_count; ++i) + { + const quad color{source[i]}; + const quad color_transformed(transform(color.v1 & mask, color.v2 & mask, color.v3 & mask), + color.v4 & mask); + + destination[i] = color_transformed.v1; + destination[i + pixel_stride] = color_transformed.v2; + destination[i + 2 * pixel_stride] = color_transformed.v3; + destination[i + 3 * pixel_stride] = color_transformed.v4; } } template -void TransformRgbToBgr(T* pDest, int samplesPerPixel, int pixelCount) noexcept +void transform_rgb_to_bgr(T* buffer, int samples_per_pixel, const size_t pixel_count) noexcept { - for (auto i = 0; i < pixelCount; ++i) + for (size_t i{}; i < pixel_count; ++i) { - std::swap(pDest[0], pDest[2]); - pDest += samplesPerPixel; + std::swap(buffer[0], buffer[2]); + buffer += samples_per_pixel; } } -template -void TransformLine(Triplet* pDest, const Triplet* pSrc, int pixelCount, TRANSFORM& transform) noexcept +template +void transform_line(triplet* destination, const triplet* source, const size_t pixel_count, + Transform& transform) noexcept { - for (auto i = 0; i < pixelCount; ++i) + for (size_t i{}; i < pixel_count; ++i) { - pDest[i] = transform(pSrc[i].v1, pSrc[i].v2, pSrc[i].v3); + destination[i] = transform(source[i].v1, source[i].v2, source[i].v3); } } -template -void TransformLine(Quad* pDest, const Quad* pSrc, int pixelCount, TRANSFORM& transform) noexcept +template +void transform_line(triplet* destination, const triplet* source, const size_t pixel_count, + Transform& transform, const uint32_t mask) noexcept { - for (auto i = 0; i < pixelCount; ++i) + for (size_t i{}; i < pixel_count; ++i) { - pDest[i] = Quad(transform(pSrc[i].v1, pSrc[i].v2, pSrc[i].v3), pSrc[i].v4); + destination[i] = transform(source[i].v1 & mask, source[i].v2 & mask, source[i].v3 & mask); } } -template -void TransformLineToTriplet(const T* ptypeInput, int32_t pixelStrideIn, Triplet* byteBuffer, int32_t pixelStride, TRANSFORM& transform) noexcept +template +void transform_line(quad* destination, const quad* source, const size_t pixel_count, + Transform& transform) noexcept { - const auto cpixel = std::min(pixelStride, pixelStrideIn); - Triplet* ptypeBuffer = byteBuffer; + for (size_t i{}; i < pixel_count; ++i) + { + destination[i] = quad(transform(source[i].v1, source[i].v2, source[i].v3), source[i].v4); + } +} - for (auto x = 0; x < cpixel; ++x) + +template +void transform_line(quad* destination, const quad* source, const size_t pixel_count, + Transform& transform, const uint32_t mask) noexcept +{ + for (size_t i{}; i < pixel_count; ++i) { - ptypeBuffer[x] = transform(ptypeInput[x], ptypeInput[x + pixelStrideIn], ptypeInput[x + 2 * pixelStrideIn]); + destination[i] = + quad(transform(source[i].v1 & mask, source[i].v2 & mask, source[i].v3 & mask), source[i].v4 & mask); } } -template -void TransformTripletToLine(const Triplet* byteInput, int32_t pixelStrideIn, T* ptypeBuffer, int32_t pixelStride, TRANSFORM& transform) noexcept +template +void transform_line_to_triplet(const PixelType* source, const size_t pixel_stride_in, triplet* destination, + const size_t pixel_stride, Transform& transform) noexcept { - const auto cpixel = std::min(pixelStride, pixelStrideIn); - const Triplet* ptypeBufferIn = byteInput; + const auto pixel_count{std::min(pixel_stride, pixel_stride_in)}; + triplet* type_buffer = destination; - for (auto x = 0; x < cpixel; ++x) + for (size_t i{}; i < pixel_count; ++i) { - const Triplet color = ptypeBufferIn[x]; - const Triplet colorTransformed = transform(color.v1, color.v2, color.v3); - - ptypeBuffer[x] = colorTransformed.v1; - ptypeBuffer[x + pixelStride] = colorTransformed.v2; - ptypeBuffer[x + 2 * pixelStride] = colorTransformed.v3; + type_buffer[i] = transform(source[i], source[i + pixel_stride_in], source[i + 2 * pixel_stride_in]); } } -template -class ProcessTransformed final : public ProcessLine +template +void transform_triplet_to_line(const triplet* source, const size_t pixel_stride_in, PixelType* destination, + const size_t pixel_stride, Transform& transform) noexcept { -public: - ProcessTransformed(ByteStreamInfo rawStream, const JlsParameters& info, TRANSFORM transform) : - params_{info}, - tempLine_(static_cast(info.width) * info.components), - buffer_(static_cast(info.width) * info.components * sizeof(size_type)), - transform_{transform}, - inverseTransform_{transform}, - rawPixels_{rawStream} + const auto pixel_count{std::min(pixel_stride, pixel_stride_in)}; + const triplet* type_buffer_in{source}; + + for (size_t i{}; i < pixel_count; ++i) { + const triplet color = type_buffer_in[i]; + const triplet color_transformed = transform(color.v1, color.v2, color.v3); + + destination[i] = color_transformed.v1; + destination[i + pixel_stride] = color_transformed.v2; + destination[i + 2 * pixel_stride] = color_transformed.v3; } +} + + +template +void transform_triplet_to_line(const triplet* source, const size_t pixel_stride_in, PixelType* destination, + const size_t pixel_stride, Transform& transform, const uint32_t mask) noexcept +{ + const auto pixel_count{std::min(pixel_stride, pixel_stride_in)}; + const triplet* type_buffer_in{source}; - void NewLineRequested(void* dest, int pixelCount, int destStride) override + for (size_t i{}; i < pixel_count; ++i) { - if (!rawPixels_.rawStream) - { - Transform(rawPixels_.rawData, dest, pixelCount, destStride); - rawPixels_.rawData += params_.stride; - return; - } + const triplet color = type_buffer_in[i]; + const triplet color_transformed = transform(color.v1 & mask, color.v2 & mask, color.v3 & mask); - Transform(rawPixels_.rawStream, dest, pixelCount, destStride); + destination[i] = color_transformed.v1; + destination[i + pixel_stride] = color_transformed.v2; + destination[i + 2 * pixel_stride] = color_transformed.v3; } +} - void Transform(std::basic_streambuf* rawStream, void* destination, int pixelCount, int destinationStride) + +template +class process_transformed final : public process_line +{ +public: + process_transformed(byte_span source_pixels, const size_t stride, const frame_info& info, + const coding_parameters& parameters, TransformType transform) : + frame_info_{info}, + parameters_{parameters}, + stride_{stride}, + temp_line_(static_cast(info.component_count) * info.width), + buffer_(static_cast(info.component_count) * info.width * sizeof(size_type)), + transform_{transform}, + inverse_transform_{transform}, + raw_pixels_{source_pixels}, + mask_{(1U << info.bits_per_sample) - 1U} { - std::streamsize bytesToRead = static_cast(pixelCount) * params_.components * sizeof(size_type); - while (bytesToRead != 0) - { - const auto read = rawStream->sgetn(reinterpret_cast(buffer_.data()), bytesToRead); - if (read == 0) - throw jpegls_error{jpegls_errc::source_buffer_too_small}; + } - bytesToRead -= read; - } - Transform(buffer_.data(), destination, pixelCount, destinationStride); + void new_line_requested(void* destination, const size_t pixel_count, + const size_t destination_stride) noexcept(false) override + { + encode_transform(raw_pixels_.data, destination, pixel_count, destination_stride); + raw_pixels_.data += stride_; } - void Transform(const void* source, void* dest, int pixelCount, int destStride) noexcept + void encode_transform(const void* source, void* destination, const size_t pixel_count, const size_t destination_stride) noexcept { - if (params_.outputBgr) + if (parameters_.output_bgr) { - memcpy(tempLine_.data(), source, sizeof(Triplet) * pixelCount); - TransformRgbToBgr(tempLine_.data(), params_.components, pixelCount); - source = tempLine_.data(); + memcpy(temp_line_.data(), source, sizeof(triplet) * pixel_count); + transform_rgb_to_bgr(temp_line_.data(), frame_info_.component_count, pixel_count); + source = temp_line_.data(); } - if (params_.components == 3) + if (frame_info_.component_count == 3) { - if (params_.interleaveMode == interleave_mode::sample) + if (parameters_.interleave_mode == interleave_mode::sample) { - TransformLine(static_cast*>(dest), static_cast*>(source), pixelCount, transform_); + transform_line(static_cast*>(destination), static_cast*>(source), + pixel_count, transform_, mask_); } else { - TransformTripletToLine(static_cast*>(source), pixelCount, static_cast(dest), destStride, transform_); + transform_triplet_to_line(static_cast*>(source), pixel_count, + static_cast(destination), destination_stride, transform_, mask_); } } - else if (params_.components == 4) + else if (frame_info_.component_count == 4) { - if (params_.interleaveMode == interleave_mode::sample) + if (parameters_.interleave_mode == interleave_mode::sample) { - TransformLine(static_cast*>(dest), static_cast*>(source), pixelCount, transform_); + transform_line(static_cast*>(destination), static_cast*>(source), + pixel_count, transform_, mask_); } - else if (params_.interleaveMode == interleave_mode::line) + else if (parameters_.interleave_mode == interleave_mode::line) { - TransformQuadToLine(static_cast*>(source), pixelCount, static_cast(dest), destStride, transform_); + transform_quad_to_line(static_cast*>(source), pixel_count, + static_cast(destination), destination_stride, transform_, mask_); } } } - void DecodeTransform(const void* pSrc, void* rawData, int pixelCount, int byteStride) noexcept + void decode_transform(const void* source, void* destination, const size_t pixel_count, const size_t byte_stride) noexcept { - if (params_.components == 3) + if (frame_info_.component_count == 3) { - if (params_.interleaveMode == interleave_mode::sample) + if (parameters_.interleave_mode == interleave_mode::sample) { - TransformLine(static_cast*>(rawData), static_cast*>(pSrc), pixelCount, inverseTransform_); + transform_line(static_cast*>(destination), static_cast*>(source), + pixel_count, inverse_transform_); } else { - TransformLineToTriplet(static_cast(pSrc), byteStride, static_cast*>(rawData), pixelCount, inverseTransform_); + transform_line_to_triplet(static_cast(source), byte_stride, + static_cast*>(destination), pixel_count, inverse_transform_); } } - else if (params_.components == 4) + else if (frame_info_.component_count == 4) { - if (params_.interleaveMode == interleave_mode::sample) + if (parameters_.interleave_mode == interleave_mode::sample) { - TransformLine(static_cast*>(rawData), static_cast*>(pSrc), pixelCount, inverseTransform_); + transform_line(static_cast*>(destination), static_cast*>(source), + pixel_count, inverse_transform_); } - else if (params_.interleaveMode == interleave_mode::line) + else if (parameters_.interleave_mode == interleave_mode::line) { - TransformLineToQuad(static_cast(pSrc), byteStride, static_cast*>(rawData), pixelCount, inverseTransform_); + transform_line_to_quad(static_cast(source), byte_stride, + static_cast*>(destination), pixel_count, inverse_transform_); } } - if (params_.outputBgr) + if (parameters_.output_bgr) { - TransformRgbToBgr(static_cast(rawData), params_.components, pixelCount); + transform_rgb_to_bgr(static_cast(destination), frame_info_.component_count, pixel_count); } } - void NewLineDecoded(const void* pSrc, int pixelCount, int sourceStride) override + void new_line_decoded(const void* source, const size_t pixel_count, const size_t source_stride) noexcept(false) override { - if (rawPixels_.rawStream) - { - const std::streamsize bytesToWrite = static_cast(pixelCount) * params_.components * sizeof(size_type); - DecodeTransform(pSrc, buffer_.data(), pixelCount, sourceStride); - - const auto bytesWritten = rawPixels_.rawStream->sputn(reinterpret_cast(buffer_.data()), bytesToWrite); - if (bytesWritten != bytesToWrite) - throw jpegls_error{jpegls_errc::destination_buffer_too_small}; - } - else - { - DecodeTransform(pSrc, rawPixels_.rawData, pixelCount, sourceStride); - rawPixels_.rawData += params_.stride; - } + decode_transform(source, raw_pixels_.data, pixel_count, source_stride); + raw_pixels_.data += stride_; } private: - using size_type = typename TRANSFORM::size_type; + using size_type = typename TransformType::size_type; - const JlsParameters& params_; - std::vector tempLine_; + const frame_info& frame_info_; + const coding_parameters& parameters_; + const size_t stride_; + std::vector temp_line_; std::vector buffer_; - TRANSFORM transform_; - typename TRANSFORM::Inverse inverseTransform_; - ByteStreamInfo rawPixels_; + TransformType transform_; + typename TransformType::inverse inverse_transform_; + byte_span raw_pixels_; + uint32_t mask_; }; } // namespace charls diff -Nru charls-2.1.0+dfsg/src/scan.h charls-2.2.0+dfsg/src/scan.h --- charls-2.1.0+dfsg/src/scan.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/scan.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,7 +3,9 @@ #pragma once +#include "coding_parameters.h" #include "color_transform.h" +#include "constants.h" #include "context.h" #include "context_run_mode.h" #include "lookup_table.h" @@ -13,16 +15,25 @@ #include // This file contains the code for handling a "scan". Usually an image is encoded as a single scan. +// Note: the functions in this header could be moved into jpegls.cpp as they are only used in that file. namespace charls { -extern CTable decodingTables[16]; -extern std::vector rgquant8Ll; -extern std::vector rgquant10Ll; -extern std::vector rgquant12Ll; -extern std::vector rgquant16Ll; +class decoder_strategy; +class encoder_strategy; -constexpr int32_t ApplySign(int32_t i, int32_t sign) noexcept +extern const std::array decoding_tables; +extern const std::vector quantization_lut_lossless_8; +extern const std::vector quantization_lut_lossless_10; +extern const std::vector quantization_lut_lossless_12; +extern const std::vector quantization_lut_lossless_16; + +// Used to determine how large runs should be encoded at a time. Defined by the JPEG-LS standard, A.2.1., Initialization +// step 3. +constexpr std::array J{0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + +constexpr int32_t apply_sign(const int32_t i, const int32_t sign) noexcept { return (sign ^ i) - sign; } @@ -32,7 +43,7 @@ #if 0 -inline int32_t GetPredictedValue(int32_t Ra, int32_t Rb, int32_t Rc) +inline int32_t get_predicted_value(int32_t Ra, int32_t Rb, int32_t Rc) { if (Ra < Rb) { @@ -56,858 +67,817 @@ #else -inline int32_t GetPredictedValue(int32_t Ra, int32_t Rb, int32_t Rc) noexcept +inline int32_t get_predicted_value(const int32_t ra, const int32_t rb, const int32_t rc) noexcept { // sign trick reduces the number of if statements (branches) - const int32_t sgn = BitWiseSign(Rb - Ra); + const int32_t sign{bit_wise_sign(rb - ra)}; // is Ra between Rc and Rb? - if ((sgn ^ (Rc - Ra)) < 0) + if ((sign ^ (rc - ra)) < 0) { - return Rb; + return rb; } - if ((sgn ^ (Rb - Rc)) < 0) + if ((sign ^ (rb - rc)) < 0) { - return Ra; + return ra; } // default case, valid if Rc element of [Ra,Rb] - return Ra + Rb - Rc; + return ra + rb - rc; } #endif -constexpr int32_t UnMapErrVal(int32_t mappedError) noexcept +/// +/// This is the optimized inverse algorithm of ISO/IEC 14495-1, A.5.2, Code Segment A.11 (second else branch) +/// It will map unsigned values back to signed values. +/// +CONSTEXPR int32_t unmap_error_value(const int32_t mapped_error) noexcept { - const int32_t sign = mappedError << (int32_t_bit_count - 1) >> (int32_t_bit_count - 1); - return sign ^ (mappedError >> 1); + const int32_t sign{static_cast(static_cast(mapped_error) << (int32_t_bit_count - 1)) >> + (int32_t_bit_count - 1)}; + return sign ^ (mapped_error >> 1); } -constexpr int32_t GetMappedErrVal(int32_t errorValue) noexcept +/// +/// This is the algorithm of ISO/IEC 14495-1, A.5.2, Code Segment A.11 (second else branch) +/// It will map signed values to unsigned values. It has been optimized to prevent branching. +/// +CONSTEXPR int32_t map_error_value(const int32_t error_value) noexcept { - const int32_t mappedError = (errorValue >> (int32_t_bit_count - 2)) ^ (2 * errorValue); - return mappedError; + ASSERT(error_value <= INT32_MAX / 2); + + const int32_t mapped_error{(error_value >> (int32_t_bit_count - 2)) ^ (2 * error_value)}; + return mapped_error; } -constexpr int32_t ComputeContextID(int32_t Q1, int32_t Q2, int32_t Q3) noexcept +constexpr int32_t compute_context_id(const int32_t q1, const int32_t q2, const int32_t q3) noexcept { - return (Q1 * 9 + Q2) * 9 + Q3; + return (q1 * 9 + q2) * 9 + q3; } template -class JlsCodec final : public Strategy +class jls_codec final : public Strategy { public: - using PIXEL = typename Traits::PIXEL; - using SAMPLE = typename Traits::SAMPLE; + using pixel_type = typename Traits::pixel_type; + using sample_type = typename Traits::sample_type; - JlsCodec(Traits inTraits, const JlsParameters& params) : - Strategy{params}, - traits{std::move(inTraits)}, - width_{params.width} + jls_codec(Traits traits, const frame_info& frame_info, const coding_parameters& parameters) noexcept : + Strategy{update_component_count(frame_info, parameters), parameters}, + traits_{std::move(traits)}, + width_{frame_info.width} { - if (Info().interleaveMode == interleave_mode::none) - { - Info().components = 1; - } + ASSERT((parameters.interleave_mode == interleave_mode::none && this->frame_info().component_count == 1) || + parameters.interleave_mode != interleave_mode::none); } - void SetPresets(const jpegls_pc_parameters& presets) override + // Factory function for ProcessLine objects to copy/transform un encoded pixels to/from our scan line buffers. + std::unique_ptr create_process_line(byte_span info, const size_t stride) override { - const jpegls_pc_parameters presetDefault{compute_default(traits.MAXVAL, traits.NEAR)}; - - InitParams(presets.threshold1 != 0 ? presets.threshold1 : presetDefault.threshold1, - presets.threshold2 != 0 ? presets.threshold2 : presetDefault.threshold2, - presets.threshold3 != 0 ? presets.threshold3 : presetDefault.threshold3, - presets.reset_value != 0 ? presets.reset_value : presetDefault.reset_value); - } + if (!is_interleaved()) + { + if (frame_info().bits_per_sample == sizeof(sample_type) * 8) + { + return std::make_unique(info.data, stride, + sizeof(typename Traits::pixel_type)); + } - std::unique_ptr CreateProcess(ByteStreamInfo info) override; + return std::make_unique( + info.data, stride, sizeof(typename Traits::pixel_type), frame_info().bits_per_sample); + } - bool IsInterleaved() noexcept - { - if (Info().interleaveMode == interleave_mode::none) - return false; + if (parameters().transformation == color_transformation::none) + return std::make_unique>>( + info, stride, frame_info(), parameters(), transform_none()); - if (Info().components == 1) - return false; + if (frame_info().bits_per_sample == sizeof(sample_type) * 8) + { + switch (parameters().transformation) + { + case color_transformation::hp1: + return std::make_unique>>( + info, stride, frame_info(), parameters(), transform_hp1()); + case color_transformation::hp2: + return std::make_unique>>( + info, stride, frame_info(), parameters(), transform_hp2()); + case color_transformation::hp3: + return std::make_unique>>( + info, stride, frame_info(), parameters(), transform_hp3()); + default: + impl::throw_jpegls_error(jpegls_errc::color_transform_not_supported); + } + } - return true; + impl::throw_jpegls_error(jpegls_errc::bit_depth_for_transform_not_supported); } - JlsParameters& Info() noexcept +private: + void set_presets(const jpegls_pc_parameters& presets) override { - return Strategy::params_; - } - - signed char QuantizeGradientOrg(int32_t Di) const noexcept; + const jpegls_pc_parameters preset_default{compute_default(traits_.maximum_sample_value, traits_.near_lossless)}; - FORCE_INLINE int32_t QuantizeGradient(int32_t Di) const noexcept - { - ASSERT(QuantizeGradientOrg(Di) == *(pquant_ + Di)); - return *(pquant_ + Di); + initialize_parameters(presets.threshold1 != 0 ? presets.threshold1 : preset_default.threshold1, + presets.threshold2 != 0 ? presets.threshold2 : preset_default.threshold2, + presets.threshold3 != 0 ? presets.threshold3 : preset_default.threshold3, + presets.reset_value != 0 ? presets.reset_value : preset_default.reset_value); } - void InitQuantizationLUT(); + bool is_interleaved() noexcept + { + ASSERT((parameters().interleave_mode == interleave_mode::none && frame_info().component_count == 1) || + parameters().interleave_mode != interleave_mode::none); - int32_t DecodeValue(int32_t k, int32_t limit, int32_t qbpp); - FORCE_INLINE void EncodeMappedValue(int32_t k, int32_t mappedError, int32_t limit); + return parameters().interleave_mode != interleave_mode::none; + } - void IncrementRunIndex() noexcept + const coding_parameters& parameters() const noexcept { - RUNindex_ = std::min(31, RUNindex_ + 1); + return Strategy::parameters_; } - void DecrementRunIndex() noexcept + const charls::frame_info& frame_info() const noexcept { - RUNindex_ = std::max(0, RUNindex_ - 1); + return Strategy::frame_info_; } - int32_t DecodeRIError(CContextRunMode& ctx); - Triplet DecodeRIPixel(Triplet Ra, Triplet Rb); - Quad DecodeRIPixel(Quad Ra, Quad Rb); - SAMPLE DecodeRIPixel(int32_t Ra, int32_t Rb); - int32_t DecodeRunPixels(PIXEL Ra, PIXEL* startPos, int32_t cpixelMac); - int32_t DoRunMode(int32_t startIndex, DecoderStrategy*); - - void EncodeRIError(CContextRunMode& ctx, int32_t errorValue); - SAMPLE EncodeRIPixel(int32_t x, int32_t Ra, int32_t Rb); - Triplet EncodeRIPixel(Triplet x, Triplet Ra, Triplet Rb); - Quad EncodeRIPixel(Quad x, Quad Ra, Quad Rb); - void EncodeRunPixels(int32_t runLength, bool endOfLine); - int32_t DoRunMode(int32_t index, EncoderStrategy*); - - FORCE_INLINE SAMPLE DoRegular(int32_t Qs, int32_t, int32_t pred, DecoderStrategy*); - FORCE_INLINE SAMPLE DoRegular(int32_t Qs, int32_t x, int32_t pred, EncoderStrategy*); - - void DoLine(SAMPLE* dummy); - void DoLine(Triplet* dummy); - void DoLine(Quad* dummy); - void DoScan(); - - void InitParams(int32_t t1, int32_t t2, int32_t t3, int32_t nReset); - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Winconsistent-missing-override" -#endif - - // Note: depending on the base class EncodeScan OR DecodeScan will be virtual and abstract, cannot use override in all cases. - size_t EncodeScan(std::unique_ptr processLine, ByteStreamInfo& compressedData); - void DecodeScan(std::unique_ptr processLine, const JlsRect& rect, ByteStreamInfo& compressedData); + int8_t quantize_gradient_org(const int32_t di) const noexcept + { + if (di <= -t3_) + return -4; + if (di <= -t2_) + return -3; + if (di <= -t1_) + return -2; + if (di < -traits_.near_lossless) + return -1; + if (di <= traits_.near_lossless) + return 0; + if (di < t1_) + return 1; + if (di < t2_) + return 2; + if (di < t3_) + return 3; -#if defined(__clang__) -#pragma clang diagnostic pop -#endif + return 4; + } -protected: - // codec parameters - Traits traits; - JlsRect rect_{}; - int width_; - int32_t T1{}; - int32_t T2{}; - int32_t T3{}; + FORCE_INLINE int32_t quantize_gradient(const int32_t di) const noexcept + { + ASSERT(quantize_gradient_org(di) == *(quantization_ + di)); + return *(quantization_ + di); + } - // compression context - std::array contexts_; - std::array contextRunmode_; - int32_t RUNindex_{}; - PIXEL* previousLine_{}; - PIXEL* currentLine_{}; + // C4127 = conditional expression is constant (caused by some template methods that are not fully specialized) [VS2017] + // C6326 = Potential comparison of a constant with another constant. (false warning, triggered by template construction + // in Checked build) C26814 = The const variable 'RANGE' can be computed at compile-time. [incorrect warning, VS 16.3.0 + // P3] + MSVC_WARNING_SUPPRESS(4127 6326 26814) - // quantization lookup table - signed char* pquant_{}; - std::vector rgquant_; -}; + void initialize_quantization_lut() + { + // for lossless mode with default parameters, we have precomputed the look up table for bit counts 8, 10, 12 and 16. + if (traits_.near_lossless == 0 && traits_.maximum_sample_value == (1 << traits_.bits_per_pixel) - 1) + { + const jpegls_pc_parameters presets{compute_default(traits_.maximum_sample_value, traits_.near_lossless)}; + if (presets.threshold1 == t1_ && presets.threshold2 == t2_ && presets.threshold3 == t3_) + { + if (traits_.bits_per_pixel == 8) + { + quantization_ = &quantization_lut_lossless_8[quantization_lut_lossless_8.size() / 2]; + return; + } + if (traits_.bits_per_pixel == 10) + { + quantization_ = &quantization_lut_lossless_10[quantization_lut_lossless_10.size() / 2]; + return; + } + if (traits_.bits_per_pixel == 12) + { + quantization_ = &quantization_lut_lossless_12[quantization_lut_lossless_12.size() / 2]; + return; + } + if (traits_.bits_per_pixel == 16) + { + quantization_ = &quantization_lut_lossless_16[quantization_lut_lossless_16.size() / 2]; + return; + } + } + } + // Initialize the quantization lookup table dynamic. + const int32_t range{1 << traits_.bits_per_pixel}; + quantization_lut_.resize(static_cast(range) * 2); + for (size_t i{}; i < quantization_lut_.size(); ++i) + { + quantization_lut_[i] = quantize_gradient_org(-range + static_cast(i)); + } -// Encode/decode a single sample. Performance wise the #1 important functions -template -typename Traits::SAMPLE JlsCodec::DoRegular(int32_t Qs, int32_t, int32_t pred, DecoderStrategy*) -{ - const int32_t sign = BitWiseSign(Qs); - JlsContext& ctx = contexts_[ApplySign(Qs, sign)]; - const int32_t k = ctx.GetGolomb(); - const int32_t Px = traits.CorrectPrediction(pred + ApplySign(ctx.C, sign)); - - int32_t ErrVal; - const Code& code = decodingTables[k].Get(Strategy::PeekByte()); - if (code.GetLength() != 0) - { - Strategy::Skip(code.GetLength()); - ErrVal = code.GetValue(); - ASSERT(std::abs(ErrVal) < 65535); - } - else - { - ErrVal = UnMapErrVal(DecodeValue(k, traits.LIMIT, traits.qbpp)); - if (std::abs(ErrVal) > 65535) - throw jpegls_error{jpegls_errc::invalid_encoded_data}; + quantization_ = &quantization_lut_[range]; } - if (k == 0) + MSVC_WARNING_UNSUPPRESS() + + int32_t decode_value(const int32_t k, const int32_t limit, const int32_t quantized_bits_per_pixel) { - ErrVal = ErrVal ^ ctx.GetErrorCorrection(traits.NEAR); - } - ctx.UpdateVariables(ErrVal, traits.NEAR, traits.RESET); - ErrVal = ApplySign(ErrVal, sign); - return traits.ComputeReconstructedSample(Px, ErrVal); -} + const int32_t high_bits{Strategy::read_high_bits()}; + if (high_bits >= limit - (quantized_bits_per_pixel + 1)) + return Strategy::read_value(quantized_bits_per_pixel) + 1; -template -typename Traits::SAMPLE JlsCodec::DoRegular(int32_t Qs, int32_t x, int32_t pred, EncoderStrategy*) -{ - const int32_t sign = BitWiseSign(Qs); - JlsContext& ctx = contexts_[ApplySign(Qs, sign)]; - const int32_t k = ctx.GetGolomb(); - const int32_t Px = traits.CorrectPrediction(pred + ApplySign(ctx.C, sign)); - const int32_t ErrVal = traits.ComputeErrVal(ApplySign(x - Px, sign)); - - EncodeMappedValue(k, GetMappedErrVal(ctx.GetErrorCorrection(k | traits.NEAR) ^ ErrVal), traits.LIMIT); - ctx.UpdateVariables(ErrVal, traits.NEAR, traits.RESET); - ASSERT(traits.IsNear(traits.ComputeReconstructedSample(Px, ApplySign(ErrVal, sign)), x)); - return static_cast(traits.ComputeReconstructedSample(Px, ApplySign(ErrVal, sign))); -} + if (k == 0) + return high_bits; + return (high_bits << k) + Strategy::read_value(k); + } -// Functions to build tables used to decode short Golomb codes. + FORCE_INLINE void encode_mapped_value(const int32_t k, const int32_t mapped_error, const int32_t limit) + { + int32_t high_bits{mapped_error >> k}; -inline std::pair CreateEncodedValue(int32_t k, int32_t mappedError) noexcept -{ - const int32_t highBits = mappedError >> k; - return std::make_pair(highBits + k + 1, (1 << k) | (mappedError & ((1 << k) - 1))); -} + if (high_bits < limit - traits_.quantized_bits_per_pixel - 1) + { + if (high_bits + 1 > 31) + { + Strategy::append_to_bit_stream(0, high_bits / 2); + high_bits = high_bits - high_bits / 2; + } + Strategy::append_to_bit_stream(1, high_bits + 1); + Strategy::append_to_bit_stream((mapped_error & ((1 << k) - 1)), k); + return; + } + if (limit - traits_.quantized_bits_per_pixel > 31) + { + Strategy::append_to_bit_stream(0, 31); + Strategy::append_to_bit_stream(1, limit - traits_.quantized_bits_per_pixel - 31); + } + else + { + Strategy::append_to_bit_stream(1, limit - traits_.quantized_bits_per_pixel); + } + Strategy::append_to_bit_stream((mapped_error - 1) & ((1 << traits_.quantized_bits_per_pixel) - 1), + traits_.quantized_bits_per_pixel); + } -inline CTable InitTable(int32_t k) noexcept -{ - CTable table; - for (short nerr = 0;; nerr++) + void increment_run_index() noexcept { - // Q is not used when k != 0 - const int32_t merrval = GetMappedErrVal(nerr); - const std::pair pairCode = CreateEncodedValue(k, merrval); - if (static_cast(pairCode.first) > CTable::byte_bit_count) - break; - - const Code code(nerr, static_cast(pairCode.first)); - table.AddEntry(static_cast(pairCode.second), code); + run_index_ = std::min(31, run_index_ + 1); } - for (short nerr = -1;; nerr--) + void decrement_run_index() noexcept { - // Q is not used when k != 0 - const int32_t merrval = GetMappedErrVal(nerr); - const std::pair pairCode = CreateEncodedValue(k, merrval); - if (static_cast(pairCode.first) > CTable::byte_bit_count) - break; - - const Code code = Code(nerr, static_cast(pairCode.first)); - table.AddEntry(static_cast(pairCode.second), code); + run_index_ = std::max(0, run_index_ - 1); } - return table; -} - - -// Encoding/decoding of Golomb codes - -template -int32_t JlsCodec::DecodeValue(int32_t k, int32_t limit, int32_t qbpp) -{ - const int32_t highBits = Strategy::ReadHighBits(); - - if (highBits >= limit - (qbpp + 1)) - return Strategy::ReadValue(qbpp) + 1; - - if (k == 0) - return highBits; - - return (highBits << k) + Strategy::ReadValue(k); -} - - -template -FORCE_INLINE void JlsCodec::EncodeMappedValue(int32_t k, int32_t mappedError, int32_t limit) -{ - int32_t highBits = mappedError >> k; - - if (highBits < limit - traits.qbpp - 1) + FORCE_INLINE sample_type do_regular(const int32_t qs, int32_t, const int32_t predicted, decoder_strategy*) { - if (highBits + 1 > 31) + const int32_t sign = bit_wise_sign(qs); + jls_context& context = contexts_[apply_sign(qs, sign)]; + const int32_t k = context.get_golomb_coding_parameter(); + const int32_t predicted_value = traits_.correct_prediction(predicted + apply_sign(context.C, sign)); + + int32_t error_value; + const golomb_code& code = decoding_tables[k].get(Strategy::peek_byte()); + if (code.length() != 0) + { + Strategy::skip(code.length()); + error_value = code.value(); + ASSERT(std::abs(error_value) < 65535); + } + else { - Strategy::AppendToBitStream(0, highBits / 2); - highBits = highBits - highBits / 2; + error_value = unmap_error_value(decode_value(k, traits_.limit, traits_.quantized_bits_per_pixel)); + if (std::abs(error_value) > 65535) + impl::throw_jpegls_error(jpegls_errc::invalid_encoded_data); } - Strategy::AppendToBitStream(1, highBits + 1); - Strategy::AppendToBitStream((mappedError & ((1 << k) - 1)), k); - return; + if (k == 0) + { + error_value = error_value ^ context.get_error_correction(traits_.near_lossless); + } + context.update_variables(error_value, traits_.near_lossless, traits_.reset_threshold); + error_value = apply_sign(error_value, sign); + return traits_.compute_reconstructed_sample(predicted_value, error_value); } - if (limit - traits.qbpp > 31) - { - Strategy::AppendToBitStream(0, 31); - Strategy::AppendToBitStream(1, limit - traits.qbpp - 31); - } - else + FORCE_INLINE sample_type do_regular(const int32_t qs, const int32_t x, const int32_t predicted, encoder_strategy*) { - Strategy::AppendToBitStream(1, limit - traits.qbpp); + const int32_t sign{bit_wise_sign(qs)}; + jls_context& context{contexts_[apply_sign(qs, sign)]}; + const int32_t k{context.get_golomb_coding_parameter()}; + const int32_t predicted_value{traits_.correct_prediction(predicted + apply_sign(context.C, sign))}; + const int32_t error_value{traits_.compute_error_value(apply_sign(x - predicted_value, sign))}; + + encode_mapped_value(k, map_error_value(context.get_error_correction(k | traits_.near_lossless) ^ error_value), + traits_.limit); + context.update_variables(error_value, traits_.near_lossless, traits_.reset_threshold); + ASSERT(traits_.is_near(traits_.compute_reconstructed_sample(predicted_value, apply_sign(error_value, sign)), x)); + return static_cast( + traits_.compute_reconstructed_sample(predicted_value, apply_sign(error_value, sign))); } - Strategy::AppendToBitStream((mappedError - 1) & ((1 << traits.qbpp) - 1), traits.qbpp); -} + /// Encodes/Decodes a scan line of samples + void do_line(sample_type*) + { + int32_t index{}; + int32_t rb{previous_line_[index - 1]}; + int32_t rd{previous_line_[index]}; -// C4127 = conditional expression is constant (caused by some template methods that are not fully specialized) [VS2017] -// 6326 = Potential comparison of a constant with another constant. (false warning, triggered by template construction in Checked build) -// 26814 = The const variable 'RANGE' can be computed at compile-time. [incorrect warning, VS 16.3.0 P3] -MSVC_WARNING_SUPPRESS(4127 6326 26814) + while (static_cast(index) < width_) + { + const int32_t ra{current_line_[index - 1]}; + const int32_t rc{rb}; + rb = rd; + rd = previous_line_[index + 1]; -// Sets up a lookup table to "Quantize" sample difference. + const int32_t qs{ + compute_context_id(quantize_gradient(rd - rb), quantize_gradient(rb - rc), quantize_gradient(rc - ra))}; -template -void JlsCodec::InitQuantizationLUT() -{ - // for lossless mode with default parameters, we have precomputed the look up table for bit counts 8, 10, 12 and 16. - if (traits.NEAR == 0 && traits.MAXVAL == (1 << traits.bpp) - 1) - { - const jpegls_pc_parameters presets{compute_default(traits.MAXVAL, traits.NEAR)}; - if (presets.threshold1 == T1 && presets.threshold2 == T2 && presets.threshold3 == T3) - { - if (traits.bpp == 8) + if (qs != 0) { - pquant_ = &rgquant8Ll[rgquant8Ll.size() / 2]; - return; + current_line_[index] = + do_regular(qs, current_line_[index], get_predicted_value(ra, rb, rc), static_cast(nullptr)); + ++index; } - if (traits.bpp == 10) + else { - pquant_ = &rgquant10Ll[rgquant10Ll.size() / 2]; - return; + index += do_run_mode(index, static_cast(nullptr)); + rb = previous_line_[index - 1]; + rd = previous_line_[index]; } - if (traits.bpp == 12) + } + } + + /// Encodes/Decodes a scan line of triplets in ILV_SAMPLE mode + void do_line(triplet*) + { + int32_t index{}; + while (static_cast(index) < width_) + { + const triplet ra{current_line_[index - 1]}; + const triplet rc{previous_line_[index - 1]}; + const triplet rb{previous_line_[index]}; + const triplet rd{previous_line_[index + 1]}; + + const int32_t qs1{compute_context_id(quantize_gradient(rd.v1 - rb.v1), quantize_gradient(rb.v1 - rc.v1), + quantize_gradient(rc.v1 - ra.v1))}; + const int32_t qs2{compute_context_id(quantize_gradient(rd.v2 - rb.v2), quantize_gradient(rb.v2 - rc.v2), + quantize_gradient(rc.v2 - ra.v2))}; + const int32_t qs3{compute_context_id(quantize_gradient(rd.v3 - rb.v3), quantize_gradient(rb.v3 - rc.v3), + quantize_gradient(rc.v3 - ra.v3))}; + + if (qs1 == 0 && qs2 == 0 && qs3 == 0) { - pquant_ = &rgquant12Ll[rgquant12Ll.size() / 2]; - return; + index += do_run_mode(index, static_cast(nullptr)); } - if (traits.bpp == 16) + else { - pquant_ = &rgquant16Ll[rgquant16Ll.size() / 2]; - return; + triplet rx; + rx.v1 = do_regular(qs1, current_line_[index].v1, get_predicted_value(ra.v1, rb.v1, rc.v1), + static_cast(nullptr)); + rx.v2 = do_regular(qs2, current_line_[index].v2, get_predicted_value(ra.v2, rb.v2, rc.v2), + static_cast(nullptr)); + rx.v3 = do_regular(qs3, current_line_[index].v3, get_predicted_value(ra.v3, rb.v3, rc.v3), + static_cast(nullptr)); + current_line_[index] = rx; + ++index; } } } - const int32_t RANGE = 1 << traits.bpp; + // Setup codec for encoding and calls do_scan +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winconsistent-missing-override" +#endif - rgquant_.resize(static_cast(RANGE) * 2); + MSVC_WARNING_SUPPRESS(26433) // C.128: Virtual functions should specify exactly one of virtual, override, or final - pquant_ = &rgquant_[RANGE]; - for (int32_t i = -RANGE; i < RANGE; ++i) + // Note: depending on the base class encode_scan OR decode_scan will be virtual and abstract, + // cannot use override in all cases. + // clang-format off + // NOLINTNEXTLINE(cppcoreguidelines-explicit-virtual-functions, hicpp-use-override, modernize-use-override,clang-diagnostic-suggest-override) + size_t encode_scan(std::unique_ptr process_line, byte_span destination) { - pquant_[i] = QuantizeGradientOrg(i); - } -} + Strategy::process_line_ = std::move(process_line); -MSVC_WARNING_UNSUPPRESS() - -template -signed char JlsCodec::QuantizeGradientOrg(int32_t Di) const noexcept -{ - if (Di <= -T3) return -4; - if (Di <= -T2) return -3; - if (Di <= -T1) return -2; - if (Di < -traits.NEAR) return -1; - if (Di <= traits.NEAR) return 0; - if (Di < T1) return 1; - if (Di < T2) return 2; - if (Di < T3) return 3; - - return 4; -} + Strategy::initialize(destination); + do_scan(); + return Strategy::get_length(); + } -// RI = Run interruption: functions that handle the sample terminating a run. + // NOLINTNEXTLINE(cppcoreguidelines-explicit-virtual-functions, hicpp-use-override, modernize-use-override, clang-diagnostic-suggest-override) + void decode_scan(std::unique_ptr process_line, const JlsRect& rect, byte_span& compressed_data) + { + Strategy::process_line_ = std::move(process_line); -template -int32_t JlsCodec::DecodeRIError(CContextRunMode& ctx) -{ - const int32_t k = ctx.GetGolomb(); - const int32_t EMErrval = DecodeValue(k, traits.LIMIT - J[RUNindex_] - 1, traits.qbpp); - const int32_t errorValue = ctx.ComputeErrVal(EMErrval + ctx.nRItype_, k); - ctx.UpdateVariables(errorValue, EMErrval); - return errorValue; -} + const uint8_t* compressed_bytes{compressed_data.data}; + rect_ = rect; + Strategy::initialize(compressed_data); + do_scan(); + skip_bytes(compressed_data, static_cast(Strategy::get_cur_byte_pos() - compressed_bytes)); + } -template -void JlsCodec::EncodeRIError(CContextRunMode& ctx, int32_t errorValue) -{ - const int32_t k = ctx.GetGolomb(); - const bool map = ctx.ComputeMap(errorValue, k); - const int32_t EMErrval = 2 * std::abs(errorValue) - ctx.nRItype_ - static_cast(map); - - ASSERT(errorValue == ctx.ComputeErrVal(EMErrval + ctx.nRItype_, k)); - EncodeMappedValue(k, EMErrval, traits.LIMIT - J[RUNindex_] - 1); - ctx.UpdateVariables(errorValue, EMErrval); -} + // clang-format on + MSVC_WARNING_UNSUPPRESS() +#if defined(__clang__) +#pragma clang diagnostic pop +#endif -template -Triplet JlsCodec::DecodeRIPixel(Triplet Ra, Triplet Rb) -{ - const int32_t errorValue1 = DecodeRIError(contextRunmode_[0]); - const int32_t errorValue2 = DecodeRIError(contextRunmode_[0]); - const int32_t errorValue3 = DecodeRIError(contextRunmode_[0]); - - return Triplet(traits.ComputeReconstructedSample(Rb.v1, errorValue1 * Sign(Rb.v1 - Ra.v1)), - traits.ComputeReconstructedSample(Rb.v2, errorValue2 * Sign(Rb.v2 - Ra.v2)), - traits.ComputeReconstructedSample(Rb.v3, errorValue3 * Sign(Rb.v3 - Ra.v3))); -} + void initialize_parameters(const int32_t t1, const int32_t t2, const int32_t t3, const int32_t reset_threshold) + { + t1_ = t1; + t2_ = t2; + t3_ = t3; + initialize_quantization_lut(); -template -Triplet JlsCodec::EncodeRIPixel(Triplet x, Triplet Ra, Triplet Rb) -{ - const int32_t errorValue1 = traits.ComputeErrVal(Sign(Rb.v1 - Ra.v1) * (x.v1 - Rb.v1)); - EncodeRIError(contextRunmode_[0], errorValue1); + const jls_context context_initial_value(std::max(2, (traits_.range + 32) / 64)); + for (auto& context : contexts_) + { + context = context_initial_value; + } - const int32_t errorValue2 = traits.ComputeErrVal(Sign(Rb.v2 - Ra.v2) * (x.v2 - Rb.v2)); - EncodeRIError(contextRunmode_[0], errorValue2); + context_runmode_[0] = context_run_mode(0, std::max(2, (traits_.range + 32) / 64), reset_threshold); + context_runmode_[1] = context_run_mode(1, std::max(2, (traits_.range + 32) / 64), reset_threshold); + run_index_ = 0; + } - const int32_t errorValue3 = traits.ComputeErrVal(Sign(Rb.v3 - Ra.v3) * (x.v3 - Rb.v3)); - EncodeRIError(contextRunmode_[0], errorValue3); + static charls::frame_info update_component_count(charls::frame_info frame, const coding_parameters& parameters) noexcept + { + if (parameters.interleave_mode == interleave_mode::none) + { + frame.component_count = 1; + } - return Triplet(traits.ComputeReconstructedSample(Rb.v1, errorValue1 * Sign(Rb.v1 - Ra.v1)), - traits.ComputeReconstructedSample(Rb.v2, errorValue2 * Sign(Rb.v2 - Ra.v2)), - traits.ComputeReconstructedSample(Rb.v3, errorValue3 * Sign(Rb.v3 - Ra.v3))); -} + return frame; + } -template -Quad JlsCodec::DecodeRIPixel(Quad Ra, Quad Rb) -{ - const int32_t errorValue1 = DecodeRIError(contextRunmode_[0]); - const int32_t errorValue2 = DecodeRIError(contextRunmode_[0]); - const int32_t errorValue3 = DecodeRIError(contextRunmode_[0]); - const int32_t errorValue4 = DecodeRIError(contextRunmode_[0]); - - return Quad(Triplet(traits.ComputeReconstructedSample(Rb.v1, errorValue1 * Sign(Rb.v1 - Ra.v1)), - traits.ComputeReconstructedSample(Rb.v2, errorValue2 * Sign(Rb.v2 - Ra.v2)), - traits.ComputeReconstructedSample(Rb.v3, errorValue3 * Sign(Rb.v3 - Ra.v3))), - traits.ComputeReconstructedSample(Rb.v4, errorValue4 * Sign(Rb.v4 - Ra.v4))); -} + // do_scan: Encodes or decodes a scan. + // In ILV_SAMPLE mode, multiple components are handled in do_line + // In ILV_LINE mode, a call do do_line is made for every component + // In ILV_NONE mode, do_scan is called for each component + void do_scan() + { + const uint32_t pixel_stride{width_ + 4U}; + const size_t component_count{ + parameters().interleave_mode == interleave_mode::line ? static_cast(frame_info().component_count) : 1U}; + std::vector line_buffer(static_cast(2) * component_count * pixel_stride); + std::vector run_index(component_count); -template -Quad JlsCodec::EncodeRIPixel(Quad x, Quad Ra, Quad Rb) -{ - const int32_t errorValue1 = traits.ComputeErrVal(Sign(Rb.v1 - Ra.v1) * (x.v1 - Rb.v1)); - EncodeRIError(contextRunmode_[0], errorValue1); + for (uint32_t line{}; line < frame_info().height; ++line) + { + previous_line_ = &line_buffer[1]; + current_line_ = &line_buffer[1 + static_cast(component_count) * pixel_stride]; + if ((line & 1) == 1) + { + std::swap(previous_line_, current_line_); + } - const int32_t errorValue2 = traits.ComputeErrVal(Sign(Rb.v2 - Ra.v2) * (x.v2 - Rb.v2)); - EncodeRIError(contextRunmode_[0], errorValue2); + Strategy::on_line_begin(width_, current_line_, pixel_stride); - const int32_t errorValue3 = traits.ComputeErrVal(Sign(Rb.v3 - Ra.v3) * (x.v3 - Rb.v3)); - EncodeRIError(contextRunmode_[0], errorValue3); + for (size_t component{}; component < component_count; ++component) + { + run_index_ = run_index[component]; - const int32_t errorValue4 = traits.ComputeErrVal(Sign(Rb.v4 - Ra.v4) * (x.v4 - Rb.v4)); - EncodeRIError(contextRunmode_[0], errorValue4); - - return Quad(Triplet(traits.ComputeReconstructedSample(Rb.v1, errorValue1 * Sign(Rb.v1 - Ra.v1)), - traits.ComputeReconstructedSample(Rb.v2, errorValue2 * Sign(Rb.v2 - Ra.v2)), - traits.ComputeReconstructedSample(Rb.v3, errorValue3 * Sign(Rb.v3 - Ra.v3))), - traits.ComputeReconstructedSample(Rb.v4, errorValue4 * Sign(Rb.v4 - Ra.v4))); -} + // initialize edge pixels used for prediction + previous_line_[width_] = previous_line_[width_ - 1]; + current_line_[-1] = previous_line_[0]; + do_line(static_cast(nullptr)); // dummy argument for overload resolution + + run_index[component] = run_index_; + previous_line_ += pixel_stride; + current_line_ += pixel_stride; + } + if (static_cast(rect_.Y) <= line && line < static_cast(rect_.Y + rect_.Height)) + { + Strategy::on_line_end(rect_.Width, + current_line_ + rect_.X - (static_cast(component_count) * pixel_stride), + pixel_stride); + } + } -template -typename Traits::SAMPLE JlsCodec::DecodeRIPixel(int32_t Ra, int32_t Rb) -{ - if (std::abs(Ra - Rb) <= traits.NEAR) - { - const int32_t ErrVal = DecodeRIError(contextRunmode_[1]); - return static_cast(traits.ComputeReconstructedSample(Ra, ErrVal)); + Strategy::end_scan(); } - const int32_t ErrVal = DecodeRIError(contextRunmode_[0]); - return static_cast(traits.ComputeReconstructedSample(Rb, ErrVal * Sign(Rb - Ra))); -} + /// Encodes/Decodes a scan line of quads in ILV_SAMPLE mode + void do_line(quad*) + { + int32_t index{}; + while (static_cast(index) < width_) + { + const quad ra{current_line_[index - 1]}; + const quad rc{previous_line_[index - 1]}; + const quad rb{previous_line_[index]}; + const quad rd{previous_line_[index + 1]}; + + const int32_t qs1{compute_context_id(quantize_gradient(rd.v1 - rb.v1), quantize_gradient(rb.v1 - rc.v1), + quantize_gradient(rc.v1 - ra.v1))}; + const int32_t qs2{compute_context_id(quantize_gradient(rd.v2 - rb.v2), quantize_gradient(rb.v2 - rc.v2), + quantize_gradient(rc.v2 - ra.v2))}; + const int32_t qs3{compute_context_id(quantize_gradient(rd.v3 - rb.v3), quantize_gradient(rb.v3 - rc.v3), + quantize_gradient(rc.v3 - ra.v3))}; + const int32_t qs4{compute_context_id(quantize_gradient(rd.v4 - rb.v4), quantize_gradient(rb.v4 - rc.v4), + quantize_gradient(rc.v4 - ra.v4))}; + if (qs1 == 0 && qs2 == 0 && qs3 == 0 && qs4 == 0) + { + index += do_run_mode(index, static_cast(nullptr)); + } + else + { + quad rx; + rx.v1 = do_regular(qs1, current_line_[index].v1, get_predicted_value(ra.v1, rb.v1, rc.v1), + static_cast(nullptr)); + rx.v2 = do_regular(qs2, current_line_[index].v2, get_predicted_value(ra.v2, rb.v2, rc.v2), + static_cast(nullptr)); + rx.v3 = do_regular(qs3, current_line_[index].v3, get_predicted_value(ra.v3, rb.v3, rc.v3), + static_cast(nullptr)); + rx.v4 = do_regular(qs4, current_line_[index].v4, get_predicted_value(ra.v4, rb.v4, rc.v4), + static_cast(nullptr)); + current_line_[index] = rx; + ++index; + } + } + } -template -typename Traits::SAMPLE JlsCodec::EncodeRIPixel(int32_t x, int32_t Ra, int32_t Rb) -{ - if (std::abs(Ra - Rb) <= traits.NEAR) + int32_t decode_run_interruption_error(context_run_mode& context) { - const int32_t ErrVal = traits.ComputeErrVal(x - Ra); - EncodeRIError(contextRunmode_[1], ErrVal); - return static_cast(traits.ComputeReconstructedSample(Ra, ErrVal)); + const int32_t k{context.get_golomb_code()}; + const int32_t e_mapped_error_value{ + decode_value(k, traits_.limit - J[run_index_] - 1, traits_.quantized_bits_per_pixel)}; + const int32_t error_value{context.compute_error_value(e_mapped_error_value + context.run_interruption_type, k)}; + context.update_variables(error_value, e_mapped_error_value); + return error_value; } - const int32_t ErrVal = traits.ComputeErrVal((x - Rb) * Sign(Rb - Ra)); - EncodeRIError(contextRunmode_[0], ErrVal); - return static_cast(traits.ComputeReconstructedSample(Rb, ErrVal * Sign(Rb - Ra))); -} - + triplet decode_run_interruption_pixel(triplet ra, triplet rb) + { + const int32_t error_value1{decode_run_interruption_error(context_runmode_[0])}; + const int32_t error_value2{decode_run_interruption_error(context_runmode_[0])}; + const int32_t error_value3{decode_run_interruption_error(context_runmode_[0])}; -// RunMode: Functions that handle run-length encoding + return triplet(traits_.compute_reconstructed_sample(rb.v1, error_value1 * sign(rb.v1 - ra.v1)), + traits_.compute_reconstructed_sample(rb.v2, error_value2 * sign(rb.v2 - ra.v2)), + traits_.compute_reconstructed_sample(rb.v3, error_value3 * sign(rb.v3 - ra.v3))); + } -template -void JlsCodec::EncodeRunPixels(int32_t runLength, bool endOfLine) -{ - while (runLength >= static_cast(1 << J[RUNindex_])) + quad decode_run_interruption_pixel(quad ra, quad rb) { - Strategy::AppendOnesToBitStream(1); - runLength = runLength - static_cast(1 << J[RUNindex_]); - IncrementRunIndex(); + const int32_t error_value1{decode_run_interruption_error(context_runmode_[0])}; + const int32_t error_value2{decode_run_interruption_error(context_runmode_[0])}; + const int32_t error_value3{decode_run_interruption_error(context_runmode_[0])}; + const int32_t error_value4{decode_run_interruption_error(context_runmode_[0])}; + + return quad( + triplet(traits_.compute_reconstructed_sample(rb.v1, error_value1 * sign(rb.v1 - ra.v1)), + traits_.compute_reconstructed_sample(rb.v2, error_value2 * sign(rb.v2 - ra.v2)), + traits_.compute_reconstructed_sample(rb.v3, error_value3 * sign(rb.v3 - ra.v3))), + traits_.compute_reconstructed_sample(rb.v4, error_value4 * sign(rb.v4 - ra.v4))); } - if (endOfLine) + sample_type decode_run_interruption_pixel(int32_t ra, int32_t rb) { - if (runLength != 0) + if (std::abs(ra - rb) <= traits_.near_lossless) { - Strategy::AppendOnesToBitStream(1); + const int32_t error_value{decode_run_interruption_error(context_runmode_[1])}; + return static_cast(traits_.compute_reconstructed_sample(ra, error_value)); } + + const int32_t error_value{decode_run_interruption_error(context_runmode_[0])}; + return static_cast(traits_.compute_reconstructed_sample(rb, error_value * sign(rb - ra))); } - else + + int32_t decode_run_pixels(pixel_type ra, pixel_type* start_pos, const int32_t pixel_count) { - Strategy::AppendToBitStream(runLength, J[RUNindex_] + 1); // leading 0 + actual remaining length - } -} + int32_t index{}; + while (Strategy::read_bit()) + { + const int count{std::min(1 << J[run_index_], pixel_count - index)}; + index += count; + ASSERT(index <= pixel_count); + if (count == (1 << J[run_index_])) + { + increment_run_index(); + } -template -int32_t JlsCodec::DecodeRunPixels(PIXEL Ra, PIXEL* startPos, int32_t cpixelMac) -{ - int32_t index = 0; - while (Strategy::ReadBit()) - { - const int count = std::min(1 << J[RUNindex_], int(cpixelMac - index)); - index += count; - ASSERT(index <= cpixelMac); + if (index == pixel_count) + break; + } - if (count == (1 << J[RUNindex_])) + if (index != pixel_count) { - IncrementRunIndex(); + // incomplete run. + index += (J[run_index_] > 0) ? Strategy::read_value(J[run_index_]) : 0; } - if (index == cpixelMac) - break; - } - - if (index != cpixelMac) - { - // incomplete run. - index += (J[RUNindex_] > 0) ? Strategy::ReadValue(J[RUNindex_]) : 0; - } + if (index > pixel_count) + impl::throw_jpegls_error(jpegls_errc::invalid_encoded_data); - if (index > cpixelMac) - throw jpegls_error{jpegls_errc::invalid_encoded_data}; + for (int32_t i{}; i < index; ++i) + { + start_pos[i] = ra; + } - for (int32_t i = 0; i < index; ++i) - { - startPos[i] = Ra; + return index; } - return index; -} + int32_t do_run_mode(const int32_t start_index, decoder_strategy*) + { + const pixel_type ra{current_line_[start_index - 1]}; -template -int32_t JlsCodec::DoRunMode(int32_t index, EncoderStrategy*) -{ - const int32_t ctypeRem = width_ - index; - PIXEL* ptypeCurX = currentLine_ + index; - const PIXEL* ptypePrevX = previousLine_ + index; + const int32_t run_length{decode_run_pixels(ra, current_line_ + start_index, width_ - start_index)}; + const uint32_t end_index{static_cast(start_index + run_length)}; - const PIXEL Ra = ptypeCurX[-1]; + if (end_index == width_) + return end_index - start_index; - int32_t runLength = 0; + // run interruption + const pixel_type rb{previous_line_[end_index]}; + current_line_[end_index] = decode_run_interruption_pixel(ra, rb); + decrement_run_index(); + return end_index - start_index + 1; + } - while (traits.IsNear(ptypeCurX[runLength], Ra)) + void encode_run_interruption_error(context_run_mode& context, const int32_t error_value) { - ptypeCurX[runLength] = Ra; - runLength++; + const int32_t k{context.get_golomb_code()}; + const bool map{context.compute_map(error_value, k)}; + const int32_t e_mapped_error_value{2 * std::abs(error_value) - context.run_interruption_type - + static_cast(map)}; - if (runLength == ctypeRem) - break; + ASSERT(error_value == context.compute_error_value(e_mapped_error_value + context.run_interruption_type, k)); + encode_mapped_value(k, e_mapped_error_value, traits_.limit - J[run_index_] - 1); + context.update_variables(error_value, e_mapped_error_value); } - EncodeRunPixels(runLength, runLength == ctypeRem); - - if (runLength == ctypeRem) - return runLength; - - ptypeCurX[runLength] = EncodeRIPixel(ptypeCurX[runLength], Ra, ptypePrevX[runLength]); - DecrementRunIndex(); - return runLength + 1; -} - - -template -int32_t JlsCodec::DoRunMode(int32_t startIndex, DecoderStrategy*) -{ - const PIXEL Ra = currentLine_[startIndex - 1]; - - const int32_t runLength = DecodeRunPixels(Ra, currentLine_ + startIndex, width_ - startIndex); - const int32_t endIndex = startIndex + runLength; - - if (endIndex == width_) - return endIndex - startIndex; - - // run interruption - const PIXEL Rb = previousLine_[endIndex]; - currentLine_[endIndex] = DecodeRIPixel(Ra, Rb); - DecrementRunIndex(); - return endIndex - startIndex + 1; -} - - -/// Encodes/Decodes a scan line of samples -template -void JlsCodec::DoLine(SAMPLE*) -{ - int32_t index = 0; - int32_t Rb = previousLine_[index - 1]; - int32_t Rd = previousLine_[index]; - - while (index < width_) + sample_type encode_run_interruption_pixel(const int32_t x, const int32_t ra, const int32_t rb) { - const int32_t Ra = currentLine_[index - 1]; - const int32_t Rc = Rb; - Rb = Rd; - Rd = previousLine_[index + 1]; - - const int32_t Qs = ComputeContextID(QuantizeGradient(Rd - Rb), QuantizeGradient(Rb - Rc), QuantizeGradient(Rc - Ra)); - - if (Qs != 0) + if (std::abs(ra - rb) <= traits_.near_lossless) { - currentLine_[index] = DoRegular(Qs, currentLine_[index], GetPredictedValue(Ra, Rb, Rc), static_cast(nullptr)); - index++; + const int32_t error_value{traits_.compute_error_value(x - ra)}; + encode_run_interruption_error(context_runmode_[1], error_value); + return static_cast(traits_.compute_reconstructed_sample(ra, error_value)); } - else - { - index += DoRunMode(index, static_cast(nullptr)); - Rb = previousLine_[index - 1]; - Rd = previousLine_[index]; - } - } -} + const int32_t error_value{traits_.compute_error_value((x - rb) * sign(rb - ra))}; + encode_run_interruption_error(context_runmode_[0], error_value); + return static_cast(traits_.compute_reconstructed_sample(rb, error_value * sign(rb - ra))); + } -/// Encodes/Decodes a scan line of triplets in ILV_SAMPLE mode -template -void JlsCodec::DoLine(Triplet*) -{ - int32_t index = 0; - while (index < width_) + triplet encode_run_interruption_pixel(const triplet x, const triplet ra, + const triplet rb) { - const Triplet Ra = currentLine_[index - 1]; - const Triplet Rc = previousLine_[index - 1]; - const Triplet Rb = previousLine_[index]; - const Triplet Rd = previousLine_[index + 1]; + const int32_t error_value1{traits_.compute_error_value(sign(rb.v1 - ra.v1) * (x.v1 - rb.v1))}; + encode_run_interruption_error(context_runmode_[0], error_value1); - const int32_t Qs1 = ComputeContextID(QuantizeGradient(Rd.v1 - Rb.v1), QuantizeGradient(Rb.v1 - Rc.v1), QuantizeGradient(Rc.v1 - Ra.v1)); - const int32_t Qs2 = ComputeContextID(QuantizeGradient(Rd.v2 - Rb.v2), QuantizeGradient(Rb.v2 - Rc.v2), QuantizeGradient(Rc.v2 - Ra.v2)); - const int32_t Qs3 = ComputeContextID(QuantizeGradient(Rd.v3 - Rb.v3), QuantizeGradient(Rb.v3 - Rc.v3), QuantizeGradient(Rc.v3 - Ra.v3)); + const int32_t error_value2{traits_.compute_error_value(sign(rb.v2 - ra.v2) * (x.v2 - rb.v2))}; + encode_run_interruption_error(context_runmode_[0], error_value2); - if (Qs1 == 0 && Qs2 == 0 && Qs3 == 0) - { - index += DoRunMode(index, static_cast(nullptr)); - } - else - { - Triplet Rx; - Rx.v1 = DoRegular(Qs1, currentLine_[index].v1, GetPredictedValue(Ra.v1, Rb.v1, Rc.v1), static_cast(nullptr)); - Rx.v2 = DoRegular(Qs2, currentLine_[index].v2, GetPredictedValue(Ra.v2, Rb.v2, Rc.v2), static_cast(nullptr)); - Rx.v3 = DoRegular(Qs3, currentLine_[index].v3, GetPredictedValue(Ra.v3, Rb.v3, Rc.v3), static_cast(nullptr)); - currentLine_[index] = Rx; - index++; - } - } -} + const int32_t error_value3{traits_.compute_error_value(sign(rb.v3 - ra.v3) * (x.v3 - rb.v3))}; + encode_run_interruption_error(context_runmode_[0], error_value3); + return triplet(traits_.compute_reconstructed_sample(rb.v1, error_value1 * sign(rb.v1 - ra.v1)), + traits_.compute_reconstructed_sample(rb.v2, error_value2 * sign(rb.v2 - ra.v2)), + traits_.compute_reconstructed_sample(rb.v3, error_value3 * sign(rb.v3 - ra.v3))); + } -/// Encodes/Decodes a scan line of quads in ILV_SAMPLE mode -template -void JlsCodec::DoLine(Quad*) -{ - int32_t index = 0; - while (index < width_) + quad encode_run_interruption_pixel(const quad x, const quad ra, + const quad rb) { - const Quad Ra = currentLine_[index - 1]; - const Quad Rc = previousLine_[index - 1]; - const Quad Rb = previousLine_[index]; - const Quad Rd = previousLine_[index + 1]; - - const int32_t Qs1 = ComputeContextID(QuantizeGradient(Rd.v1 - Rb.v1), QuantizeGradient(Rb.v1 - Rc.v1), QuantizeGradient(Rc.v1 - Ra.v1)); - const int32_t Qs2 = ComputeContextID(QuantizeGradient(Rd.v2 - Rb.v2), QuantizeGradient(Rb.v2 - Rc.v2), QuantizeGradient(Rc.v2 - Ra.v2)); - const int32_t Qs3 = ComputeContextID(QuantizeGradient(Rd.v3 - Rb.v3), QuantizeGradient(Rb.v3 - Rc.v3), QuantizeGradient(Rc.v3 - Ra.v3)); - const int32_t Qs4 = ComputeContextID(QuantizeGradient(Rd.v4 - Rb.v4), QuantizeGradient(Rb.v4 - Rc.v4), QuantizeGradient(Rc.v4 - Ra.v4)); + const int32_t error_value1{traits_.compute_error_value(sign(rb.v1 - ra.v1) * (x.v1 - rb.v1))}; + encode_run_interruption_error(context_runmode_[0], error_value1); - if (Qs1 == 0 && Qs2 == 0 && Qs3 == 0 && Qs4 == 0) - { - index += DoRunMode(index, static_cast(nullptr)); - } - else - { - Quad Rx; - Rx.v1 = DoRegular(Qs1, currentLine_[index].v1, GetPredictedValue(Ra.v1, Rb.v1, Rc.v1), static_cast(nullptr)); - Rx.v2 = DoRegular(Qs2, currentLine_[index].v2, GetPredictedValue(Ra.v2, Rb.v2, Rc.v2), static_cast(nullptr)); - Rx.v3 = DoRegular(Qs3, currentLine_[index].v3, GetPredictedValue(Ra.v3, Rb.v3, Rc.v3), static_cast(nullptr)); - Rx.v4 = DoRegular(Qs4, currentLine_[index].v4, GetPredictedValue(Ra.v4, Rb.v4, Rc.v4), static_cast(nullptr)); - currentLine_[index] = Rx; - index++; - } - } -} + const int32_t error_value2{traits_.compute_error_value(sign(rb.v2 - ra.v2) * (x.v2 - rb.v2))}; + encode_run_interruption_error(context_runmode_[0], error_value2); + const int32_t error_value3{traits_.compute_error_value(sign(rb.v3 - ra.v3) * (x.v3 - rb.v3))}; + encode_run_interruption_error(context_runmode_[0], error_value3); -// DoScan: Encodes or decodes a scan. -// In ILV_SAMPLE mode, multiple components are handled in DoLine -// In ILV_LINE mode, a call do DoLine is made for every component -// In ILV_NONE mode, DoScan is called for each component + const int32_t error_value4{traits_.compute_error_value(sign(rb.v4 - ra.v4) * (x.v4 - rb.v4))}; + encode_run_interruption_error(context_runmode_[0], error_value4); -template -void JlsCodec::DoScan() -{ - const int32_t pixelStride = width_ + 4; - const int components = Info().interleaveMode == interleave_mode::line ? Info().components : 1; - - std::vector vectmp(static_cast(2) * components * pixelStride); - std::vector rgRUNindex(components); + return quad( + triplet(traits_.compute_reconstructed_sample(rb.v1, error_value1 * sign(rb.v1 - ra.v1)), + traits_.compute_reconstructed_sample(rb.v2, error_value2 * sign(rb.v2 - ra.v2)), + traits_.compute_reconstructed_sample(rb.v3, error_value3 * sign(rb.v3 - ra.v3))), + traits_.compute_reconstructed_sample(rb.v4, error_value4 * sign(rb.v4 - ra.v4))); + } - for (int32_t line = 0; line < Info().height; ++line) + void encode_run_pixels(int32_t run_length, const bool end_of_line) { - previousLine_ = &vectmp[1]; - currentLine_ = &vectmp[1 + static_cast(components) * pixelStride]; - if ((line & 1) == 1) + while (run_length >= 1 << J[run_index_]) { - std::swap(previousLine_, currentLine_); + Strategy::append_ones_to_bit_stream(1); + run_length = run_length - (1 << J[run_index_]); + increment_run_index(); } - Strategy::OnLineBegin(width_, currentLine_, pixelStride); - - for (int component = 0; component < components; ++component) + if (end_of_line) { - RUNindex_ = rgRUNindex[component]; - - // initialize edge pixels used for prediction - previousLine_[width_] = previousLine_[width_ - 1]; - currentLine_[-1] = previousLine_[0]; - DoLine(static_cast(nullptr)); // dummy argument for overload resolution - - rgRUNindex[component] = RUNindex_; - previousLine_ += pixelStride; - currentLine_ += pixelStride; + if (run_length != 0) + { + Strategy::append_ones_to_bit_stream(1); + } } - - if (rect_.Y <= line && line < rect_.Y + rect_.Height) + else { - Strategy::OnLineEnd(rect_.Width, currentLine_ + rect_.X - (static_cast(components) * pixelStride), pixelStride); + Strategy::append_to_bit_stream(run_length, J[run_index_] + 1); // leading 0 + actual remaining length } } - Strategy::EndScan(); -} - - -// Factory function for ProcessLine objects to copy/transform un encoded pixels to/from our scan line buffers. -template -std::unique_ptr JlsCodec::CreateProcess(ByteStreamInfo info) -{ - if (!IsInterleaved()) + int32_t do_run_mode(const int32_t index, encoder_strategy*) { - return info.rawData ? - std::unique_ptr(std::make_unique(info.rawData, Info().stride, sizeof(typename Traits::PIXEL))) : - std::unique_ptr(std::make_unique(info.rawStream, Info().stride, sizeof(typename Traits::PIXEL))); - } + const int32_t count_type_remain = width_ - index; + pixel_type* type_cur_x{current_line_ + index}; + const pixel_type* type_prev_x{previous_line_ + index}; - if (Info().colorTransformation == color_transformation::none) - return std::make_unique>>(info, Info(), TransformNone()); + const pixel_type ra{type_cur_x[-1]}; - if (Info().bitsPerSample == sizeof(SAMPLE) * 8) - { - switch (Info().colorTransformation) + int32_t run_length{}; + while (traits_.is_near(type_cur_x[run_length], ra)) { - case color_transformation::hp1: - return std::make_unique>>(info, Info(), TransformHp1()); - case color_transformation::hp2: - return std::make_unique>>(info, Info(), TransformHp2()); - case color_transformation::hp3: - return std::make_unique>>(info, Info(), TransformHp3()); - default: - throw jpegls_error{jpegls_errc::color_transform_not_supported}; - } - } + type_cur_x[run_length] = ra; + ++run_length; - if (Info().bitsPerSample > 8) - { - const int shift = 16 - Info().bitsPerSample; - switch (Info().colorTransformation) - { - case color_transformation::hp1: - return std::make_unique>>>(info, Info(), TransformShifted>(shift)); - case color_transformation::hp2: - return std::make_unique>>>(info, Info(), TransformShifted>(shift)); - case color_transformation::hp3: - return std::make_unique>>>(info, Info(), TransformShifted>(shift)); - default: - throw jpegls_error{jpegls_errc::color_transform_not_supported}; + if (run_length == count_type_remain) + break; } - } - throw jpegls_error{jpegls_errc::bit_depth_for_transform_not_supported}; -} + encode_run_pixels(run_length, run_length == count_type_remain); + if (run_length == count_type_remain) + return run_length; -// Setup codec for encoding and calls DoScan -MSVC_WARNING_SUPPRESS(26433) // C.128: Virtual functions should specify exactly one of virtual, override, or final -template -size_t JlsCodec::EncodeScan(std::unique_ptr processLine, ByteStreamInfo& compressedData) -{ - Strategy::processLine_ = std::move(processLine); + type_cur_x[run_length] = encode_run_interruption_pixel(type_cur_x[run_length], ra, type_prev_x[run_length]); + decrement_run_index(); + return run_length + 1; + } - Strategy::Init(compressedData); - DoScan(); + // codec parameters + Traits traits_; + JlsRect rect_{}; + uint32_t width_; + int32_t t1_{}; + int32_t t2_{}; + int32_t t3_{}; - return Strategy::GetLength(); -} + // compression context + std::array contexts_; + std::array context_runmode_; + int32_t run_index_{}; + pixel_type* previous_line_{}; + pixel_type* current_line_{}; + // quantization lookup table + const int8_t* quantization_{}; + std::vector quantization_lut_; +}; -// Setup codec for decoding and calls DoScan -template -void JlsCodec::DecodeScan(std::unique_ptr processLine, const JlsRect& rect, ByteStreamInfo& compressedData) -{ - Strategy::processLine_ = std::move(processLine); - const uint8_t* compressedBytes = compressedData.rawData; - rect_ = rect; +// Functions to build tables used to decode short Golomb codes. - Strategy::Init(compressedData); - DoScan(); - SkipBytes(compressedData, Strategy::GetCurBytePos() - compressedBytes); +inline std::pair create_encoded_value(const int32_t k, const int32_t mapped_error) noexcept +{ + const int32_t high_bits{mapped_error >> k}; + return std::make_pair(high_bits + k + 1, (1 << k) | (mapped_error & ((1 << k) - 1))); } -MSVC_WARNING_UNSUPPRESS() -// Initialize the codec data structures. Depends on JPEG-LS parameters like Threshold1-Threshold3. -template -void JlsCodec::InitParams(int32_t t1, int32_t t2, int32_t t3, int32_t nReset) +inline golomb_code_table initialize_table(const int32_t k) noexcept { - T1 = t1; - T2 = t2; - T3 = t3; + golomb_code_table table; + for (int16_t error_value{};; ++error_value) + { + // Q is not used when k != 0 + const int32_t mapped_error_value{map_error_value(error_value)}; + const std::pair pair_code{create_encoded_value(k, mapped_error_value)}; + if (static_cast(pair_code.first) > golomb_code_table::byte_bit_count) + break; - InitQuantizationLUT(); + const golomb_code code(error_value, static_cast(pair_code.first)); + table.add_entry(static_cast(pair_code.second), code); + } - const JlsContext contextInitValue(std::max(2, (traits.RANGE + 32) / 64)); - for (auto& context : contexts_) + for (int16_t error_value{-1};; --error_value) { - context = contextInitValue; + // Q is not used when k != 0 + const int32_t mapped_error_value{map_error_value(error_value)}; + const std::pair pair_code{create_encoded_value(k, mapped_error_value)}; + if (static_cast(pair_code.first) > golomb_code_table::byte_bit_count) + break; + + const auto code{golomb_code(error_value, static_cast(pair_code.first))}; + table.add_entry(static_cast(pair_code.second), code); } - contextRunmode_[0] = CContextRunMode(std::max(2, (traits.RANGE + 32) / 64), 0, nReset); - contextRunmode_[1] = CContextRunMode(std::max(2, (traits.RANGE + 32) / 64), 1, nReset); - RUNindex_ = 0; + return table; } } // namespace charls diff -Nru charls-2.1.0+dfsg/src/util.h charls-2.2.0+dfsg/src/util.h --- charls-2.1.0+dfsg/src/util.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/util.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,15 +3,25 @@ #pragma once -#include +#include #include +#include "byte_span.h" + +#include #include #include +#include #include -// Use an uppercase alias for assert to make it clear that it is a pre-processor macro. -#define ASSERT(t) assert(t) + +// Use an uppercase alias for assert to make it clear that ASSERT is a pre-processor macro. +#ifdef _MSC_VER +#define ASSERT(expression) \ + __pragma(warning(push)) __pragma(warning(disable : 26493)) assert(expression) __pragma(warning(pop)) +#else +#define ASSERT(expression) assert(expression) +#endif // Only use __forceinline for the Microsoft C++ compiler in release mode (verified scenario) // Use the build-in optimizer for all other C++ compilers. @@ -30,13 +40,26 @@ #endif #ifdef _MSC_VER -#define MSVC_WARNING_SUPPRESS(x) __pragma(warning(push)) \ - __pragma(warning(disable \ - : x)) // NOLINT(misc-macro-parentheses, bugprone-macro-parentheses) +#define MSVC_WARNING_SUPPRESS(x) \ + __pragma(warning(push)) __pragma(warning(disable : x)) // NOLINT(misc-macro-parentheses, bugprone-macro-parentheses) #define MSVC_WARNING_UNSUPPRESS() __pragma(warning(pop)) + +#define MSVC_WARNING_SUPPRESS_NEXT_LINE(x) \ + __pragma(warning(suppress \ + : x)) // NOLINT(misc-macro-parentheses, bugprone-macro-parentheses, cppcoreguidelines-macro-usage) + +// Visual Studio 2015 supports C++14, but not all constexpr scenarios. VS 2017 has full C++14 support. +#if _MSC_VER >= 1910 +#define CONSTEXPR constexpr +#else +#define CONSTEXPR inline +#endif + #else #define MSVC_WARNING_SUPPRESS(x) #define MSVC_WARNING_UNSUPPRESS() +#define MSVC_WARNING_SUPPRESS_NEXT_LINE(x) +#define CONSTEXPR constexpr #endif namespace charls { @@ -62,11 +85,11 @@ } } -inline void clear_error_message(char* errorMessage) noexcept +inline void clear_error_message(OUT_OPT_ char* error_message) noexcept { - if (errorMessage) + if (error_message) { - errorMessage[0] = 0; + error_message[0] = 0; } } @@ -74,19 +97,21 @@ /// /// Cross platform safe version of strcpy. /// -inline void string_copy(const char* source, char* destination, const size_t size_in_bytes) noexcept +inline void string_copy(IN_Z_ const char* source, OUT_WRITES_Z_(size_in_bytes) char* destination, + const size_t size_in_bytes) noexcept { ASSERT(strlen(source) < size_in_bytes && "String will be truncated"); #if defined(__STDC_SECURE_LIB__) && defined(__STDC_WANT_SECURE_LIB__) && __STDC_WANT_SECURE_LIB__ == 1 - strncpy_s(destination, size_in_bytes, source, _TRUNCATE); + constexpr size_t truncate{static_cast(-1)}; + strncpy_s(destination, size_in_bytes, source, truncate); #else strncpy(destination, source, size_in_bytes); destination[size_in_bytes - 1] = 0; #endif } -inline jpegls_errc set_error_message(const jpegls_errc error, char* error_message) noexcept +inline jpegls_errc set_error_message(const jpegls_errc error, OUT_WRITES_Z_(ErrorMessageSize) char* error_message) noexcept { if (error_message) { @@ -100,14 +125,14 @@ constexpr size_t int32_t_bit_count = sizeof(int32_t) * 8; -inline void push_back(std::vector& values, uint16_t value) +inline void push_back(std::vector& values, const uint16_t value) { values.push_back(static_cast(value >> 8)); values.push_back(static_cast(value)); } -inline void push_back(std::vector& values, uint32_t value) +inline void push_back(std::vector& values, const uint32_t value) { values.push_back(static_cast(value >> 24)); values.push_back(static_cast(value >> 16)); @@ -116,9 +141,9 @@ } -constexpr int32_t log_2(int32_t n) noexcept +CONSTEXPR int32_t log_2(const int32_t n) noexcept { - int32_t x = 0; + int32_t x{}; while (n > (1 << x)) { ++x; @@ -127,102 +152,112 @@ } -constexpr int32_t Sign(int32_t n) noexcept +constexpr int32_t sign(const int32_t n) noexcept { return (n >> (int32_t_bit_count - 1)) | 1; } -constexpr int32_t BitWiseSign(int32_t i) noexcept +constexpr int32_t bit_wise_sign(const int32_t i) noexcept { return i >> (int32_t_bit_count - 1); } -template -struct Triplet +/// +/// Computes the parameter RANGE. When NEAR = 0, RANGE = MAXVAL + 1. (see ISO/IEC 14495-1, A.2.1) +/// +constexpr int32_t compute_range_parameter(const int32_t maximum_sample_value, const int32_t near_lossless) noexcept +{ + return (maximum_sample_value + 2 * near_lossless) / (2 * near_lossless + 1) + 1; +} + + +/// +/// Computes the parameter LIMIT. (see ISO/IEC 14495-1, A.2.1) +/// +constexpr int32_t compute_limit_parameter(const int32_t bits_per_pixel) +{ + return 2 * (bits_per_pixel + std::max(8, bits_per_pixel)); +} + + +template +struct triplet { - Triplet() noexcept : - v1(0), - v2(0), - v3(0) + triplet() noexcept : v1{0}, v2{0}, v3{0} { } - Triplet(int32_t x1, int32_t x2, int32_t x3) noexcept : - v1(static_cast(x1)), - v2(static_cast(x2)), - v3(static_cast(x3)) + triplet(int32_t x1, int32_t x2, int32_t x3) noexcept : + v1(static_cast(x1)), v2(static_cast(x2)), v3(static_cast(x3)) { } union { - T v1; - T R; + SampleType v1; + SampleType R; }; union { - T v2; - T G; + SampleType v2; + SampleType G; }; union { - T v3; - T B; + SampleType v3; + SampleType B; }; }; -inline bool operator==(const Triplet& lhs, const Triplet& rhs) noexcept +inline bool operator==(const triplet& lhs, const triplet& rhs) noexcept { return lhs.v1 == rhs.v1 && lhs.v2 == rhs.v2 && lhs.v3 == rhs.v3; } -inline bool operator!=(const Triplet& lhs, const Triplet& rhs) noexcept +inline bool operator!=(const triplet& lhs, const triplet& rhs) noexcept { return !(lhs == rhs); } -template -struct Quad final : Triplet +template +struct quad final : triplet { MSVC_WARNING_SUPPRESS(26495) // false warning that v4 is uninitialized [VS 2017 15.9.4] - Quad() noexcept : - Triplet(), - v4(0) + quad() noexcept : triplet(), v4{0} { } MSVC_WARNING_UNSUPPRESS() MSVC_WARNING_SUPPRESS(26495) // false warning that v4 is uninitialized [VS 2017 15.9.4] - Quad(Triplet triplet, int32_t alpha) noexcept : - Triplet(triplet), - A(static_cast(alpha)) + quad(triplet triplet_value, int32_t alpha) noexcept : + triplet(triplet_value), A(static_cast(alpha)) { } MSVC_WARNING_UNSUPPRESS() union { - sample v4; - sample A; + SampleType v4; + SampleType A; }; }; -template -struct FromBigEndian final +template +struct from_big_endian final { }; template<> -struct FromBigEndian<4> final +struct from_big_endian<4> final { - FORCE_INLINE static unsigned int Read(const uint8_t* buffer) noexcept + FORCE_INLINE static unsigned int read(const uint8_t* buffer) noexcept { return (static_cast(buffer[0]) << 24U) + (static_cast(buffer[1]) << 16U) + (static_cast(buffer[2]) << 8U) + (static_cast(buffer[3]) << 0U); @@ -231,9 +266,9 @@ template<> -struct FromBigEndian<8> final +struct from_big_endian<8> final { - FORCE_INLINE static uint64_t Read(const uint8_t* buffer) noexcept + FORCE_INLINE static uint64_t read(const uint8_t* buffer) noexcept { return (static_cast(buffer[0]) << 56U) + (static_cast(buffer[1]) << 48U) + (static_cast(buffer[2]) << 40U) + (static_cast(buffer[3]) << 32U) + @@ -243,13 +278,10 @@ }; -inline void SkipBytes(ByteStreamInfo& streamInfo, std::size_t count) noexcept +inline void skip_bytes(byte_span& stream_info, const size_t count) noexcept { - if (!streamInfo.rawData) - return; - - streamInfo.rawData += count; - streamInfo.count -= count; + stream_info.data += count; + stream_info.size -= count; } @@ -259,4 +291,39 @@ return stream << static_cast::type>(e); } + +template +T* check_pointer(T* pointer) +{ + if (!pointer) + { + impl::throw_jpegls_error(jpegls_errc::invalid_argument); + } + + return pointer; +} + + +CONSTEXPR uint32_t calculate_maximum_sample_value(const int32_t bits_per_sample) +{ + ASSERT(bits_per_sample > 0 && bits_per_sample <= 16); + return (1U << bits_per_sample) - 1; +} + + +/// +/// Computes how many bytes are needed to hold the number of bits. +/// +constexpr uint32_t bit_to_byte_count(const int32_t bit_count) noexcept +{ + return static_cast((bit_count + 7) / 8); +} + + +template +constexpr auto to_underlying_type(Enum e) noexcept +{ + return static_cast>(e); +} + } // namespace charls diff -Nru charls-2.1.0+dfsg/src/version.cpp charls-2.2.0+dfsg/src/version.cpp --- charls-2.1.0+dfsg/src/version.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/src/version.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -5,17 +5,20 @@ // Turn A into a string literal without expanding macro definitions // (however, if invoked from a macro, macro arguments are expanded). -#define TO_STRING_NX(A) #A +#define TO_STRING_NX(A) #A // NOLINT(cppcoreguidelines-macro-usage) // Turn A into a string literal after macro-expanding it. -#define TO_STRING(A) TO_STRING_NX(A) +#define TO_STRING(A) TO_STRING_NX(A) // NOLINT(cppcoreguidelines-macro-usage) -const char* CHARLS_API_CALLING_CONVENTION charls_get_version_string() noexcept +extern "C" { + +const char* CHARLS_API_CALLING_CONVENTION charls_get_version_string() { return TO_STRING(CHARLS_VERSION_MAJOR) "." TO_STRING(CHARLS_VERSION_MINOR) "." TO_STRING(CHARLS_VERSION_PATCH); } -void CHARLS_API_CALLING_CONVENTION charls_get_version_number(int32_t* major, int32_t* minor, int32_t* patch) noexcept +void CHARLS_API_CALLING_CONVENTION charls_get_version_number(OUT_OPT_ int32_t* major, OUT_OPT_ int32_t* minor, + OUT_OPT_ int32_t* patch) { if (major) { @@ -32,3 +35,4 @@ *patch = CHARLS_VERSION_PATCH; } } +} diff -Nru charls-2.1.0+dfsg/test/app.manifest charls-2.2.0+dfsg/test/app.manifest --- charls-2.1.0+dfsg/test/app.manifest 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/test/app.manifest 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,21 @@ + + + + + + + + + + + + true" + + + UTF-8 + + + SegmentHeap + + + diff -Nru charls-2.1.0+dfsg/test/bitstreamdamage.cpp charls-2.2.0+dfsg/test/bitstreamdamage.cpp --- charls-2.1.0+dfsg/test/bitstreamdamage.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/bitstreamdamage.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -5,82 +5,127 @@ #include "util.h" #include +#include #include +using charls::jpegls_decoder; +using charls::jpegls_errc; +using charls::jpegls_error; using std::cout; +using std::error_code; +using std::mt19937; +using std::uniform_int_distribution; using std::vector; -using charls::jpegls_errc; -namespace -{ +namespace { -void TestDamagedBitStream1() +void test_damaged_bit_stream1() { - vector encodedBuffer = ReadFile("test/incorrect_images/InfiniteLoopFFMPEG.jls"); - + const vector encoded_buffer = read_file("test/incorrect_images/InfiniteLoopFFMPEG.jls"); vector destination(256 * 256 * 2); - const auto error = JpegLsDecode(destination.data(), destination.size(), encodedBuffer.data(), encodedBuffer.size(), nullptr, nullptr); - Assert::IsTrue(error == jpegls_errc::invalid_encoded_data); + + error_code error; + try + { + jpegls_decoder::decode(encoded_buffer, destination); + } + catch (const jpegls_error& e) + { + error = e.code(); + } + + assert::is_true(error == jpegls_errc::invalid_encoded_data); } -void TestDamagedBitStream2() +void test_damaged_bit_stream2() { - vector encodedBuffer = ReadFile("test/lena8b.jls"); - - encodedBuffer.resize(900); - encodedBuffer.resize(40000, 3); + vector encoded_buffer = read_file("test/lena8b.jls"); + + encoded_buffer.resize(900); + encoded_buffer.resize(40000, 3); vector destination(512 * 512); - const auto error = JpegLsDecode(destination.data(), destination.size(), encodedBuffer.data(), encodedBuffer.size(), nullptr, nullptr); - Assert::IsTrue(error == jpegls_errc::invalid_encoded_data); + + error_code error; + try + { + jpegls_decoder::decode(encoded_buffer, destination); + } + catch (const jpegls_error& e) + { + error = e.code(); + } + + assert::is_true(error == jpegls_errc::invalid_encoded_data); } -void TestDamagedBitStream3() +void test_damaged_bit_stream3() { - vector encodedBuffer = ReadFile("test/lena8b.jls"); + vector encoded_buffer = read_file("test/lena8b.jls"); - encodedBuffer[300] = 0xFF; - encodedBuffer[301] = 0xFF; + encoded_buffer[300] = 0xFF; + encoded_buffer[301] = 0xFF; vector destination(512 * 512); - const auto error = JpegLsDecode(destination.data(), destination.size(), encodedBuffer.data(), encodedBuffer.size(), nullptr, nullptr); - Assert::IsTrue(error == jpegls_errc::invalid_encoded_data); + + error_code error; + try + { + jpegls_decoder::decode(encoded_buffer, destination); + } + catch (const jpegls_error& e) + { + error = e.code(); + } + + assert::is_true(error == jpegls_errc::invalid_encoded_data); } -void TestFileWithRandomHeaderDamage(const char* filename) +void test_file_with_random_header_damage(const char* filename) { - const vector encodedBufferOriginal = ReadFile(filename); + const vector encoded_buffer_original = read_file(filename); + + mt19937 generator(102347325); - srand(102347325); + MSVC_WARNING_SUPPRESS_NEXT_LINE(26496) // cannot be marked as const as operator() is not always defined const. + MSVC_CONST uniform_int_distribution distribution(0, 255); vector destination(512 * 512); for (size_t i = 0; i < 40; ++i) { - vector encodedBuffer(encodedBufferOriginal); + vector encoded_buffer(encoded_buffer_original); vector errors(10, 0); for (int j = 0; j < 20; ++j) { - encodedBuffer[i] = static_cast(rand()); - encodedBuffer[i+1] = static_cast(rand()); - encodedBuffer[i+2] = static_cast(rand()); - encodedBuffer[i+3] = static_cast(rand()); - - const auto error = JpegLsDecode(destination.data(), destination.size(), &encodedBuffer[0], encodedBuffer.size(), nullptr, nullptr); - errors[static_cast(error)]++; + encoded_buffer[i] = static_cast(distribution(generator)); + encoded_buffer[i + 1] = static_cast(distribution(generator)); + encoded_buffer[i + 2] = static_cast(distribution(generator)); + encoded_buffer[i + 3] = static_cast(distribution(generator)); + + error_code error; + try + { + jpegls_decoder::decode(encoded_buffer, destination); + } + catch (const jpegls_error& e) + { + error = e.code(); + } + errors[static_cast(error.value())]++; } cout << "With garbage input at index " << i << ": "; - for(unsigned int error = 0; error < errors.size(); ++error) + for (unsigned int error = 0; error < errors.size(); ++error) { if (errors[error] == 0) continue; - cout << errors[error] << "x error (" << error << "); "; + cout << errors[error] << "x error (" << error << "); "; } cout << "\r\n"; @@ -88,25 +133,25 @@ } -void TestRandomMalformedHeader() +void test_random_malformed_header() { - TestFileWithRandomHeaderDamage("test/conformance/T8C0E0.JLS"); - TestFileWithRandomHeaderDamage("test/conformance/T8C1E0.JLS"); - TestFileWithRandomHeaderDamage("test/conformance/T8C2E0.JLS"); + test_file_with_random_header_damage("test/conformance/t8c0e0.jls"); + test_file_with_random_header_damage("test/conformance/t8c1e0.jls"); + test_file_with_random_header_damage("test/conformance/t8c2e0.jls"); } } // namespace -void DamagedBitStreamTests() +void damaged_bit_stream_tests() { cout << "Test Damaged bit stream\r\n"; - TestDamagedBitStream1(); - TestDamagedBitStream2(); - TestDamagedBitStream3(); + test_damaged_bit_stream1(); + test_damaged_bit_stream2(); + test_damaged_bit_stream3(); cout << "Begin random malformed bit stream tests:\n"; - TestRandomMalformedHeader(); + test_random_malformed_header(); cout << "End random malformed bit stream tests:\n"; } diff -Nru charls-2.1.0+dfsg/test/bitstreamdamage.h charls-2.2.0+dfsg/test/bitstreamdamage.h --- charls-2.1.0+dfsg/test/bitstreamdamage.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/bitstreamdamage.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,4 +3,4 @@ #pragma once -void DamagedBitStreamTests(); +void damaged_bit_stream_tests(); diff -Nru charls-2.1.0+dfsg/test/CMakeLists.txt charls-2.2.0+dfsg/test/CMakeLists.txt --- charls-2.1.0+dfsg/test/CMakeLists.txt 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/CMakeLists.txt 2021-01-10 18:07:25.000000000 +0000 @@ -16,8 +16,15 @@ performance.h util.cpp util.h + legacy.cpp + legacy.h ) +if(WIN32 AND MSVC_VERSION GREATER_EQUAL 1920) + # Only add the manifest file when building a Windows app + target_sources(charlstest PRIVATE app.manifest) +endif() + set_target_properties(charlstest PROPERTIES CXX_VISIBILITY_PRESET hidden) target_link_libraries(charlstest PRIVATE charls) diff -Nru charls-2.1.0+dfsg/test/compliance.cpp charls-2.2.0+dfsg/test/compliance.cpp --- charls-2.1.0+dfsg/test/compliance.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/compliance.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -4,116 +4,134 @@ #include "compliance.h" #include "util.h" +#include +#include #include #include -#include -#include +using std::array; using std::cout; -using std::error_code; using std::swap; using std::vector; -using std::array; using namespace charls; -namespace -{ +namespace { -void Triplet2Planar(vector& buffer, Size size) +void triplet2_planar(vector& buffer, const rect_size size) { - vector workBuffer(buffer.size()); + vector work_buffer(buffer.size()); - const size_t byteCount = size.cx * size.cy; - for (size_t index = 0; index < byteCount; index++) + const size_t byte_count = size.cx * size.cy; + for (size_t index = 0; index < byte_count; ++index) { - workBuffer[index] = buffer[index * 3 + 0]; - workBuffer[index + 1 * byteCount] = buffer[index * 3 + 1]; - workBuffer[index + 2 * byteCount] = buffer[index * 3 + 2]; + work_buffer[index] = buffer[index * 3 + 0]; + work_buffer[index + 1 * byte_count] = buffer[index * 3 + 1]; + work_buffer[index + 2 * byte_count] = buffer[index * 3 + 2]; } - swap(buffer, workBuffer); + swap(buffer, work_buffer); } -bool VerifyEncodedBytes(const void* uncompressedData, size_t uncompressedLength, const void* compressedData, size_t compressedLength) +bool verify_encoded_bytes(const void* uncompressed_data, const size_t uncompressed_length, const void* compressed_data, + const size_t compressed_length) { - JlsParameters info{}; - error_code error = JpegLsReadHeader(compressedData, compressedLength, &info, nullptr); - if (error) - return false; + try + { + jpegls_decoder decoder; + decoder.source(compressed_data, compressed_length).read_header(); - vector ourEncodedBytes(compressedLength + 16); - size_t bytesWritten; - error = JpegLsEncode(ourEncodedBytes.data(), ourEncodedBytes.size(), &bytesWritten, uncompressedData, uncompressedLength, &info, nullptr); - if (error) - return false; + vector our_encoded_bytes(compressed_length + 16); - for (size_t i = 0; i < compressedLength; ++i) - { - if (static_cast(compressedData)[i] != ourEncodedBytes[i]) + jpegls_encoder encoder; + encoder.destination(our_encoded_bytes); + encoder.frame_info(decoder.frame_info()); + encoder.interleave_mode(decoder.interleave_mode()); + encoder.near_lossless(decoder.near_lossless()); + encoder.preset_coding_parameters(decoder.preset_coding_parameters()); + static_cast(encoder.encode(uncompressed_data, uncompressed_length)); + + for (size_t i = 0; i < compressed_length; ++i) { - return false; + if (static_cast(compressed_data)[i] != our_encoded_bytes[i]) + { + return false; + } } - } - return true; + return true; + } + catch (...) + { + return false; + } } -void TestCompliance(const uint8_t* compressedBytes, size_t compressedLength, const uint8_t* uncompressedData, size_t uncompressedLength, bool checkEncode) +void test_compliance(const uint8_t* compressed_bytes, const size_t compressed_length, const uint8_t* uncompressed_data, + const size_t uncompressed_length, const bool check_encode) { - JlsParameters info{}; - error_code error = JpegLsReadHeader(compressedBytes, compressedLength, &info, nullptr); - Assert::IsTrue(!error); - - if (checkEncode) + try { - Assert::IsTrue(VerifyEncodedBytes(uncompressedData, uncompressedLength, compressedBytes, compressedLength)); - } + jpegls_decoder decoder; + decoder.source(compressed_bytes, compressed_length).read_header(); - vector destination(static_cast(info.height) *info.width * ((info.bitsPerSample + 7) / 8) * info.components); + if (check_encode) + { + assert::is_true( + verify_encoded_bytes(uncompressed_data, uncompressed_length, compressed_bytes, compressed_length)); + } - error = JpegLsDecode(destination.data(), destination.size(), compressedBytes, compressedLength, nullptr, nullptr); - Assert::IsTrue(!error); + const auto destination{decoder.decode>()}; - if (info.allowedLossyError == 0) - { - for (size_t i = 0; i < uncompressedLength; ++i) + if (decoder.near_lossless() == 0) { - if (uncompressedData[i] != destination[i]) + for (size_t i = 0; i < uncompressed_length; ++i) { - Assert::IsTrue(false); - break; + if (uncompressed_data[i] != destination[i]) + { + assert::is_true(false); + break; + } } } } + catch (const jpegls_error&) + { + assert::is_true(false); + } } -void DecompressFile(const char* strNameEncoded, const char* strNameRaw, int offset, bool checkEncode = true) +void decompress_file(const char* name_encoded, const char* name_raw, const int offset, const bool check_encode = true) { - cout << "Conformance test:" << strNameEncoded << "\n\r"; - vector encodedBuffer = ReadFile(strNameEncoded); + cout << "Conformance test:" << name_encoded << "\n\r"; + const vector encoded_buffer = read_file(name_encoded); - JlsParameters params{}; - if (make_error_code(JpegLsReadHeader(encodedBuffer.data(), encodedBuffer.size(), ¶ms, nullptr))) + jpegls_decoder decoder; + try + { + decoder.source(encoded_buffer).read_header(); + } + catch (...) { - Assert::IsTrue(false); + assert::is_true(false); return; } - vector rawBuffer = ReadFile(strNameRaw, offset); + vector raw_buffer = read_file(name_raw, offset); - if (params.bitsPerSample > 8) + const auto& frame_info = decoder.frame_info(); + if (frame_info.bits_per_sample > 8) { - FixEndian(&rawBuffer, false); + fix_endian(&raw_buffer, false); } - if (params.interleaveMode == interleave_mode::none && params.components == 3) + if (decoder.interleave_mode() == interleave_mode::none && frame_info.component_count == 3) { - Triplet2Planar(rawBuffer, Size(params.width, params.height)); + triplet2_planar(raw_buffer, rect_size(frame_info.width, frame_info.height)); } - TestCompliance(encodedBuffer.data(), encodedBuffer.size(), rawBuffer.data(), rawBuffer.size(), checkEncode); + test_compliance(encoded_buffer.data(), encoded_buffer.size(), raw_buffer.data(), raw_buffer.size(), check_encode); } @@ -152,71 +170,68 @@ ////}; -const array buffer = { 0, 0, 90, 74, -68, 50, 43, 205, -64, 145, 145, 145, -100, 145, 145, 145}; -////const uint8_t bufferEncoded[] = { 0xFF, 0xD8, 0xFF, 0xF7, 0x00, 0x0B, 0x08, 0x00, 0x04, 0x00, 0x04, 0x01, 0x01, 0x11, 0x00, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, -////0xC0, 0x00, 0x00, 0x6C, 0x80, 0x20, 0x8E, -////0x01, 0xC0, 0x00, 0x00, 0x57, 0x40, 0x00, 0x00, 0x6E, 0xE6, 0x00, 0x00, 0x01, 0xBC, 0x18, 0x00, -////0x00, 0x05, 0xD8, 0x00, 0x00, 0x91, 0x60, 0xFF, 0xD9}; +const array buffer = {0, 0, 90, 74, 68, 50, 43, 205, 64, 145, 145, 145, 100, 145, 145, 145}; +////const uint8_t bufferEncoded[] = { 0xFF, 0xD8, 0xFF, 0xF7, 0x00, 0x0B, 0x08, 0x00, 0x04, 0x00, 0x04, 0x01, 0x01, 0x11, +///0x00, 0xFF, 0xDA, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, /0xC0, 0x00, 0x00, 0x6C, 0x80, 0x20, 0x8E, /0x01, 0xC0, +///0x00, 0x00, 0x57, 0x40, 0x00, 0x00, 0x6E, 0xE6, 0x00, 0x00, 0x01, 0xBC, 0x18, 0x00, /0x00, 0x05, 0xD8, 0x00, 0x00, 0x91, +///0x60, 0xFF, 0xD9}; } // namespace -void TestSampleAnnexH3() +void test_sample_annex_h3() { - ////Size size = Size(4,4); - vector vecRaw(16); - memcpy(vecRaw.data(), buffer.data(), buffer.size()); + ////rect_size size = rect_size(4,4); + vector vec_raw(16); + memcpy(vec_raw.data(), buffer.data(), buffer.size()); //// TestJls(vecRaw, size, 8, 1, ILV_NONE, bufferEncoded, sizeof(bufferEncoded), false); } -void TestColorTransforms_HpImages() +void test_color_transforms_hp_images() { - DecompressFile("test/jlsimage/banny_normal.jls", "test/jlsimage/banny.ppm", 38, false); - DecompressFile("test/jlsimage/banny_HP1.jls", "test/jlsimage/banny.ppm", 38, false); - DecompressFile("test/jlsimage/banny_HP2.jls", "test/jlsimage/banny.ppm", 38, false); - DecompressFile("test/jlsimage/banny_HP3.jls", "test/jlsimage/banny.ppm", 38, false); + decompress_file("test/jlsimage/banny_normal.jls", "test/jlsimage/banny.ppm", 38, false); + decompress_file("test/jlsimage/banny_HP1.jls", "test/jlsimage/banny.ppm", 38, false); + decompress_file("test/jlsimage/banny_HP2.jls", "test/jlsimage/banny.ppm", 38, false); + decompress_file("test/jlsimage/banny_HP3.jls", "test/jlsimage/banny.ppm", 38, false); } -void TestConformance() +void test_conformance() { // Test 1 - DecompressFile("test/conformance/T8C0E0.JLS", "test/conformance/TEST8.PPM",15); + decompress_file("test/conformance/t8c0e0.jls", "test/conformance/test8.ppm", 15); // Test 2 - DecompressFile("test/conformance/T8C1E0.JLS", "test/conformance/TEST8.PPM",15); + decompress_file("test/conformance/t8c1e0.jls", "test/conformance/test8.ppm", 15); // Test 3 - DecompressFile("test/conformance/T8C2E0.JLS", "test/conformance/TEST8.PPM", 15); + decompress_file("test/conformance/t8c2e0.jls", "test/conformance/test8.ppm", 15); // Test 4 - DecompressFile("test/conformance/T8C0E3.JLS", "test/conformance/TEST8.PPM",15); + decompress_file("test/conformance/t8c0e3.jls", "test/conformance/test8.ppm", 15); // Test 5 - DecompressFile("test/conformance/T8C1E3.JLS", "test/conformance/TEST8.PPM",15); + decompress_file("test/conformance/t8c1e3.jls", "test/conformance/test8.ppm", 15); // Test 6 - DecompressFile("test/conformance/T8C2E3.JLS", "test/conformance/TEST8.PPM",15); + decompress_file("test/conformance/t8c2e3.jls", "test/conformance/test8.ppm", 15); // Test 7 // Test 8 // Test 9 - DecompressFile("test/conformance/T8NDE0.JLS", "test/conformance/TEST8BS2.PGM",15); + decompress_file("test/conformance/t8nde0.jls", "test/conformance/test8bs2.pgm", 15); // Test 10 - DecompressFile("test/conformance/T8NDE3.JLS", "test/conformance/TEST8BS2.PGM",15); + decompress_file("test/conformance/t8nde3.jls", "test/conformance/test8bs2.pgm", 15); // Test 11 - DecompressFile("test/conformance/T16E0.JLS", "test/conformance/TEST16.PGM",16); + decompress_file("test/conformance/t16e0.jls", "test/conformance/test16.pgm", 16); // Test 12 - DecompressFile("test/conformance/T16E3.JLS", "test/conformance/TEST16.PGM",16); + decompress_file("test/conformance/t16e3.jls", "test/conformance/test16.pgm", 16); // additional, Lena compressed with other codec (UBC?), vfy with CharLS - DecompressFile("test/lena8b.jls", "test/lena8b.raw",0); + decompress_file("test/lena8b.jls", "test/lena8b.raw", 0); } diff -Nru charls-2.1.0+dfsg/test/compliance.h charls-2.2.0+dfsg/test/compliance.h --- charls-2.1.0+dfsg/test/compliance.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/compliance.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,6 +3,6 @@ #pragma once -void TestConformance(); -void TestColorTransforms_HpImages(); -void TestSampleAnnexH3(); +void test_conformance(); +void test_color_transforms_hp_images(); +void test_sample_annex_h3(); diff -Nru charls-2.1.0+dfsg/test/dicomsamples.cpp charls-2.2.0+dfsg/test/dicomsamples.cpp --- charls-2.1.0+dfsg/test/dicomsamples.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/dicomsamples.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -4,83 +4,87 @@ #include "dicomsamples.h" #include "util.h" +#include #include #include -#include -using std::cout; -using std::vector; using std::array; +using std::cout; using std::error_code; +using std::vector; -namespace -{ +namespace { -bool ContainsString(const uint8_t* container, const uint8_t* bytesToFind, size_t bytesLength) noexcept +bool contains_string(const uint8_t* container, const uint8_t* bytes_to_find, const size_t bytes_length) noexcept { - for (size_t j = 0; j < bytesLength; ++j) + for (size_t j = 0; j < bytes_length; ++j) { - if (bytesToFind[j] != container[j]) + if (bytes_to_find[j] != container[j]) return false; } return true; } -int FindString(vector& container, const uint8_t* bytesToFind, size_t bytesLength) noexcept +int find_string(vector& container, const uint8_t* bytes_to_find, const size_t bytes_length) noexcept { - for (size_t i = 0; i < container.size() - bytesLength; ++i) + for (size_t i = 0; i < container.size() - bytes_length; ++i) { - if (ContainsString(&container[i], bytesToFind, bytesLength)) + if (contains_string(&container[i], bytes_to_find, bytes_length)) return static_cast(i); } return -1; } +// ReSharper disable CppDeprecatedEntity +DISABLE_DEPRECATED_WARNING -void TestDicomSampleImage(const char* name) +void test_dicom_sample_image(const char* name) { - vector data = ReadFile(name); + vector data = read_file(name); - const array pixelDataStart = {0x00, 0x00, 0x01, 0x00, 0xFF, 0xD8, 0xFF, 0xF7}; + const array pixel_data_start = {0x00, 0x00, 0x01, 0x00, 0xFF, 0xD8, 0xFF, 0xF7}; - const int offset = FindString(data, pixelDataStart.data(), pixelDataStart.size()); + const int offset = find_string(data, pixel_data_start.data(), pixel_data_start.size()); data.erase(data.begin(), data.begin() + offset - 4); // remove the DICOM fragment headers (in the concerned images they occur every 64k) - for (unsigned int i = 0; i < data.size(); i+= 64 * 1024) + for (unsigned int i = 0; i < data.size(); i += 64 * 1024) { data.erase(data.begin() + i, data.begin() + i + 8); } JlsParameters params{}; error_code error = JpegLsReadHeader(data.data(), data.size(), ¶ms, nullptr); - Assert::IsTrue(!error); + assert::is_true(!error); - vector dataUnc; - dataUnc.resize(static_cast(params.stride) * params.height); + vector data_unc; + data_unc.resize(static_cast(params.stride) * params.height); - error = JpegLsDecode(dataUnc.data(), dataUnc.size(), data.data(), data.size(), nullptr, nullptr); - Assert::IsTrue(!error); + error = JpegLsDecode(data_unc.data(), data_unc.size(), data.data(), data.size(), nullptr, nullptr); + assert::is_true(!error); cout << "."; } +// ReSharper restore CppDeprecatedEntity +RESTORE_DEPRECATED_WARNING + } // namespace -void TestDicomWG4Images() +void test_dicom_wg4_images() { - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/XA1_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/CT2_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/MG1_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/MR1_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/MR2_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/MR3_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/MR4_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/NM1_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/RG1_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/RG2_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/RG3_JLSL"); - TestDicomSampleImage("test/compsamples_jpegls/IMAGES/JLSL/SC1_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/XA1_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/CT2_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/MG1_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/MR1_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/MR2_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/MR3_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/MR4_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/NM1_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/RG1_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/RG2_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/RG3_JLSL"); + test_dicom_sample_image("test/compsamples_jpegls/IMAGES/JLSL/SC1_JLSL"); } diff -Nru charls-2.1.0+dfsg/test/dicomsamples.h charls-2.2.0+dfsg/test/dicomsamples.h --- charls-2.1.0+dfsg/test/dicomsamples.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/dicomsamples.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,4 +3,4 @@ #pragma once -void TestDicomWG4Images(); +void test_dicom_wg4_images(); diff -Nru charls-2.1.0+dfsg/test/legacy.cpp charls-2.2.0+dfsg/test/legacy.cpp --- charls-2.1.0+dfsg/test/legacy.cpp 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/test/legacy.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,67 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#include "legacy.h" + +#include "util.h" + +#include +#include +#include + +using std::array; +using std::cout; +using std::vector; +using namespace charls; + +namespace { + +// ReSharper disable CppDeprecatedEntity +DISABLE_DEPRECATED_WARNING + +void test_jpegls_read_header(const char* filename, const int width, const int height, const int bits_per_sample, + const int stride, const int component_count, const int interleave_mode) +{ + cout << "LegacyAPI JpegLsReadHeader:" << filename << "\n"; + + vector encoded_buffer = read_file(filename); + + array error_message{}; + JlsParameters parameters{}; + const auto error = JpegLsReadHeader(encoded_buffer.data(), encoded_buffer.size(), ¶meters, error_message.data()); + assert::is_true(error == jpegls_errc::success); + + assert::is_true(parameters.width == width || parameters.height == height || + parameters.bitsPerSample == bits_per_sample || parameters.stride == stride || + parameters.components == component_count || + parameters.interleaveMode == static_cast(interleave_mode)); +} + +// ReSharper restore CppDeprecatedEntity +RESTORE_DEPRECATED_WARNING + +void test_jpegls_read_header() +{ + cout << "Test JpegLsReadHeader\n"; + + test_jpegls_read_header("test/conformance/t8c0e0.jls", 256, 256, 8, 256, 3, 0); + test_jpegls_read_header("test/conformance/t8c1e0.jls", 256, 256, 8, 768, 3, 1); + test_jpegls_read_header("test/conformance/t8c2e0.jls", 256, 256, 8, 768, 3, 2); + test_jpegls_read_header("test/conformance/t8c0e3.jls", 256, 256, 8, 256, 3, 0); + test_jpegls_read_header("test/conformance/t8c1e3.jls", 256, 256, 8, 768, 3, 1); + test_jpegls_read_header("test/conformance/t8c2e3.jls", 256, 256, 8, 768, 3, 2); + test_jpegls_read_header("test/conformance/t8nde0.jls", 128, 128, 8, 128, 1, 0); + test_jpegls_read_header("test/conformance/t8nde3.jls", 128, 128, 8, 128, 1, 0); + test_jpegls_read_header("test/conformance/t16e0.jls", 256, 256, 12, 512, 1, 0); + test_jpegls_read_header("test/conformance/t16e3.jls", 256, 256, 12, 512, 1, 0); + test_jpegls_read_header("test/lena8b.jls", 512, 512, 8, 512, 1, 0); +} + +} // namespace + +void test_legacy_api() +{ + cout << "Test LegacyAPIs\n"; + + test_jpegls_read_header(); +} diff -Nru charls-2.1.0+dfsg/test/legacy.h charls-2.2.0+dfsg/test/legacy.h --- charls-2.1.0+dfsg/test/legacy.h 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/test/legacy.h 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,6 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#pragma once + +void test_legacy_api(); diff -Nru charls-2.1.0+dfsg/test/main.cpp charls-2.2.0+dfsg/test/main.cpp --- charls-2.1.0+dfsg/test/main.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/main.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -1,410 +1,490 @@ // Copyright (c) Team CharLS. // SPDX-License-Identifier: BSD-3-Clause -#include "util.h" - #include "../src/default_traits.h" #include "../src/lossless_traits.h" #include "../src/process_line.h" +#include "util.h" + #include "bitstreamdamage.h" #include "compliance.h" -#include "performance.h" #include "dicomsamples.h" +#include "legacy.h" +#include "performance.h" -#include -#include -#include #include #include +#include +#include +#include #include +#include +#include +using std::array; using std::cout; +using std::error_code; +using std::getline; +using std::ifstream; using std::ios; using std::ios_base; -using std::error_code; -using std::array; -using std::vector; -using std::basic_filebuf; -using std::streamoff; -using std::stringstream; using std::istream; +using std::iter_swap; +using std::mt19937; +using std::ofstream; using std::ostream; -using std::fstream; +using std::streamoff; using std::string; -using std::iter_swap; -using std::getline; +using std::stringstream; +using std::system_error; +using std::uniform_int_distribution; +using std::vector; using namespace charls; -namespace -{ - -constexpr ios_base::openmode mode_input = ios_base::in | ios::binary; -constexpr ios_base::openmode mode_output = ios_base::out | ios::binary; +namespace { +constexpr ios::openmode mode_input = ios::in | ios::binary; +constexpr ios::openmode mode_output = ios::out | ios::binary; -vector ScanFile(const char* strNameEncoded, JlsParameters* params) +template +void read(istream& input, Container& destination_container) { - vector buffer = ReadFile(strNameEncoded); - - basic_filebuf jlsFile; - jlsFile.open(strNameEncoded, mode_input); - - const ByteStreamInfo rawStreamInfo {&jlsFile, nullptr, 0}; + input.read(reinterpret_cast(destination_container.data()), destination_container.size()); +} - const error_code error = JpegLsReadHeaderStream(rawStreamInfo, params); - if (error) - throw UnitTestException(); +size_t get_stream_length(istream& stream, const size_t end_offset = 0) +{ + stream.seekg(0, ios::end); + const auto length{static_cast(stream.tellg())}; + stream.seekg(static_cast(end_offset), ios::beg); - return buffer; + return length; } - -void TestTraits16bit() +void test_traits16_bit() { - const auto traits1 = DefaultTraits(4095,0); - const auto traits2 = LosslessTraits(); + const auto traits1 = default_traits(4095, 0); + using lossless_traits = lossless_traits; - Assert::IsTrue(traits1.LIMIT == traits2.LIMIT); - Assert::IsTrue(traits1.MAXVAL == traits2.MAXVAL); - Assert::IsTrue(traits1.RESET == traits2.RESET); - Assert::IsTrue(traits1.bpp == traits2.bpp); - Assert::IsTrue(traits1.qbpp == traits2.qbpp); + assert::is_true(traits1.limit == lossless_traits::limit); + assert::is_true(traits1.maximum_sample_value == lossless_traits::maximum_sample_value); + assert::is_true(traits1.reset_threshold == lossless_traits::reset_threshold); + assert::is_true(traits1.bits_per_pixel == lossless_traits::bits_per_pixel); + assert::is_true(traits1.quantized_bits_per_pixel == lossless_traits::quantized_bits_per_pixel); for (int i = -4096; i < 4096; ++i) { - Assert::IsTrue(traits1.ModuloRange(i) == traits2.ModuloRange(i)); - Assert::IsTrue(traits1.ComputeErrVal(i) == traits2.ComputeErrVal(i)); + assert::is_true(traits1.modulo_range(i) == lossless_traits::modulo_range(i)); + assert::is_true(traits1.compute_error_value(i) == lossless_traits::compute_error_value(i)); } for (int i = -8095; i < 8095; ++i) { - Assert::IsTrue(traits1.CorrectPrediction(i) == traits2.CorrectPrediction(i)); - Assert::IsTrue(traits1.IsNear(i,2) == traits2.IsNear(i,2)); + assert::is_true(traits1.correct_prediction(i) == lossless_traits::correct_prediction(i)); + assert::is_true(traits1.is_near(i, 2) == lossless_traits::is_near(i, 2)); } } -void TestTraits8bit() +void test_traits8_bit() { - const auto traits1 = DefaultTraits(255,0); - const auto traits2 = LosslessTraits(); + const auto traits1 = default_traits(255, 0); + using lossless_traits = lossless_traits; - Assert::IsTrue(traits1.LIMIT == traits2.LIMIT); - Assert::IsTrue(traits1.MAXVAL == traits2.MAXVAL); - Assert::IsTrue(traits1.RESET == traits2.RESET); - Assert::IsTrue(traits1.bpp == traits2.bpp); - Assert::IsTrue(traits1.qbpp == traits2.qbpp); + assert::is_true(traits1.limit == lossless_traits::limit); + assert::is_true(traits1.maximum_sample_value == lossless_traits::maximum_sample_value); + assert::is_true(traits1.reset_threshold == lossless_traits::reset_threshold); + assert::is_true(traits1.bits_per_pixel == lossless_traits::bits_per_pixel); + assert::is_true(traits1.quantized_bits_per_pixel == lossless_traits::quantized_bits_per_pixel); for (int i = -255; i < 255; ++i) { - Assert::IsTrue(traits1.ModuloRange(i) == traits2.ModuloRange(i)); - Assert::IsTrue(traits1.ComputeErrVal(i) == traits2.ComputeErrVal(i)); + assert::is_true(traits1.modulo_range(i) == lossless_traits::modulo_range(i)); + assert::is_true(traits1.compute_error_value(i) == lossless_traits::compute_error_value(i)); } for (int i = -255; i < 512; ++i) { - Assert::IsTrue(traits1.CorrectPrediction(i) == traits2.CorrectPrediction(i)); - Assert::IsTrue(traits1.IsNear(i,2) == traits2.IsNear(i,2)); + assert::is_true(traits1.correct_prediction(i) == lossless_traits::correct_prediction(i)); + assert::is_true(traits1.is_near(i, 2) == lossless_traits::is_near(i, 2)); } } -vector MakeSomeNoise(size_t length, size_t bitCount, int seed) +vector make_some_noise(const size_t length, const size_t bit_count, const int seed) { - srand(seed); + const auto max_value = (1U << bit_count) - 1U; + mt19937 generator(seed); + MSVC_CONST uniform_int_distribution distribution(0, max_value); + vector buffer(length); - const auto mask = static_cast((1 << bitCount) - 1); - for (size_t i = 0; i < length; ++i) + for (auto& pixel_value : buffer) { - const auto val = static_cast(rand()); - buffer[i] = static_cast(val & mask); + pixel_value = static_cast(distribution(generator)); } + return buffer; } -vector MakeSomeNoise16bit(size_t length, int bitCount, int seed) +vector make_some_noise16_bit(const size_t length, const int bit_count, const int seed) { - srand(seed); + const auto max_value = static_cast((1U << bit_count) - 1U); + mt19937 generator(seed); + MSVC_CONST uniform_int_distribution distribution(0, max_value); + vector buffer(length * 2); - const auto mask = static_cast((1 << bitCount) - 1); for (size_t i = 0; i < length; i = i + 2) { - const uint16_t value = static_cast(rand()) & mask; + const uint16_t value = distribution(generator); buffer[i] = static_cast(value); buffer[i] = static_cast(value >> 8); - } + return buffer; } -void TestNoiseImage() +void test_noise_image() { - const Size size2 = Size(512, 512); + const rect_size size2 = rect_size(512, 512); - for (size_t bitDepth = 8; bitDepth >=2; --bitDepth) + for (size_t bit_depth = 8; bit_depth >= 2; --bit_depth) { stringstream label; - label << "noise, bit depth: " << bitDepth; + label << "noise, bit depth: " << bit_depth; - const vector noiseBytes = MakeSomeNoise(size2.cx * size2.cy, bitDepth, 21344); - TestRoundTrip(label.str().c_str(), noiseBytes, size2, static_cast(bitDepth), 1); + const vector noise_bytes = make_some_noise(size2.cx * size2.cy, bit_depth, 21344); + test_round_trip(label.str().c_str(), noise_bytes, size2, static_cast(bit_depth), 1); } - for (int bitDepth = 16; bitDepth > 8; --bitDepth) + for (int bit_depth = 16; bit_depth > 8; --bit_depth) { stringstream label; - label << "noise, bit depth: " << bitDepth; + label << "noise, bit depth: " << bit_depth; - const vector noiseBytes = MakeSomeNoise16bit(size2.cx * size2.cy, bitDepth, 21344); - TestRoundTrip(label.str().c_str(), noiseBytes, size2, bitDepth, 1); + const vector noise_bytes = make_some_noise16_bit(size2.cx * size2.cy, bit_depth, 21344); + test_round_trip(label.str().c_str(), noise_bytes, size2, bit_depth, 1); } } -void TestNoiseImageWithCustomReset() +void test_noise_image_with_custom_reset() { - const Size size{512, 512}; - constexpr int bitDepth = 16; - const vector noiseBytes = MakeSomeNoise16bit(size.cx * size.cy, bitDepth, 21344); + const rect_size size{512, 512}; + constexpr int bit_depth{16}; + const vector noise_bytes = make_some_noise16_bit(size.cx * size.cy, bit_depth, 21344); JlsParameters params{}; params.components = 1; - params.bitsPerSample = bitDepth; + params.bitsPerSample = bit_depth; params.height = static_cast(size.cy); params.width = static_cast(size.cx); - params.custom.MaximumSampleValue = (1 << bitDepth) - 1; + params.custom.MaximumSampleValue = (1 << bit_depth) - 1; params.custom.ResetValue = 63; - TestRoundTrip("TestNoiseImageWithCustomReset", noiseBytes, params); + test_round_trip("TestNoiseImageWithCustomReset", noise_bytes, params); } -void TestFailOnTooSmallOutputBuffer() +void test_fail_on_too_small_output_buffer() { - auto inputBuffer = MakeSomeNoise(8 * 8, 8, 21344); - size_t compressedLength; - - auto params = JlsParameters(); - params.components = 1; - params.bitsPerSample = 8; - params.height = 8; - params.width = 8; + const auto input_buffer = make_some_noise(8 * 8, 8, 21344); // Trigger a "destination buffer too small" when writing the header markers. - vector outputBuffer1(1); - auto result = JpegLsEncode(outputBuffer1.data(), outputBuffer1.size(), &compressedLength, inputBuffer.data(), inputBuffer.size(), ¶ms, nullptr); - Assert::IsTrue(result == jpegls_errc::destination_buffer_too_small); + try + { + vector output_buffer(1); + jpegls_encoder encoder; + encoder.destination(output_buffer); + encoder.frame_info({8, 8, 8, 1}); + static_cast(encoder.encode(input_buffer)); + assert::is_true(false); + } + catch (const jpegls_error& e) + { + assert::is_true(e.code() == jpegls_errc::destination_buffer_too_small); + } // Trigger a "destination buffer too small" when writing the encoded pixel bytes. - vector outputBuffer2(100); - result = JpegLsEncode(outputBuffer2.data(), outputBuffer2.size(), &compressedLength, inputBuffer.data(), inputBuffer.size(), ¶ms, nullptr); - Assert::IsTrue(result == jpegls_errc::destination_buffer_too_small); + try + { + vector output_buffer(100); + jpegls_encoder encoder; + encoder.destination(output_buffer); + encoder.frame_info({8, 8, 8, 1}); + static_cast(encoder.encode(input_buffer)); + assert::is_true(false); + } + catch (const jpegls_error& e) + { + assert::is_true(e.code() == jpegls_errc::destination_buffer_too_small); + } } -void TestBgra() +void test_bgra() { - char input[] = "RGBARGBARGBARGBA1234"; - const char expected[] = "BGRABGRABGRABGRA1234"; - TransformRgbToBgr(input, 4, 4); - Assert::IsTrue(strcmp(input, expected) == 0); + array input{'R', 'G', 'B', 'A', 'R', 'G', 'B', 'A', 'R', 'G', 'B', 'A', 'R', 'G', 'B', 'A', 1, 2, 3, 4}; + const array expected{'B', 'G', 'R', 'A', 'B', 'G', 'R', 'A', 'B', 'G', + 'R', 'A', 'B', 'G', 'R', 'A', 1, 2, 3, 4}; + + transform_rgb_to_bgr(input.data(), 4, 4); + assert::is_true(expected == input); } -void TestBgr() +void test_bgr() { - JlsParameters params{}; - vector encodedBuffer = ScanFile("test/conformance/T8C2E3.JLS", ¶ms); - vector decodedBuffer(static_cast(params.width) * params.height * params.components); + vector encoded_source = read_file("test/conformance/t8c2e3.jls"); + + jpegls_decoder decoder; + decoder.source(encoded_source); + decoder.read_header(); + + vector decoded_buffer(decoder.destination_size()); + // ReSharper disable CppDeprecatedEntity + DISABLE_DEPRECATED_WARNING + + JlsParameters params{}; params.outputBgr = static_cast(true); - const error_code error = JpegLsDecode(decodedBuffer.data(), decodedBuffer.size(), encodedBuffer.data(), encodedBuffer.size(), ¶ms, nullptr); - Assert::IsTrue(!error); + const error_code error = JpegLsDecode(decoded_buffer.data(), decoded_buffer.size(), encoded_source.data(), + encoded_source.size(), ¶ms, nullptr); + + // ReSharper restore CppDeprecatedEntity + RESTORE_DEPRECATED_WARNING + + assert::is_true(!error); - Assert::IsTrue(decodedBuffer[0] == 0x69); - Assert::IsTrue(decodedBuffer[1] == 0x77); - Assert::IsTrue(decodedBuffer[2] == 0xa1); - Assert::IsTrue(decodedBuffer[static_cast(params.width) * 6 + 3] == 0x2d); - Assert::IsTrue(decodedBuffer[static_cast(params.width) * 6 + 4] == 0x43); - Assert::IsTrue(decodedBuffer[static_cast(params.width) * 6 + 5] == 0x4d); + assert::is_true(decoded_buffer[0] == 0x69); + assert::is_true(decoded_buffer[1] == 0x77); + assert::is_true(decoded_buffer[2] == 0xa1); + assert::is_true(decoded_buffer[static_cast(decoder.frame_info().width) * 6 + 3] == 0x2d); + assert::is_true(decoded_buffer[static_cast(decoder.frame_info().width) * 6 + 4] == 0x43); + assert::is_true(decoded_buffer[static_cast(decoder.frame_info().width) * 6 + 5] == 0x4d); } -void TestTooSmallOutputBuffer() +void test_too_small_output_buffer() { - vector encoded = ReadFile("test/lena8b.jls"); - + const vector encoded = read_file("test/lena8b.jls"); vector destination(512 * 511); - const auto error = JpegLsDecode(destination.data(), destination.size(), encoded.data(), encoded.size(), nullptr, nullptr); - Assert::IsTrue(error == jpegls_errc::destination_buffer_too_small); -} + jpegls_decoder decoder; + decoder.source(encoded).read_header(); + error_code error; + try + { + decoder.decode(destination); + } + catch (const jpegls_error& e) + { + error = e.code(); + } -////void TestBadImage() -////{ -//// vector rgbyteCompressed; -//// if (!ReadFile("test/BadCompressedStream.jls", &rgbyteCompressed, 0)) -//// return; -//// -//// vector rgbyteOut(2500 * 3000 * 2); -//// auto error = JpegLsDecode(&rgbyteOut[0], rgbyteOut.size(), &rgbyteCompressed[0], rgbyteCompressed.size(), nullptr, nullptr); -//// -//// Assert::IsTrue(error == jpegls_errc::UncompressedBufferTooSmall); -////} + assert::is_true(error == jpegls_errc::destination_buffer_too_small); +} -void TestDecodeBitStreamWithNoMarkerStart() +void test_decode_bit_stream_with_no_marker_start() { - const array encodedData = {0x33, 0x33}; + const array encoded_data{0x33, 0x33}; array output{}; - const auto error = JpegLsDecode(output.data(), output.size(), encodedData.data(), encodedData.size(), nullptr, nullptr); - Assert::IsTrue(error == jpegls_errc::jpeg_marker_start_byte_not_found); + error_code error; + try + { + jpegls_decoder decoder; + decoder.source(encoded_data).read_header(); + decoder.decode(output); + } + catch (const jpegls_error& e) + { + error = e.code(); + } + + assert::is_true(error == jpegls_errc::jpeg_marker_start_byte_not_found); } -void TestDecodeBitStreamWithUnsupportedEncoding() +void test_decode_bit_stream_with_unsupported_encoding() { - const array encodedData = { + const array encoded_data{ 0xFF, 0xD8, // Start Of Image (JPEG_SOI) 0xFF, 0xC3, // Start Of Frame (lossless, Huffman) (JPEG_SOF_3) 0x00, 0x00 // Length of data of the marker }; array output{}; - const auto error = JpegLsDecode(output.data(), output.size(), encodedData.data(), encodedData.size(), nullptr, nullptr); - Assert::IsTrue(error == jpegls_errc::encoding_not_supported); + error_code error; + try + { + jpegls_decoder decoder; + decoder.source(encoded_data).read_header(); + decoder.decode(output); + } + catch (const jpegls_error& e) + { + error = e.code(); + } + + assert::is_true(error == jpegls_errc::encoding_not_supported); } -void TestDecodeBitStreamWithUnknownJpegMarker() +void test_decode_bit_stream_with_unknown_jpeg_marker() { - const array encodedData = { + const array encoded_data{ 0xFF, 0xD8, // Start Of Image (JPEG_SOI) 0xFF, 0x01, // Undefined marker 0x00, 0x00 // Length of data of the marker }; array output{}; - const auto error = JpegLsDecode(output.data(), output.size(), encodedData.data(), encodedData.size(), nullptr, nullptr); - Assert::IsTrue(error == jpegls_errc::unknown_jpeg_marker_found); + error_code error; + try + { + jpegls_decoder decoder; + decoder.source(encoded_data).read_header(); + decoder.decode(output); + } + catch (const jpegls_error& e) + { + error = e.code(); + } + + assert::is_true(error == jpegls_errc::unknown_jpeg_marker_found); } -void TestDecodeRect() +void test_decode_rect() { + vector encoded_source = read_file("test/lena8b.jls"); + + jpegls_decoder decoder; + decoder.source(encoded_source); + decoder.read_header(); + + vector decoded_buffer(decoder.destination_size()); + + // ReSharper disable CppDeprecatedEntity + DISABLE_DEPRECATED_WARNING + JlsParameters params{}; - vector encodedData = ScanFile("test/lena8b.jls", ¶ms); - vector decodedBuffer(static_cast(params.width) * params.height*params.components); - error_code error = JpegLsDecode(decodedBuffer.data(), decodedBuffer.size(), encodedData.data(), encodedData.size(), nullptr, nullptr); - Assert::IsTrue(!error); + error_code error = JpegLsDecode(decoded_buffer.data(), decoded_buffer.size(), encoded_source.data(), + encoded_source.size(), ¶ms, nullptr); + assert::is_true(!error); + + const JlsRect rect = {128, 128, 256, 1}; + vector decoded_data(static_cast(rect.Width) * rect.Height); + decoded_data.push_back(0x1f); + + error = JpegLsDecodeRect(decoded_data.data(), decoded_data.size(), encoded_source.data(), encoded_source.size(), rect, + nullptr, nullptr); - const JlsRect rect = { 128, 128, 256, 1 }; - vector decodedData(static_cast(rect.Width) * rect.Height); - decodedData.push_back(0x1f); - error = JpegLsDecodeRect(decodedData.data(), decodedData.size(), encodedData.data(), encodedData.size(), rect, nullptr, nullptr); - Assert::IsTrue(!error); + // ReSharper restore CppDeprecatedEntity + RESTORE_DEPRECATED_WARNING - Assert::IsTrue(memcmp(&decodedBuffer[rect.X + static_cast(rect.Y) * 512], decodedData.data(), static_cast(rect.Width) * rect.Height) == 0); - Assert::IsTrue(decodedData[static_cast(rect.Width) * rect.Height] == 0x1f); + assert::is_true(!error); + + assert::is_true(memcmp(&decoded_buffer[rect.X + static_cast(rect.Y) * 512], decoded_data.data(), + static_cast(rect.Width) * rect.Height) == 0); + assert::is_true(decoded_data[static_cast(rect.Width) * rect.Height] == 0x1f); } -void TestEncodeFromStream(const char* file, int offset, int width, int height, int bpp, int componentCount, interleave_mode ilv, size_t expectedLength) +void test_encode_from_stream(const char* filename, const size_t offset, const uint32_t width, const uint32_t height, + const int32_t bits_per_sample, const int32_t component_count, + const interleave_mode interleave_mode, const size_t expected_length) { - basic_filebuf myFile; // On the stack - myFile.open(file, mode_input); - Assert::IsTrue(myFile.is_open()); + ifstream source_file{filename, ios::in | ios::binary}; + assert::is_true(source_file.good()); + + size_t length = get_stream_length(source_file, offset); + assert::is_true(length >= offset); + length -= offset; - myFile.pubseekoff(static_cast(offset), ios_base::cur); - const ByteStreamInfo rawStreamInfo = {&myFile, nullptr, 0}; + // Note: use a buffer until the new API provides passing a callback function to read. + vector source(length); + read(source_file, source); - vector compressed(static_cast(width) * height * componentCount * 2); - JlsParameters params = JlsParameters(); - params.height = height; - params.width = width; - params.components = componentCount; - params.bitsPerSample = bpp; - params.interleaveMode = ilv; - size_t bytesWritten = 0; + jpegls_encoder encoder; + encoder.frame_info({width, height, bits_per_sample, component_count}).interleave_mode(interleave_mode); - JpegLsEncodeStream(FromByteArray(compressed.data(), static_cast(width) * height * componentCount * 2), bytesWritten, rawStreamInfo, params); - Assert::IsTrue(bytesWritten == expectedLength); + vector encoded_destination(encoder.estimated_destination_size()); + encoder.destination(encoded_destination); - myFile.close(); + assert::is_true(encoder.encode(source) == expected_length); } -bool DecodeToPnm(istream& input, ostream& output) +bool decode_to_pnm(istream& input, ostream& output) { - const ByteStreamInfo inputInfo{input.rdbuf(), nullptr, 0}; - - auto params = JlsParameters(); - error_code error = JpegLsReadHeaderStream(inputInfo, ¶ms); - if (error) - return false; + const size_t length = get_stream_length(input); + vector encoded_source(length); + read(input, encoded_source); - input.seekg(0); + vector decoded_destination; + frame_info frame_info; + interleave_mode interleave_mode; + std::tie(frame_info, interleave_mode) = jpegls_decoder::decode(encoded_source, decoded_destination); - const int maxValue = (1 << params.bitsPerSample) - 1; - const int bytesPerSample = maxValue > 255 ? 2 : 1; - vector outputBuffer(static_cast(params.width) * params.height * bytesPerSample * params.components); - const auto outputInfo = FromByteArray(outputBuffer.data(), outputBuffer.size()); - error = JpegLsDecodeStream(outputInfo, inputInfo, ¶ms); - if (error) - return false; + if (frame_info.component_count > 1 && interleave_mode == charls::interleave_mode::none) + return false; // Unsupported at the moment. // PNM format requires most significant byte first (big endian). - if (bytesPerSample == 2) + const int max_value = (1 << frame_info.bits_per_sample) - 1; + const int bytes_per_sample = max_value > 255 ? 2 : 1; + + if (bytes_per_sample == 2) { - for (auto i = outputBuffer.begin(); i != outputBuffer.end(); i += 2) + for (auto i = decoded_destination.begin(); i != decoded_destination.end(); i += 2) { iter_swap(i, i + 1); } } - const int magicNumber = params.components == 3 ? 6 : 5; - output << 'P' << magicNumber << "\n" << params.width << ' ' << params.height << "\n" << maxValue << "\n"; - output.write(reinterpret_cast(outputBuffer.data()), outputBuffer.size()); + const int magic_number = frame_info.component_count == 3 ? 6 : 5; + output << 'P' << magic_number << "\n" << frame_info.width << ' ' << frame_info.height << "\n" << max_value << "\n"; + output.write(reinterpret_cast(decoded_destination.data()), decoded_destination.size()); return true; } -vector readPnmHeader(istream& pnmFile) +vector read_pnm_header(istream& pnm_file) { - vector readValues; + vector read_values; - const auto first = static_cast(pnmFile.get()); + const auto first = static_cast(pnm_file.get()); // All portable anymap format (PNM) start with the character P. if (first != 'P') - return readValues; + return read_values; - while (readValues.size() < 4) + while (read_values.size() < 4) { string bytes; - getline(pnmFile, bytes); + getline(pnm_file, bytes); stringstream line(bytes); - while (readValues.size() < 4) + while (read_values.size() < 4) { int value = -1; line >> value; if (value <= 0) break; - readValues.push_back(value); + read_values.push_back(value); } } - return readValues; + return read_values; } @@ -412,53 +492,54 @@ // into the JPEG-LS format. The 2 binary formats P5 and P6 are supported: // Portable GrayMap: P5 = binary, extension = .pgm, 0-2^16 (gray scale) // Portable PixMap: P6 = binary, extension.ppm, range 0-2^16 (RGB) -bool EncodePnm(istream& pnmFile, const ostream& jlsFileStream) +bool encode_pnm(istream& pnm_file, ostream& jls_file_stream) { - vector readValues = readPnmHeader(pnmFile); - if (readValues.size() !=4) + vector read_values = read_pnm_header(pnm_file); + if (read_values.size() != 4) return false; - JlsParameters params{}; - params.components = readValues[0] == 6 ? 3 : 1; - params.width = readValues[1]; - params.height = readValues[2]; - params.bitsPerSample = log_2(readValues[3] + 1); - params.interleaveMode = params.components == 3 ? interleave_mode::line : interleave_mode::none; - - const int bytesPerSample = (params.bitsPerSample + 7) / 8; - vector inputBuffer(static_cast(params.width) * params.height * bytesPerSample * params.components); - pnmFile.read(reinterpret_cast(inputBuffer.data()), inputBuffer.size()); - if (!pnmFile.good()) + const frame_info frame_info{static_cast(read_values[1]), static_cast(read_values[2]), + log_2(read_values[3] + 1), read_values[0] == 6 ? 3 : 1}; + + const int bytes_per_sample = ::bit_to_byte_count(frame_info.bits_per_sample); + vector input_buffer(static_cast(frame_info.width) * frame_info.height * bytes_per_sample * + frame_info.component_count); + pnm_file.read(reinterpret_cast(input_buffer.data()), input_buffer.size()); + if (!pnm_file.good()) return false; // PNM format is stored with most significant byte first (big endian). - if (bytesPerSample == 2) + if (bytes_per_sample == 2) { - for (auto i = inputBuffer.begin(); i != inputBuffer.end(); i += 2) + for (auto i = input_buffer.begin(); i != input_buffer.end(); i += 2) { iter_swap(i, i + 1); } } - const auto rawStreamInfo = FromByteArray(inputBuffer.data(), inputBuffer.size()); - const ByteStreamInfo jlsStreamInfo = {jlsFileStream.rdbuf(), nullptr, 0}; + jpegls_encoder encoder; + encoder.frame_info(frame_info) + .interleave_mode(frame_info.component_count == 3 ? interleave_mode::line : interleave_mode::none); - size_t bytesWritten = 0; - JpegLsEncodeStream(jlsStreamInfo, bytesWritten, rawStreamInfo, params); - return true; + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + const size_t bytes_encoded{encoder.encode(input_buffer)}; + + jls_file_stream.write(reinterpret_cast(destination.data()), static_cast(bytes_encoded)); + return jls_file_stream.good(); } -bool ComparePnm(istream& pnmFile1, istream& pnmFile2) +bool compare_pnm(istream& pnm_file1, istream& pnm_file2) { - vector header1 = readPnmHeader(pnmFile1); + vector header1 = read_pnm_header(pnm_file1); if (header1.size() != 4) { cout << "Cannot read header from input file 1\n"; return false; } - vector header2 = readPnmHeader(pnmFile2); + vector header2 = read_pnm_header(pnm_file2); if (header2.size() != 4) { cout << "Cannot read header from input file 2\n"; @@ -467,7 +548,7 @@ if (header1[0] != header2[0]) { - cout << "Header type " << header1[0] << " is not equal with type "<< header2[0] << "\n"; + cout << "Header type " << header1[0] << " is not equal with type " << header2[0] << "\n"; return false; } @@ -490,20 +571,20 @@ cout << "max-value " << header1[3] << " is not equal with max-value " << header2[3] << "\n"; return false; } - const auto bytesPerSample = header1[3] > 255 ? 2 : 1; + const auto bytes_per_sample = header1[3] > 255 ? 2 : 1; - const size_t byteCount = width * height * bytesPerSample; - vector bytes1(byteCount); - vector bytes2(byteCount); + const size_t byte_count = width * height * bytes_per_sample; + vector bytes1(byte_count); + vector bytes2(byte_count); - pnmFile1.read(reinterpret_cast(&bytes1[0]), byteCount); - pnmFile2.read(reinterpret_cast(&bytes2[0]), byteCount); + pnm_file1.read(reinterpret_cast(&bytes1[0]), byte_count); + pnm_file2.read(reinterpret_cast(&bytes2[0]), byte_count); for (size_t x = 0; x < height; ++x) { - for (size_t y = 0; y < width; y += bytesPerSample) + for (size_t y = 0; y < width; y += bytes_per_sample) { - if (bytesPerSample == 1) + if (bytes_per_sample == 1) { if (bytes1[(x * width) + y] != bytes2[(x * width) + y]) { @@ -528,97 +609,77 @@ } -////void TestDecodeFromStream(const char* strNameEncoded) -////{ -//// basic_filebuf jlsFile; -//// jlsFile.open(strNameEncoded, mode_input); -//// Assert::IsTrue(jlsFile.is_open()); -//// ByteStreamInfo compressedByteStream = {&jlsFile, nullptr, 0}; -//// -//// auto params = JlsParameters(); -//// auto err = JpegLsReadHeaderStream(compressedByteStream, ¶ms, nullptr); -//// Assert::IsTrue(err == jpegls_errc::OK); -//// -//// jlsFile.pubseekpos(ios::beg, ios_base::in); -//// -//// basic_stringbuf buf; -//// ByteStreamInfo rawStreamInfo = {&buf, nullptr, 0}; -//// -//// err = JpegLsDecodeStream(rawStreamInfo, compressedByteStream, nullptr, nullptr); -//// ////size_t outputCount = buf.str().size(); -//// -//// Assert::IsTrue(err == jpegls_errc::OK); -//// //Assert::IsTrue(outputCount == 512 * 512); -////} - - -jpegls_errc DecodeRaw(const char* strNameEncoded, const char* strNameOutput) +bool decode_raw(const char* filename_encoded, const char* filename_output) { - fstream jlsFile(strNameEncoded, mode_input); - const ByteStreamInfo compressedByteStream{jlsFile.rdbuf(), nullptr, 0}; - - fstream rawFile(strNameOutput, mode_output); - const ByteStreamInfo rawStream{rawFile.rdbuf(), nullptr, 0}; - - const auto value = JpegLsDecodeStream(rawStream, compressedByteStream, nullptr); - jlsFile.close(); - rawFile.close(); - - return value; + try + { + const vector encoded_source = read_file(filename_encoded); + vector decoded_destination; + jpegls_decoder::decode(encoded_source, decoded_destination); + write_file(filename_output, decoded_destination.data(), decoded_destination.size()); + return true; + } + catch (const system_error&) + { + return false; + } } -void TestEncodeFromStream() +void test_encode_from_stream() { - ////TestDecodeFromStream("test/user_supplied/output.jls"); + ////test_encode_from_stream("test/user_supplied/output.jls"); - TestEncodeFromStream("test/0015.raw", 0, 1024, 1024, 8, 1, interleave_mode::none, 0x3D3ee); - ////TestEncodeFromStream("test/MR2_UNC", 1728, 1024, 1024, 16, 1,0, 0x926e1); - TestEncodeFromStream("test/conformance/TEST8.PPM", 15, 256, 256, 8, 3, interleave_mode::sample, 99734); - TestEncodeFromStream("test/conformance/TEST8.PPM", 15, 256, 256, 8, 3, interleave_mode::line, 100615); + test_encode_from_stream("test/0015.raw", 0, 1024, 1024, 8, 1, interleave_mode::none, 0x3D3ee); + ////test_encode_from_stream("test/MR2_UNC", 1728, 1024, 1024, 16, 1,0, 0x926e1); + test_encode_from_stream("test/conformance/test8.ppm", 15, 256, 256, 8, 3, interleave_mode::sample, 99734); + test_encode_from_stream("test/conformance/test8.ppm", 15, 256, 256, 8, 3, interleave_mode::line, 100615); } -void UnitTest() +void unit_test() { try { //// TestBadImage(); cout << "Test Conformance\n"; - TestEncodeFromStream(); - TestConformance(); + test_encode_from_stream(); + test_conformance(); - TestDecodeRect(); + test_decode_rect(); cout << "Test Traits\n"; - TestTraits16bit(); - TestTraits8bit(); + test_traits16_bit(); + test_traits8_bit(); cout << "Windows bitmap BGR/BGRA output\n"; - TestBgr(); - TestBgra(); + test_bgr(); + test_bgra(); cout << "Test Small buffer\n"; - TestTooSmallOutputBuffer(); + test_too_small_output_buffer(); - TestFailOnTooSmallOutputBuffer(); + test_fail_on_too_small_output_buffer(); cout << "Test Color transform equivalence on HP images\n"; - TestColorTransforms_HpImages(); + test_color_transforms_hp_images(); cout << "Test Annex H3\n"; - TestSampleAnnexH3(); + test_sample_annex_h3(); - TestNoiseImage(); - TestNoiseImageWithCustomReset(); + test_noise_image(); + test_noise_image_with_custom_reset(); cout << "Test robustness\n"; - TestDecodeBitStreamWithNoMarkerStart(); - TestDecodeBitStreamWithUnsupportedEncoding(); - TestDecodeBitStreamWithUnknownJpegMarker(); + test_decode_bit_stream_with_no_marker_start(); + test_decode_bit_stream_with_unsupported_encoding(); + test_decode_bit_stream_with_unknown_jpeg_marker(); + + cout << "Test Legacy API\n"; + test_legacy_api(); } - catch (const UnitTestException&) + catch (const unit_test_exception&) { cout << "==> Unit test failed <==\n"; } @@ -627,11 +688,12 @@ } // namespace -int main(const int argc, const char * const argv[]) +int main(const int argc, const char* const argv[]) // NOLINT(bugprone-exception-escape) { if (argc == 1) { - cout << "CharLS test runner.\nOptions: -unittest, -bitstreamdamage, -performance[:loop-count], -decodeperformance[:loop-count], -decoderaw -encodepnm -decodetopnm -comparepnm\n"; + cout << "CharLS test runner.\nOptions: -unittest, -bitstreamdamage, -performance[:loop-count], " + "-decodeperformance[:loop-count], -decoderaw -encodepnm -decodetopnm -comparepnm -legacy\n"; return EXIT_FAILURE; } @@ -640,7 +702,7 @@ string str = argv[i]; if (str == "-unittest") { - UnitTest(); + unit_test(); continue; } @@ -648,107 +710,136 @@ { if (i != 1 || argc != 4) { - cout << "Syntax: -decoderaw inputfile outputfile\n"; + cout << "Syntax: -decoderaw input-file output-file\n"; return EXIT_FAILURE; } - return make_error_code(DecodeRaw(argv[2], argv[3])) ? EXIT_FAILURE : EXIT_SUCCESS; + return decode_raw(argv[2], argv[3]) ? EXIT_SUCCESS : EXIT_FAILURE; } if (str == "-decodetopnm") { if (i != 1 || argc != 4) { - cout << "Syntax: -decodetopnm inputfile outputfile\n"; + cout << "Syntax: -decodetopnm input-file output-file\n"; return EXIT_FAILURE; } - fstream pnmFile(argv[3], mode_output); - fstream jlsFile(argv[2], mode_input); + ofstream pnm_file(argv[3], mode_output); + ifstream jls_file(argv[2], mode_input); - return DecodeToPnm(jlsFile, pnmFile) ? EXIT_SUCCESS : EXIT_FAILURE; + return decode_to_pnm(jls_file, pnm_file) ? EXIT_SUCCESS : EXIT_FAILURE; } if (str == "-encodepnm") { if (i != 1 || argc != 4) { - cout << "Syntax: -encodepnm inputfile outputfile\n"; + cout << "Syntax: -encodepnm input-file output-file\n"; return EXIT_FAILURE; } - fstream pnmFile(argv[2], mode_input); - const fstream jlsFile(argv[3], mode_output); + ifstream pnm_file(argv[2], mode_input); + ofstream jls_file(argv[3], mode_output); - return EncodePnm(pnmFile, jlsFile) ? EXIT_SUCCESS : EXIT_FAILURE; + return encode_pnm(pnm_file, jls_file) ? EXIT_SUCCESS : EXIT_FAILURE; } if (str == "-comparepnm") { if (i != 1 || argc != 4) { - cout << "Syntax: -encodepnm inputfile outputfile\n"; + cout << "Syntax: -encodepnm input-file output-file\n"; return EXIT_FAILURE; } - fstream pnmFile1(argv[2], mode_input); - fstream pnmFile2(argv[3], mode_input); + ifstream pnm_file1(argv[2], mode_input); + ifstream pnm_file2(argv[3], mode_input); - return ComparePnm(pnmFile1, pnmFile2) ? EXIT_SUCCESS : EXIT_FAILURE; + return compare_pnm(pnm_file1, pnm_file2) ? EXIT_SUCCESS : EXIT_FAILURE; } if (str == "-bitstreamdamage") { - DamagedBitStreamTests(); + damaged_bit_stream_tests(); continue; } if (str.compare(0, 12, "-performance") == 0) { - int loopCount = 1; + int loop_count{1}; - // Extract the optional loop count from the command line. Longer running tests make the measurements more reliable. + // Extract the optional loop count from the command line. Longer running tests make the measurements more + // reliable. auto index = str.find(':'); if (index != string::npos) { - loopCount = stoi(str.substr(++index)); - if (loopCount < 1) + loop_count = stoi(str.substr(++index)); + if (loop_count < 1) { cout << "Loop count not understood or invalid: %s" << str << "\n"; break; } } - PerformanceTests(loopCount); + performance_tests(loop_count); continue; } if (str.compare(0, 17, "-rgb8_performance") == 0) { // See the comments in function, how to prepare this test. - TestLargeImagePerformanceRgb8(1); + test_large_image_performance_rgb8(1); continue; } if (str.compare(0, 18, "-decodeperformance") == 0) { - int loopCount = 1; + int loop_count{1}; + + // Extract the optional loop count from the command line. Longer running tests make the measurements more + // reliable. + auto index = str.find(':'); + if (index != string::npos) + { + loop_count = stoi(str.substr(++index)); + if (loop_count < 1) + { + cout << "Loop count not understood or invalid: " << str << "\n"; + break; + } + } + + decode_performance_tests(loop_count); + continue; + } + + if (str.compare(0, 19, "-encode-performance") == 0) + { + int loop_count{1}; - // Extract the optional loop count from the command line. Longer running tests make the measurements more reliable. + // Extract the optional loop count from the command line. Longer running tests make the measurements more + // reliable. auto index = str.find(':'); if (index != string::npos) { - loopCount = stoi(str.substr(++index)); - if (loopCount < 1) + loop_count = stoi(str.substr(++index)); + if (loop_count < 1) { cout << "Loop count not understood or invalid: " << str << "\n"; break; } } - DecodePerformanceTests(loopCount); + encode_performance_tests(loop_count); continue; } if (str == "-dicom") { - TestDicomWG4Images(); + test_dicom_wg4_images(); + continue; + } + + if (str == "-legacy") + { + test_legacy_api(); continue; } diff -Nru charls-2.1.0+dfsg/test/performance.cpp charls-2.2.0+dfsg/test/performance.cpp --- charls-2.1.0+dfsg/test/performance.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/performance.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -2,83 +2,88 @@ // SPDX-License-Identifier: BSD-3-Clause #include "performance.h" + +#include "portable_anymap_file.h" #include "util.h" -#include -#include #include #include +#include +#include -using std::vector; +using charls::frame_info; +using charls::jpegls_decoder; +using charls::jpegls_encoder; +using charls::jpegls_error; using std::cout; -using std::error_code; using std::istream; -using std::chrono::steady_clock; -using std::chrono::duration; using std::milli; +using std::vector; +using std::chrono::duration; +using std::chrono::steady_clock; -namespace -{ +namespace { -void TestFile16BitAs12(const char* filename, int offset, Size size2, int componentCount, bool littleEndianFile) +void test_file16_bit_as12(const char* filename, const int offset, const rect_size size2, const int component_count, + const bool little_endian_file) { - vector uncompressedData = ReadFile(filename, offset); + vector uncompressed_data = read_file(filename, offset); - FixEndian(&uncompressedData, littleEndianFile); + fix_endian(&uncompressed_data, little_endian_file); - const auto p = reinterpret_cast(uncompressedData.data()); + auto* const p = reinterpret_cast(uncompressed_data.data()); - for (size_t i = 0; i < uncompressedData.size() / 2; ++i) + for (size_t i = 0; i < uncompressed_data.size() / 2; ++i) { p[i] = p[i] >> 4; } - TestRoundTrip(filename, uncompressedData, size2, 12, componentCount); + test_round_trip(filename, uncompressed_data, size2, 12, component_count); } -void TestPerformance(int loopCount) +void test_performance(const int loop_count) { - ////TestFile("test/bad.raw", 0, Size(512, 512), 8, 1); + ////TestFile("test/bad.raw", 0, rect_size(512, 512), 8, 1); // RGBA image (This is a common PNG sample) - TestFile("test/alphatest.raw", 0, Size(380, 287), 8, 4, false, loopCount); + test_file("test/alphatest.raw", 0, rect_size(380, 287), 8, 4, false, loop_count); - const Size size1024 = Size(1024, 1024); - const Size size512 = Size(512, 512); + const rect_size size1024 = rect_size(1024, 1024); + const rect_size size512 = rect_size(512, 512); // 16 bit mono - TestFile("test/MR2_UNC", 1728, size1024, 16, 1, true, loopCount); + test_file("test/MR2_UNC", 1728, size1024, 16, 1, true, loop_count); // 8 bit mono - TestFile("test/0015.raw", 0, size1024, 8, 1, false, loopCount); - TestFile("test/lena8b.raw", 0, size512, 8, 1, false, loopCount); + test_file("test/0015.raw", 0, size1024, 8, 1, false, loop_count); + test_file("test/lena8b.raw", 0, size512, 8, 1, false, loop_count); // 8 bit color - TestFile("test/desktop.ppm", 40, Size(1280, 1024), 8, 3, false, loopCount); + test_file("test/desktop.ppm", 40, rect_size(1280, 1024), 8, 3, false, loop_count); // 12 bit RGB - TestFile("test/SIEMENS-MR-RGB-16Bits.dcm", -1, Size(192, 256), 12, 3, true, loopCount); - TestFile16BitAs12("test/DSC_5455.raw", 142949, Size(300, 200), 3, true); + test_file("test/SIEMENS-MR-RGB-16Bits.dcm", -1, rect_size(192, 256), 12, 3, true, loop_count); + test_file16_bit_as12("test/DSC_5455.raw", 142949, rect_size(300, 200), 3, true); // 16 bit RGB - TestFile("test/DSC_5455.raw", 142949, Size(300, 200), 16, 3, true, loopCount); + test_file("test/DSC_5455.raw", 142949, rect_size(300, 200), 16, 3, true, loop_count); } } // namespace -void PerformanceTests(int loopCount) +void performance_tests(const int loop_count) { #ifdef _DEBUG cout << "NOTE: running performance test in debug mode, performance may be slow!\n"; #endif - cout << "Test Perf (with loop count "<< loopCount << ")\n"; - TestPerformance(loopCount); + cout << "Test Perf (with loop count " << loop_count << ")\n"; + test_performance(loop_count); } -void TestLargeImagePerformanceRgb8(int loopCount) +void test_large_image_performance_rgb8(const int loop_count) { // Note: the test images are very large and not included in the repository. // The images can be downloaded from: http://imagecompression.info/test_images/ @@ -90,20 +95,20 @@ try { - test_portable_anymap_file("test/rgb8bit/artificial.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/big_building.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/big_tree.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/bridge.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/cathedral.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/deer.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/fireworks.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/flower_foveon.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/hdr.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/leaves_iso_200.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/leaves_iso_1600.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/nightshot_iso_100.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/nightshot_iso_1600.ppm", loopCount); - test_portable_anymap_file("test/rgb8bit/spider_web.ppm", loopCount); + test_portable_anymap_file("test/rgb8bit/artificial.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/big_building.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/big_tree.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/bridge.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/cathedral.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/deer.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/fireworks.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/flower_foveon.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/hdr.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/leaves_iso_200.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/leaves_iso_1600.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/nightshot_iso_100.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/nightshot_iso_1600.ppm", loop_count); + test_portable_anymap_file("test/rgb8bit/spider_web.ppm", loop_count); } catch (const istream::failure& error) { @@ -111,33 +116,67 @@ } } -void DecodePerformanceTests(int loopCount) +void decode_performance_tests(const int loop_count) { - cout << "Test decode Perf (with loop count " << loopCount << ")\n"; + cout << "Test decode performance with loop count " << loop_count << "\n"; + + const vector jpegls_compressed = read_file("decodetest.jls"); + + try + { + vector uncompressed; - vector jpeglsCompressed = ReadFile("decodetest.jls"); + const auto start = steady_clock::now(); + for (int i = 0; i < loop_count; ++i) + { + jpegls_decoder::decode(jpegls_compressed, uncompressed); + } - JlsParameters params{}; - error_code error = JpegLsReadHeader(jpeglsCompressed.data(), jpeglsCompressed.size(), ¶ms, nullptr); - if (error) - return; + const auto end = steady_clock::now(); + const auto diff = end - start; + cout << "Total decoding time is: " << duration(diff).count() << " ms\n"; + cout << "Decoding time per image: " << duration(diff).count() / loop_count << " ms\n"; + } + catch (const jpegls_error& e) + { + cout << "Decode failure: " << e.what() << "\n"; + } +} - vector uncompressed(static_cast(params.height) * params.width * ((params.bitsPerSample + 7) / 8) * params.components); +void encode_performance_tests(const int loop_count) +{ + cout << "Test encode performance with loop count " << loop_count << "\n"; - const auto start = steady_clock::now(); - for (int i = 0; i < loopCount; ++i) + const charls_test::portable_anymap_file anymap_file("encode-test.pnm"); + + try { + const frame_info info{static_cast(anymap_file.width()), static_cast(anymap_file.height()), + anymap_file.bits_per_sample(), anymap_file.component_count()}; + const auto interleave_mode = + anymap_file.component_count() > 1 ? charls::interleave_mode::sample : charls::interleave_mode::none; + + jpegls_encoder encoder1; + encoder1.frame_info(info).interleave_mode(interleave_mode); + vector destination(encoder1.estimated_destination_size()); - error = JpegLsDecode(uncompressed.data(), uncompressed.size(), jpeglsCompressed.data(), jpeglsCompressed.size(), ¶ms, nullptr); - if (error) + const auto start = steady_clock::now(); + for (int i = 0; i < loop_count; ++i) { - cout << "Decode failure: " << error.value() << "\n"; - return; + jpegls_encoder encoder2; + encoder2.frame_info(info).interleave_mode(interleave_mode); + encoder2.destination(destination); + + static_cast(encoder2.encode(anymap_file.image_data())); } - } - const auto end = steady_clock::now(); - const auto diff = end - start; - cout << "Total decoding time is: " << duration(diff).count() << " ms\n"; - cout << "Decoding time per image: " << duration(diff).count() / loopCount << " ms\n"; + const auto end = steady_clock::now(); + const auto diff = end - start; + cout << "Total encoding time is: " << duration(diff).count() << " ms\n"; + cout << "Encoding time per image: " << duration(diff).count() / loop_count << " ms\n"; + } + catch (const jpegls_error& e) + { + cout << "Encoding failure: " << e.what() << "\n"; + } } diff -Nru charls-2.1.0+dfsg/test/performance.h charls-2.2.0+dfsg/test/performance.h --- charls-2.1.0+dfsg/test/performance.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/performance.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,6 +3,7 @@ #pragma once -void PerformanceTests(int loopCount); -void DecodePerformanceTests(int loopCount); -void TestLargeImagePerformanceRgb8(int loopCount); +void performance_tests(int loop_count); +void decode_performance_tests(int loop_count); +void encode_performance_tests(int loop_count); +void test_large_image_performance_rgb8(int loop_count); diff -Nru charls-2.1.0+dfsg/test/portable_anymap_file.h charls-2.2.0+dfsg/test/portable_anymap_file.h --- charls-2.1.0+dfsg/test/portable_anymap_file.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/portable_anymap_file.h 2021-01-10 18:07:25.000000000 +0000 @@ -9,6 +9,21 @@ #include #include + +// Visual Studio 2015 supports C++14, but not all constexpr scenarios. VS 2017 has full C++14 support. +#ifdef _MSC_VER + +#if _MSC_VER >= 1910 +#define CONSTEXPR constexpr +#else +#define CONSTEXPR +#endif + +#else +#define CONSTEXPR constexpr +#endif + + namespace charls_test { // Purpose: this class can read an image stored in the Portable Anymap Format (PNM). @@ -76,7 +91,7 @@ { std::vector result; - const auto first = static_cast(pnm_file.get()); + const auto first{static_cast(pnm_file.get())}; // All portable anymap format (PNM) start with the character P. if (first != 'P') @@ -90,7 +105,7 @@ while (result.size() < 4) { - int value = -1; + int value{-1}; line >> value; if (value <= 0) break; @@ -101,9 +116,9 @@ return result; } - static constexpr int32_t log_2(int32_t n) noexcept + static CONSTEXPR int32_t log_2(const int32_t n) noexcept { - int32_t x = 0; + int32_t x{}; while (n > (1 << x)) { ++x; @@ -116,7 +131,7 @@ // Anymap files with multi byte pixels are stored in big endian format in the file. if (bits_per_sample_ > 8) { - for (size_t i = 0; i < input_buffer_.size() - 1; i += 2) + for (size_t i{}; i < input_buffer_.size() - 1; i += 2) { std::swap(input_buffer_[i], input_buffer_[i + 1]); } diff -Nru charls-2.1.0+dfsg/test/util.cpp charls-2.2.0+dfsg/test/util.cpp --- charls-2.1.0+dfsg/test/util.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/util.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -11,9 +11,10 @@ #include using std::cout; -using std::error_code; using std::ifstream; +using std::ios; using std::milli; +using std::ofstream; using std::setprecision; using std::setw; using std::swap; @@ -28,22 +29,21 @@ MSVC_WARNING_SUPPRESS(26497) // cannot be marked constexpr, check must be executed at runtime. -bool IsMachineLittleEndian() noexcept +bool is_machine_little_endian() noexcept { - constexpr int a = 0xFF000001; // NOLINT(bugprone-narrowing-conversions) + constexpr int a = 0xFF000001; // NOLINT(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions) const auto* chars = reinterpret_cast(&a); return chars[0] == 0x01; } MSVC_WARNING_UNSUPPRESS() - } // namespace -void FixEndian(vector* buffer, bool littleEndianData) noexcept +void fix_endian(vector* buffer, const bool little_endian_data) noexcept { - if (littleEndianData == IsMachineLittleEndian()) + if (little_endian_data == is_machine_little_endian()) return; for (size_t i = 0; i < buffer->size() - 1; i += 2) @@ -53,24 +53,24 @@ } -vector ReadFile(const char* filename, long offset, size_t bytes) +vector read_file(const char* filename, long offset, size_t bytes) { ifstream input; - input.exceptions(input.eofbit | input.failbit | input.badbit); - input.open(filename, input.in | input.binary); + input.exceptions(ios::eofbit | ios::failbit | ios::badbit); + input.open(filename, ios::in | ios::binary); - input.seekg(0, input.end); - const auto byteCountFile = static_cast(input.tellg()); - input.seekg(offset, input.beg); + input.seekg(0, ios::end); + const auto byte_count_file = static_cast(input.tellg()); + input.seekg(offset, ios::beg); if (offset < 0) { - Assert::IsTrue(bytes != 0); - offset = static_cast(byteCountFile - bytes); + assert::is_true(bytes != 0); + offset = static_cast(byte_count_file - bytes); } if (bytes == 0) { - bytes = static_cast(byteCountFile) - offset; + bytes = static_cast(byte_count_file) - offset; } vector buffer(bytes); @@ -79,93 +79,131 @@ return buffer; } +void write_file(const char* filename, const void* data, const size_t size) +{ + ofstream output; + output.exceptions(ios::eofbit | ios::failbit | ios::badbit); + output.open(filename, ios::out | ios::binary); + output.write(static_cast(data), size); +} -void TestRoundTrip(const char* strName, const vector& decodedBuffer, Size size, int bitsPerSample, int componentCount, int loopCount) +void test_round_trip(const char* name, const vector& decoded_buffer, const rect_size size, + const int bits_per_sample, const int component_count, const int loop_count) { JlsParameters params = JlsParameters(); - params.components = componentCount; - params.bitsPerSample = bitsPerSample; + params.components = component_count; + params.bitsPerSample = bits_per_sample; params.height = static_cast(size.cy); params.width = static_cast(size.cx); - TestRoundTrip(strName, decodedBuffer, params, loopCount); + test_round_trip(name, decoded_buffer, params, loop_count); } -void TestRoundTrip(const char* strName, const vector& originalBuffer, JlsParameters& params, int loopCount) +void test_round_trip(const char* name, const vector& original_buffer, const JlsParameters& params, + const int loop_count) { - vector encodedBuffer(params.height * params.width * params.components * params.bitsPerSample / 4); + vector encoded_buffer(params.height * params.width * params.components * params.bitsPerSample / 4); + + vector decoded_buffer(static_cast(params.height) * params.width * + bit_to_byte_count(params.bitsPerSample) * params.components); - vector decodedBuffer(static_cast(params.height) * params.width * ((params.bitsPerSample + 7) / 8) * params.components); + interleave_mode interleave_mode{params.interleaveMode}; + color_transformation color_transformation{params.colorTransformation}; if (params.components == 4) { - params.interleaveMode = interleave_mode::line; + interleave_mode = charls::interleave_mode::line; } else if (params.components == 3) { - params.interleaveMode = interleave_mode::line; - params.colorTransformation = color_transformation::hp1; + interleave_mode = charls::interleave_mode::line; + color_transformation = charls::color_transformation::hp1; } size_t encoded_actual_size{}; auto start = steady_clock::now(); - for (int i = 0; i < loopCount; ++i) + for (int i = 0; i < loop_count; ++i) { - const error_code error = JpegLsEncode(encodedBuffer.data(), encodedBuffer.size(), &encoded_actual_size, - originalBuffer.data(), originalBuffer.size(), ¶ms, nullptr); - Assert::IsTrue(!error); + try + { + jpegls_encoder encoder; + encoder.destination(encoded_buffer) + .frame_info({static_cast(params.width), static_cast(params.height), params.bitsPerSample, + params.components}) + .interleave_mode(interleave_mode) + .color_transformation(color_transformation); + + encoded_actual_size = encoder.encode(original_buffer); + } + catch (...) + { + assert::is_true(false); + } } - const auto totalEncodeDuration = steady_clock::now() - start; + const auto total_encode_duration = steady_clock::now() - start; start = steady_clock::now(); - for (int i = 0; i < loopCount; ++i) + for (int i = 0; i < loop_count; ++i) { - const error_code error = JpegLsDecode(decodedBuffer.data(), decodedBuffer.size(), encodedBuffer.data(), encoded_actual_size, nullptr, nullptr); - Assert::IsTrue(!error); + try + { + jpegls_decoder decoder; + decoder.source(encoded_buffer.data(), encoded_actual_size).read_header(); + decoder.decode(decoded_buffer); + } + catch (...) + { + assert::is_true(false); + } } - const auto totalDecodeDuration = steady_clock::now() - start; - - const double bitsPerSample = 1.0 * encoded_actual_size * 8 / (static_cast(params.components) * params.height * params.width); - cout << "RoundTrip test for: " << strName << "\n\r"; - const double encodeTime = duration(totalEncodeDuration).count() / loopCount; - const double decodeTime = duration(totalDecodeDuration).count() / loopCount; - const double symbolRate = (static_cast(params.components) * params.height * params.width) / (1000.0 * decodeTime); + const auto total_decode_duration = steady_clock::now() - start; - cout << "Size:" << setw(10) << params.width << "x" << params.height << setw(7) << setprecision(2) << ", Encode time:" << encodeTime << " ms, Decode time:" << decodeTime << " ms, Bits per sample:" << bitsPerSample << ", Decode rate:" << symbolRate << " M/s\n"; + const double bits_per_sample = 1.0 * static_cast(encoded_actual_size) * 8. / + (static_cast(params.components) * params.height * params.width); + cout << "RoundTrip test for: " << name << "\n\r"; + const double encode_time = duration(total_encode_duration).count() / loop_count; + const double decode_time = duration(total_decode_duration).count() / loop_count; + const double symbol_rate = + (static_cast(params.components) * params.height * params.width) / (1000.0 * decode_time); + + cout << "Size:" << setw(10) << params.width << "x" << params.height << setw(7) << setprecision(2) + << ", Encode time:" << encode_time << " ms, Decode time:" << decode_time + << " ms, Bits per sample:" << bits_per_sample << ", Decode rate:" << symbol_rate << " M/s\n"; - const uint8_t* byteOut = decodedBuffer.data(); - for (size_t i = 0; i < decodedBuffer.size(); ++i) + const uint8_t* byte_out = decoded_buffer.data(); + for (size_t i = 0; i < decoded_buffer.size(); ++i) { - if (originalBuffer[i] != byteOut[i]) + if (original_buffer[i] != byte_out[i]) { - Assert::IsTrue(false); + assert::is_true(false); break; } } } -void TestFile(const char* filename, int offset, Size size2, int bitsPerSample, int componentCount, bool littleEndianFile, int loopCount) +void test_file(const char* filename, const int offset, const rect_size size2, const int bits_per_sample, + const int component_count, const bool little_endian_file, const int loop_count) { - const size_t byteCount = size2.cx * size2.cy * componentCount * ((bitsPerSample + 7) / 8); - vector uncompressedBuffer = ReadFile(filename, offset, byteCount); + const size_t byte_count = size2.cx * size2.cy * component_count * bit_to_byte_count(bits_per_sample); + vector uncompressed_buffer = read_file(filename, offset, byte_count); - if (bitsPerSample > 8) + if (bits_per_sample > 8) { - FixEndian(&uncompressedBuffer, littleEndianFile); + fix_endian(&uncompressed_buffer, little_endian_file); } - TestRoundTrip(filename, uncompressedBuffer, size2, bitsPerSample, componentCount, loopCount); + test_round_trip(filename, uncompressed_buffer, size2, bits_per_sample, component_count, loop_count); } -void test_portable_anymap_file(const char* filename, int loopCount) +void test_portable_anymap_file(const char* filename, const int loop_count) { - portable_anymap_file anymapFile(filename); + portable_anymap_file anymap_file(filename); - TestRoundTrip(filename, anymapFile.image_data(), Size(anymapFile.width(), anymapFile.height()), - anymapFile.bits_per_sample(), anymapFile.component_count(), loopCount); + test_round_trip(filename, anymap_file.image_data(), rect_size(anymap_file.width(), anymap_file.height()), + anymap_file.bits_per_sample(), anymap_file.component_count(), loop_count); } diff -Nru charls-2.1.0+dfsg/test/util.h charls-2.2.0+dfsg/test/util.h --- charls-2.1.0+dfsg/test/util.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/test/util.h 2021-01-10 18:07:25.000000000 +0000 @@ -3,50 +3,95 @@ #pragma once -#include #include -#include #include +#include -struct Size final +struct rect_size final { - Size(size_t width, size_t height) noexcept - : - cx(width), - cy(height) - {} + rect_size(const size_t width, const size_t height) noexcept : cx(width), cy(height) + { + } size_t cx; size_t cy; }; -void FixEndian(std::vector* buffer, bool littleEndianData) noexcept; -std::vector ReadFile(const char* filename, long offset = 0, size_t bytes = 0); -void TestFile(const char* filename, int offset, Size size2, int bitsPerSample, int componentCount, bool littleEndianFile = false, int loopCount = 1); -void TestRoundTrip(const char* strName, const std::vector& decodedBuffer, Size size, int bitsPerSample, int componentCount, int loopCount = 1); -void TestRoundTrip(const char* strName, const std::vector& originalBuffer, JlsParameters& params, int loopCount = 1); -void test_portable_anymap_file(const char* filename, int loopCount = 1); +void fix_endian(std::vector* buffer, bool little_endian_data) noexcept; +std::vector read_file(const char* filename, long offset = 0, size_t bytes = 0); +void write_file(const char* filename, const void* data, size_t size); +void test_file(const char* filename, int offset, rect_size size2, int bits_per_sample, int component_count, + bool little_endian_file = false, int loop_count = 1); +void test_round_trip(const char* name, const std::vector& decoded_buffer, rect_size size, int bits_per_sample, + int component_count, int loop_count = 1); +void test_round_trip(const char* name, const std::vector& original_buffer, const JlsParameters& params, + int loop_count = 1); +void test_portable_anymap_file(const char* filename, int loop_count = 1); + +/// +/// Computes how many bytes are needed to hold the number of bits. +/// +constexpr uint32_t bit_to_byte_count(const int32_t bit_count) noexcept +{ + return static_cast((bit_count + 7) / 8); +} -class UnitTestException final : public std::exception { +class unit_test_exception final : public std::exception +{ public: - explicit UnitTestException() = default; + explicit unit_test_exception() = default; }; -class Assert final +class assert final { public: - static void IsTrue(bool condition) + static void is_true(const bool condition) { if (!condition) - throw UnitTestException(); + throw unit_test_exception(); } }; #ifdef _MSC_VER -#define MSVC_WARNING_SUPPRESS(x) __pragma(warning(push)) __pragma(warning(disable : x)) // NOLINT(misc-macro-parentheses, bugprone-macro-parentheses) +#define MSVC_WARNING_SUPPRESS(x) \ + __pragma(warning(push)) __pragma(warning(disable : x)) // NOLINT(misc-macro-parentheses, bugprone-macro-parentheses) #define MSVC_WARNING_UNSUPPRESS() __pragma(warning(pop)) +#define MSVC_WARNING_SUPPRESS_NEXT_LINE(x) \ + __pragma(warning(suppress \ + : x)) // NOLINT(misc-macro-parentheses, bugprone-macro-parentheses, cppcoreguidelines-macro-usage) +#define MSVC_CONST const #else #define MSVC_WARNING_SUPPRESS(x) #define MSVC_WARNING_UNSUPPRESS() +#define MSVC_WARNING_SUPPRESS_NEXT_LINE(x) +#define MSVC_CONST +#endif + +// clang-format off +#ifdef __clang__ +#define DISABLE_DEPRECATED_WARNING \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif defined(__GNUC__) || defined(__GNUG__) +#define DISABLE_DEPRECATED_WARNING \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif defined(_MSC_VER) +#define DISABLE_DEPRECATED_WARNING \ + __pragma(warning(push)) \ + __pragma(warning(disable : 4996)) // was declared deprecated +#else +#define DISABLE_DEPRECATED_WARNING +#endif +// clang-format on + +#ifdef __clang__ +#define RESTORE_DEPRECATED_WARNING _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) || defined(__GNUG__) +#define RESTORE_DEPRECATED_WARNING _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +#define RESTORE_DEPRECATED_WARNING __pragma(warning(pop)) +#else +#define RESTORE_DEPRECATED_WARNING #endif Binary files /tmp/tmptxyqr8/iDZBmIvU8B/charls-2.1.0+dfsg/unittest/4bit-monochrome.pgm and /tmp/tmptxyqr8/i0J_6ju0mM/charls-2.2.0+dfsg/unittest/4bit-monochrome.pgm differ diff -Nru charls-2.1.0+dfsg/unittest/charls_jpegls_decoder_test.cpp charls-2.2.0+dfsg/unittest/charls_jpegls_decoder_test.cpp --- charls-2.1.0+dfsg/unittest/charls_jpegls_decoder_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/charls_jpegls_decoder_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -1,24 +1,32 @@ // Copyright (c) Team CharLS. // SPDX-License-Identifier: BSD-3-Clause +// ReSharper disable CppClangTidyClangDiagnosticNonnull #include "pch.h" #include "util.h" #include +#include + using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -using namespace charls; +using std::array; +using std::vector; +MSVC_WARNING_SUPPRESS(6387) // '_Param_(x)' could be '0': this does not adhere to the specification for the function. -namespace CharLSUnitTest { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" +#endif -// clang-format off +namespace charls { namespace test { TEST_CLASS(charls_jpegls_decoder_test) { public: - TEST_METHOD(destroy_nullptr) + TEST_METHOD(destroy_nullptr) // NOLINT { charls_jpegls_decoder_destroy(nullptr); @@ -26,119 +34,144 @@ Assert::IsTrue(true); } - TEST_METHOD(set_source_buffer_nullptr) + TEST_METHOD(set_source_buffer_nullptr) // NOLINT { - const uint8_t buffer[10]{}; + const array buffer{}; - auto error = charls_jpegls_decoder_set_source_buffer(nullptr, buffer, sizeof buffer); + auto error = charls_jpegls_decoder_set_source_buffer(nullptr, buffer.data(), buffer.size()); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto decoder = charls_jpegls_decoder_create(); - error = charls_jpegls_decoder_set_source_buffer(decoder, nullptr, sizeof buffer); + auto* const decoder = charls_jpegls_decoder_create(); + error = charls_jpegls_decoder_set_source_buffer(decoder, nullptr, buffer.size()); charls_jpegls_decoder_destroy(decoder); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(read_spiff_header_nullptr) + TEST_METHOD(read_spiff_header_nullptr) // NOLINT { - charls_spiff_header spiff_header; + charls_spiff_header spiff_header{}; int32_t header_found; auto error = charls_jpegls_decoder_read_spiff_header(nullptr, &spiff_header, &header_found); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto decoder = charls_jpegls_decoder_create(); + const vector source{read_file("DataFiles/t8c0e0.jls")}; + auto* decoder = charls_jpegls_decoder_create(); + error = charls_jpegls_decoder_set_source_buffer(decoder, source.data(), source.size()); + Assert::AreEqual(jpegls_errc::success, error); error = charls_jpegls_decoder_read_spiff_header(decoder, nullptr, &header_found); Assert::AreEqual(jpegls_errc::invalid_argument, error); + charls_jpegls_decoder_destroy(decoder); + decoder = charls_jpegls_decoder_create(); + error = charls_jpegls_decoder_set_source_buffer(decoder, source.data(), source.size()); + Assert::AreEqual(jpegls_errc::success, error); error = charls_jpegls_decoder_read_spiff_header(decoder, &spiff_header, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); - charls_jpegls_decoder_destroy(decoder); } - TEST_METHOD(read_header_nullptr) + TEST_METHOD(read_header_nullptr) // NOLINT { const auto error = charls_jpegls_decoder_read_header(nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(get_frame_info_nullptr) + TEST_METHOD(get_frame_info_nullptr) // NOLINT { frame_info frame_info; auto error = charls_jpegls_decoder_get_frame_info(nullptr, &frame_info); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto* decoder = charls_jpegls_decoder_create(); + const auto* decoder = get_initialized_decoder(); error = charls_jpegls_decoder_get_frame_info(decoder, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); charls_jpegls_decoder_destroy(decoder); } - TEST_METHOD(get_near_lossless_nullptr) + TEST_METHOD(get_near_lossless_nullptr) // NOLINT { int32_t near_lossless; auto error = charls_jpegls_decoder_get_near_lossless(nullptr, 0, &near_lossless); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto* decoder = charls_jpegls_decoder_create(); + const auto* decoder = get_initialized_decoder(); error = charls_jpegls_decoder_get_near_lossless(decoder, 0, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); charls_jpegls_decoder_destroy(decoder); } - TEST_METHOD(get_interleave_mode_nullptr) + TEST_METHOD(get_interleave_mode_nullptr) // NOLINT { interleave_mode interleave_mode; auto error = charls_jpegls_decoder_get_interleave_mode(nullptr, &interleave_mode); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto* decoder = charls_jpegls_decoder_create(); + const auto* decoder = get_initialized_decoder(); error = charls_jpegls_decoder_get_interleave_mode(decoder, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); charls_jpegls_decoder_destroy(decoder); } - TEST_METHOD(get_preset_coding_parameters_nullptr) + TEST_METHOD(get_preset_coding_parameters_nullptr) // NOLINT { jpegls_pc_parameters preset_coding_parameters; auto error = charls_jpegls_decoder_get_preset_coding_parameters(nullptr, 0, &preset_coding_parameters); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto* decoder = charls_jpegls_decoder_create(); + const auto* decoder = get_initialized_decoder(); error = charls_jpegls_decoder_get_preset_coding_parameters(decoder, 0, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); charls_jpegls_decoder_destroy(decoder); } - TEST_METHOD(get_destination_size_nullptr) + TEST_METHOD(get_destination_size_nullptr) // NOLINT { size_t destination_size_bytes; auto error = charls_jpegls_decoder_get_destination_size(nullptr, 0, &destination_size_bytes); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto* decoder = charls_jpegls_decoder_create(); + const auto* decoder = get_initialized_decoder(); error = charls_jpegls_decoder_get_destination_size(decoder, 0, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); charls_jpegls_decoder_destroy(decoder); } - TEST_METHOD(decode_to_buffer_nullptr) + TEST_METHOD(decode_to_buffer_nullptr) // NOLINT { - uint8_t buffer[5]; - auto error = charls_jpegls_decoder_decode_to_buffer(nullptr, buffer, 5, 0); + array buffer{}; + auto error = charls_jpegls_decoder_decode_to_buffer(nullptr, buffer.data(), buffer.size(), 0); Assert::AreEqual(jpegls_errc::invalid_argument, error); const auto* decoder = charls_jpegls_decoder_create(); - error = charls_jpegls_decoder_decode_to_buffer(decoder, nullptr, 5, 0); + error = charls_jpegls_decoder_decode_to_buffer(decoder, nullptr, buffer.size(), 0); Assert::AreEqual(jpegls_errc::invalid_argument, error); charls_jpegls_decoder_destroy(decoder); } + +private: + static charls_jpegls_decoder* get_initialized_decoder() + { + const vector source{read_file("DataFiles/t8c0e0.jls")}; + auto* const decoder = charls_jpegls_decoder_create(); + auto error = charls_jpegls_decoder_set_source_buffer(decoder, source.data(), source.size()); + Assert::AreEqual(jpegls_errc::success, error); + error = charls_jpegls_decoder_read_header(decoder); + Assert::AreEqual(jpegls_errc::success, error); + + return decoder; + } }; -} +}} // namespace charls::test + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +MSVC_WARNING_UNSUPPRESS() diff -Nru charls-2.1.0+dfsg/unittest/charls_jpegls_encoder_test.cpp charls-2.2.0+dfsg/unittest/charls_jpegls_encoder_test.cpp --- charls-2.1.0+dfsg/unittest/charls_jpegls_encoder_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/charls_jpegls_encoder_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -7,18 +7,24 @@ #include +#include + using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -using namespace charls; +using std::array; +MSVC_WARNING_SUPPRESS(6387) // '_Param_(x)' could be '0': this does not adhere to the specification for the function. -namespace CharLSUnitTest { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" +#endif -// clang-format off +namespace charls { namespace test { TEST_CLASS(charls_jpegls_encoder_test) { public: - TEST_METHOD(destroy_nullptr) + TEST_METHOD(destroy_nullptr) // NOLINT { charls_jpegls_encoder_destroy(nullptr); @@ -26,126 +32,137 @@ Assert::IsTrue(true); } - TEST_METHOD(set_destination_buffer_nullptr) + TEST_METHOD(set_destination_buffer_nullptr) // NOLINT { - uint8_t buffer[10]{}; - auto error = charls_jpegls_encoder_set_destination_buffer(nullptr, buffer, sizeof buffer); + array buffer{}; + auto error = charls_jpegls_encoder_set_destination_buffer(nullptr, buffer.data(), buffer.size()); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto encoder = charls_jpegls_encoder_create(); - error = charls_jpegls_encoder_set_destination_buffer(encoder, nullptr, sizeof buffer); + auto* const encoder = charls_jpegls_encoder_create(); + error = charls_jpegls_encoder_set_destination_buffer(encoder, nullptr, buffer.size()); charls_jpegls_encoder_destroy(encoder); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(set_frame_info_buffer_nullptr) + TEST_METHOD(set_frame_info_buffer_nullptr) // NOLINT { charls_frame_info frame_info{}; auto error = charls_jpegls_encoder_set_frame_info(nullptr, &frame_info); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto encoder = charls_jpegls_encoder_create(); + auto* const encoder = charls_jpegls_encoder_create(); error = charls_jpegls_encoder_set_frame_info(encoder, nullptr); charls_jpegls_encoder_destroy(encoder); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(set_near_lossless_nullptr) + TEST_METHOD(set_near_lossless_nullptr) // NOLINT { const auto error = charls_jpegls_encoder_set_near_lossless(nullptr, 1); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(set_interleave_mode_nullptr) + TEST_METHOD(set_interleave_mode_nullptr) // NOLINT { const auto error = charls_jpegls_encoder_set_interleave_mode(nullptr, charls_interleave_mode::line); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(set_preset_coding_parameters_nullptr) + TEST_METHOD(set_preset_coding_parameters_nullptr) // NOLINT { charls_jpegls_pc_parameters parameters{}; auto error = charls_jpegls_encoder_set_preset_coding_parameters(nullptr, ¶meters); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto encoder = charls_jpegls_encoder_create(); + auto* const encoder = charls_jpegls_encoder_create(); error = charls_jpegls_encoder_set_preset_coding_parameters(encoder, nullptr); charls_jpegls_encoder_destroy(encoder); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(set_color_transformation_nullptr) + TEST_METHOD(set_color_transformation_nullptr) // NOLINT { const auto error = charls_jpegls_encoder_set_color_transformation(nullptr, charls_color_transformation::hp1); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(get_estimated_destination_size_nullptr) + TEST_METHOD(get_estimated_destination_size_nullptr) // NOLINT { size_t size_in_bytes{}; auto error = charls_jpegls_encoder_get_estimated_destination_size(nullptr, &size_in_bytes); Assert::AreEqual(jpegls_errc::invalid_argument, error); - charls_jpegls_encoder const * const encoder = charls_jpegls_encoder_create(); + charls_jpegls_encoder* const encoder = charls_jpegls_encoder_create(); + + charls_frame_info frame_info{1, 1, 2, 1}; + error = charls_jpegls_encoder_set_frame_info(encoder, &frame_info); + Assert::AreEqual(jpegls_errc::success, error); + error = charls_jpegls_encoder_get_estimated_destination_size(encoder, nullptr); charls_jpegls_encoder_destroy(encoder); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(get_bytes_written_nullptr) + TEST_METHOD(get_bytes_written_nullptr) // NOLINT { size_t bytes_written{}; auto error = charls_jpegls_encoder_get_bytes_written(nullptr, &bytes_written); Assert::AreEqual(jpegls_errc::invalid_argument, error); - charls_jpegls_encoder const * const encoder = charls_jpegls_encoder_create(); + charls_jpegls_encoder const* const encoder = charls_jpegls_encoder_create(); error = charls_jpegls_encoder_get_bytes_written(encoder, nullptr); charls_jpegls_encoder_destroy(encoder); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(encode_from_buffer_nullptr) + TEST_METHOD(encode_from_buffer_nullptr) // NOLINT { - const uint8_t source_buffer[10]{}; - auto error = charls_jpegls_encoder_encode_from_buffer(nullptr, source_buffer, sizeof source_buffer, 0); + const array source_buffer{}; + auto error = charls_jpegls_encoder_encode_from_buffer(nullptr, source_buffer.data(), source_buffer.size(), 0); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto encoder = charls_jpegls_encoder_create(); - error = charls_jpegls_encoder_encode_from_buffer(encoder, nullptr, sizeof source_buffer, 0); + auto* const encoder = charls_jpegls_encoder_create(); + error = charls_jpegls_encoder_encode_from_buffer(encoder, nullptr, source_buffer.size(), 0); charls_jpegls_encoder_destroy(encoder); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(write_spiff_header_nullptr) + TEST_METHOD(write_spiff_header_nullptr) // NOLINT { charls_spiff_header spiff_header{}; auto error = charls_jpegls_encoder_write_spiff_header(nullptr, &spiff_header); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto encoder = charls_jpegls_encoder_create(); + auto* const encoder = charls_jpegls_encoder_create(); error = charls_jpegls_encoder_write_spiff_header(encoder, nullptr); charls_jpegls_encoder_destroy(encoder); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(write_standard_spiff_header_nullptr) + TEST_METHOD(write_standard_spiff_header_nullptr) // NOLINT { - const auto error = charls_jpegls_encoder_write_standard_spiff_header(nullptr, charls_spiff_color_space::cie_lab, - charls_spiff_resolution_units::dots_per_centimeter, 1, 1); - Assert::AreEqual(charls::jpegls_errc::invalid_argument, error); + const auto error = charls_jpegls_encoder_write_standard_spiff_header( + nullptr, charls_spiff_color_space::cie_lab, charls_spiff_resolution_units::dots_per_centimeter, 1, 1); + Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(write_spiff_entry_nullptr) + TEST_METHOD(write_spiff_entry_nullptr) // NOLINT { - const uint8_t entry_data[10]{}; - auto error = charls_jpegls_encoder_write_spiff_entry(nullptr, 5, entry_data, sizeof(entry_data)); + const array entry_data{}; + auto error = charls_jpegls_encoder_write_spiff_entry(nullptr, 5, entry_data.data(), entry_data.size()); Assert::AreEqual(jpegls_errc::invalid_argument, error); - const auto encoder = charls_jpegls_encoder_create(); + auto* const encoder = charls_jpegls_encoder_create(); error = charls_jpegls_encoder_write_spiff_entry(encoder, 5, nullptr, sizeof(entry_data)); charls_jpegls_encoder_destroy(encoder); Assert::AreEqual(jpegls_errc::invalid_argument, error); } }; -} +}} // namespace charls::test + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +MSVC_WARNING_UNSUPPRESS() diff -Nru charls-2.1.0+dfsg/unittest/color_transform_test.cpp charls-2.2.0+dfsg/unittest/color_transform_test.cpp --- charls-2.1.0+dfsg/unittest/color_transform_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/color_transform_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -3,97 +3,125 @@ #include "pch.h" +#include "util.h" + #include "../src/color_transform.h" -using Microsoft::VisualStudio::CppUnitTestFramework::Assert; +#include -namespace CharLSUnitTest { +#include + +using Microsoft::VisualStudio::CppUnitTestFramework::Assert; +using std::vector; -// clang-format off +namespace charls { namespace test { -TEST_CLASS(ColorTransformTest) +TEST_CLASS(color_transform_test) { public: - TEST_METHOD(TransformHp1RoundTrip) + TEST_METHOD(transform_hp1_round_trip) // NOLINT { // For the normal unit test keep the range small for a quick test. // For a complete test which will take a while set the start and end to 0 and 255. - constexpr int startValue = 123; - constexpr int endValue = 124; + constexpr int start_value = 123; + constexpr int end_value = 124; - for (int red = startValue; red < endValue; ++red) + for (int red = start_value; red < end_value; ++red) { for (int green = 0; green < 255; ++green) { for (int blue = 0; blue < 255; ++blue) { - const charls::TransformHp1 transform; + const transform_hp1 transform; const auto sample = transform(red, green, blue); - const charls::TransformHp1::Inverse inverse(transform); + const transform_hp1::inverse inverse(transform); - const auto roundTrip = inverse(sample.v1, sample.v2, sample.v3); + const auto round_trip = inverse(sample.v1, sample.v2, sample.v3); - Assert::AreEqual(static_cast(red), roundTrip.R); - Assert::AreEqual(static_cast(green), roundTrip.G); - Assert::AreEqual(static_cast(blue), roundTrip.B); + Assert::AreEqual(static_cast(red), round_trip.R); + Assert::AreEqual(static_cast(green), round_trip.G); + Assert::AreEqual(static_cast(blue), round_trip.B); } } } } - TEST_METHOD(TransformHp2RoundTrip) + TEST_METHOD(transform_hp2_round_trip) // NOLINT { // For the normal unit test keep the range small for a quick test. // For a complete test which will take a while set the start and end to 0 and 255. - constexpr int startValue = 123; - constexpr int endValue = 124; + constexpr int start_value = 123; + constexpr int end_value = 124; - for (int red = startValue; red < endValue; ++red) + for (int red = start_value; red < end_value; ++red) { for (int green = 0; green < 255; ++green) { for (int blue = 0; blue < 255; ++blue) { - const charls::TransformHp2 transform; + const transform_hp2 transform; const auto sample = transform(red, green, blue); - const charls::TransformHp2::Inverse inverse(transform); + const transform_hp2::inverse inverse(transform); - const auto roundTrip = inverse(sample.v1, sample.v2, sample.v3); + const auto round_trip = inverse(sample.v1, sample.v2, sample.v3); - Assert::AreEqual(static_cast(red), roundTrip.R); - Assert::AreEqual(static_cast(green), roundTrip.G); - Assert::AreEqual(static_cast(blue), roundTrip.B); + Assert::AreEqual(static_cast(red), round_trip.R); + Assert::AreEqual(static_cast(green), round_trip.G); + Assert::AreEqual(static_cast(blue), round_trip.B); } } } } - TEST_METHOD(TransformHp3RoundTrip) + TEST_METHOD(transform_hp3_round_trip) // NOLINT { // For the normal unit test keep the range small for a quick test. // For a complete test which will take a while set the start and end to 0 and 255. - constexpr uint8_t startValue = 123; - constexpr uint8_t endValue = 124; + constexpr uint8_t start_value = 123; + constexpr uint8_t end_value = 124; - const charls::TransformHp3 transformation; + const transform_hp3 transformation; - for (int red = startValue; red < endValue; ++red) + for (int red = start_value; red < end_value; ++red) { for (int green = 0; green < 255; ++green) { for (int blue = 0; blue < 255; ++blue) { const auto sample = transformation(red, green, blue); - const charls::TransformHp3::Inverse inverse(transformation); - const auto roundTrip = inverse(sample.v1, sample.v2, sample.v3); + const transform_hp3::inverse inverse(transformation); + const auto round_trip = inverse(sample.v1, sample.v2, sample.v3); - Assert::AreEqual(static_cast(red), roundTrip.R); - Assert::AreEqual(static_cast(green), roundTrip.G); - Assert::AreEqual(static_cast(blue), roundTrip.B); + Assert::AreEqual(static_cast(red), round_trip.R); + Assert::AreEqual(static_cast(green), round_trip.G); + Assert::AreEqual(static_cast(blue), round_trip.B); } } } } + + TEST_METHOD(decode_non_8_or_16_bit_is_not_supported) // NOLINT + { + const vector jpegls_data = read_file("land10-10bit-rgb-hp3-invalid.jls"); + + jpegls_decoder decoder{jpegls_data, true}; + + vector destination(decoder.destination_size()); + + assert_expect_exception(jpegls_errc::bit_depth_for_transform_not_supported, [&] { decoder.decode(destination); }); + } + + TEST_METHOD(encode_non_8_or_16_bit_is_not_supported) // NOLINT + { + const frame_info frame_info{2, 1, 10, 3}; + jpegls_encoder encoder; + + vector destination(40); + encoder.destination(destination).frame_info(frame_info).color_transformation(color_transformation::hp3); + vector source(20); + assert_expect_exception(jpegls_errc::bit_depth_for_transform_not_supported, + [&] { static_cast(encoder.encode(source)); }); + } }; -} +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/compliance_test.cpp charls-2.2.0+dfsg/unittest/compliance_test.cpp --- charls-2.1.0+dfsg/unittest/compliance_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/compliance_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -10,91 +10,87 @@ #include "../test/portable_anymap_file.h" using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -using namespace charls; using namespace charls_test; using std::error_code; using std::vector; - -// clang-format off - -namespace CharLSUnitTest { +namespace charls { namespace test { TEST_CLASS(compliance_test) { public: - TEST_METHOD(decompress_color_8_bit_interleave_none_lossless) + TEST_METHOD(decompress_color_8_bit_interleave_none_lossless) // NOLINT { // ISO 14495-1: official test image 1 (T87_test-1-2-3-4-5-6.zip) - decompress_file("DataFiles/T8C0E0.JLS", "DataFiles/TEST8.PPM"); + decompress_file("DataFiles/t8c0e0.jls", "DataFiles/test8.ppm"); } - TEST_METHOD(decompress_color_8_bit_interleave_line_lossless) + TEST_METHOD(decompress_color_8_bit_interleave_line_lossless) // NOLINT { // ISO 14495-1: official test image 2 (T87_test-1-2-3-4-5-6.zip) - decompress_file("DataFiles/T8C1E0.JLS", "DataFiles/TEST8.PPM"); + decompress_file("DataFiles/t8c1e0.jls", "DataFiles/test8.ppm"); } - TEST_METHOD(decompress_color_8_bit_interleave_sample_lossless) + TEST_METHOD(decompress_color_8_bit_interleave_sample_lossless) // NOLINT { // ISO 14495-1: official test image 3 (T87_test-1-2-3-4-5-6.zip) - decompress_file("DataFiles/T8C2E0.JLS", "DataFiles/TEST8.PPM"); + decompress_file("DataFiles/t8c2e0.jls", "DataFiles/test8.ppm"); } - TEST_METHOD(decompress_color_8_bit_interleave_none_near_lossless_3) + TEST_METHOD(decompress_color_8_bit_interleave_none_near_lossless_3) // NOLINT { // ISO 14495-1: official test image 4 (T87_test-1-2-3-4-5-6.zip) - decompress_file("DataFiles/T8C2E3.JLS", "DataFiles/TEST8.PPM"); + decompress_file("DataFiles/t8c2e3.jls", "DataFiles/test8.ppm"); } - TEST_METHOD(decompress_color_8_bit_interleave_line_near_lossless_3) + TEST_METHOD(decompress_color_8_bit_interleave_line_near_lossless_3) // NOLINT { // ISO 14495-1: official test image 5 (T87_test-1-2-3-4-5-6.zip) - decompress_file("DataFiles/T8C1E3.JLS", "DataFiles/TEST8.PPM"); + decompress_file("DataFiles/t8c1e3.jls", "DataFiles/test8.ppm"); } - TEST_METHOD(decompress_color_8_bit_interleave_sample_near_lossless_3) + TEST_METHOD(decompress_color_8_bit_interleave_sample_near_lossless_3) // NOLINT { // ISO 14495-1: official test image 6 (T87_test-1-2-3-4-5-6.zip) - decompress_file("DataFiles/T8C2E3.JLS", "DataFiles/TEST8.PPM"); + decompress_file("DataFiles/t8c2e3.jls", "DataFiles/test8.ppm"); } - TEST_METHOD(decompress_color_8_bit_interleave_line_lossless_non_default) + TEST_METHOD(decompress_color_8_bit_interleave_line_lossless_non_default) // NOLINT { // ISO 14495-1: official test image 9 (T87_test-1-2-3-4-5-6.zip) // NON-DEFAULT parameters T1=T2=T3=9,RESET=31. - decompress_file("DataFiles/T8NDE0.JLS", "DataFiles/TEST8BS2.PGM"); + decompress_file("DataFiles/t8nde0.jls", "DataFiles/test8bs2.pgm"); } - TEST_METHOD(decompress_color_8_bit_interleave_line_near_lossless_3_non_default) + TEST_METHOD(decompress_color_8_bit_interleave_line_near_lossless_3_non_default) // NOLINT { // ISO 14495-1: official test image 10 (T87_test-1-2-3-4-5-6.zip) // NON-DEFAULT parameters T1=T2=T3=9,RESET=31. - decompress_file("DataFiles/T8NDE3.JLS", "DataFiles/TEST8BS2.PGM"); + decompress_file("DataFiles/t8nde3.jls", "DataFiles/test8bs2.pgm"); } - TEST_METHOD(decompress_monochrome_16_bit_lossless) + TEST_METHOD(decompress_monochrome_16_bit_lossless) // NOLINT { // ISO 14495-1: official test image 11 (T87_test-11-12.zip) // Note: test image is actually 12 bit. - decompress_file("DataFiles/T16E0.JLS", "DataFiles/TEST16.PGM"); + decompress_file("DataFiles/t16e0.jls", "DataFiles/test16.pgm"); } - TEST_METHOD(decompress_monochrome_16_bit_near_lossless_3) + TEST_METHOD(decompress_monochrome_16_bit_near_lossless_3) // NOLINT { // ISO 14495-1: official test image 12 (T87_test-11-12.zip) // Note: test image is actually 12 bit. - decompress_file("DataFiles/T16E3.JLS", "DataFiles/TEST16.pgm", false); + decompress_file("DataFiles/t16e3.jls", "DataFiles/TEST16.pgm", false); } - TEST_METHOD(lena_monochrome_8_bit_lossless_ubc) + TEST_METHOD(lena_monochrome_8_bit_lossless_ubc) // NOLINT { // Additional, Lena compressed with other codec (UBC?) decompress_file("DataFiles/lena8b.jls", "DataFiles/lena8b.pgm"); } private: - static void decompress_file(const char* encoded_filename, const char* raw_filename, bool check_encode = true) + static void decompress_file(const char* encoded_filename, const char* raw_filename, const bool check_encode = true) { vector encoded_source = read_file(encoded_filename); @@ -102,19 +98,22 @@ decoder.source(encoded_source); decoder.read_header(); - portable_anymap_file reference_file = read_anymap_reference_file(raw_filename, decoder.interleave_mode(), decoder.frame_info()); + portable_anymap_file reference_file = + read_anymap_reference_file(raw_filename, decoder.interleave_mode(), decoder.frame_info()); test_compliance(encoded_source, reference_file.image_data(), check_encode); - test_compliance_legacy_api(encoded_source.data(), encoded_source.size(), reference_file.image_data().data(), reference_file.image_data().size(), check_encode); + test_compliance_legacy_api(encoded_source.data(), encoded_source.size(), reference_file.image_data().data(), + reference_file.image_data().size(), check_encode); } - static void test_compliance(const vector& encoded_source, const vector& uncompressed_source, bool checkEncode) + static void test_compliance(const vector& encoded_source, const vector& uncompressed_source, + const bool check_encode) { jpegls_decoder decoder; decoder.source(encoded_source); decoder.read_header(); - if (checkEncode) + if (check_encode) { Assert::IsTrue(verify_encoded_bytes(uncompressed_source, encoded_source)); } @@ -141,7 +140,8 @@ { for (size_t i = 0; i < uncompressed_source.size(); ++i) { - if (abs(uncompressed_source[i] - destination[i]) > near_lossless) // AreEqual is very slow, pre-test to speed up 50X + if (abs(uncompressed_source[i] - destination[i]) > + near_lossless) // AreEqual is very slow, pre-test to speed up 50X { Assert::AreEqual(uncompressed_source[i], destination[i]); } @@ -154,7 +154,8 @@ for (size_t i = 0; i < uncompressed_source.size() / 2; ++i) { - if (abs(source16[i] - destination16[i]) > near_lossless) // AreEqual is very slow, pre-test to speed up 50X + if (abs(source16[i] - destination16[i]) > + near_lossless) // AreEqual is very slow, pre-test to speed up 50X { Assert::AreEqual(static_cast(source16[i]), static_cast(destination16[i])); } @@ -163,56 +164,74 @@ } } - static void test_compliance_legacy_api(const uint8_t* compressedBytes, size_t compressedLength, const uint8_t* uncompressedData, size_t uncompressedLength, bool checkEncode) + static void test_compliance_legacy_api(const uint8_t* compressed_bytes, const size_t compressed_length, + const uint8_t* uncompressed_data, const size_t uncompressed_length, + const bool check_encode) { + // ReSharper disable CppDeprecatedEntity + DISABLE_DEPRECATED_WARNING + JlsParameters info{}; - error_code error = JpegLsReadHeader(compressedBytes, compressedLength, &info, nullptr); + error_code error = JpegLsReadHeader(compressed_bytes, compressed_length, &info, nullptr); Assert::IsFalse(static_cast(error)); - if (checkEncode) + if (check_encode) { - Assert::IsTrue(verify_encoded_bytes_legacy_api(uncompressedData, uncompressedLength, compressedBytes, compressedLength)); + Assert::IsTrue(verify_encoded_bytes_legacy_api(uncompressed_data, uncompressed_length, compressed_bytes, + compressed_length)); } - vector destination(static_cast(info.height) *info.width * ((info.bitsPerSample + 7) / 8) * info.components); + vector destination(static_cast(info.height) * info.width * bit_to_byte_count(info.bitsPerSample) * + info.components); - error = JpegLsDecode(destination.data(), destination.size(), compressedBytes, compressedLength, nullptr, nullptr); + error = JpegLsDecode(destination.data(), destination.size(), compressed_bytes, compressed_length, nullptr, nullptr); Assert::IsTrue(!error); if (info.allowedLossyError == 0) { - for (size_t i = 0; i < uncompressedLength; ++i) + for (size_t i = 0; i < uncompressed_length; ++i) { - if (uncompressedData[i] != destination[i]) // AreEqual is very slow, pre-test to speed up 50X + if (uncompressed_data[i] != destination[i]) // AreEqual is very slow, pre-test to speed up 50X { - Assert::AreEqual(uncompressedData[i], destination[i]); + Assert::AreEqual(uncompressed_data[i], destination[i]); } } } + + // ReSharper restore CppDeprecatedEntity + RESTORE_DEPRECATED_WARNING } - static bool verify_encoded_bytes_legacy_api(const void* uncompressedData, size_t uncompressedLength, const void* compressedData, size_t compressedLength) + static bool verify_encoded_bytes_legacy_api(const void* uncompressed_data, const size_t uncompressed_length, + const void* compressed_data, const size_t compressed_length) { + // ReSharper disable CppDeprecatedEntity + DISABLE_DEPRECATED_WARNING + JlsParameters info{}; - error_code error = JpegLsReadHeader(compressedData, compressedLength, &info, nullptr); + error_code error = JpegLsReadHeader(compressed_data, compressed_length, &info, nullptr); if (error) return false; - vector ourEncodedBytes(compressedLength + 16); - size_t bytesWritten; - error = JpegLsEncode(ourEncodedBytes.data(), ourEncodedBytes.size(), &bytesWritten, uncompressedData, uncompressedLength, &info, nullptr); + vector our_encoded_bytes(compressed_length + 16); + size_t bytes_written; + error = JpegLsEncode(our_encoded_bytes.data(), our_encoded_bytes.size(), &bytes_written, uncompressed_data, + uncompressed_length, &info, nullptr); if (error) return false; - for (size_t i = 0; i < compressedLength; ++i) + for (size_t i = 0; i < compressed_length; ++i) { - if (static_cast(compressedData)[i] != ourEncodedBytes[i]) + if (static_cast(compressed_data)[i] != our_encoded_bytes[i]) { return false; } } return true; + + // ReSharper restore CppDeprecatedEntity + RESTORE_DEPRECATED_WARNING } static bool verify_encoded_bytes(const vector& uncompressed_source, const vector& encoded_source) @@ -223,12 +242,12 @@ jpegls_encoder encoder; encoder.frame_info(decoder.frame_info()) - .interleave_mode(decoder.interleave_mode()) - .near_lossless(decoder.near_lossless()) - .preset_coding_parameters(decoder.preset_coding_parameters()); + .interleave_mode(decoder.interleave_mode()) + .near_lossless(decoder.near_lossless()) + .preset_coding_parameters(decoder.preset_coding_parameters()); - vector ourEncodedBytes(encoded_source.size() + 16); - encoder.destination(ourEncodedBytes); + vector our_encoded_bytes(encoded_source.size() + 16); + encoder.destination(our_encoded_bytes); const size_t bytes_written = encoder.encode(uncompressed_source); @@ -237,7 +256,7 @@ for (size_t i = 0; i < encoded_source.size(); ++i) { - if (encoded_source[i] != ourEncodedBytes[i]) + if (encoded_source[i] != our_encoded_bytes[i]) { return false; } @@ -247,4 +266,4 @@ } }; -} // namespace CharLSUnitTest +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/ctable_test.cpp charls-2.2.0+dfsg/unittest/ctable_test.cpp --- charls-2.1.0+dfsg/unittest/ctable_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/ctable_test.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -// Copyright (c) Team CharLS. -// SPDX-License-Identifier: BSD-3-Clause - -#include "pch.h" - -#include "util.h" - -#include "../src/lookup_table.h" - - -using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -using namespace charls; - -// clang-format off - -namespace CharLSUnitTest { - -TEST_CLASS(ctable_test) -{ -public: - TEST_METHOD(CTable_create) - { - const CTable golomb_table; - - for (int i = 0; i < 256; i++) - { - Assert::AreEqual(0, golomb_table.Get(i).GetLength()); - Assert::AreEqual(0, golomb_table.Get(i).GetValue()); - } - } -}; - -} // namespace CharLSUnitTest diff -Nru charls-2.1.0+dfsg/unittest/decoder_strategy_test.cpp charls-2.2.0+dfsg/unittest/decoder_strategy_test.cpp --- charls-2.1.0+dfsg/unittest/decoder_strategy_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/decoder_strategy_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -7,83 +7,88 @@ #include "encoder_strategy_tester.h" +#include + using Microsoft::VisualStudio::CppUnitTestFramework::Assert; +using std::array; using std::unique_ptr; namespace { -class DecoderStrategyTester final : public charls::DecoderStrategy +class decoder_strategy_tester final : public charls::decoder_strategy { public: - DecoderStrategyTester(const JlsParameters& params, uint8_t* destination, size_t nOutBufLen) : - DecoderStrategy(params) + decoder_strategy_tester(const charls::frame_info& frame_info, const charls::coding_parameters& parameters, + uint8_t* const destination, const size_t count) : + // NOLINT + decoder_strategy(frame_info, parameters) { - ByteStreamInfo stream{nullptr, destination, nOutBufLen}; - Init(stream); + initialize({destination, count}); } - void SetPresets(const charls::jpegls_pc_parameters& /*preset_coding_parameters*/) noexcept(false) override + void set_presets(const charls::jpegls_pc_parameters& /*preset_coding_parameters*/) noexcept(false) override { } - unique_ptr CreateProcess(ByteStreamInfo /*rawStreamInfo*/) noexcept(false) override + unique_ptr create_process_line(byte_span /*rawStreamInfo*/, + size_t /*stride*/) noexcept(false) override { return nullptr; } - void DecodeScan(unique_ptr /*outputData*/, const JlsRect& /*size*/, ByteStreamInfo& /*compressedData*/) noexcept(false) override + void decode_scan(unique_ptr /*outputData*/, const JlsRect& /*size*/, + byte_span& /*compressedData*/) noexcept(false) override { } - int32_t Read(int32_t length) + int32_t read(const int32_t length) { - return ReadLongValue(length); + return read_long_value(length); } }; } // namespace -namespace CharLSUnitTest { -// clang-format off +namespace charls { namespace test { -TEST_CLASS(DecoderStrategyTest) +TEST_CLASS(decoder_strategy_test) { public: - TEST_METHOD(DecodeEncodedFFPattern) + TEST_METHOD(decode_encoded_ff_pattern) // NOLINT { - const struct + struct data_t final { - int32_t val; + int32_t value; int bits; - } inData[5] = { { 0x00, 24 },{ 0xFF, 8 },{ 0xFFFF, 16 },{ 0xFFFF, 16 },{ 0x12345678, 31 } }; + }; + + const array in_data{{{0x00, 24}, {0xFF, 8}, {0xFFFF, 16}, {0xFFFF, 16}, {0x12345678, 31}}}; - uint8_t encBuf[100]; - const JlsParameters params{}; + array enc_buf{}; + const frame_info frame_info{}; + const coding_parameters parameters{}; - EncoderStrategyTester encoder(params); + encoder_strategy_tester encoder(frame_info, parameters); - ByteStreamInfo stream; - stream.rawStream = nullptr; - stream.rawData = encBuf; - stream.count = sizeof(encBuf); - encoder.InitForward(stream); + encoder.initialize_forward({enc_buf.data(), enc_buf.size()}); - for (size_t i = 0; i < sizeof(inData) / sizeof(inData[0]); i++) + for (const auto& data : in_data) { - encoder.AppendToBitStreamForward(inData[i].val, inData[i].bits); + encoder.append_to_bit_stream_forward(data.value, data.bits); } - encoder.EndScanForward(); - // Note: Correct encoding is tested in EncoderStrategyTest::AppendToBitStreamFFPattern. - const auto length = encoder.GetLengthForward(); - DecoderStrategyTester dec(params, encBuf, length); - for (auto i = 0U; i < sizeof(inData) / sizeof(inData[0]); i++) + encoder.end_scan_forward(); + // Note: Correct encoding is tested in encoder_strategy_test::append_to_bit_stream_ff_pattern. + + const auto length = encoder.get_length_forward(); + decoder_strategy_tester dec(frame_info, parameters, enc_buf.data(), length); + for (auto i = 0U; i < sizeof(in_data) / sizeof(in_data[0]); ++i) { - const auto actual = dec.Read(inData[i].bits); - Assert::AreEqual(inData[i].val, actual); + const auto actual = dec.read(in_data[i].bits); + Assert::AreEqual(in_data[i].value, actual); } } }; -} +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/default_traits_test.cpp charls-2.2.0+dfsg/unittest/default_traits_test.cpp --- charls-2.1.0+dfsg/unittest/default_traits_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/default_traits_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -7,37 +7,35 @@ using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -namespace CharLSUnitTest { +namespace charls { namespace test { -// clang-format off - -TEST_CLASS(DefaultTraitsTest) +TEST_CLASS(default_traits_test) { public: - TEST_METHOD(Create) + TEST_METHOD(create) // NOLINT { - const charls::DefaultTraits traits((1 << 8) - 1, 0); + const default_traits traits((1 << 8) - 1, 0); - Assert::AreEqual(255, traits.MAXVAL); - Assert::AreEqual(256, traits.RANGE); - Assert::AreEqual(0, traits.NEAR); - Assert::AreEqual(8, traits.qbpp); - Assert::AreEqual(8, traits.bpp); - Assert::AreEqual(32, traits.LIMIT); - Assert::AreEqual(64, traits.RESET); + Assert::AreEqual(255, traits.maximum_sample_value); + Assert::AreEqual(256, traits.range); + Assert::AreEqual(0, traits.near_lossless); + Assert::AreEqual(8, traits.quantized_bits_per_pixel); + Assert::AreEqual(8, traits.bits_per_pixel); + Assert::AreEqual(32, traits.limit); + Assert::AreEqual(64, traits.reset_threshold); } - TEST_METHOD(ModuloRange) + TEST_METHOD(modulo_range) // NOLINT { - const charls::DefaultTraits traits(24, 0); + const default_traits traits(24, 0); for (int i = -25; i < 26; ++i) { - const auto error_value = traits.ModuloRange(i); + const auto error_value = traits.modulo_range(i); constexpr int range = 24 + 1; Assert::IsTrue(-range / 2 <= error_value && error_value <= ((range + 1) / 2) - 1); } } }; -} +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/documentation_test.cpp charls-2.2.0+dfsg/unittest/documentation_test.cpp --- charls-2.1.0+dfsg/unittest/documentation_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/documentation_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -7,17 +7,21 @@ #include +#include #include #include #include "../test/portable_anymap_file.h" +// ReSharper disable CppDeprecatedEntity +DISABLE_DEPRECATED_WARNING + namespace { // The following functions are used as sample code in the documentation // Ensure that the code compiles and works by unit testing it. -std::vector decode_simple_8bit_monochrome(const std::vector& source) +std::vector decode_simple_8_bit_monochrome(const std::vector& source) { std::vector destination; @@ -32,18 +36,12 @@ std::vector decode_advanced(const std::vector& source) { - charls::jpegls_decoder decoder; - - decoder.source(source); + const charls::jpegls_decoder decoder{source, true}; // Standalone JPEG-LS files may have a SPIFF header (color space info, etc.) - bool spiff_header_found; - const auto spiff_header{decoder.read_spiff_header(spiff_header_found)}; - if (spiff_header_found && spiff_header.color_space != charls::spiff_color_space::grayscale) + if (decoder.spiff_header_has_value() && decoder.spiff_header().color_space != charls::spiff_color_space::grayscale) throw std::exception("Not a grayscale image"); - decoder.read_header(); - // After read_header() other properties can also be retrieved. if (decoder.near_lossless() != 0) { @@ -53,38 +51,38 @@ return decoder.decode>(); } -std::vector decode_simple_8bit_monochrome_legacy(const std::vector& source) +std::vector decode_simple_8_bit_monochrome_legacy(const std::vector& source) { - char error_message[ErrorMessageSize]; - JlsParameters parameters; - auto error = JpegLsReadHeader(source.data(), source.size(), ¶meters, error_message); + std::array error_message{}; + JlsParameters parameters{}; + auto error = JpegLsReadHeader(source.data(), source.size(), ¶meters, error_message.data()); if (error != CharlsApiResultType::OK) - throw std::exception(error_message); + throw std::exception(error_message.data()); if (parameters.components != 1 || parameters.bitsPerSample != 8) throw std::exception("Not a 8 bit monochrome image"); - const size_t destination_size = static_cast(parameters.width) * parameters.height; + const size_t destination_size = static_cast(parameters.width) * static_cast(parameters.height); std::vector destination(destination_size); - error = JpegLsDecode(destination.data(), destination.size(), - source.data(), source.size(), - ¶meters, error_message); + error = JpegLsDecode(destination.data(), destination.size(), source.data(), source.size(), ¶meters, + error_message.data()); if (error != CharlsApiResultType::OK) - throw std::exception(error_message); + throw std::exception(error_message.data()); return destination; } -std::vector encode_simple_8bit_monochrome(const std::vector& source, uint32_t width, uint32_t height) +std::vector encode_simple_8_bit_monochrome(const std::vector& source, const uint32_t width, + const uint32_t height) { constexpr auto bits_per_sample = 8; constexpr auto component_count = 1; - return charls::jpegls_encoder::encode(source, - {width, height, bits_per_sample, component_count}); + return charls::jpegls_encoder::encode(source, {width, height, bits_per_sample, component_count}); } -std::vector encode_advanced_8bit_monochrome(const std::vector& source, uint32_t width, uint32_t height) +std::vector encode_advanced_8_bit_monochrome(const std::vector& source, const uint32_t width, + const uint32_t height) { charls::jpegls_encoder encoder; encoder.frame_info({width, height, 8, 1}); @@ -100,12 +98,13 @@ return destination; } -std::vector encode_simple_8bit_monochrome_legacy(const std::vector& source, uint32_t width, uint32_t height) +std::vector encode_simple_8_bit_monochrome_legacy(const std::vector& source, const uint32_t width, + const uint32_t height) { - char error_message[ErrorMessageSize]; + std::array error_message{}; JlsParameters parameters{}; - parameters.width = width; - parameters.height = height; + parameters.width = static_cast(width); + parameters.height = static_cast(height); parameters.bitsPerSample = 8; parameters.components = 1; @@ -113,11 +112,10 @@ std::vector destination(estimated_destination_size); size_t bytes_written; - const auto error = JpegLsEncode(destination.data(), destination.size(), - &bytes_written, - source.data(), source.size(), ¶meters, error_message); + const auto error = JpegLsEncode(destination.data(), destination.size(), &bytes_written, source.data(), source.size(), + ¶meters, error_message.data()); if (error != CharlsApiResultType::OK) - throw std::exception(error_message); + throw std::exception(error_message.data()); destination.resize(bytes_written); return destination; @@ -128,25 +126,22 @@ using Microsoft::VisualStudio::CppUnitTestFramework::Assert; using std::vector; using namespace charls_test; -using namespace charls; - -// clang-format off -namespace CharLSUnitTest { +namespace charls { namespace test { TEST_CLASS(documentation_test) { public: - TEST_METHOD(call_decode_simple_8bit_monochrome) + TEST_METHOD(call_decode_simple_8_bit_monochrome) // NOLINT { const vector source{read_file("DataFiles/lena8b.jls")}; - const vector charls_decoded = decode_simple_8bit_monochrome(source); + const vector charls_decoded = decode_simple_8_bit_monochrome(source); test_decoded_data(charls_decoded, "DataFiles/lena8b.pgm"); } - TEST_METHOD(call_decode_advanced) + TEST_METHOD(call_decode_advanced) // NOLINT { const vector source{read_file("DataFiles/lena8b.jls")}; @@ -155,41 +150,44 @@ test_decoded_data(charls_decoded, "DataFiles/lena8b.pgm"); } - TEST_METHOD(call_decode_simple_8bit_monochrome_legacy) + TEST_METHOD(call_decode_simple_8_bit_monochrome_legacy) // NOLINT { const vector source{read_file("DataFiles/lena8b.jls")}; - const vector charls_decoded = decode_simple_8bit_monochrome_legacy(source); + const vector charls_decoded = decode_simple_8_bit_monochrome_legacy(source); test_decoded_data(charls_decoded, "DataFiles/lena8b.pgm"); } - TEST_METHOD(call_encode_simple_8bit_monochrome) + TEST_METHOD(call_encode_simple_8_bit_monochrome) // NOLINT { portable_anymap_file reference_file("DataFiles/lena8b.pgm"); - const vector charls_encoded = encode_simple_8bit_monochrome(reference_file.image_data(), - reference_file.width(), reference_file.height()); + const vector charls_encoded = + encode_simple_8_bit_monochrome(reference_file.image_data(), static_cast(reference_file.width()), + static_cast(reference_file.height())); test_by_decoding(charls_encoded, reference_file, interleave_mode::none); } - TEST_METHOD(call_encode_advanced_8bit_monochrome) + TEST_METHOD(call_encode_advanced_8_bit_monochrome) // NOLINT { portable_anymap_file reference_file("DataFiles/lena8b.pgm"); - const vector charls_encoded = encode_advanced_8bit_monochrome(reference_file.image_data(), - reference_file.width(), reference_file.height()); + const vector charls_encoded = + encode_advanced_8_bit_monochrome(reference_file.image_data(), static_cast(reference_file.width()), + static_cast(reference_file.height())); test_by_decoding(charls_encoded, reference_file, interleave_mode::none); } - TEST_METHOD(call_encode_simple_8bit_monochrome_legacy) + TEST_METHOD(call_encode_simple_8_bit_monochrome_legacy) // NOLINT { portable_anymap_file reference_file("DataFiles/lena8b.pgm"); - const vector charls_encoded = encode_simple_8bit_monochrome_legacy(reference_file.image_data(), - reference_file.width(), reference_file.height()); + const vector charls_encoded = + encode_simple_8_bit_monochrome_legacy(reference_file.image_data(), static_cast(reference_file.width()), + static_cast(reference_file.height())); test_by_decoding(charls_encoded, reference_file, interleave_mode::none); } @@ -211,13 +209,14 @@ } } - static void test_by_decoding(const vector& encoded_source, const portable_anymap_file& reference_file, const interleave_mode interleave_mode) + static void test_by_decoding(const vector& encoded_source, const portable_anymap_file& reference_file, + const interleave_mode interleave_mode) { jpegls_decoder decoder; decoder.source(encoded_source); decoder.read_header(); - const auto frame_info = decoder.frame_info(); + const auto& frame_info = decoder.frame_info(); Assert::AreEqual(static_cast(reference_file.width()), frame_info.width); Assert::AreEqual(static_cast(reference_file.height()), frame_info.height); Assert::AreEqual(reference_file.component_count(), frame_info.component_count); @@ -241,4 +240,7 @@ } }; -} // namespace CharLSUnitTest +}} // namespace charls::test + +// ReSharper restore CppDeprecatedEntity +RESTORE_DEPRECATED_WARNING diff -Nru charls-2.1.0+dfsg/unittest/encoder_strategy_test.cpp charls-2.2.0+dfsg/unittest/encoder_strategy_test.cpp --- charls-2.1.0+dfsg/unittest/encoder_strategy_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/encoder_strategy_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -5,78 +5,73 @@ #include "encoder_strategy_tester.h" -using Microsoft::VisualStudio::CppUnitTestFramework::Assert; +#include -namespace CharLSUnitTest { +using Microsoft::VisualStudio::CppUnitTestFramework::Assert; +using std::array; -// clang-format off +namespace charls { namespace test { -TEST_CLASS(EncoderStrategyTest) +TEST_CLASS(encoder_strategy_test) { public: - TEST_METHOD(AppendToBitStreamZeroLength) + TEST_METHOD(append_to_bit_stream_zero_length) // NOLINT { - JlsParameters params; + const frame_info frame_info{}; + const coding_parameters parameters{}; - EncoderStrategyTester strategy(params); + encoder_strategy_tester strategy(frame_info, parameters); - uint8_t data[1024]; + array data{}; - ByteStreamInfo stream; - stream.rawStream = nullptr; - stream.rawData = data; - stream.count = sizeof(data); - strategy.InitForward(stream); + strategy.initialize_forward({data.data(), data.size()}); - strategy.AppendToBitStreamForward(0, 0); - strategy.FlushForward(); + strategy.append_to_bit_stream_forward(0, 0); + strategy.flush_forward(); } - TEST_METHOD(AppendToBitStreamFFPattern) + TEST_METHOD(append_to_bit_stream_ff_pattern) // NOLINT { - JlsParameters params; + const frame_info frame_info{}; + const coding_parameters parameters{}; - EncoderStrategyTester strategy(params); + encoder_strategy_tester strategy(frame_info, parameters); - uint8_t data[1024]; - data[13] = 0x77; // marker byte to detect overruns. + array destination{}; + destination[13] = 0x77; // marker byte to detect overruns. - ByteStreamInfo stream; - stream.rawStream = nullptr; - stream.rawData = data; - stream.count = sizeof(data); - strategy.InitForward(stream); + strategy.initialize_forward({destination.data(), destination.size()}); // We want _isFFWritten == true. - strategy.AppendToBitStreamForward(0, 24); - strategy.AppendToBitStreamForward(0xff, 8); + strategy.append_to_bit_stream_forward(0, 24); + strategy.append_to_bit_stream_forward(0xff, 8); // We need the buffer filled with set bits. - strategy.AppendToBitStreamForward(0xffff, 16); - strategy.AppendToBitStreamForward(0xffff, 16); + strategy.append_to_bit_stream_forward(0xffff, 16); + strategy.append_to_bit_stream_forward(0xffff, 16); // Buffer is full with FFs and _isFFWritten = true: Flush can only write 30 date bits. - strategy.AppendToBitStreamForward(0x3, 31); + strategy.append_to_bit_stream_forward(0x3, 31); - strategy.FlushForward(); + strategy.flush_forward(); // Verify output. - Assert::AreEqual(static_cast(13), strategy.GetLengthForward()); - Assert::AreEqual(static_cast(0x00), data[0]); - Assert::AreEqual(static_cast(0x00), data[1]); - Assert::AreEqual(static_cast(0x00), data[2]); - Assert::AreEqual(static_cast(0xFF), data[3]); - Assert::AreEqual(static_cast(0x7F), data[4]); // extra 0 bit. - Assert::AreEqual(static_cast(0xFF), data[5]); - Assert::AreEqual(static_cast(0x7F), data[6]); // extra 0 bit. - Assert::AreEqual(static_cast(0xFF), data[7]); - Assert::AreEqual(static_cast(0x60), data[8]); - Assert::AreEqual(static_cast(0x00), data[9]); - Assert::AreEqual(static_cast(0x00), data[10]); - Assert::AreEqual(static_cast(0x00), data[11]); - Assert::AreEqual(static_cast(0xC0), data[12]); - Assert::AreEqual(static_cast(0x77), data[13]); + Assert::AreEqual(static_cast(13), strategy.get_length_forward()); + Assert::AreEqual(static_cast(0x00), destination[0]); + Assert::AreEqual(static_cast(0x00), destination[1]); + Assert::AreEqual(static_cast(0x00), destination[2]); + Assert::AreEqual(static_cast(0xFF), destination[3]); + Assert::AreEqual(static_cast(0x7F), destination[4]); // extra 0 bit. + Assert::AreEqual(static_cast(0xFF), destination[5]); + Assert::AreEqual(static_cast(0x7F), destination[6]); // extra 0 bit. + Assert::AreEqual(static_cast(0xFF), destination[7]); + Assert::AreEqual(static_cast(0x60), destination[8]); + Assert::AreEqual(static_cast(0x00), destination[9]); + Assert::AreEqual(static_cast(0x00), destination[10]); + Assert::AreEqual(static_cast(0x00), destination[11]); + Assert::AreEqual(static_cast(0xC0), destination[12]); + Assert::AreEqual(static_cast(0x77), destination[13]); } }; -} +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/encoder_strategy_tester.h charls-2.2.0+dfsg/unittest/encoder_strategy_tester.h --- charls-2.1.0+dfsg/unittest/encoder_strategy_tester.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/encoder_strategy_tester.h 2021-01-10 18:07:25.000000000 +0000 @@ -5,54 +5,54 @@ #include "../src/encoder_strategy.h" -namespace CharLSUnitTest { +namespace charls { namespace test { -class EncoderStrategyTester final : charls::EncoderStrategy +class encoder_strategy_tester final : encoder_strategy { public: - explicit EncoderStrategyTester(const JlsParameters& params) : - EncoderStrategy(params) + explicit encoder_strategy_tester(const frame_info& frame_info, const coding_parameters& parameters) noexcept : + encoder_strategy(frame_info, parameters) { } - void SetPresets(const charls::jpegls_pc_parameters&) noexcept(false) override + void set_presets(const jpegls_pc_parameters&) noexcept(false) override { } - size_t EncodeScan(std::unique_ptr, ByteStreamInfo&) noexcept(false) override + size_t encode_scan(std::unique_ptr, byte_span) noexcept(false) override { return 0; } - std::unique_ptr CreateProcess(ByteStreamInfo) noexcept(false) override + std::unique_ptr create_process_line(byte_span, size_t /*stride*/) noexcept(false) override { return nullptr; } - void InitForward(ByteStreamInfo& info) + void initialize_forward(const byte_span info) noexcept { - Init(info); + initialize(info); } - void AppendToBitStreamForward(int32_t value, int32_t length) + void append_to_bit_stream_forward(const uint32_t bits, const int32_t bit_count) { - AppendToBitStream(value, length); + append_to_bit_stream(bits, bit_count); } - void FlushForward() + void flush_forward() { - Flush(); + flush(); } - std::size_t GetLengthForward() const noexcept + size_t get_length_forward() const noexcept { - return GetLength(); + return get_length(); } - void EndScanForward() + void end_scan_forward() { - EndScan(); + end_scan(); } }; -} // namespace CharLSUnitTest +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/encode_test.cpp charls-2.2.0+dfsg/unittest/encode_test.cpp --- charls-2.1.0+dfsg/unittest/encode_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/encode_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -10,44 +10,46 @@ #include "../test/portable_anymap_file.h" using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -using namespace charls; using namespace charls_test; using std::vector; -// clang-format off - -namespace CharLSUnitTest { +namespace charls { namespace test { TEST_CLASS(encode_test) { public: - TEST_METHOD(encode_monochrome_12_bit_lossless) + TEST_METHOD(encode_monochrome_4_bit_lossless) // NOLINT + { + encode("DataFiles/4bit-monochrome.pgm"); + } + + TEST_METHOD(encode_monochrome_12_bit_lossless) // NOLINT { - encode("DataFiles/TEST16.pgm", interleave_mode::none); + encode("DataFiles/test16.pgm"); } - TEST_METHOD(encode_monochrome_16_bit_lossless) + TEST_METHOD(encode_monochrome_16_bit_lossless) // NOLINT { - encode("DataFiles/16-bit-640-480-many-dots.pgm", interleave_mode::none); + encode("DataFiles/16-bit-640-480-many-dots.pgm"); } - TEST_METHOD(encode_color_8_bit_interleave_none_lossless) + TEST_METHOD(encode_color_8_bit_interleave_none_lossless) // NOLINT { - encode("DataFiles/TEST8.PPM", interleave_mode::none); + encode("DataFiles/test8.ppm"); } - TEST_METHOD(encode_color_8_bit_interleave_line_lossless) + TEST_METHOD(encode_color_8_bit_interleave_line_lossless) // NOLINT { - encode("DataFiles/TEST8.PPM", interleave_mode::line); + encode("DataFiles/test8.ppm", interleave_mode::line); } - TEST_METHOD(encode_color_8_bit_interleave_sample_lossless) + TEST_METHOD(encode_color_8_bit_interleave_sample_lossless) // NOLINT { - encode("DataFiles/TEST8.PPM", interleave_mode::sample); + encode("DataFiles/test8.ppm", interleave_mode::sample); } private: - static void encode(const char* filename, const interleave_mode interleave_mode) + static void encode(const char* filename, const interleave_mode interleave_mode = interleave_mode::none) { const portable_anymap_file reference_file = read_anymap_reference_file(filename, interleave_mode); @@ -58,9 +60,9 @@ static void encode(const portable_anymap_file& reference_file, const interleave_mode interleave_mode) { jpegls_encoder encoder; - encoder.frame_info({ - static_cast(reference_file.width()), static_cast(reference_file.height()), - reference_file.bits_per_sample(), reference_file.component_count()}) + encoder + .frame_info({static_cast(reference_file.width()), static_cast(reference_file.height()), + reference_file.bits_per_sample(), reference_file.component_count()}) .interleave_mode(interleave_mode); vector charls_encoded(encoder.estimated_destination_size()); @@ -82,23 +84,33 @@ info.interleaveMode = interleave_mode; vector charls_encoded(estimated_destination_size(reference_file.width(), reference_file.height(), - reference_file.component_count(), reference_file.bits_per_sample())); + reference_file.component_count(), + reference_file.bits_per_sample())); + + // ReSharper disable CppDeprecatedEntity + DISABLE_DEPRECATED_WARNING + + size_t bytes_written; + const auto error = + JpegLsEncode(charls_encoded.data(), charls_encoded.size(), &bytes_written, reference_file.image_data().data(), + reference_file.image_data().size(), &info, nullptr); + + // ReSharper restore CppDeprecatedEntity + RESTORE_DEPRECATED_WARNING - size_t bytesWritten; - const auto error = JpegLsEncode(charls_encoded.data(), charls_encoded.size(), &bytesWritten, - reference_file.image_data().data(), reference_file.image_data().size(), &info, nullptr); Assert::AreEqual(jpegls_errc::success, error); test_by_decoding(charls_encoded, reference_file, interleave_mode); } - static void test_by_decoding(const vector& encoded_source, const portable_anymap_file& reference_file, const interleave_mode interleave_mode) + static void test_by_decoding(const vector& encoded_source, const portable_anymap_file& reference_file, + const interleave_mode interleave_mode) { jpegls_decoder decoder; decoder.source(encoded_source); decoder.read_header(); - const auto frame_info = decoder.frame_info(); + const auto& frame_info = decoder.frame_info(); Assert::AreEqual(static_cast(reference_file.width()), frame_info.width); Assert::AreEqual(static_cast(reference_file.height()), frame_info.height); Assert::AreEqual(reference_file.bits_per_sample(), frame_info.bits_per_sample); @@ -124,11 +136,11 @@ } } - constexpr static size_t estimated_destination_size(int width, int height, int component_count, int bits_per_sample) noexcept + constexpr static size_t estimated_destination_size(const int width, const int height, const int component_count, + const int bits_per_sample) noexcept { - return static_cast(width) * height * - component_count * (bits_per_sample < 9 ? 1 : 2) + 1024; + return static_cast(width) * height * component_count * (bits_per_sample < 9 ? 1 : 2) + 1024; } }; -} // namespace CharLSUnitTest +}} // namespace charls::test Binary files /tmp/tmptxyqr8/iDZBmIvU8B/charls-2.1.0+dfsg/unittest/fuzzy_input_golomb_16.jls and /tmp/tmptxyqr8/i0J_6ju0mM/charls-2.2.0+dfsg/unittest/fuzzy_input_golomb_16.jls differ diff -Nru charls-2.1.0+dfsg/unittest/golomb_table_test.cpp charls-2.2.0+dfsg/unittest/golomb_table_test.cpp --- charls-2.1.0+dfsg/unittest/golomb_table_test.cpp 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/golomb_table_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,30 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#include "pch.h" + +#include "util.h" + +#include "../src/lookup_table.h" + + +using Microsoft::VisualStudio::CppUnitTestFramework::Assert; + +namespace charls { namespace test { + +TEST_CLASS(golomb_table_test) +{ +public: + TEST_METHOD(golomb_table_create) // NOLINT + { + const golomb_code_table golomb_table; + + for (uint32_t i{}; i < 256U; i++) + { + Assert::AreEqual(0U, golomb_table.get(i).length()); + Assert::AreEqual(0, golomb_table.get(i).value()); + } + } +}; + +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/interface_test.cpp charls-2.2.0+dfsg/unittest/interface_test.cpp --- charls-2.1.0+dfsg/unittest/interface_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/interface_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -6,26 +6,32 @@ #include "util.h" #include -#include #include #include using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -using namespace charls; using std::array; using std::vector; -// clang-format off +MSVC_WARNING_SUPPRESS(6387) // '_Param_(x)' could be '0': this does not adhere to the specification for the function. -namespace CharLSUnitTest { +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" +#endif + +// ReSharper disable CppDeprecatedEntity +DISABLE_DEPRECATED_WARNING + +namespace charls { namespace test { TEST_CLASS(interface_test) { public: - TEST_METHOD(GetMetadataInfoFromNearLosslessEncodedColorImage) + TEST_METHOD(get_metadata_info_from_near_lossless_encoded_color_image) // NOLINT { - vector encoded_source{read_file("DataFiles/T8C0E3.JLS")}; + vector encoded_source{read_file("DataFiles/t8c0e3.jls")}; JlsParameters params{}; const jpegls_errc result = JpegLsReadHeader(encoded_source.data(), encoded_source.size(), ¶ms, nullptr); @@ -38,31 +44,31 @@ Assert::AreEqual(params.allowedLossyError, 3); } - TEST_METHOD(JpegLsReadHeader_nullptr) + TEST_METHOD(JpegLsReadHeader_nullptr) // NOLINT { JlsParameters params{}; - vector buffer(100); - auto error = JpegLsReadHeader(nullptr, buffer.size(), ¶ms, nullptr); - Assert::AreEqual(charls::jpegls_errc::invalid_argument, error); + vector encoded_source{read_file("DataFiles/t8c0e3.jls")}; + auto error = JpegLsReadHeader(nullptr, encoded_source.size(), ¶ms, nullptr); + Assert::AreEqual(jpegls_errc::invalid_argument, error); - error = JpegLsReadHeader(buffer.data(), buffer.size(), nullptr, nullptr); - Assert::AreEqual(charls::jpegls_errc::invalid_argument, error); + error = JpegLsReadHeader(encoded_source.data(), encoded_source.size(), nullptr, nullptr); + Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(JpegLsReadHeader_empty_source) + TEST_METHOD(JpegLsReadHeader_empty_source) // NOLINT { - array error_message; + array error_message{}; JlsParameters params{}; - array source; + array source{}; const auto error = JpegLsReadHeader(source.data(), 0, ¶ms, error_message.data()); - Assert::AreEqual(charls::jpegls_errc::source_buffer_too_small, error); + Assert::AreEqual(jpegls_errc::source_buffer_too_small, error); Assert::IsTrue(strlen(error_message.data()) > 0); } - TEST_METHOD(JpegLsReadHeader_custom_preset_parameters) + TEST_METHOD(JpegLsReadHeader_custom_preset_parameters) // NOLINT { // NON-DEFAULT parameters T1=T2=T3=9,RESET=31. - vector encoded_source{read_file("DataFiles/T8NDE0.JLS")}; + vector encoded_source{read_file("DataFiles/t8nde0.jls")}; JlsParameters params{}; const jpegls_errc result = JpegLsReadHeader(encoded_source.data(), encoded_source.size(), ¶ms, nullptr); @@ -75,27 +81,32 @@ Assert::AreEqual(31, params.custom.ResetValue); } - TEST_METHOD(JpegLsEncode_nullptr) + TEST_METHOD(JpegLsEncode_nullptr) // NOLINT { JlsParameters params{}; - size_t bytesWritten{}; - vector buffer(100); - auto error = JpegLsEncode(nullptr, 100, &bytesWritten, buffer.data(), buffer.size(), ¶ms, nullptr); + params.bitsPerSample = 8; + params.height = 10; + params.width = 10; + params.components = 1; + + size_t bytes_written{}; + vector buffer(10000); + auto error = JpegLsEncode(nullptr, buffer.size(), &bytes_written, buffer.data(), buffer.size(), ¶ms, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); - error = JpegLsEncode(buffer.data(), 100, nullptr, buffer.data(), buffer.size(), ¶ms, nullptr); + error = JpegLsEncode(buffer.data(), buffer.size(), nullptr, buffer.data(), buffer.size(), ¶ms, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); - error = JpegLsEncode(buffer.data(), buffer.size(), &bytesWritten, nullptr, buffer.size(), ¶ms, nullptr); + error = JpegLsEncode(buffer.data(), buffer.size(), &bytes_written, nullptr, buffer.size(), ¶ms, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); - error = JpegLsEncode(buffer.data(), buffer.size(), &bytesWritten, buffer.data(), buffer.size(), nullptr, nullptr); + error = JpegLsEncode(buffer.data(), buffer.size(), &bytes_written, buffer.data(), buffer.size(), nullptr, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(JpegLsEncode_empty_destination) + TEST_METHOD(JpegLsEncode_empty_destination) // NOLINT { - array error_message; + array error_message{}; JlsParameters params{}; params.bitsPerSample = 8; @@ -103,80 +114,83 @@ params.width = 10; params.components = 1; - size_t bytesWritten{}; - array destination; + size_t bytes_written{}; + array destination{}; vector source(100); - const auto error = JpegLsEncode(destination.data(), 0, &bytesWritten, source.data(), source.size(), ¶ms, error_message.data()); - Assert::AreEqual(charls::jpegls_errc::destination_buffer_too_small, error); + const auto error = + JpegLsEncode(destination.data(), 0, &bytes_written, source.data(), source.size(), ¶ms, error_message.data()); + Assert::AreEqual(jpegls_errc::destination_buffer_too_small, error); Assert::IsTrue(strlen(error_message.data()) > 0); } - TEST_METHOD(JpegLsDecode_nullptr) + TEST_METHOD(JpegLsDecode_nullptr) // NOLINT { JlsParameters params{}; - vector buffer(100); - auto error = JpegLsDecode(nullptr, 100, buffer.data(), buffer.size(), ¶ms, nullptr); + vector encoded_source = read_file("DataFiles/lena8b.jls"); + auto error = JpegLsDecode(nullptr, 100, encoded_source.data(), encoded_source.size(), ¶ms, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); - error = JpegLsDecode(buffer.data(), 100, nullptr, buffer.size(), ¶ms, nullptr); + error = JpegLsDecode(encoded_source.data(), 100, nullptr, encoded_source.size(), ¶ms, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(JpegLsDecode_empty_source) + TEST_METHOD(JpegLsDecode_empty_source) // NOLINT { - array error_message; + array error_message{}; JlsParameters params{}; params.bitsPerSample = 8; params.height = 10; params.width = 10; params.components = 1; - array source; + array source{}; vector destination(100); - const auto error = JpegLsDecode(destination.data(), destination.size(), source.data(), 0, ¶ms, error_message.data()); + const auto error = + JpegLsDecode(destination.data(), destination.size(), source.data(), 0, ¶ms, error_message.data()); Assert::AreEqual(jpegls_errc::source_buffer_too_small, error); Assert::IsTrue(strlen(error_message.data()) > 0); } - TEST_METHOD(JpegLsDecodeRect_lena) + TEST_METHOD(JpegLsDecodeRect_lena) // NOLINT { JlsParameters params{}; vector encoded_source = read_file("DataFiles/lena8b.jls"); auto error = JpegLsReadHeader(encoded_source.data(), encoded_source.size(), ¶ms, nullptr); Assert::AreEqual(jpegls_errc::success, error); - vector decoded_destination(static_cast(params.width) * params.height*params.components); + vector decoded_destination(static_cast(params.width) * params.height * params.components); - error = JpegLsDecode(decoded_destination.data(), decoded_destination.size(), - encoded_source.data(), encoded_source.size(), ¶ms, nullptr); + error = JpegLsDecode(decoded_destination.data(), decoded_destination.size(), encoded_source.data(), + encoded_source.size(), ¶ms, nullptr); Assert::IsFalse(static_cast(error)); - const JlsRect rect = { 128, 128, 256, 1 }; + const JlsRect rect = {128, 128, 256, 1}; vector decoded_rect(static_cast(rect.Width) * rect.Height); decoded_rect.push_back(0x1f); - error = JpegLsDecodeRect(decoded_rect.data(), decoded_rect.size(), - encoded_source.data(), encoded_source.size(), rect, ¶ms, nullptr); + error = JpegLsDecodeRect(decoded_rect.data(), decoded_rect.size(), encoded_source.data(), encoded_source.size(), + rect, ¶ms, nullptr); Assert::IsFalse(static_cast(error)); - Assert::IsTrue(memcmp(&decoded_destination[rect.X + static_cast(rect.Y) * 512], decoded_rect.data(), static_cast(rect.Width) * rect.Height) == 0); + Assert::IsTrue(memcmp(&decoded_destination[rect.X + static_cast(rect.Y) * 512], decoded_rect.data(), + static_cast(rect.Width) * rect.Height) == 0); Assert::IsTrue(decoded_rect[static_cast(rect.Width) * rect.Height] == 0x1f); } - TEST_METHOD(JpegLsDecodeRect_nullptr) + TEST_METHOD(JpegLsDecodeRect_nullptr) // NOLINT { JlsParameters params{}; const JlsRect roi{}; - vector buffer(100); - auto error = JpegLsDecodeRect(nullptr, 100, buffer.data(), buffer.size(), roi, ¶ms, nullptr); + vector encoded_source = read_file("DataFiles/lena8b.jls"); + auto error = JpegLsDecodeRect(nullptr, 100, encoded_source.data(), encoded_source.size(), roi, ¶ms, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); - error = JpegLsDecodeRect(buffer.data(), 100, nullptr, buffer.size(), roi, ¶ms, nullptr); + error = JpegLsDecodeRect(encoded_source.data(), 100, nullptr, encoded_source.size(), roi, ¶ms, nullptr); Assert::AreEqual(jpegls_errc::invalid_argument, error); } - TEST_METHOD(JpegLsDecodeRect_empty_source) + TEST_METHOD(JpegLsDecodeRect_empty_source) // NOLINT { - array error_message; + array error_message{}; JlsParameters params{}; params.bitsPerSample = 8; params.height = 10; @@ -184,15 +198,16 @@ params.components = 1; const JlsRect roi{}; - array source; + array source{}; vector destination(100); - const auto error = JpegLsDecodeRect(destination.data(), destination.size(), source.data(), 0, roi, ¶ms, error_message.data()); + const auto error = + JpegLsDecodeRect(destination.data(), destination.size(), source.data(), 0, roi, ¶ms, error_message.data()); Assert::AreEqual(jpegls_errc::source_buffer_too_small, error); Assert::IsTrue(strlen(error_message.data()) > 0); } - TEST_METHOD(noise_image_with_custom_reset) + TEST_METHOD(noise_image_with_custom_reset) // NOLINT { JlsParameters params{}; params.components = 1; @@ -202,43 +217,20 @@ params.custom.MaximumSampleValue = (1 << params.bitsPerSample) - 1; params.custom.ResetValue = 63; - const vector noise_image = create_noise_image_16bit(static_cast(params.height) * params.width, params.bitsPerSample, 21344); + const vector noise_image = + create_noise_image_16_bit(static_cast(params.height) * params.width, params.bitsPerSample, 21344); test_round_trip_legacy(noise_image, params); } +}; - TEST_METHOD(JpegLsReadHeaderStream_valid_input) - { - vector source{read_file("DataFiles/T8C0E3.JLS")}; - - JlsParameters params{}; - const auto source_info = FromByteArrayConst(source.data(), source.size()); - const jpegls_errc error = JpegLsReadHeaderStream(source_info, ¶ms); - - Assert::AreEqual(jpegls_errc::success, error); - Assert::AreEqual(params.height, 256); - Assert::AreEqual(params.width, 256); - Assert::AreEqual(params.bitsPerSample, 8); - Assert::AreEqual(params.components, 3); - Assert::AreEqual(params.allowedLossyError, 3); - } - - TEST_METHOD(JpegLsDecodeStream_valid_input) - { - vector source{read_file("DataFiles/T8C0E3.JLS")}; - - JlsParameters params{}; - const auto source_info = FromByteArrayConst(source.data(), source.size()); - jpegls_errc error = JpegLsReadHeaderStream(source_info, ¶ms); - Assert::AreEqual(jpegls_errc::success, error); +}} // namespace charls::test - const int bytesPerSample = params.bitsPerSample > 8 ? 2 : 1; - vector destination(static_cast(params.width) * params.height * bytesPerSample * params.components); +// ReSharper restore CppDeprecatedEntity +RESTORE_DEPRECATED_WARNING - const auto destination_info = FromByteArray(destination.data(), destination.size()); - error = JpegLsDecodeStream(destination_info, source_info, nullptr); - Assert::AreEqual(jpegls_errc::success, error); - } -}; +#if defined(__clang__) +#pragma clang diagnostic pop +#endif -} // namespace CharLSUnitTest +MSVC_WARNING_UNSUPPRESS() diff -Nru charls-2.1.0+dfsg/unittest/jpeg_error_test.cpp charls-2.2.0+dfsg/unittest/jpeg_error_test.cpp --- charls-2.1.0+dfsg/unittest/jpeg_error_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/jpeg_error_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -5,38 +5,41 @@ #include -#include - using Microsoft::VisualStudio::CppUnitTestFramework::Assert; using Microsoft::VisualStudio::CppUnitTestFramework::TestClass; -using namespace charls; - -namespace CharLSUnitTest { -// clang-format off +namespace charls { namespace test { TEST_CLASS(jpegls_error_test) { public: - TEST_METHOD(get_error_message_success) + TEST_METHOD(get_error_message_success) // NOLINT { - const auto result = charls_get_error_message(jpegls_errc::success); + const auto* const result{charls_get_error_message(jpegls_errc::success)}; Assert::IsNotNull(result); Assert::IsTrue(strlen(result) > 0); } - TEST_METHOD(get_error_message_unknown) + TEST_METHOD(get_error_message_unknown) // NOLINT { constexpr jpegls_errc unknown_error_code{static_cast(3000)}; - const auto result = charls_get_error_message(unknown_error_code); + const auto* const result{charls_get_error_message(unknown_error_code)}; Assert::IsNotNull(result); Assert::IsTrue(strlen(result) > 0); } - TEST_METHOD(jpegls_category_name_is_not_an_empty_string) + TEST_METHOD(jpegls_category_name_is_not_an_empty_string) // NOLINT { Assert::IsTrue(strlen(jpegls_category().name()) > 0); } + + TEST_METHOD(jpegls_category_call_message) // NOLINT + { + const std::error_category& category{jpegls_category()}; + + std::string message{category.message(0)}; + Assert::IsTrue(message.size() > 0); + } }; -} // namespace CharLSUnitTest +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/jpegls_decoder_test.cpp charls-2.2.0+dfsg/unittest/jpegls_decoder_test.cpp --- charls-2.1.0+dfsg/unittest/jpegls_decoder_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/jpegls_decoder_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -7,80 +7,88 @@ #include +#include #include #include +#include "../src/jpeg_marker_code.h" +#include "../src/jpegls_preset_parameters_type.h" + +#include "jpeg_test_stream_writer.h" + using Microsoft::VisualStudio::CppUnitTestFramework::Assert; +using std::array; using std::error_code; using std::tie; using std::vector; -using namespace charls; using namespace charls_test; -namespace CharLSUnitTest { +namespace { + +charls::jpegls_decoder create_decoder(const vector& source) +{ + return {source, true}; +} + +} // namespace -// clang-format off +namespace charls { namespace test { TEST_CLASS(jpegls_decoder_test) { public: - TEST_METHOD(create_destroy) + TEST_METHOD(create_destroy) // NOLINT { // ReSharper disable once CppLocalVariableWithNonTrivialDtorIsNeverUsed jpegls_decoder decoder; } - TEST_METHOD(create_and_move) + TEST_METHOD(create_and_move) // NOLINT { jpegls_decoder decoder1; jpegls_decoder decoder2(std::move(decoder1)); - // ReSharper disable once CppLocalVariableWithNonTrivialDtorIsNeverUsed - jpegls_decoder decoder3 = std::move(decoder2); + jpegls_decoder decoder3; + const array buffer{}; + decoder3.source(buffer.data(), buffer.size()); + decoder3 = std::move(decoder2); } - TEST_METHOD(set_source_twice) + TEST_METHOD(set_source_twice) // NOLINT { jpegls_decoder decoder; vector source(2000); decoder.source(source); - assert_expect_exception(jpegls_errc::invalid_operation, - [&] { decoder.source(source); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { decoder.source(source); }); } - TEST_METHOD(read_spiff_header_without_source) + TEST_METHOD(read_spiff_header_without_source) // NOLINT { jpegls_decoder decoder; - assert_expect_exception(jpegls_errc::invalid_operation, - [&] { - bool header_found; - decoder.read_spiff_header(header_found); - }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { decoder.read_spiff_header(); }); } - TEST_METHOD(destination_size_without_reading_header) + TEST_METHOD(destination_size_without_reading_header) // NOLINT { jpegls_decoder decoder; - assert_expect_exception(jpegls_errc::invalid_operation, - [&] { static_cast(decoder.destination_size()); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { static_cast(decoder.destination_size()); }); } - TEST_METHOD(read_header_without_source) + TEST_METHOD(read_header_without_source) // NOLINT { jpegls_decoder decoder; - assert_expect_exception(jpegls_errc::invalid_operation, - [&] { decoder.read_header(); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { decoder.read_header(); }); } - TEST_METHOD(read_header_from_non_jpegls_data) + TEST_METHOD(read_header_from_non_jpegls_data) // NOLINT { const vector source(100); - jpegls_decoder decoder{source}; + jpegls_decoder decoder{source, false}; error_code ec; decoder.read_header(ec); @@ -88,34 +96,44 @@ Assert::IsTrue(ec == jpegls_errc::jpeg_marker_start_byte_not_found); } - TEST_METHOD(frame_info_without_read_header) + TEST_METHOD(frame_info_without_read_header) // NOLINT { const vector source(2000); - jpegls_decoder decoder{source}; + const jpegls_decoder decoder{source, false}; - assert_expect_exception(jpegls_errc::invalid_operation, - [&] { static_cast(decoder.frame_info()); }); + Assert::AreEqual(0, decoder.frame_info().bits_per_sample); + Assert::AreEqual(0, decoder.frame_info().component_count); + Assert::AreEqual(0U, decoder.frame_info().height); + Assert::AreEqual(0U, decoder.frame_info().width); } - TEST_METHOD(interleave_mode_without_read_header) + TEST_METHOD(frame_info_from_temporary_object) // NOLINT + { + const frame_info info{create_decoder(read_file("DataFiles/t8c0e0.jls")).frame_info()}; + + Assert::AreEqual(8, info.bits_per_sample); + Assert::AreEqual(3, info.component_count); + Assert::AreEqual(256U, info.height); + Assert::AreEqual(256U, info.width); + } + + TEST_METHOD(interleave_mode_without_read_header) // NOLINT { const vector source(2000); - jpegls_decoder decoder{source}; + jpegls_decoder decoder{source, false}; - assert_expect_exception(jpegls_errc::invalid_operation, - [&] { static_cast(decoder.interleave_mode()); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { static_cast(decoder.interleave_mode()); }); } - TEST_METHOD(near_lossless_without_read_header) + TEST_METHOD(near_lossless_without_read_header) // NOLINT { const vector source(2000); - jpegls_decoder decoder{source}; + jpegls_decoder decoder{source, false}; - assert_expect_exception(jpegls_errc::invalid_operation, - [&] { static_cast(decoder.near_lossless()); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { static_cast(decoder.near_lossless()); }); } - TEST_METHOD(preset_coding_parameters_without_read_header) + TEST_METHOD(preset_coding_parameters_without_read_header) // NOLINT { jpegls_decoder decoder; @@ -123,67 +141,63 @@ decoder.source(source); assert_expect_exception(jpegls_errc::invalid_operation, - [&] { static_cast(decoder.preset_coding_parameters()); }); + [&] { static_cast(decoder.preset_coding_parameters()); }); } - TEST_METHOD(destination_size) + TEST_METHOD(destination_size) // NOLINT { - const vector source{read_file("DataFiles/T8C0E0.JLS")}; + const vector source{read_file("DataFiles/t8c0e0.jls")}; - jpegls_decoder decoder{source}; - decoder.read_header(); + const jpegls_decoder decoder{source, true}; - constexpr size_t expected_destination_size{256 * 256 * 3}; + constexpr size_t expected_destination_size{static_cast(256) * 256 * 3}; Assert::AreEqual(expected_destination_size, decoder.destination_size()); } - TEST_METHOD(destination_size_stride_interleave_none) + TEST_METHOD(destination_size_stride_interleave_none) // NOLINT { - const vector source{read_file("DataFiles/T8C0E0.JLS")}; + const vector source{read_file("DataFiles/t8c0e0.jls")}; - jpegls_decoder decoder{source}; - decoder.read_header(); + const jpegls_decoder decoder{source, true}; - constexpr uint32_t stride = 512; + constexpr size_t stride{512}; constexpr size_t expected_destination_size{stride * 256 * 3}; Assert::AreEqual(expected_destination_size, decoder.destination_size(stride)); } - TEST_METHOD(destination_size_stride_interleave_line) + TEST_METHOD(destination_size_stride_interleave_line) // NOLINT { - const vector source{read_file("DataFiles/T8C1E0.JLS")}; + const vector source{read_file("DataFiles/t8c1e0.jls")}; - jpegls_decoder decoder{source}; - decoder.read_header(); + const jpegls_decoder decoder{source, true}; - constexpr uint32_t stride = 1024; + constexpr size_t stride{1024}; constexpr size_t expected_destination_size{stride * 256}; Assert::AreEqual(expected_destination_size, decoder.destination_size(stride)); } - TEST_METHOD(destination_size_stride_interleave_sample) + TEST_METHOD(destination_size_stride_interleave_sample) // NOLINT { - const vector source{read_file("DataFiles/T8C2E0.JLS")}; + const vector source{read_file("DataFiles/t8c2e0.jls")}; - jpegls_decoder decoder{source}; - decoder.read_header(); + const jpegls_decoder decoder{source, true}; - constexpr uint32_t stride = 1024; + constexpr size_t stride = 1024; constexpr size_t expected_destination_size{stride * 256}; Assert::AreEqual(expected_destination_size, decoder.destination_size(stride)); } - TEST_METHOD(decode_reference_file_from_buffer) + TEST_METHOD(decode_reference_file_from_buffer) // NOLINT { - const vector source{read_file("DataFiles/T8C0E0.JLS")}; + const vector source{read_file("DataFiles/t8c0e0.jls")}; - jpegls_decoder decoder{source}; - decoder.read_header(); + const jpegls_decoder decoder{source, true}; vector destination(decoder.destination_size()); decoder.decode(destination); - portable_anymap_file reference_file = read_anymap_reference_file("DataFiles/TEST8.PPM", decoder.interleave_mode(), decoder.frame_info()); + portable_anymap_file reference_file = + read_anymap_reference_file("DataFiles/test8.ppm", decoder.interleave_mode(), decoder.frame_info()); const auto& reference_image_data = reference_file.image_data(); for (size_t i = 0; i < destination.size(); ++i) @@ -192,16 +206,18 @@ } } - TEST_METHOD(decode_with_destination_as_return) + TEST_METHOD(decode_with_default_pc_parameters_before_each_sos) // NOLINT { - const vector source{read_file("DataFiles/T8C0E0.JLS")}; + vector source{read_file("DataFiles/t8c0e0.jls")}; + insert_pc_parameters_segments(source, 3); - jpegls_decoder decoder{source}; - decoder.read_header(); + const jpegls_decoder decoder{source, true}; - const auto destination = decoder.decode>(); + vector destination(decoder.destination_size()); + decoder.decode(destination); - portable_anymap_file reference_file = read_anymap_reference_file("DataFiles/TEST8.PPM", decoder.interleave_mode(), decoder.frame_info()); + portable_anymap_file reference_file = + read_anymap_reference_file("DataFiles/test8.ppm", decoder.interleave_mode(), decoder.frame_info()); const auto& reference_image_data = reference_file.image_data(); for (size_t i = 0; i < destination.size(); ++i) @@ -210,79 +226,125 @@ } } - TEST_METHOD(decode_with_16bit_destination_as_return) + TEST_METHOD(decode_with_destination_as_return) // NOLINT { - const vector source{read_file("DataFiles/T8C0E0.JLS")}; + const vector source{read_file("DataFiles/t8c0e0.jls")}; - jpegls_decoder decoder{source}; - decoder.read_header(); + const jpegls_decoder decoder{source, true}; - const auto destination = decoder.decode>(); + const auto destination = decoder.decode>(); - portable_anymap_file reference_file = read_anymap_reference_file("DataFiles/TEST8.PPM", decoder.interleave_mode(), decoder.frame_info()); + portable_anymap_file reference_file = + read_anymap_reference_file("DataFiles/test8.ppm", decoder.interleave_mode(), decoder.frame_info()); const auto& reference_image_data = reference_file.image_data(); - const auto* destination_as_bytes = reinterpret_cast(destination.data()); - for (size_t i = 0; i < reference_image_data.size(); ++i) + for (size_t i{}; i < destination.size(); ++i) + { + Assert::AreEqual(reference_image_data[i], destination[i]); + } + } + + TEST_METHOD(decode_with_16_bit_destination_as_return) // NOLINT + { + const vector source{read_file("DataFiles/t8c0e0.jls")}; + + const jpegls_decoder decoder{source, true}; + + const auto destination = decoder.decode>(); + + portable_anymap_file reference_file = + read_anymap_reference_file("DataFiles/test8.ppm", decoder.interleave_mode(), decoder.frame_info()); + + const auto& reference_image_data{reference_file.image_data()}; + const auto* destination_as_bytes{reinterpret_cast(destination.data())}; + for (size_t i{}; i < reference_image_data.size(); ++i) { Assert::AreEqual(reference_image_data[i], destination_as_bytes[i]); } } - TEST_METHOD(decode_without_reading_header) + TEST_METHOD(decode_without_reading_header) // NOLINT { jpegls_decoder decoder; vector buffer(1000); - assert_expect_exception(jpegls_errc::invalid_operation, - [&] { decoder.decode(buffer); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { decoder.decode(buffer); }); + } + + TEST_METHOD(decode_reference_to_mapping_table_selector) // NOLINT + { + jpeg_test_stream_writer writer; + + writer.write_start_of_image(); + writer.write_start_of_frame_segment(10, 10, 8, 3); + writer.mapping_table_selector = 1; + writer.write_start_of_scan_segment(0, 1, 0, interleave_mode::none); + + jpegls_decoder decoder{writer.buffer, false}; + + assert_expect_exception(jpegls_errc::parameter_value_not_supported, [&] { decoder.read_header(); }); } - TEST_METHOD(read_spiff_header) + TEST_METHOD(read_spiff_header) // NOLINT { const vector source = create_test_spiff_header(); - const jpegls_decoder decoder{source}; + const jpegls_decoder decoder{source, true}; - bool found; - const auto header = decoder.read_spiff_header(found); + Assert::IsTrue(decoder.spiff_header_has_value()); - Assert::IsTrue(found); + const auto& header{decoder.spiff_header()}; Assert::AreEqual(static_cast(spiff_profile_id::none), static_cast(header.profile_id)); Assert::AreEqual(3, header.component_count); Assert::AreEqual(800U, header.height); Assert::AreEqual(600U, header.width); Assert::AreEqual(static_cast(spiff_color_space::rgb), static_cast(header.color_space)); Assert::AreEqual(8, header.bits_per_sample); - Assert::AreEqual(static_cast(spiff_compression_type::jpeg_ls), static_cast(header.compression_type)); - Assert::AreEqual(static_cast(spiff_resolution_units::dots_per_inch), static_cast(header.resolution_units)); + Assert::AreEqual(static_cast(spiff_compression_type::jpeg_ls), + static_cast(header.compression_type)); + Assert::AreEqual(static_cast(spiff_resolution_units::dots_per_inch), + static_cast(header.resolution_units)); Assert::AreEqual(96U, header.vertical_resolution); Assert::AreEqual(1024U, header.horizontal_resolution); } - TEST_METHOD(read_spiff_header_from_non_jpegls_data) + TEST_METHOD(read_spiff_header_from_temporary_object) // NOLINT + { + const spiff_header header{create_decoder(create_test_spiff_header()).spiff_header()}; + + Assert::AreEqual(static_cast(spiff_profile_id::none), static_cast(header.profile_id)); + Assert::AreEqual(3, header.component_count); + Assert::AreEqual(800U, header.height); + Assert::AreEqual(600U, header.width); + Assert::AreEqual(static_cast(spiff_color_space::rgb), static_cast(header.color_space)); + Assert::AreEqual(8, header.bits_per_sample); + Assert::AreEqual(static_cast(spiff_compression_type::jpeg_ls), + static_cast(header.compression_type)); + Assert::AreEqual(static_cast(spiff_resolution_units::dots_per_inch), + static_cast(header.resolution_units)); + Assert::AreEqual(96U, header.vertical_resolution); + Assert::AreEqual(1024U, header.horizontal_resolution); + } + + TEST_METHOD(read_spiff_header_from_non_jpegls_data) // NOLINT { const vector source(100); - const jpegls_decoder decoder{source}; + jpegls_decoder decoder{source, false}; - bool found; error_code ec; - static_cast(decoder.read_spiff_header(found, ec)); + static_cast(decoder.read_spiff_header(ec)); Assert::IsTrue(ec == jpegls_errc::jpeg_marker_start_byte_not_found); } - TEST_METHOD(read_spiff_header_from_jpegls_without_spiff) + TEST_METHOD(read_spiff_header_from_jpegls_without_spiff) // NOLINT { - const vector source{read_file("DataFiles/T8C0E0.JLS")}; + const vector source{read_file("DataFiles/t8c0e0.jls")}; - jpegls_decoder decoder{source}; + const jpegls_decoder decoder{source, true}; - bool found; - static_cast(decoder.read_spiff_header(found)); - Assert::IsFalse(found); + Assert::IsFalse(decoder.spiff_header_has_value()); - decoder.read_header(); - const frame_info frame_info{decoder.frame_info()}; + const frame_info& frame_info{decoder.frame_info()}; Assert::AreEqual(3, frame_info.component_count); Assert::AreEqual(8, frame_info.bits_per_sample); @@ -290,21 +352,18 @@ Assert::AreEqual(256U, frame_info.width); } - TEST_METHOD(read_header_twice) + TEST_METHOD(read_header_twice) // NOLINT { - const vector source{read_file("DataFiles/T8C0E0.JLS")}; - - jpegls_decoder decoder{source}; + const vector source{read_file("DataFiles/t8c0e0.jls")}; - decoder.read_header(); + jpegls_decoder decoder{source, true}; - assert_expect_exception(jpegls_errc::invalid_operation, - [&] { static_cast(decoder.read_header()); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { static_cast(decoder.read_header()); }); } - TEST_METHOD(simple_decode) + TEST_METHOD(simple_decode) // NOLINT { - const vector encoded_source{read_file("DataFiles/T8C0E0.JLS")}; + const vector encoded_source{read_file("DataFiles/t8c0e0.jls")}; vector decoded_destination; frame_info frame_info; @@ -321,9 +380,9 @@ Assert::AreEqual(expected_size, decoded_destination.size()); } - TEST_METHOD(simple_decode_to_uint16_buffer) + TEST_METHOD(simple_decode_to_uint16_buffer) // NOLINT { - const vector encoded_source{read_file("DataFiles/T8C0E0.JLS")}; + const vector encoded_source{read_file("DataFiles/t8c0e0.jls")}; vector decoded_destination; frame_info frame_info; @@ -336,18 +395,17 @@ Assert::AreEqual(256U, frame_info.width); Assert::AreEqual(interleave_mode::none, interleave_mode); - const size_t expected_size = static_cast(frame_info.height) * frame_info.width * frame_info.component_count; + const size_t expected_size{static_cast(frame_info.height) * frame_info.width * frame_info.component_count}; Assert::AreEqual(expected_size, decoded_destination.size() * sizeof(uint16_t)); } - TEST_METHOD(decode_file_with_ff_in_entropy_data) + TEST_METHOD(decode_file_with_ff_in_entropy_data) // NOLINT { const vector source{read_file("ff_in_entropy_data.jls")}; - jpegls_decoder decoder{source}; - decoder.read_header(); + jpegls_decoder decoder{source, true}; - const auto frame_info{decoder.frame_info()}; + const auto& frame_info{decoder.frame_info()}; Assert::AreEqual(1, frame_info.component_count); Assert::AreEqual(12, frame_info.bits_per_sample); Assert::AreEqual(1216U, frame_info.height); @@ -355,9 +413,71 @@ vector destination(decoder.destination_size()); - assert_expect_exception(jpegls_errc::invalid_encoded_data, - [&] { static_cast(decoder.decode(destination)); }); + assert_expect_exception(jpegls_errc::invalid_encoded_data, [&] { static_cast(decoder.decode(destination)); }); + } + + TEST_METHOD(decode_file_with_golomb_large_then_k_max) // NOLINT + { + const vector source{read_file("fuzzy_input_golomb_16.jls")}; + + jpegls_decoder decoder{source, true}; + + const auto& frame_info{decoder.frame_info()}; + Assert::AreEqual(3, frame_info.component_count); + Assert::AreEqual(16, frame_info.bits_per_sample); + Assert::AreEqual(65516U, frame_info.height); + Assert::AreEqual(1U, frame_info.width); + + vector destination(decoder.destination_size()); + + assert_expect_exception(jpegls_errc::invalid_encoded_data, [&] { static_cast(decoder.decode(destination)); }); + } + + +private: + static vector::iterator find_scan_header(const vector::iterator& begin, + const vector::iterator& end) noexcept + { + constexpr uint8_t start_of_scan{0xDA}; + + for (auto it{begin}; it != end; ++it) + { + if (*it == 0xFF && it + 1 != end && *(it + 1) == start_of_scan) + return it; + } + + return end; + } + + static vector create_default_pc_parameters_segment() + { + vector segment; + + segment.push_back(static_cast(0xFF)); + segment.push_back(static_cast(jpeg_marker_code::jpegls_preset_parameters)); + push_back(segment, static_cast(11 + sizeof(uint16_t))); + segment.push_back(static_cast(jpegls_preset_parameters_type::preset_coding_parameters)); + push_back(segment, static_cast(0)); + push_back(segment, static_cast(0)); + push_back(segment, static_cast(0)); + push_back(segment, static_cast(0)); + push_back(segment, static_cast(0)); + + return segment; + } + + static void insert_pc_parameters_segments(vector & jpegls_source, const int component_count) + { + const auto pcp_segment{create_default_pc_parameters_segment()}; + + auto it{jpegls_source.begin()}; + for (int i{}; i < component_count; ++i) + { + it = find_scan_header(it, jpegls_source.end()); + it = jpegls_source.insert(it, pcp_segment.cbegin(), pcp_segment.cend()); + it += static_cast::difference_type>(pcp_segment.size() + 2U); + } } }; -} // namespace CharLSUnitTest +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/jpegls_encoder_test.cpp charls-2.2.0+dfsg/unittest/jpegls_encoder_test.cpp --- charls-2.1.0+dfsg/unittest/jpegls_encoder_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/jpegls_encoder_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -12,44 +12,43 @@ #include using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -using namespace charls; using std::array; using std::vector; constexpr size_t serialized_spiff_header_size = 34; -namespace CharLSUnitTest { - -// clang-format off +namespace charls { namespace test { TEST_CLASS(jpegls_encoder_test) { public: - TEST_METHOD(create_destroy) + TEST_METHOD(create_destroy) // NOLINT { // ReSharper disable once CppLocalVariableWithNonTrivialDtorIsNeverUsed jpegls_encoder encoder; } - TEST_METHOD(create_and_move) + TEST_METHOD(create_and_move) // NOLINT { jpegls_encoder encoder1; jpegls_encoder encoder2(std::move(encoder1)); - // ReSharper disable once CppLocalVariableWithNonTrivialDtorIsNeverUsed - jpegls_encoder encoder3 = std::move(encoder2); + jpegls_encoder encoder3; + array buffer{}; + encoder3.destination(buffer.data(), buffer.size()); + encoder3 = std::move(encoder2); } - TEST_METHOD(frame_info_max_and_min) + TEST_METHOD(frame_info_max_and_min) // NOLINT { jpegls_encoder encoder; - encoder.frame_info({1, 1, 2, 1}); // minimum. + encoder.frame_info({1, 1, 2, 1}); // minimum. encoder.frame_info({UINT16_MAX, UINT16_MAX, 16, 255}); // maximum. } - TEST_METHOD(frame_info_bad_width) + TEST_METHOD(frame_info_bad_width) // NOLINT { jpegls_encoder encoder; @@ -57,15 +56,17 @@ assert_expect_exception(jpegls_errc::invalid_argument_width, [&] { encoder.frame_info({UINT16_MAX + 1, 1, 2, 1}); }); } - TEST_METHOD(frame_info_bad_height) + TEST_METHOD(frame_info_bad_height) // NOLINT { jpegls_encoder encoder; assert_expect_exception(jpegls_errc::invalid_argument_height, [&] { encoder.frame_info({1, 0, 2, 1}); }); - assert_expect_exception(jpegls_errc::invalid_argument_height, [&] { encoder.frame_info({1, UINT16_MAX + 1, 2, 1}); }); + assert_expect_exception(jpegls_errc::invalid_argument_height, [&] { + encoder.frame_info({1, UINT16_MAX + 1, 2, 1}); + }); } - TEST_METHOD(frame_info_bad_bits_per_sample) + TEST_METHOD(frame_info_bad_bits_per_sample) // NOLINT { jpegls_encoder encoder; @@ -73,7 +74,7 @@ assert_expect_exception(jpegls_errc::invalid_argument_bits_per_sample, [&] { encoder.frame_info({1, 1, 17, 1}); }); } - TEST_METHOD(frame_info_bad_component_count) + TEST_METHOD(frame_info_bad_component_count) // NOLINT { jpegls_encoder encoder; @@ -81,7 +82,7 @@ assert_expect_exception(jpegls_errc::invalid_argument_component_count, [&] { encoder.frame_info({1, 1, 2, 256}); }); } - TEST_METHOD(interleave_mode) + TEST_METHOD(interleave_mode) // NOLINT { jpegls_encoder encoder; @@ -90,25 +91,25 @@ encoder.interleave_mode(interleave_mode::sample); } - TEST_METHOD(interleave_mode_bad) + TEST_METHOD(interleave_mode_bad) // NOLINT { jpegls_encoder encoder; assert_expect_exception(jpegls_errc::invalid_argument_interleave_mode, - [&] { encoder.interleave_mode(static_cast(-1)); }); + [&] { encoder.interleave_mode(static_cast(-1)); }); assert_expect_exception(jpegls_errc::invalid_argument_interleave_mode, - [&] { encoder.interleave_mode(static_cast(3)); }); + [&] { encoder.interleave_mode(static_cast(3)); }); } - TEST_METHOD(near_lossless) + TEST_METHOD(near_lossless) // NOLINT { jpegls_encoder encoder; - encoder.near_lossless(0); // set lowest value. + encoder.near_lossless(0); // set lowest value. encoder.near_lossless(255); // set highest value. } - TEST_METHOD(near_lossless_bad) + TEST_METHOD(near_lossless_bad) // NOLINT { jpegls_encoder encoder; @@ -116,69 +117,70 @@ assert_expect_exception(jpegls_errc::invalid_argument_near_lossless, [&] { encoder.near_lossless(256); }); } - TEST_METHOD(estimated_destination_size_minimal_frame_info) + TEST_METHOD(estimated_destination_size_minimal_frame_info) // NOLINT { jpegls_encoder encoder; encoder.frame_info({1, 1, 2, 1}); // = minimum. - const auto size = encoder.estimated_destination_size(); + const auto size{encoder.estimated_destination_size()}; Assert::IsTrue(size >= 1024); } - TEST_METHOD(estimated_destination_size_maximal_frame_info) + TEST_METHOD(estimated_destination_size_maximal_frame_info) // NOLINT { jpegls_encoder encoder; encoder.frame_info({UINT16_MAX, UINT16_MAX, 8, 1}); // = maximum. - const auto size = encoder.estimated_destination_size(); - constexpr auto expected = static_cast(UINT16_MAX) * UINT16_MAX * 1 * 1; + const auto size{encoder.estimated_destination_size()}; + constexpr auto expected{static_cast(UINT16_MAX) * UINT16_MAX * 1 * 1}; Assert::IsTrue(size >= expected); } - TEST_METHOD(estimated_destination_size_monochrome_16_bit) + TEST_METHOD(estimated_destination_size_monochrome_16_bit) // NOLINT { jpegls_encoder encoder; encoder.frame_info({100, 100, 16, 1}); // minimum. - const auto size = encoder.estimated_destination_size(); + const auto size{encoder.estimated_destination_size()}; Assert::IsTrue(size >= 100 * 100 * 2); } - TEST_METHOD(estimated_destination_size_color_8_bit) + TEST_METHOD(estimated_destination_size_color_8_bit) // NOLINT { jpegls_encoder encoder; encoder.frame_info({2000, 2000, 8, 3}); - const auto size = encoder.estimated_destination_size(); + const auto size{encoder.estimated_destination_size()}; Assert::IsTrue(size >= 2000 * 2000 * 3); } - TEST_METHOD(estimated_destination_size_very_wide) + TEST_METHOD(estimated_destination_size_very_wide) // NOLINT { jpegls_encoder encoder; encoder.frame_info({UINT16_MAX, 1, 8, 1}); - const auto size = encoder.estimated_destination_size(); + const auto size{encoder.estimated_destination_size()}; Assert::IsTrue(size >= UINT16_MAX + 1024); } - TEST_METHOD(estimated_destination_size_very_high) + TEST_METHOD(estimated_destination_size_very_high) // NOLINT { jpegls_encoder encoder; encoder.frame_info({1, UINT16_MAX, 8, 1}); - const auto size = encoder.estimated_destination_size(); + const auto size{encoder.estimated_destination_size()}; Assert::IsTrue(size >= UINT16_MAX + 1024); } - TEST_METHOD(estimated_destination_size_too_soon) + TEST_METHOD(estimated_destination_size_too_soon) // NOLINT { jpegls_encoder encoder; - assert_expect_exception(jpegls_errc::invalid_operation, [&] { static_cast(encoder.estimated_destination_size()); }); + assert_expect_exception(jpegls_errc::invalid_operation, + [&] { static_cast(encoder.estimated_destination_size()); }); } - TEST_METHOD(destination) + TEST_METHOD(destination) // NOLINT { jpegls_encoder encoder; @@ -186,17 +188,17 @@ encoder.destination(destination); } - TEST_METHOD(destination_can_only_be_set_once) + TEST_METHOD(destination_can_only_be_set_once) // NOLINT { jpegls_encoder encoder; vector destination(200); encoder.destination(destination); - assert_expect_exception(jpegls_errc::invalid_operation, [&] {encoder.destination(destination); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { encoder.destination(destination); }); } - TEST_METHOD(write_standard_spiff_header) + TEST_METHOD(write_standard_spiff_header) // NOLINT { jpegls_encoder encoder; @@ -211,11 +213,11 @@ // Check that SOI marker has been written. Assert::AreEqual(static_cast(0xFF), destination[0]); - Assert::AreEqual(static_cast(JpegMarkerCode::StartOfImage), destination[1]); + Assert::AreEqual(static_cast(jpeg_marker_code::start_of_image), destination[1]); // Verify that a APP8 with SPIFF has been written (details already verified by jpeg_stream_writer_test). Assert::AreEqual(static_cast(0xFF), destination[2]); - Assert::AreEqual(static_cast(JpegMarkerCode::ApplicationData8), destination[3]); + Assert::AreEqual(static_cast(jpeg_marker_code::application_data8), destination[3]); Assert::AreEqual(static_cast(0), destination[4]); Assert::AreEqual(static_cast(32), destination[5]); Assert::AreEqual(static_cast('S'), destination[6]); @@ -226,39 +228,42 @@ Assert::AreEqual(static_cast(0), destination[11]); } - TEST_METHOD(write_standard_spiff_header_without_destination) + TEST_METHOD(write_standard_spiff_header_without_destination) // NOLINT { jpegls_encoder encoder; - encoder.frame_info({ 1, 1, 2, 1 }); + encoder.frame_info({1, 1, 2, 1}); - assert_expect_exception(jpegls_errc::invalid_operation, [&] { encoder.write_standard_spiff_header(spiff_color_space::cmyk); }); + assert_expect_exception(jpegls_errc::invalid_operation, + [&] { encoder.write_standard_spiff_header(spiff_color_space::cmyk); }); } - TEST_METHOD(write_standard_spiff_header_without_frame_info) + TEST_METHOD(write_standard_spiff_header_without_frame_info) // NOLINT { jpegls_encoder encoder; vector destination(100); encoder.destination(destination); - assert_expect_exception(jpegls_errc::invalid_operation, [&] { encoder.write_standard_spiff_header(spiff_color_space::cmyk); }); + assert_expect_exception(jpegls_errc::invalid_operation, + [&] { encoder.write_standard_spiff_header(spiff_color_space::cmyk); }); } - TEST_METHOD(write_standard_spiff_header_twice) + TEST_METHOD(write_standard_spiff_header_twice) // NOLINT { jpegls_encoder encoder; - encoder.frame_info({ 1, 1, 2, 1 }); + encoder.frame_info({1, 1, 2, 1}); vector destination(encoder.estimated_destination_size()); encoder.destination(destination); encoder.write_standard_spiff_header(spiff_color_space::cmyk); - assert_expect_exception(jpegls_errc::invalid_operation, [&] { encoder.write_standard_spiff_header(spiff_color_space::cmyk); }); + assert_expect_exception(jpegls_errc::invalid_operation, + [&] { encoder.write_standard_spiff_header(spiff_color_space::cmyk); }); } - TEST_METHOD(write_spiff_header) + TEST_METHOD(write_spiff_header) // NOLINT { jpegls_encoder encoder; @@ -276,11 +281,11 @@ // Check that SOI marker has been written. Assert::AreEqual(static_cast(0xFF), destination[0]); - Assert::AreEqual(static_cast(JpegMarkerCode::StartOfImage), destination[1]); + Assert::AreEqual(static_cast(jpeg_marker_code::start_of_image), destination[1]); // Verify that a APP8 with SPIFF has been written (details already verified by jpeg_stream_writer_test). Assert::AreEqual(static_cast(0xFF), destination[2]); - Assert::AreEqual(static_cast(JpegMarkerCode::ApplicationData8), destination[3]); + Assert::AreEqual(static_cast(jpeg_marker_code::application_data8), destination[3]); Assert::AreEqual(static_cast(0), destination[4]); Assert::AreEqual(static_cast(32), destination[5]); Assert::AreEqual(static_cast('S'), destination[6]); @@ -291,7 +296,7 @@ Assert::AreEqual(static_cast(0), destination[11]); } - TEST_METHOD(write_spiff_header_invalid_height) + TEST_METHOD(write_spiff_header_invalid_height) // NOLINT { jpegls_encoder encoder; @@ -307,7 +312,7 @@ Assert::AreEqual(static_cast(0), encoder.bytes_written()); } - TEST_METHOD(write_spiff_header_invalid_width) + TEST_METHOD(write_spiff_header_invalid_width) // NOLINT { jpegls_encoder encoder; @@ -323,7 +328,7 @@ Assert::AreEqual(static_cast(0), encoder.bytes_written()); } - TEST_METHOD(write_spiff_entry) + TEST_METHOD(write_spiff_entry) // NOLINT { jpegls_encoder encoder; @@ -338,7 +343,7 @@ Assert::AreEqual(static_cast(48), encoder.bytes_written()); } - TEST_METHOD(write_spiff_entry_twice) + TEST_METHOD(write_spiff_entry_twice) // NOLINT { jpegls_encoder encoder; @@ -354,7 +359,7 @@ Assert::AreEqual(static_cast(60), encoder.bytes_written()); } - TEST_METHOD(write_empty_spiff_entry) + TEST_METHOD(write_empty_spiff_entry) // NOLINT { jpegls_encoder encoder; @@ -369,7 +374,7 @@ Assert::AreEqual(static_cast(44), encoder.bytes_written()); } - TEST_METHOD(write_spiff_entry_with_invalid_tag) + TEST_METHOD(write_spiff_entry_with_invalid_tag) // NOLINT { jpegls_encoder encoder; @@ -382,7 +387,7 @@ assert_expect_exception(jpegls_errc::invalid_argument, [&] { encoder.write_spiff_entry(1, "test", 4); }); } - TEST_METHOD(write_spiff_entry_with_invalid_size) + TEST_METHOD(write_spiff_entry_with_invalid_size) // NOLINT { jpegls_encoder encoder; @@ -392,11 +397,13 @@ encoder.destination(destination); encoder.write_standard_spiff_header(spiff_color_space::cmyk); - assert_expect_exception(jpegls_errc::invalid_argument_spiff_entry_size, - [&] { encoder.write_spiff_entry(spiff_entry_tag::image_title, "test", 65528 + 1); }); + assert_expect_exception(jpegls_errc::invalid_argument_spiff_entry_size, [&] { + vector spiff_entry(65528 + 1); + encoder.write_spiff_entry(spiff_entry_tag::image_title, spiff_entry.data(), spiff_entry.size()); + }); } - TEST_METHOD(write_spiff_entry_without_spiff_header) + TEST_METHOD(write_spiff_entry_without_spiff_header) // NOLINT { jpegls_encoder encoder; @@ -405,11 +412,13 @@ vector destination(encoder.estimated_destination_size()); encoder.destination(destination); - assert_expect_exception(jpegls_errc::invalid_operation, - [&] { encoder.write_spiff_entry(spiff_entry_tag::image_title, "test", 65528); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { + vector spiff_entry(65528); + encoder.write_spiff_entry(spiff_entry_tag::image_title, spiff_entry.data(), spiff_entry.size()); + }); } - TEST_METHOD(set_preset_coding_parameters) + TEST_METHOD(set_preset_coding_parameters) // NOLINT { jpegls_encoder encoder; @@ -420,44 +429,44 @@ Assert::IsTrue(true); } - TEST_METHOD(set_preset_coding_parameters_bad_values) + TEST_METHOD(set_preset_coding_parameters_bad_values) // NOLINT { jpegls_encoder encoder; - charls_jpegls_pc_parameters pc_parameters{1,1,1,1,1}; + charls_jpegls_pc_parameters pc_parameters{1, 1, 1, 1, 1}; - assert_expect_exception(jpegls_errc::invalid_argument_pc_parameters, - [&] { encoder.preset_coding_parameters(pc_parameters); }); + assert_expect_exception(jpegls_errc::invalid_argument_jpegls_pc_parameters, + [&] { encoder.preset_coding_parameters(pc_parameters); }); } - TEST_METHOD(set_color_transformation_bad_value) + TEST_METHOD(set_color_transformation_bad_value) // NOLINT { jpegls_encoder encoder; assert_expect_exception(jpegls_errc::invalid_argument_color_transformation, - [&] { encoder.color_transformation(static_cast(100)); }); + [&] { encoder.color_transformation(static_cast(100)); }); } - TEST_METHOD(encode_without_destination) + TEST_METHOD(encode_without_destination) // NOLINT { jpegls_encoder encoder; encoder.frame_info({1, 1, 2, 1}); vector source(20); - assert_expect_exception(jpegls_errc::invalid_operation,[&] { static_cast(encoder.encode(source)); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { static_cast(encoder.encode(source)); }); } - TEST_METHOD(encode_without_frame_info) + TEST_METHOD(encode_without_frame_info) // NOLINT { jpegls_encoder encoder; vector destination(20); encoder.destination(destination); vector source(20); - assert_expect_exception(jpegls_errc::invalid_operation,[&] { static_cast(encoder.encode(source)); }); + assert_expect_exception(jpegls_errc::invalid_operation, [&] { static_cast(encoder.encode(source)); }); } - TEST_METHOD(encode_with_spiff_header) + TEST_METHOD(encode_with_spiff_header) // NOLINT { const array source{0, 1, 2, 3, 4}; const frame_info frame_info{5, 1, 8, 1}; @@ -475,7 +484,7 @@ test_by_decoding(destination, frame_info, source.data(), source.size(), interleave_mode::none); } - TEST_METHOD(encode_with_color_space) + TEST_METHOD(encode_with_color_transformation) // NOLINT { const array source{0, 1, 2, 3, 4, 5}; const frame_info frame_info{2, 1, 8, 3}; @@ -483,16 +492,16 @@ jpegls_encoder encoder; encoder.frame_info(frame_info); vector destination(encoder.estimated_destination_size()); - encoder.destination(destination) - .color_transformation(color_transformation::hp1); + encoder.destination(destination).color_transformation(color_transformation::hp1); const size_t bytes_written{encoder.encode(source)}; destination.resize(bytes_written); - test_by_decoding(destination, frame_info, source.data(), source.size(), interleave_mode::none); + test_by_decoding(destination, frame_info, source.data(), source.size(), interleave_mode::none, + color_transformation::hp1); } - TEST_METHOD(encode_16bit) + TEST_METHOD(encode_16_bit) // NOLINT { const array source{0, 1, 2, 3, 4, 5}; const frame_info frame_info{3, 1, 16, 1}; @@ -509,46 +518,250 @@ test_by_decoding(destination, frame_info, source.data(), source.size(), interleave_mode::none); } - TEST_METHOD(simple_encode) + TEST_METHOD(simple_encode) // NOLINT { const vector source{0, 1, 2, 3, 4, 5}; const frame_info frame_info{3, 1, 16, 1}; - const auto encoded = jpegls_encoder::encode(source, frame_info); + const auto encoded{jpegls_encoder::encode(source, frame_info)}; test_by_decoding(encoded, frame_info, source.data(), source.size(), interleave_mode::none); } + TEST_METHOD(encode_with_stride) // NOLINT + { + const array source{100, 100, 100, 0, 0, 0, 0, 0, 0, 0, 150, 150, + 150, 0, 0, 0, 0, 0, 0, 0, 200, 200, 200}; + const frame_info frame_info{3, 1, 8, 3}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info); + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source, 10)}; + destination.resize(bytes_written); + + const array expected{100, 100, 100, 150, 150, 150, 200, 200, 200}; + test_by_decoding(destination, frame_info, expected.data(), expected.size(), interleave_mode::none); + } + + TEST_METHOD(encode_1_component_4_bit_with_high_bits_set) // NOLINT + { + const vector source(512 * 512, 0xFF); + const frame_info frame_info{512, 512, 4, 1}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info); + + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + const vector expected(512 * 512, 15); + test_by_decoding(destination, frame_info, expected.data(), expected.size(), interleave_mode::none); + } + + TEST_METHOD(encode_1_component_12_bit_with_high_bits_set) // NOLINT + { + const vector source(512 * 512 * 2, 0xFF); + const frame_info frame_info{512, 512, 12, 1}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info); + + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + const vector expected(512 * 512, 4095); + test_by_decoding(destination, frame_info, expected.data(), expected.size() * 2, interleave_mode::none); + } + + TEST_METHOD(encode_3_components_6_bit_with_high_bits_set_interleave_mode_sample) // NOLINT + { + const vector source(512 * 512 * 3, 0xFF); + const frame_info frame_info{512, 512, 6, 3}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info).interleave_mode(interleave_mode::sample); + + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + const vector expected(512 * 512 * 3, 63); + test_by_decoding(destination, frame_info, expected.data(), expected.size(), interleave_mode::sample); + } + + TEST_METHOD(encode_3_components_6_bit_with_high_bits_set_interleave_mode_line) // NOLINT + { + const vector source(512 * 512 * 3, 0xFF); + const frame_info frame_info{512, 512, 6, 3}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info).interleave_mode(interleave_mode::line); + + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + const vector expected(512 * 512 * 3, 63); + test_by_decoding(destination, frame_info, expected.data(), expected.size(), interleave_mode::line); + } + + TEST_METHOD(encode_3_components_10_bit_with_high_bits_set_interleave_mode_sample) // NOLINT + { + const vector source(512 * 512 * 2 * 3, 0xFF); + const frame_info frame_info{512, 512, 10, 3}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info).interleave_mode(interleave_mode::sample); + + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + const vector expected(512 * 512 * 3, 1023); + test_by_decoding(destination, frame_info, expected.data(), expected.size() * 2, interleave_mode::sample); + } + + TEST_METHOD(encode_3_components_10_bit_with_high_bits_set_interleave_mode_line) // NOLINT + { + const vector source(512 * 512 * 2 * 3, 0xFF); + const frame_info frame_info{512, 512, 10, 3}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info).interleave_mode(interleave_mode::line); + + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + const vector expected(512 * 512 * 3, 1023); + test_by_decoding(destination, frame_info, expected.data(), expected.size() * 2, interleave_mode::line); + } + + TEST_METHOD(encode_4_components_6_bit_with_high_bits_set_interleave_mode_sample) // NOLINT + { + const vector source(512 * 512 * 4, 0xFF); + const frame_info frame_info{512, 512, 6, 4}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info).interleave_mode(interleave_mode::sample); + + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + const vector expected(512 * 512 * 4, 63); + test_by_decoding(destination, frame_info, expected.data(), expected.size(), interleave_mode::sample); + } + + TEST_METHOD(encode_4_components_6_bit_with_high_bits_set_interleave_mode_line) // NOLINT + { + const vector source(512 * 512 * 4, 0xFF); + const frame_info frame_info{512, 512, 6, 4}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info).interleave_mode(interleave_mode::line); + + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + const vector expected(512 * 512 * 4, 63); + test_by_decoding(destination, frame_info, expected.data(), expected.size(), interleave_mode::line); + } + + TEST_METHOD(encode_4_components_10_bit_with_high_bits_set_interleave_mode_sample) // NOLINT + { + const vector source(512 * 512 * 2 * 4, 0xFF); + const frame_info frame_info{512, 512, 10, 4}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info).interleave_mode(interleave_mode::sample); + + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + const vector expected(512 * 512 * 4, 1023); + test_by_decoding(destination, frame_info, expected.data(), expected.size() * 2, interleave_mode::sample); + } + + TEST_METHOD(encode_4_components_10_bit_with_high_bits_set_interleave_mode_line) // NOLINT + { + const vector source(512 * 512 * 2 * 4, 0xFF); + const frame_info frame_info{512, 512, 10, 4}; + + jpegls_encoder encoder; + encoder.frame_info(frame_info).interleave_mode(interleave_mode::line); + + vector destination(encoder.estimated_destination_size()); + encoder.destination(destination); + + const size_t bytes_written{encoder.encode(source)}; + destination.resize(bytes_written); + + const vector expected(512 * 512 * 4, 1023); + test_by_decoding(destination, frame_info, expected.data(), expected.size() * 2, interleave_mode::line); + } + private: - static void test_by_decoding(const vector& encoded_source, const frame_info& source_frame_info, const uint8_t* source, const size_t source_size, const charls::interleave_mode interleave_mode) + static void test_by_decoding(const vector& encoded_source, const frame_info& source_frame_info, + const void* expected_destination, const size_t expected_destination_size, + const charls::interleave_mode interleave_mode, + const color_transformation color_transformation = color_transformation::none) { jpegls_decoder decoder; decoder.source(encoded_source); decoder.read_header(); - const auto frame_info = decoder.frame_info(); + const auto& frame_info = decoder.frame_info(); Assert::AreEqual(source_frame_info.width, frame_info.width); Assert::AreEqual(source_frame_info.height, frame_info.height); Assert::AreEqual(source_frame_info.bits_per_sample, frame_info.bits_per_sample); Assert::AreEqual(source_frame_info.component_count, frame_info.component_count); Assert::IsTrue(interleave_mode == decoder.interleave_mode()); + Assert::IsTrue(color_transformation == decoder.color_transformation()); vector destination(decoder.destination_size()); decoder.decode(destination); - Assert::AreEqual(destination.size(), source_size); + Assert::AreEqual(destination.size(), expected_destination_size); if (decoder.near_lossless() == 0) { - for (size_t i = 0; i < source_size; ++i) + const auto* expected_destination_byte{static_cast(expected_destination)}; + + for (size_t i{}; i < expected_destination_size; ++i) { - if (destination[i] != source[i]) // AreEqual is very slow, pre-test to speed up 50X + if (expected_destination_byte[i] != destination[i]) // AreEqual is very slow, pre-test to speed up 50X { - Assert::AreEqual(destination[i], source[i]); + Assert::AreEqual(expected_destination_byte[i], destination[i]); } } } } }; -} // namespace CharLSUnitTest +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/jpegls_preset_coding_parameters_test.cpp charls-2.2.0+dfsg/unittest/jpegls_preset_coding_parameters_test.cpp --- charls-2.1.0+dfsg/unittest/jpegls_preset_coding_parameters_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/jpegls_preset_coding_parameters_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -6,11 +6,10 @@ #include "../src/jpegls_preset_coding_parameters.h" using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -using namespace charls; namespace { -struct thresholds +struct thresholds final { int32_t MaxVal; int32_t T1; @@ -20,13 +19,13 @@ }; // Threshold function of JPEG-LS reference implementation. -constexpr thresholds ComputeDefaultsUsingReferenceImplementation(const int32_t max_value, const uint16_t near) noexcept +constexpr thresholds compute_defaults_using_reference_implementation(const int32_t max_value, const uint16_t near) noexcept { thresholds result{max_value, 0, 0, 0, 64}; if (result.MaxVal >= 128) { - int32_t factor = result.MaxVal; + int32_t factor{result.MaxVal}; if (factor > 4095) factor = 4095; factor = (factor + 128) >> 8; @@ -42,7 +41,7 @@ } else { - const int32_t factor = 256 / (result.MaxVal + 1); + const int32_t factor{256 / (result.MaxVal + 1)}; result.T1 = 3 / factor + 3 * near; if (result.T1 < 2) result.T1 = 2; @@ -65,14 +64,12 @@ } // namespace -// clang-format off +namespace charls { namespace test { -namespace CharLSUnitTest { - -TEST_CLASS(JpeglsPresetCodingParametersTest) +TEST_CLASS(jpegls_preset_coding_parameters_test) { public: - TEST_METHOD(TableC3) + TEST_METHOD(table_c3) // NOLINT { const auto parameters{compute_default(255, 0)}; @@ -83,9 +80,9 @@ Assert::AreEqual(64, parameters.reset_value); } - TEST_METHOD(MaxValueLossless) + TEST_METHOD(max_value_lossless) // NOLINT { - const auto expected{ComputeDefaultsUsingReferenceImplementation(65535, 0)}; + const auto expected{compute_defaults_using_reference_implementation(65535, 0)}; const auto parameters{compute_default(65535, 0)}; Assert::AreEqual(expected.MaxVal, parameters.maximum_sample_value); @@ -95,9 +92,9 @@ Assert::AreEqual(expected.Reset, parameters.reset_value); } - TEST_METHOD(MinValueLossless) + TEST_METHOD(min_value_lossless) // NOLINT { - const auto expected{ComputeDefaultsUsingReferenceImplementation(3, 0)}; + const auto expected{compute_defaults_using_reference_implementation(3, 0)}; const auto parameters{compute_default(3, 0)}; Assert::AreEqual(expected.MaxVal, parameters.maximum_sample_value); @@ -107,9 +104,9 @@ Assert::AreEqual(expected.Reset, parameters.reset_value); } - TEST_METHOD(MinHighValueLossless) + TEST_METHOD(min_high_value_lossless) // NOLINT { - const auto expected{ComputeDefaultsUsingReferenceImplementation(128, 0)}; + const auto expected{compute_defaults_using_reference_implementation(128, 0)}; const auto parameters{compute_default(128, 0)}; Assert::AreEqual(expected.MaxVal, parameters.maximum_sample_value); @@ -119,9 +116,9 @@ Assert::AreEqual(expected.Reset, parameters.reset_value); } - TEST_METHOD(MaxLowValueLossless) + TEST_METHOD(max_low_value_lossless) // NOLINT { - const auto expected{ComputeDefaultsUsingReferenceImplementation(127, 0)}; + const auto expected{compute_defaults_using_reference_implementation(127, 0)}; const auto parameters{compute_default(127, 0)}; Assert::AreEqual(expected.MaxVal, parameters.maximum_sample_value); @@ -131,9 +128,9 @@ Assert::AreEqual(expected.Reset, parameters.reset_value); } - TEST_METHOD(MaxValueMaxLossy) + TEST_METHOD(max_value_max_lossy) // NOLINT { - const auto expected{ComputeDefaultsUsingReferenceImplementation(65535, 255)}; + const auto expected{compute_defaults_using_reference_implementation(65535, 255)}; const auto parameters{compute_default(65535, 255)}; Assert::AreEqual(expected.MaxVal, parameters.maximum_sample_value); @@ -143,9 +140,9 @@ Assert::AreEqual(expected.Reset, parameters.reset_value); } - TEST_METHOD(MinValueMaxLossy) + TEST_METHOD(min_value_max_lossy) // NOLINT { - const auto expected{ComputeDefaultsUsingReferenceImplementation(3, 1)}; + const auto expected{compute_defaults_using_reference_implementation(3, 1)}; const auto parameters{compute_default(3, 1)}; Assert::AreEqual(expected.MaxVal, parameters.maximum_sample_value); @@ -155,23 +152,23 @@ Assert::AreEqual(expected.Reset, parameters.reset_value); } - TEST_METHOD(is_valid_default) + TEST_METHOD(is_valid_default) // NOLINT { - constexpr auto bits_per_sample = 16; - constexpr auto maximum_component_value = (1 << bits_per_sample) - 1; + constexpr auto bits_per_sample{16}; + constexpr auto maximum_component_value{(1 << bits_per_sample) - 1}; const jpegls_pc_parameters pc_parameters{}; Assert::IsTrue(is_valid(pc_parameters, maximum_component_value, 0)); } - TEST_METHOD(is_valid_thresholds_zero) + TEST_METHOD(is_valid_thresholds_zero) // NOLINT { - constexpr auto bits_per_sample = 16; - constexpr auto maximum_component_value = (1 << bits_per_sample) - 1; + constexpr auto bits_per_sample{16}; + constexpr auto maximum_component_value{(1 << bits_per_sample) - 1}; const jpegls_pc_parameters pc_parameters{maximum_component_value, 0, 0, 0, 63}; Assert::IsTrue(is_valid(pc_parameters, maximum_component_value, 0)); } }; -} // namespace CharLSUnitTest +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/jpeg_stream_reader_test.cpp charls-2.2.0+dfsg/unittest/jpeg_stream_reader_test.cpp --- charls-2.1.0+dfsg/unittest/jpeg_stream_reader_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/jpeg_stream_reader_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -3,39 +3,34 @@ #include "pch.h" +#include "jpeg_test_stream_writer.h" #include "util.h" #include "../src/jpeg_stream_reader.h" #include "../src/jpeg_stream_writer.h" -#include "jpeg_test_stream_writer.h" #include #include #include -using namespace charls; using Microsoft::VisualStudio::CppUnitTestFramework::Assert; using std::array; using std::system_error; using std::vector; -namespace CharLSUnitTest { +namespace charls { namespace test { -// clang-format off - -TEST_CLASS(JpegStreamReaderTest) +TEST_CLASS(jpeg_stream_reader_test) { public: - TEST_METHOD(ReadHeaderFromToSmallInputBuffer) + TEST_METHOD(read_header_from_to_small_input_buffer) // NOLINT { array buffer{}; - - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), 0); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), 0}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -46,23 +41,25 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderFromBufferPrecededWithFillBytes) + TEST_METHOD(read_header_from_buffer_preceded_with_fill_bytes) // NOLINT { - vector buffer; - buffer.push_back(0xFF); - buffer.push_back(0xFF); - buffer.push_back(0xD8); - buffer.push_back(0xFF); - buffer.push_back(0xFF); - buffer.push_back(0xDA); // SOS: Marks the start of scan. + jpeg_test_stream_writer writer; + + writer.buffer.push_back(0xFF); + writer.write_start_of_image(); + + writer.buffer.push_back(0xFF); + writer.write_start_of_frame_segment(1, 1, 2, 1); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + writer.buffer.push_back(0xFF); + writer.write_start_of_scan_segment(0, 1, 128, interleave_mode::none); - reader.ReadHeader(); // if it doesn't throw test is passed. + jpeg_stream_reader reader({writer.buffer.data(), writer.buffer.size()}); + + reader.read_header(); // if it doesn't throw test is passed. } - TEST_METHOD(ReadHeaderFromBufferNotStartingWithFFShouldThrow) + TEST_METHOD(read_header_from_buffer_not_starting_with_ff_should_throw) // NOLINT { vector buffer; buffer.push_back(0x0F); @@ -72,12 +69,11 @@ buffer.push_back(0xFF); buffer.push_back(0xDA); // SOS: Marks the start of scan. - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -88,45 +84,27 @@ Assert::Fail(); } - static void ReadHeaderWithApplicationData(uint8_t dataNumber) + TEST_METHOD(read_header_with_application_data) // NOLINT { - vector buffer; - buffer.push_back(0xFF); - buffer.push_back(0xD8); // SOI: Marks the start of an image. - buffer.push_back(0xFF); - buffer.push_back(0xE0 + dataNumber); - buffer.push_back(0x00); - buffer.push_back(0x02); - buffer.push_back(0xFF); - buffer.push_back(0xDA); // SOS: Marks the start of scan. - - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); - - reader.ReadHeader(); // if it doesn't throw test is passed. + read_header_with_application_data(0); + read_header_with_application_data(1); + read_header_with_application_data(2); + read_header_with_application_data(3); + read_header_with_application_data(4); + read_header_with_application_data(5); + read_header_with_application_data(6); + read_header_with_application_data(7); + read_header_with_application_data(8); + read_header_with_application_data(9); + read_header_with_application_data(10); + read_header_with_application_data(11); + read_header_with_application_data(12); + read_header_with_application_data(13); + read_header_with_application_data(14); + read_header_with_application_data(15); } - TEST_METHOD(ReadHeaderWithApplicationData) - { - ReadHeaderWithApplicationData(0); - ReadHeaderWithApplicationData(1); - ReadHeaderWithApplicationData(2); - ReadHeaderWithApplicationData(3); - ReadHeaderWithApplicationData(4); - ReadHeaderWithApplicationData(5); - ReadHeaderWithApplicationData(6); - ReadHeaderWithApplicationData(7); - ReadHeaderWithApplicationData(8); - ReadHeaderWithApplicationData(9); - ReadHeaderWithApplicationData(10); - ReadHeaderWithApplicationData(11); - ReadHeaderWithApplicationData(12); - ReadHeaderWithApplicationData(13); - ReadHeaderWithApplicationData(14); - ReadHeaderWithApplicationData(15); - } - - TEST_METHOD(ReadHeaderWithJpegLSExtendedFrameShouldThrow) + TEST_METHOD(read_header_with_jpegls_extended_frame_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -134,12 +112,11 @@ buffer.push_back(0xFF); buffer.push_back(0xF9); // SOF_57: Marks the start of a JPEG-LS extended (ISO/IEC 14495-2) encoded frame. - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -150,24 +127,21 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderJpegLSPresetParameterSegment) + TEST_METHOD(read_header_jpegls_preset_parameter_segment) // NOLINT { vector source(100); - const ByteStreamInfo sourceInfo = FromByteArray(source.data(), source.size()); - - charls::JpegStreamWriter writer(sourceInfo); - writer.WriteStartOfImage(); + jpeg_stream_writer writer({source.data(), source.size()}); + writer.write_start_of_image(); const jpegls_pc_parameters presets{1, 2, 3, 4, 5}; - writer.WriteJpegLSPresetParametersSegment(presets); - writer.WriteStartOfFrameSegment(1, 1, 2, 1); - writer.WriteStartOfScanSegment(1, 0, interleave_mode::none); + writer.write_jpegls_preset_parameters_segment(presets); + writer.write_start_of_frame_segment(1, 1, 2, 1); + writer.write_start_of_scan_segment(1, 0, interleave_mode::none); - const ByteStreamInfo destinationInfo = FromByteArray(source.data(), source.size()); - JpegStreamReader reader(destinationInfo); + jpeg_stream_reader reader({source.data(), source.size()}); - reader.ReadHeader(); - const auto& actual = reader.GetCustomPreset(); + reader.read_header(); + const auto& actual = reader.preset_coding_parameters(); Assert::AreEqual(presets.maximum_sample_value, actual.maximum_sample_value); Assert::AreEqual(presets.reset_value, actual.reset_value); @@ -176,7 +150,7 @@ Assert::AreEqual(presets.threshold3, actual.threshold3); } - TEST_METHOD(ReadHeaderWithTooSmallJpegLSPresetParameterSegmentShouldThrow) + TEST_METHOD(read_header_with_too_small_jpegls_preset_parameter_segment_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -187,12 +161,11 @@ buffer.push_back(0x02); buffer.push_back(0x01); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -203,7 +176,7 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderWithTooSmallJpegLSPresetParameterSegmentWithCodingParametersShouldThrow) + TEST_METHOD(read_header_with_too_small_jpegls_preset_parameter_segment_with_coding_parameters_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -214,12 +187,11 @@ buffer.push_back(0x0A); buffer.push_back(0x01); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -230,7 +202,7 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderWithTooLargeJpegLSPresetParameterSegmentWithCodingParametersShouldThrow) + TEST_METHOD(read_header_with_too_large_jpegls_preset_parameter_segment_with_coding_parameters_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -241,12 +213,11 @@ buffer.push_back(0x0C); buffer.push_back(0x01); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -257,7 +228,22 @@ Assert::Fail(); } - static void ReadHeaderWithJpegLSPresetParameterWithExtendedIdShouldThrow(uint8_t id) + TEST_METHOD(read_header_bad_jpegls_preset_coding_parameters_should_throw) // NOLINT + { + const jpegls_pc_parameters preset_coding_parameters{256, 0, 0, 0, 0}; + + jpeg_test_stream_writer writer; + writer.write_start_of_image(); + writer.write_jpegls_preset_parameters_segment(preset_coding_parameters); + writer.write_start_of_frame_segment(512, 512, 8, 3); + writer.write_start_of_scan_segment(0, 1, 127, interleave_mode::none); + + jpeg_stream_reader reader({writer.buffer.data(), writer.buffer.size()}); + + assert_expect_exception(jpegls_errc::invalid_parameter_jpegls_pc_parameters, [&]() { reader.read_header(); }); + } + + static void read_header_with_jpeg_ls_preset_parameter_with_extended_id_should_throw(const uint8_t id) { vector buffer; buffer.push_back(0xFF); @@ -268,35 +254,35 @@ buffer.push_back(0x03); buffer.push_back(id); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { - Assert::AreEqual(static_cast(jpegls_errc::jpegls_preset_extended_parameter_type_not_supported), error.code().value()); + Assert::AreEqual(static_cast(jpegls_errc::jpegls_preset_extended_parameter_type_not_supported), + error.code().value()); return; } Assert::Fail(); } - TEST_METHOD(ReadHeaderWithJpegLSPresetParameterWithExtendedIdShouldThrow) + TEST_METHOD(read_header_with_jpegls_preset_parameter_with_extended_id_should_throw) // NOLINT { - ReadHeaderWithJpegLSPresetParameterWithExtendedIdShouldThrow(0x5); - ReadHeaderWithJpegLSPresetParameterWithExtendedIdShouldThrow(0x6); - ReadHeaderWithJpegLSPresetParameterWithExtendedIdShouldThrow(0x7); - ReadHeaderWithJpegLSPresetParameterWithExtendedIdShouldThrow(0x8); - ReadHeaderWithJpegLSPresetParameterWithExtendedIdShouldThrow(0x9); - ReadHeaderWithJpegLSPresetParameterWithExtendedIdShouldThrow(0xA); - ReadHeaderWithJpegLSPresetParameterWithExtendedIdShouldThrow(0xC); - ReadHeaderWithJpegLSPresetParameterWithExtendedIdShouldThrow(0xD); + read_header_with_jpeg_ls_preset_parameter_with_extended_id_should_throw(0x5); + read_header_with_jpeg_ls_preset_parameter_with_extended_id_should_throw(0x6); + read_header_with_jpeg_ls_preset_parameter_with_extended_id_should_throw(0x7); + read_header_with_jpeg_ls_preset_parameter_with_extended_id_should_throw(0x8); + read_header_with_jpeg_ls_preset_parameter_with_extended_id_should_throw(0x9); + read_header_with_jpeg_ls_preset_parameter_with_extended_id_should_throw(0xA); + read_header_with_jpeg_ls_preset_parameter_with_extended_id_should_throw(0xC); + read_header_with_jpeg_ls_preset_parameter_with_extended_id_should_throw(0xD); } - TEST_METHOD(ReadHeaderWithTooSmallSegmentSizeShouldThrow) + TEST_METHOD(read_header_with_too_small_segment_size_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -308,12 +294,11 @@ buffer.push_back(0xFF); buffer.push_back(0xDA); // SOS: Marks the start of scan. - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -324,7 +309,7 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderWithTooSmallStartOfFrameShouldThrow) + TEST_METHOD(read_header_with_too_small_start_of_frame_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -334,12 +319,11 @@ buffer.push_back(0x00); buffer.push_back(0x07); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -350,7 +334,7 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderWithTooSmallStartOfFrameInComponentInfoShouldThrow) + TEST_METHOD(read_header_with_too_small_start_of_frame_in_component_info_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -360,12 +344,11 @@ buffer.push_back(0x00); buffer.push_back(0x07); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -376,20 +359,19 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderWithTooLargeStartOfFrameShouldThrow) + TEST_METHOD(read_header_with_too_large_start_of_frame_should_throw) // NOLINT { - JpegTestStreamWriter writer; - writer.WriteStartOfImage(); - writer.WriteStartOfFrameSegment(512, 512, 8, 3); - writer.data_.push_back(0); - writer.data_[5]++; - const ByteStreamInfo byteStream = FromByteArray(writer.data_.data(), writer.data_.size()); + jpeg_test_stream_writer writer; + writer.write_start_of_image(); + writer.write_start_of_frame_segment(512, 512, 8, 3); + writer.buffer.push_back(0); + writer.buffer[5]++; - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({writer.buffer.data(), writer.buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -400,19 +382,72 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderWithDuplicateComponentIdInStartOfFrameSegmentShouldThrow) + TEST_METHOD(read_header_sos_before_sof_should_throw) // NOLINT + { + jpeg_test_stream_writer writer; + writer.write_start_of_image(); + writer.write_start_of_scan_segment(0, 1, 128, interleave_mode::none); + + jpeg_stream_reader reader({writer.buffer.data(), writer.buffer.size()}); + + assert_expect_exception(jpegls_errc::unexpected_marker_found, [&]() { reader.read_header(); }); + } + + TEST_METHOD(read_header_extra_sof_should_throw) // NOLINT + { + jpeg_test_stream_writer writer; + writer.write_start_of_image(); + writer.write_start_of_frame_segment(512, 512, 8, 3); + writer.write_start_of_frame_segment(512, 512, 8, 3); + + jpeg_stream_reader reader({writer.buffer.data(), writer.buffer.size()}); + + assert_expect_exception(jpegls_errc::duplicate_start_of_frame_marker, [&]() { reader.read_header(); }); + } + + TEST_METHOD(read_header_too_large_near_lossless_in_sos_should_throw) // NOLINT + { + jpeg_test_stream_writer writer; + writer.write_start_of_image(); + writer.write_start_of_frame_segment(512, 512, 8, 3); + writer.write_start_of_scan_segment(0, 1, 128, interleave_mode::none); + + jpeg_stream_reader reader({writer.buffer.data(), writer.buffer.size()}); + reader.read_header(); + + assert_expect_exception(jpegls_errc::invalid_parameter_near_lossless, [&]() { reader.read_start_of_scan(); }); + } + + TEST_METHOD(read_header_too_large_near_lossless_in_sos_should_throw2) // NOLINT + { + const jpegls_pc_parameters preset_coding_parameters{200, 0, 0, 0, 0}; + + jpeg_test_stream_writer writer; + writer.write_start_of_image(); + writer.write_jpegls_preset_parameters_segment(preset_coding_parameters); + writer.write_start_of_frame_segment(512, 512, 8, 3); + + constexpr int bad_near_lossless = (200 / 2) + 1; + writer.write_start_of_scan_segment(0, 1, bad_near_lossless, interleave_mode::none); + + jpeg_stream_reader reader({writer.buffer.data(), writer.buffer.size()}); + reader.read_header(); + + assert_expect_exception(jpegls_errc::invalid_parameter_near_lossless, [&]() { reader.read_start_of_scan(); }); + } + + TEST_METHOD(read_header_with_duplicate_component_id_in_start_of_frame_segment_should_throw) // NOLINT { - JpegTestStreamWriter writer; + jpeg_test_stream_writer writer; writer.componentIdOverride = 7; - writer.WriteStartOfImage(); - writer.WriteStartOfFrameSegment(512, 512, 8, 3); - const ByteStreamInfo byteStream = FromByteArray(writer.data_.data(), writer.data_.size()); + writer.write_start_of_image(); + writer.write_start_of_frame_segment(512, 512, 8, 3); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({writer.buffer.data(), writer.buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -423,7 +458,7 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderWithTooSmallStartOfScanShouldThrow) + TEST_METHOD(read_header_with_too_small_start_of_scan_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -443,12 +478,11 @@ buffer.push_back(0x00); buffer.push_back(0x03); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -459,7 +493,7 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderWithTooSmallStartOfScanComponentCountShouldThrow) + TEST_METHOD(read_header_with_too_small_start_of_scan_component_count_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -480,12 +514,11 @@ buffer.push_back(0x07); buffer.push_back(0x01); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -496,7 +529,7 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderWithDirectlyEndOfImageShouldThrow) + TEST_METHOD(read_header_with_directly_end_of_image_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -504,12 +537,11 @@ buffer.push_back(0xFF); buffer.push_back(0xD9); // EOI. - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); } catch (const system_error& error) { @@ -520,7 +552,7 @@ Assert::Fail(); } - TEST_METHOD(ReadHeaderWithDuplicateStartOfImageShouldThrow) + TEST_METHOD(read_header_with_duplicate_start_of_image_should_throw) // NOLINT { vector buffer; buffer.push_back(0xFF); @@ -528,12 +560,11 @@ buffer.push_back(0xFF); buffer.push_back(0xD8); // SOI. - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); try { - reader.ReadHeader(); + reader.read_header(); Assert::Fail(); } catch (const system_error& error) @@ -542,69 +573,43 @@ } } - static void ReadSpiffHeader(uint8_t low_version) + TEST_METHOD(read_spiff_header) // NOLINT { - vector buffer = create_test_spiff_header(2, low_version); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); - - spiff_header spiff_header{}; - bool spiff_header_found{}; - - reader.ReadHeader(&spiff_header, &spiff_header_found); - - Assert::IsTrue(spiff_header_found); - Assert::AreEqual(static_cast(spiff_profile_id::none), static_cast(spiff_header.profile_id)); - Assert::AreEqual(3, spiff_header.component_count); - Assert::AreEqual(800U, spiff_header.height); - Assert::AreEqual(600U, spiff_header.width); - Assert::AreEqual(static_cast(spiff_color_space::rgb), static_cast(spiff_header.color_space)); - Assert::AreEqual(8, spiff_header.bits_per_sample); - Assert::AreEqual(static_cast(spiff_compression_type::jpeg_ls), static_cast(spiff_header.compression_type)); - Assert::AreEqual(static_cast(spiff_resolution_units::dots_per_inch), static_cast(spiff_header.resolution_units)); - Assert::AreEqual(96U, spiff_header.vertical_resolution); - Assert::AreEqual(1024U, spiff_header.horizontal_resolution); - } - - TEST_METHOD(ReadSpiffHeader) - { - ReadSpiffHeader(0); + read_spiff_header(0); } - TEST_METHOD(read_spiff_header_low_version_newer) + TEST_METHOD(read_spiff_header_low_version_newer) // NOLINT { - ReadSpiffHeader(1); + read_spiff_header(1); } - TEST_METHOD(read_spiff_header_high_version_to_new) + TEST_METHOD(read_spiff_header_high_version_to_new) // NOLINT { vector buffer = create_test_spiff_header(3); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); spiff_header spiff_header{}; bool spiff_header_found{}; - reader.ReadHeader(&spiff_header, &spiff_header_found); + reader.read_header(&spiff_header, &spiff_header_found); Assert::IsFalse(spiff_header_found); } - TEST_METHOD(read_spiff_header_without_end_of_directory) + TEST_METHOD(read_spiff_header_without_end_of_directory) // NOLINT { vector buffer = create_test_spiff_header(2, 0, false); - const ByteStreamInfo byteStream = FromByteArray(buffer.data(), buffer.size()); - JpegStreamReader reader(byteStream); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); spiff_header spiff_header{}; bool spiff_header_found{}; - reader.ReadHeader(&spiff_header, &spiff_header_found); + reader.read_header(&spiff_header, &spiff_header_found); Assert::IsTrue(spiff_header_found); try { - reader.ReadHeader(); + reader.read_header(); Assert::Fail(); } catch (const system_error& error) @@ -612,6 +617,50 @@ Assert::AreEqual(static_cast(jpegls_errc::missing_end_of_spiff_directory), error.code().value()); } } + +private: + static void read_spiff_header(const uint8_t low_version) + { + vector buffer = create_test_spiff_header(2, low_version); + jpeg_stream_reader reader({buffer.data(), buffer.size()}); + + spiff_header spiff_header{}; + bool spiff_header_found{}; + + reader.read_header(&spiff_header, &spiff_header_found); + + Assert::IsTrue(spiff_header_found); + Assert::AreEqual(static_cast(spiff_profile_id::none), static_cast(spiff_header.profile_id)); + Assert::AreEqual(3, spiff_header.component_count); + Assert::AreEqual(800U, spiff_header.height); + Assert::AreEqual(600U, spiff_header.width); + Assert::AreEqual(static_cast(spiff_color_space::rgb), static_cast(spiff_header.color_space)); + Assert::AreEqual(8, spiff_header.bits_per_sample); + Assert::AreEqual(static_cast(spiff_compression_type::jpeg_ls), + static_cast(spiff_header.compression_type)); + Assert::AreEqual(static_cast(spiff_resolution_units::dots_per_inch), + static_cast(spiff_header.resolution_units)); + Assert::AreEqual(96U, spiff_header.vertical_resolution); + Assert::AreEqual(1024U, spiff_header.horizontal_resolution); + } + + static void read_header_with_application_data(const uint8_t data_number) + { + jpeg_test_stream_writer writer; + writer.write_start_of_image(); + + writer.buffer.push_back(0xFF); + writer.buffer.push_back(0xE0 + data_number); + writer.buffer.push_back(0x00); + writer.buffer.push_back(0x02); + + writer.write_start_of_frame_segment(1, 1, 2, 1); + writer.write_start_of_scan_segment(0, 1, 128, interleave_mode::none); + + jpeg_stream_reader reader({writer.buffer.data(), writer.buffer.size()}); + + reader.read_header(); // if it doesn't throw test is passed. + } }; -} // namespace CharLSUnitTest +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/jpeg_stream_writer_test.cpp charls-2.2.0+dfsg/unittest/jpeg_stream_writer_test.cpp --- charls-2.1.0+dfsg/unittest/jpeg_stream_writer_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/jpeg_stream_writer_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -8,78 +8,87 @@ #include +#include "util.h" + using Microsoft::VisualStudio::CppUnitTestFramework::Assert; using std::array; -using namespace charls; - -namespace CharLSUnitTest { -// clang-format off +namespace charls { namespace test { -TEST_CLASS(JpegStreamWriterTest) +TEST_CLASS(jpeg_stream_writer_test) { public: - TEST_METHOD(LengthWillbeZeroAfterCreate) + TEST_METHOD(remaining_destination_will_be_zero_after_create_with_default) // NOLINT { - const JpegStreamWriter writer; - Assert::AreEqual(static_cast(0), writer.GetLength()); + const jpeg_stream_writer writer; + Assert::AreEqual(static_cast(0), writer.remaining_destination().size); + Assert::IsNull(writer.remaining_destination().data); } - TEST_METHOD(WriteStartOfImage) + TEST_METHOD(write_start_of_image) // NOLINT { array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); - - JpegStreamWriter writer(info); + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - writer.WriteStartOfImage(); + writer.write_start_of_image(); - Assert::AreEqual(static_cast(2), writer.GetBytesWritten()); + Assert::AreEqual(static_cast(2), writer.bytes_written()); Assert::AreEqual(static_cast(0xFF), buffer[0]); - Assert::AreEqual(static_cast(JpegMarkerCode::StartOfImage), buffer[1]); + Assert::AreEqual(static_cast(jpeg_marker_code::start_of_image), buffer[1]); } - TEST_METHOD(WriteEndOfImage) + TEST_METHOD(write_start_of_image_in_too_small_buffer) // NOLINT { - array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); + array buffer{}; + jpeg_stream_writer writer({buffer.data(), buffer.size()}); + + assert_expect_exception(jpegls_errc::destination_buffer_too_small, [&] { writer.write_start_of_image(); }); + Assert::AreEqual(static_cast(0), writer.bytes_written()); + } - JpegStreamWriter writer(info); + TEST_METHOD(write_end_of_image) // NOLINT + { + array buffer{}; + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - writer.WriteEndOfImage(); + writer.write_end_of_image(); - Assert::AreEqual(static_cast(2), writer.GetBytesWritten()); + Assert::AreEqual(static_cast(2), writer.bytes_written()); Assert::AreEqual(static_cast(0xFF), buffer[0]); - Assert::AreEqual(static_cast(JpegMarkerCode::EndOfImage), buffer[1]); + Assert::AreEqual(static_cast(jpeg_marker_code::end_of_image), buffer[1]); } - TEST_METHOD(WriteSpiffSegment) + TEST_METHOD(write_end_of_image_in_too_small_buffer) // NOLINT { - array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); + array buffer{}; + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - JpegStreamWriter writer(info); + assert_expect_exception(jpegls_errc::destination_buffer_too_small, [&] { writer.write_end_of_image(); }); + Assert::AreEqual(static_cast(0), writer.bytes_written()); + } - spiff_header header - { - spiff_profile_id::none, - 3, - 800, - 600, - spiff_color_space::rgb, - 8, - spiff_compression_type::jpeg_ls, - spiff_resolution_units::dots_per_inch, - 96, - 1024 - }; + TEST_METHOD(write_spiff_segment) // NOLINT + { + array buffer{}; + jpeg_stream_writer writer({buffer.data(), buffer.size()}); + + spiff_header header{spiff_profile_id::none, + 3, + 800, + 600, + spiff_color_space::rgb, + 8, + spiff_compression_type::jpeg_ls, + spiff_resolution_units::dots_per_inch, + 96, + 1024}; - writer.WriteSpiffHeaderSegment(header); + writer.write_spiff_header_segment(header); - Assert::AreEqual(static_cast(34), writer.GetBytesWritten()); + Assert::AreEqual(static_cast(34), writer.bytes_written()); Assert::AreEqual(static_cast(0xFF), buffer[0]); - Assert::AreEqual(static_cast(JpegMarkerCode::ApplicationData8), buffer[1]); + Assert::AreEqual(static_cast(jpeg_marker_code::application_data8), buffer[1]); Assert::AreEqual(static_cast(0), buffer[2]); Assert::AreEqual(static_cast(32), buffer[3]); @@ -129,20 +138,39 @@ Assert::AreEqual(static_cast(0), buffer[33]); } - TEST_METHOD(WriteSpiffEndOfDirectorySegment) + TEST_METHOD(write_spiff_segment_in_too_small_buffer) // NOLINT { - array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); + array buffer{}; + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - JpegStreamWriter writer(info); + spiff_header header{spiff_profile_id::none, + 3, + 800, + 600, + spiff_color_space::rgb, + 8, + spiff_compression_type::jpeg_ls, + spiff_resolution_units::dots_per_inch, + 96, + 1024}; + + assert_expect_exception(jpegls_errc::destination_buffer_too_small, + [&] { writer.write_spiff_header_segment(header); }); + Assert::AreEqual(static_cast(0), writer.bytes_written()); + } - writer.WriteSpiffEndOfDirectoryEntry(); + TEST_METHOD(write_spiff_end_of_directory_segment) // NOLINT + { + array buffer{}; + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - Assert::AreEqual(static_cast(10), writer.GetBytesWritten()); + writer.write_spiff_end_of_directory_entry(); + + Assert::AreEqual(static_cast(10), writer.bytes_written()); // Verify Entry Magic Number (EMN) Assert::AreEqual(static_cast(0xFF), buffer[0]); - Assert::AreEqual(static_cast(JpegMarkerCode::ApplicationData8), buffer[1]); + Assert::AreEqual(static_cast(jpeg_marker_code::application_data8), buffer[1]); // Verify EOD Entry Length (EOD = End Of Directory) Assert::AreEqual(static_cast(0), buffer[2]); @@ -156,23 +184,21 @@ // Verify embedded SOI tag Assert::AreEqual(static_cast(0xFF), buffer[8]); - Assert::AreEqual(static_cast(JpegMarkerCode::StartOfImage), buffer[9]); + Assert::AreEqual(static_cast(jpeg_marker_code::start_of_image), buffer[9]); } - TEST_METHOD(WriteSpiffDirectoryEntry) + TEST_METHOD(write_spiff_directory_entry) // NOLINT { array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); - - JpegStreamWriter writer{info}; + jpeg_stream_writer writer{{buffer.data(), buffer.size()}}; array data{0x77, 0x66}; - writer.WriteSpiffDirectoryEntry(2, data.data(), data.size()); + writer.write_spiff_directory_entry(2, data.data(), data.size()); // Verify Entry Magic Number (EMN) Assert::AreEqual(static_cast(0xFF), buffer[0]); - Assert::AreEqual(static_cast(JpegMarkerCode::ApplicationData8), buffer[1]); + Assert::AreEqual(static_cast(jpeg_marker_code::application_data8), buffer[1]); // Verify Entry Length Assert::AreEqual(static_cast(0), buffer[2]); @@ -189,29 +215,28 @@ Assert::AreEqual(data[1], buffer[9]); } - TEST_METHOD(WriteStartOfFrameSegment) + TEST_METHOD(write_start_of_frame_segment) // NOLINT { - constexpr int32_t bitsPerSample = 8; - constexpr int32_t componentCount = 3; + constexpr int32_t bits_per_sample{8}; + constexpr int32_t component_count{3}; array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); - JpegStreamWriter writer(info); + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - writer.WriteStartOfFrameSegment(100, UINT16_MAX, bitsPerSample, componentCount); + writer.write_start_of_frame_segment(100, UINT16_MAX, bits_per_sample, component_count); - Assert::AreEqual(static_cast(19), writer.GetBytesWritten()); + Assert::AreEqual(static_cast(19), writer.bytes_written()); Assert::AreEqual(static_cast(0xFF), buffer[0]); Assert::AreEqual(static_cast(0xF7), buffer[1]); // JPEG_SOF_55 Assert::AreEqual(static_cast(0), buffer[2]); // 6 + (3 * 3) + 2 (in big endian) Assert::AreEqual(static_cast(17), buffer[3]); // 6 + (3 * 3) + 2 (in big endian) - Assert::AreEqual(static_cast(bitsPerSample), buffer[4]); - Assert::AreEqual(static_cast(255), buffer[5]); // height (in big endian) - Assert::AreEqual(static_cast(255), buffer[6]); // height (in big endian) - Assert::AreEqual(static_cast(0), buffer[7]); // width (in big endian) - Assert::AreEqual(static_cast(100), buffer[8]); // width (in big endian) - Assert::AreEqual(static_cast(componentCount), buffer[9]); + Assert::AreEqual(static_cast(bits_per_sample), buffer[4]); + Assert::AreEqual(static_cast(255), buffer[5]); // height (in big endian) + Assert::AreEqual(static_cast(255), buffer[6]); // height (in big endian) + Assert::AreEqual(static_cast(0), buffer[7]); // width (in big endian) + Assert::AreEqual(static_cast(100), buffer[8]); // width (in big endian) + Assert::AreEqual(static_cast(component_count), buffer[9]); Assert::AreEqual(static_cast(1), buffer[10]); Assert::AreEqual(static_cast(0x11), buffer[11]); @@ -226,47 +251,44 @@ Assert::AreEqual(static_cast(0), buffer[18]); } - TEST_METHOD(WriteStartOfFrameMarkerSegmentWithLowBoundaryValues) + TEST_METHOD(write_start_of_frame_marker_segment_with_low_boundary_values) // NOLINT { - constexpr int32_t bitsPerSample = 2; - constexpr int32_t componentCount = 1; + constexpr int32_t bits_per_sample{2}; + constexpr int32_t component_count{1}; array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); - JpegStreamWriter writer(info); + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - writer.WriteStartOfFrameSegment(0, 0, bitsPerSample, componentCount); + writer.write_start_of_frame_segment(0, 0, bits_per_sample, component_count); - Assert::AreEqual(buffer.size(), writer.GetBytesWritten()); - Assert::AreEqual(static_cast(bitsPerSample), buffer[4]); - Assert::AreEqual(static_cast(componentCount), buffer[9]); + Assert::AreEqual(buffer.size(), writer.bytes_written()); + Assert::AreEqual(static_cast(bits_per_sample), buffer[4]); + Assert::AreEqual(static_cast(component_count), buffer[9]); } - TEST_METHOD(WriteStartOfFrameMarkerSegmentWithHighBoundaryValuesAndSerialize) + TEST_METHOD(write_start_of_frame_marker_segment_with_high_boundary_values_and_serialize) // NOLINT { array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); - JpegStreamWriter writer(info); + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - writer.WriteStartOfFrameSegment(UINT16_MAX, UINT16_MAX, 16, UINT8_MAX); + writer.write_start_of_frame_segment(UINT16_MAX, UINT16_MAX, 16, UINT8_MAX); - Assert::AreEqual(buffer.size(), writer.GetBytesWritten()); + Assert::AreEqual(buffer.size(), writer.bytes_written()); Assert::AreEqual(static_cast(16), buffer[4]); Assert::AreEqual(static_cast(UINT8_MAX), buffer[9]); Assert::AreEqual(static_cast(UINT8_MAX), buffer[buffer.size() - 3]); // Last component index. } - TEST_METHOD(WriteColorTransformSegment) + TEST_METHOD(write_color_transform_segment) // NOLINT { const color_transformation transformation = color_transformation::hp1; array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); - JpegStreamWriter writer(info); + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - writer.WriteColorTransformSegment(transformation); - Assert::AreEqual(buffer.size(), writer.GetBytesWritten()); + writer.write_color_transform_segment(transformation); + Assert::AreEqual(buffer.size(), writer.bytes_written()); // Verify mrfx identifier string. Assert::AreEqual(static_cast('m'), buffer[4]); @@ -277,16 +299,15 @@ Assert::AreEqual(static_cast(transformation), buffer[8]); } - TEST_METHOD(WriteJpegLSExtendedParametersMarkerAndSerialize) + TEST_METHOD(write_jpegls_extended_parameters_marker_and_serialize) // NOLINT { const jpegls_pc_parameters presets{2, 1, 2, 3, 7}; array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); - JpegStreamWriter writer(info); + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - writer.WriteJpegLSPresetParametersSegment(presets); - Assert::AreEqual(buffer.size(), writer.GetBytesWritten()); + writer.write_jpegls_preset_parameters_segment(presets); + Assert::AreEqual(buffer.size(), writer.bytes_written()); // Parameter ID. Assert::AreEqual(static_cast(0x1), buffer[4]); @@ -312,22 +333,21 @@ Assert::AreEqual(static_cast(7), buffer[14]); } - TEST_METHOD(WriteStartOfScanMarker) + TEST_METHOD(write_start_of_scan_marker) // NOLINT { array buffer{}; - const ByteStreamInfo info = FromByteArray(buffer.data(), buffer.size()); - JpegStreamWriter writer(info); + jpeg_stream_writer writer({buffer.data(), buffer.size()}); - writer.WriteStartOfScanSegment(1, 2, interleave_mode::none); + writer.write_start_of_scan_segment(1, 2, interleave_mode::none); - Assert::AreEqual(buffer.size(), writer.GetBytesWritten()); - Assert::AreEqual(static_cast(1), buffer[4]); // component count. - Assert::AreEqual(static_cast(1), buffer[5]); // component index. - Assert::AreEqual(static_cast(0), buffer[6]); // table ID. - Assert::AreEqual(static_cast(2), buffer[7]); // NEAR parameter. + Assert::AreEqual(buffer.size(), writer.bytes_written()); + Assert::AreEqual(static_cast(1), buffer[4]); // component count. + Assert::AreEqual(static_cast(1), buffer[5]); // component index. + Assert::AreEqual(static_cast(0), buffer[6]); // table ID. + Assert::AreEqual(static_cast(2), buffer[7]); // NEAR parameter. Assert::AreEqual(static_cast(0), buffer[8]); // ILV parameter. Assert::AreEqual(static_cast(0), buffer[9]); // transformation. } }; -} +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/jpeg_test_stream_writer.h charls-2.2.0+dfsg/unittest/jpeg_test_stream_writer.h --- charls-2.1.0+dfsg/unittest/jpeg_test_stream_writer.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/jpeg_test_stream_writer.h 2021-01-10 18:07:25.000000000 +0000 @@ -4,82 +4,121 @@ #pragma once #include "../src/jpeg_marker_code.h" +#include "../src/jpegls_preset_parameters_type.h" #include "../src/util.h" -namespace CharLSUnitTest { +namespace charls { namespace test { -class JpegTestStreamWriter final +class jpeg_test_stream_writer final { public: - void WriteStartOfImage() + void write_start_of_image() { - WriteMarker(charls::JpegMarkerCode::StartOfImage); + write_marker(jpeg_marker_code::start_of_image); } - void WriteStartOfFrameSegment(int width, int height, int bitsPerSample, int componentCount) + void write_start_of_frame_segment(const int width, const int height, const int bits_per_sample, + const int component_count) { // Create a Frame Header as defined in T.87, C.2.2 and T.81, B.2.2 std::vector segment; - segment.push_back(static_cast(bitsPerSample)); // P = Sample precision - charls::push_back(segment, static_cast(height)); // Y = Number of lines - charls::push_back(segment, static_cast(width)); // X = Number of samples per line + segment.push_back(static_cast(bits_per_sample)); // P = Sample precision + push_back(segment, static_cast(height)); // Y = Number of lines + push_back(segment, static_cast(width)); // X = Number of samples per line // Components - segment.push_back(static_cast(componentCount)); // Nf = Number of image components in frame - for (auto componentId = 0; componentId < componentCount; ++componentId) + segment.push_back(static_cast(component_count)); // Nf = Number of image components in frame + for (int component_id{}; component_id < component_count; ++component_id) { // Component Specification parameters if (componentIdOverride == 0) { - segment.push_back(static_cast(componentId)); // Ci = Component identifier + segment.push_back(static_cast(component_id)); // Ci = Component identifier } else { segment.push_back(static_cast(componentIdOverride)); // Ci = Component identifier } segment.push_back(0x11); // Hi + Vi = Horizontal sampling factor + Vertical sampling factor - segment.push_back(0); // Tqi = Quantization table destination selector (reserved for JPEG-LS, should be set to 0) + segment.push_back(0); // Tqi = Quantization table destination selector (reserved for JPEG-LS, should be set to 0) } - WriteSegment(charls::JpegMarkerCode::StartOfFrameJpegLS, segment.data(), segment.size()); + write_segment(jpeg_marker_code::start_of_frame_jpegls, segment.data(), segment.size()); } - void WriteSegment(charls::JpegMarkerCode markerCode, const void* data, size_t dataSize) + void write_jpegls_preset_parameters_segment(const jpegls_pc_parameters& preset_coding_parameters) { - WriteMarker(markerCode); - WriteUInt16(static_cast(dataSize + 2)); - WriteBytes(data, dataSize); + std::vector segment; + + segment.push_back(static_cast(jpegls_preset_parameters_type::preset_coding_parameters)); + + push_back(segment, static_cast(preset_coding_parameters.maximum_sample_value)); + push_back(segment, static_cast(preset_coding_parameters.threshold1)); + push_back(segment, static_cast(preset_coding_parameters.threshold2)); + push_back(segment, static_cast(preset_coding_parameters.threshold3)); + push_back(segment, static_cast(preset_coding_parameters.reset_value)); + + write_segment(jpeg_marker_code::jpegls_preset_parameters, segment.data(), segment.size()); + } + + void write_start_of_scan_segment(int component_id, const int component_count, const int near_lossless, + const interleave_mode interleave_mode) + { + // Create a Scan Header as defined in T.87, C.2.3 and T.81, B.2.3 + std::vector segment; + + segment.push_back(static_cast(component_count)); + for (int i{}; i < component_count; ++i) + { + segment.push_back(static_cast(component_id)); + ++component_id; + segment.push_back(mapping_table_selector); // Mapping table selector (0 = no table) + } + + segment.push_back(static_cast(near_lossless)); // NEAR parameter + segment.push_back(static_cast(interleave_mode)); // ILV parameter + segment.push_back(0); // transformation + + write_segment(jpeg_marker_code::start_of_scan, segment.data(), segment.size()); + } + + void write_segment(const jpeg_marker_code marker_code, const void* data, const size_t data_size) + { + write_marker(marker_code); + write_uint16(static_cast(data_size + 2)); + write_bytes(data, data_size); } - void WriteMarker(charls::JpegMarkerCode markerCode) + void write_marker(jpeg_marker_code marker_code) { - WriteByte(charls::JpegMarkerStartByte); - WriteByte(static_cast(markerCode)); + write_byte(jpeg_marker_start_byte); + write_byte(static_cast(marker_code)); } - void WriteUInt16(uint16_t value) + void write_uint16(const uint16_t value) { - WriteByte(static_cast(value / 0x100)); - WriteByte(static_cast(value % 0x100)); + write_byte(static_cast(value / 0x100)); + write_byte(static_cast(value % 0x100)); } - void WriteByte(uint8_t value) + void write_byte(const uint8_t value) { - data_.push_back(value); + buffer.push_back(value); } - void WriteBytes(const void* data, const size_t dataSize) + void write_bytes(const void* data, const size_t data_size) { - const auto bytes = static_cast(data); + const auto* const bytes{static_cast(data)}; - for (std::size_t i = 0; i < dataSize; ++i) + for (size_t i{}; i < data_size; ++i) { - WriteByte(bytes[i]); + write_byte(bytes[i]); } } int componentIdOverride{}; - std::vector data_; + uint8_t mapping_table_selector{}; + std::vector buffer; }; -} // namespace CharLSUnitTest +}} // namespace charls::test Binary files /tmp/tmptxyqr8/iDZBmIvU8B/charls-2.1.0+dfsg/unittest/land10-10bit-rgb-hp3-invalid.jls and /tmp/tmptxyqr8/i0J_6ju0mM/charls-2.2.0+dfsg/unittest/land10-10bit-rgb-hp3-invalid.jls differ diff -Nru charls-2.1.0+dfsg/unittest/lossless_traits_test.cpp charls-2.2.0+dfsg/unittest/lossless_traits_test.cpp --- charls-2.1.0+dfsg/unittest/lossless_traits_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/lossless_traits_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -8,60 +8,60 @@ using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -namespace CharLSUnitTest { - -// clang-format off +namespace charls { namespace test { TEST_CLASS(lossless_traits_test) { public: - TEST_METHOD(TestTraits16bit) + TEST_METHOD(test_traits_16_bit) // NOLINT { - const auto traits1 = charls::DefaultTraits(4095,0); - const auto traits2 = charls::LosslessTraits(); - - Assert::IsTrue(traits1.LIMIT == traits2.LIMIT); - Assert::IsTrue(traits1.MAXVAL == traits2.MAXVAL); - Assert::IsTrue(traits1.RESET == traits2.RESET); - Assert::IsTrue(traits1.bpp == traits2.bpp); - Assert::IsTrue(traits1.qbpp == traits2.qbpp); + using lossless_traits = lossless_traits; + const auto traits1{default_traits(4095, 0)}; + const lossless_traits traits2{}; + + Assert::IsTrue(traits1.limit == traits2.limit); + Assert::IsTrue(traits1.maximum_sample_value == traits2.maximum_sample_value); + Assert::IsTrue(traits1.reset_threshold == traits2.reset_threshold); + Assert::IsTrue(traits1.bits_per_pixel == traits2.bits_per_pixel); + Assert::IsTrue(traits1.quantized_bits_per_pixel == traits2.quantized_bits_per_pixel); - for (int i = -4096; i <= 4096; ++i) + for (int i{-4096}; i <= 4096; ++i) { - Assert::IsTrue(traits1.ModuloRange(i) == traits2.ModuloRange(i)); - Assert::IsTrue(traits1.ComputeErrVal(i) == traits2.ComputeErrVal(i)); + Assert::IsTrue(traits1.modulo_range(i) == lossless_traits::modulo_range(i)); + Assert::IsTrue(traits1.compute_error_value(i) == lossless_traits::compute_error_value(i)); } - for (int i = -8095; i <= 8095; ++i) + for (int i{-8095}; i <= 8095; ++i) { - Assert::IsTrue(traits1.CorrectPrediction(i) == traits2.CorrectPrediction(i)); - Assert::IsTrue(traits1.IsNear(i,2) == traits2.IsNear(i,2)); + Assert::IsTrue(traits1.correct_prediction(i) == lossless_traits::correct_prediction(i)); + Assert::IsTrue(traits1.is_near(i, 2) == lossless_traits::is_near(i, 2)); } } - TEST_METHOD(TestTraits8bit) + TEST_METHOD(test_traits_8_bit) // NOLINT { - const auto traits1 = charls::DefaultTraits(255,0); - const auto traits2 = charls::LosslessTraits(); - - Assert::IsTrue(traits1.LIMIT == traits2.LIMIT); - Assert::IsTrue(traits1.MAXVAL == traits2.MAXVAL); - Assert::IsTrue(traits1.RESET == traits2.RESET); - Assert::IsTrue(traits1.bpp == traits2.bpp); - Assert::IsTrue(traits1.qbpp == traits2.qbpp); + using lossless_traits = lossless_traits; + const auto traits1{default_traits(255, 0)}; + const lossless_traits traits2{}; + + Assert::IsTrue(traits1.limit == traits2.limit); + Assert::IsTrue(traits1.maximum_sample_value == traits2.maximum_sample_value); + Assert::IsTrue(traits1.reset_threshold == traits2.reset_threshold); + Assert::IsTrue(traits1.bits_per_pixel == traits2.bits_per_pixel); + Assert::IsTrue(traits1.quantized_bits_per_pixel == traits2.quantized_bits_per_pixel); - for (int i = -255; i <= 255; ++i) + for (int i{-255}; i <= 255; ++i) { - Assert::IsTrue(traits1.ModuloRange(i) == traits2.ModuloRange(i)); - Assert::IsTrue(traits1.ComputeErrVal(i) == traits2.ComputeErrVal(i)); + Assert::IsTrue(traits1.modulo_range(i) == lossless_traits::modulo_range(i)); + Assert::IsTrue(traits1.compute_error_value(i) == lossless_traits::compute_error_value(i)); } - for (int i = -255; i <= 512; ++i) + for (int i{-255}; i <= 512; ++i) { - Assert::IsTrue(traits1.CorrectPrediction(i) == traits2.CorrectPrediction(i)); - Assert::IsTrue(traits1.IsNear(i,2) == traits2.IsNear(i,2)); + Assert::IsTrue(traits1.correct_prediction(i) == lossless_traits::correct_prediction(i)); + Assert::IsTrue(traits1.is_near(i, 2) == lossless_traits::is_near(i, 2)); } } }; -} +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/pch.h charls-2.2.0+dfsg/unittest/pch.h --- charls-2.1.0+dfsg/unittest/pch.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/pch.h 2021-01-10 18:07:25.000000000 +0000 @@ -4,13 +4,16 @@ #pragma once #pragma warning(push) -#pragma warning(disable : 5039) // '_set_invalid_parameter_handler' : pointer or reference to potentially throwing function passed to extern C function under - EHc.Undefined behavior may occur if this function throws an exception. +#pragma warning( \ + disable : 5039) // '_set_invalid_parameter_handler' : pointer or reference to potentially throwing function passed to + // extern C function under - EHc.Undefined behavior may occur if this function throws an exception. #pragma warning(disable : 26432) #pragma warning(disable : 26433) #pragma warning(disable : 26439) #pragma warning(disable : 26440) #pragma warning(disable : 26443) -#pragma warning(disable : 26455) // Default constructor may not throw. Declare it 'noexcept' (f.6). [Problem in VS 2017 15.8.0] +#pragma warning( \ + disable : 26455) // Default constructor may not throw. Declare it 'noexcept' (f.6). [Problem in VS 2017 15.8.0] #pragma warning(disable : 26461) #pragma warning(disable : 26466) #pragma warning(disable : 26477) // Use 'nullptr' rather than 0 or NULL (es.47) [Problem in VS 2017 15.8.0] @@ -19,14 +22,21 @@ #include #pragma warning(pop) -#include #include +#include #include #ifdef _MSC_VER -#define MSVC_WARNING_SUPPRESS(x) __pragma(warning(push)) __pragma(warning(disable : x)) // NOLINT(misc-macro-parentheses) +#define MSVC_WARNING_SUPPRESS(x) \ + __pragma(warning(push)) __pragma(warning(disable : x)) // NOLINT(misc-macro-parentheses, bugprone-macro-parentheses) #define MSVC_WARNING_UNSUPPRESS() __pragma(warning(pop)) +#define MSVC_WARNING_SUPPRESS_NEXT_LINE(x) \ + __pragma(warning(suppress \ + : x)) // NOLINT(misc-macro-parentheses, bugprone-macro-parentheses, cppcoreguidelines-macro-usage) +#define MSVC_CONST const #else #define MSVC_WARNING_SUPPRESS(x) #define MSVC_WARNING_UNSUPPRESS() +#define MSVC_WARNING_SUPPRESS_NEXT_LINE(x) +#define MSVC_CONST #endif diff -Nru charls-2.1.0+dfsg/unittest/scan_test.cpp charls-2.2.0+dfsg/unittest/scan_test.cpp --- charls-2.1.0+dfsg/unittest/scan_test.cpp 1970-01-01 00:00:00.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/scan_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -0,0 +1,140 @@ +// Copyright (c) Team CharLS. +// SPDX-License-Identifier: BSD-3-Clause + +#include "pch.h" + +#include "util.h" + +#include "../src/scan.h" + + +using Microsoft::VisualStudio::CppUnitTestFramework::Assert; +using std::vector; + +namespace { + +/// +/// This is the original algorithm of ISO/IEC 14495-1, A.5.2, Code Segment A.11 (second else branch) +/// It will map signed values to unsiged values. +/// +int32_t map_error_value_original(const int32_t error_value) noexcept +{ + if (error_value >= 0) + return 2 * error_value; + + return -2 * error_value - 1; +} + +/// +/// This version will be auto optimized by GCC(trunk, not 10.2) and clang(11.0). MSVC will create branches. +/// +int32_t map_error_value_alternative1(const int32_t error_value) noexcept +{ + const int32_t mapped_value = error_value * 2; + + if (error_value >= 0) + return mapped_value; + + return (-1 * mapped_value) - 1; +} + + +/// +/// This is the original inverse algorithm of ISO/IEC 14495-1, A.5.2, Code Segment A.11 (second else branch) +/// It will map unsigned values back to unsigned values. +/// +int32_t unmap_error_value_original(const int32_t mapped_error_value) noexcept +{ + if (mapped_error_value % 2 == 0) + return mapped_error_value / 2; + + return (mapped_error_value / -2) - 1; +} + +/// +/// This version will be auto optimized by GCC(trunk, not 10.2) using a cmove, +/// and clang(11.0) 8 instructions,. MSVC will create branches. +/// Optimized version uses 6 to 5 instructions. +/// +int32_t unmap_error_value_alternative1(const int32_t mapped_error_value) noexcept +{ + const int32_t error_value = mapped_error_value / 2; + + if (mapped_error_value % 2 == 0) + return error_value; + + return (-1 * error_value) - 1; +} + +} + +namespace charls { namespace test { + +TEST_CLASS(scan_test) +{ +public: + TEST_METHOD(map_error_value_algorithm) // NOLINT + { + map_error_value_algorithm(0); + map_error_value_algorithm(1); + map_error_value_algorithm(-1); + map_error_value_algorithm(INT16_MAX); + map_error_value_algorithm(INT16_MIN); + map_error_value_algorithm(INT32_MAX / 2); + map_error_value_algorithm(INT32_MIN / 2); + } + + TEST_METHOD(unmap_error_value_algorithm) // NOLINT + { + unmap_error_value_algorithm(0); + unmap_error_value_algorithm(1); + unmap_error_value_algorithm(2); + unmap_error_value_algorithm(INT16_MAX); + unmap_error_value_algorithm(INT32_MAX - 2); + unmap_error_value_algorithm(INT32_MAX - 1); + unmap_error_value_algorithm(INT32_MAX); + } + + TEST_METHOD(map_unmap_error_value_algorithm) // NOLINT + { + map_unmap_error_value_algorithm(0); + map_unmap_error_value_algorithm(1); + map_unmap_error_value_algorithm(-1); + map_unmap_error_value_algorithm(INT16_MAX); + map_unmap_error_value_algorithm(INT16_MIN); + map_unmap_error_value_algorithm(INT32_MAX / 2); + map_unmap_error_value_algorithm(INT32_MIN / 2); + } + +private: + static void map_error_value_algorithm(const int32_t error_value) + { + const int32_t actual = map_error_value(error_value); + const int32_t expected1 = map_error_value_original(error_value); + const int32_t expected2 = map_error_value_alternative1(error_value); + + Assert::IsTrue(actual >= 0); + Assert::AreEqual(expected1, actual); + Assert::AreEqual(expected2, actual); + } + + static void unmap_error_value_algorithm(const int32_t mapped_error_value) + { + const int32_t actual = unmap_error_value(mapped_error_value); + const int32_t expected1 = unmap_error_value_original(mapped_error_value); + const int32_t expected2 = unmap_error_value_alternative1(mapped_error_value); + + Assert::AreEqual(expected1, actual); + Assert::AreEqual(expected2, actual); + } + + static void map_unmap_error_value_algorithm(const int32_t error_value) + { + const int32_t mapped_error_value = map_error_value(error_value); + const int32_t actual = unmap_error_value(mapped_error_value); + + Assert::AreEqual(error_value, actual); + } +}; + +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/util.cpp charls-2.2.0+dfsg/unittest/util.cpp --- charls-2.1.0+dfsg/unittest/util.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/util.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -11,49 +11,56 @@ #include "../test/portable_anymap_file.h" +#include + using Microsoft::VisualStudio::CppUnitTestFramework::Assert; using std::ifstream; +using std::ios; +using std::mt19937; +using std::uniform_int_distribution; using std::vector; -using namespace charls; using namespace charls_test; namespace { -void triplet_to_planar(vector& buffer, uint32_t width, uint32_t height) +void triplet_to_planar(vector& buffer, const uint32_t width, const uint32_t height) { - vector workBuffer(buffer.size()); + vector work_buffer(buffer.size()); - const size_t byteCount = static_cast(width) * height; - for (size_t index = 0; index < byteCount; index++) + const size_t byte_count{static_cast(width) * height}; + for (size_t index{}; index < byte_count; index++) { - workBuffer[index] = buffer[index * 3 + 0]; - workBuffer[index + 1 * byteCount] = buffer[index * 3 + 1]; - workBuffer[index + 2 * byteCount] = buffer[index * 3 + 2]; + work_buffer[index] = buffer[index * 3 + 0]; + work_buffer[index + 1 * byte_count] = buffer[index * 3 + 1]; + work_buffer[index + 2 * byte_count] = buffer[index * 3 + 2]; } - swap(buffer, workBuffer); + swap(buffer, work_buffer); } } // namespace +namespace charls { namespace test { + vector read_file(const char* filename) { ifstream input; - input.exceptions(input.eofbit | input.failbit | input.badbit); - input.open(filename, input.in | input.binary); + input.exceptions(ios::eofbit | ios::failbit | ios::badbit); + input.open(filename, ios::in | ios::binary); - input.seekg(0, input.end); - const auto byteCountFile = static_cast(input.tellg()); - input.seekg(0, input.beg); + input.seekg(0, ios::end); + const auto byte_count_file = static_cast(input.tellg()); + input.seekg(0, ios::beg); - vector buffer(byteCountFile); + vector buffer(byte_count_file); input.read(reinterpret_cast(buffer.data()), buffer.size()); return buffer; } -portable_anymap_file read_anymap_reference_file(const char* filename, const interleave_mode interleave_mode, const frame_info& frame_info) +portable_anymap_file read_anymap_reference_file(const char* filename, const interleave_mode interleave_mode, + const frame_info& frame_info) { - portable_anymap_file reference_file(filename); + portable_anymap_file reference_file{filename}; if (interleave_mode == interleave_mode::none && frame_info.component_count == 3) { @@ -65,7 +72,7 @@ portable_anymap_file read_anymap_reference_file(const char* filename, const interleave_mode interleave_mode) { - portable_anymap_file reference_file(filename); + portable_anymap_file reference_file{filename}; if (interleave_mode == interleave_mode::none && reference_file.component_count() == 3) { @@ -129,31 +136,33 @@ buffer.push_back(4); buffer.push_back(0); - const size_t spiff_header_size = buffer.size(); + const size_t spiff_header_size{buffer.size()}; buffer.resize(buffer.size() + 100); - const ByteStreamInfo info = FromByteArray(buffer.data() + spiff_header_size, buffer.size() - spiff_header_size); - JpegStreamWriter writer(info); + jpeg_stream_writer writer({buffer.data() + spiff_header_size, buffer.size() - spiff_header_size}); if (end_of_directory) { - writer.WriteSpiffEndOfDirectoryEntry(); + writer.write_spiff_end_of_directory_entry(); } - writer.WriteStartOfFrameSegment(100, 100, 8, 1); - writer.WriteStartOfScanSegment(1, 0, interleave_mode::none); + writer.write_start_of_frame_segment(100, 100, 8, 1); + writer.write_start_of_scan_segment(1, 0, interleave_mode::none); return buffer; } -vector create_noise_image_16bit(const size_t pixel_count, const int bit_count, const uint32_t seed) +vector create_noise_image_16_bit(const size_t pixel_count, const int bit_count, const uint32_t seed) { - srand(seed); + const auto max_value = static_cast((1U << bit_count) - 1U); + mt19937 generator(seed); + + MSVC_CONST uniform_int_distribution distribution(0, max_value); + vector buffer(pixel_count * 2); - const auto mask = static_cast((1 << bit_count) - 1); - for (size_t i = 0; i < pixel_count; i = i + 2) + for (size_t i{}; i < pixel_count; i = i + 2) { - const uint16_t value = static_cast(rand()) & mask; + const uint16_t value{distribution(generator)}; buffer[i] = static_cast(value); buffer[i] = static_cast(value >> 8); @@ -163,24 +172,34 @@ void test_round_trip_legacy(const vector& source, const JlsParameters& params) { - vector encodedBuffer(params.height * params.width * params.components * params.bitsPerSample / 4); - vector decodedBuffer(static_cast(params.height) * params.width * ((params.bitsPerSample + 7) / 8) * params.components); + // ReSharper disable CppDeprecatedEntity + DISABLE_DEPRECATED_WARNING - size_t compressedLength = 0; - auto error = JpegLsEncode(encodedBuffer.data(), encodedBuffer.size(), &compressedLength, - source.data(), source.size(), ¶ms, nullptr); + vector encoded_buffer(params.height * params.width * params.components * params.bitsPerSample / 4); + vector decoded_buffer(static_cast(params.height) * params.width * + bit_to_byte_count(params.bitsPerSample) * params.components); + + size_t compressed_length{}; + auto error{JpegLsEncode(encoded_buffer.data(), encoded_buffer.size(), &compressed_length, source.data(), source.size(), + ¶ms, nullptr)}; Assert::AreEqual(jpegls_errc::success, error); - error = JpegLsDecode(decodedBuffer.data(), decodedBuffer.size(), encodedBuffer.data(), compressedLength, nullptr, nullptr); + error = JpegLsDecode(decoded_buffer.data(), decoded_buffer.size(), encoded_buffer.data(), compressed_length, nullptr, + nullptr); Assert::AreEqual(jpegls_errc::success, error); - const uint8_t* byteOut = decodedBuffer.data(); - for (size_t i = 0; i < decodedBuffer.size(); ++i) + const uint8_t* byte_out{decoded_buffer.data()}; + for (size_t i{}; i < decoded_buffer.size(); ++i) { - if (source[i] != byteOut[i]) + if (source[i] != byte_out[i]) { Assert::IsTrue(false); break; } } + + // ReSharper restore CppDeprecatedEntity + RESTORE_DEPRECATED_WARNING } + +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/util.h charls-2.2.0+dfsg/unittest/util.h --- charls-2.1.0+dfsg/unittest/util.h 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/util.h 2021-01-10 18:07:25.000000000 +0000 @@ -11,17 +11,61 @@ #include + +// clang-format off +#ifdef __clang__ +#define DISABLE_DEPRECATED_WARNING \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif defined(__GNUC__) || defined(__GNUG__) +#define DISABLE_DEPRECATED_WARNING \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif defined(_MSC_VER) +#define DISABLE_DEPRECATED_WARNING \ + __pragma(warning(push)) \ + __pragma(warning(disable : 4996)) // was declared deprecated +#else +#define DISABLE_DEPRECATED_WARNING +#endif +// clang-format on + +#ifdef __clang__ +#define RESTORE_DEPRECATED_WARNING _Pragma("clang diagnostic pop") +#elif defined(__GNUC__) || defined(__GNUG__) +#define RESTORE_DEPRECATED_WARNING _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) +#define RESTORE_DEPRECATED_WARNING __pragma(warning(pop)) +#else +#define RESTORE_DEPRECATED_WARNING +#endif + + +namespace charls { namespace test { + std::vector read_file(const char* filename); -charls_test::portable_anymap_file read_anymap_reference_file(const char* filename, charls::interleave_mode interleave_mode, const charls::frame_info& frame_info); -charls_test::portable_anymap_file read_anymap_reference_file(const char* filename, charls::interleave_mode interleave_mode); -std::vector create_test_spiff_header(uint8_t high_version = 2, uint8_t low_version = 0, bool end_of_directory = true); -std::vector create_noise_image_16bit(size_t pixel_count, int bit_count, uint32_t seed); +charls_test::portable_anymap_file read_anymap_reference_file(const char* filename, interleave_mode interleave_mode, + const frame_info& frame_info); +charls_test::portable_anymap_file read_anymap_reference_file(const char* filename, interleave_mode interleave_mode); +std::vector create_test_spiff_header(uint8_t high_version = 2, uint8_t low_version = 0, + bool end_of_directory = true); +std::vector create_noise_image_16_bit(size_t pixel_count, int bit_count, uint32_t seed); void test_round_trip_legacy(const std::vector& source, const JlsParameters& params); -namespace Microsoft { -namespace VisualStudio { -namespace CppUnitTestFramework { +/// +/// Computes how many bytes are needed to hold the number of bits. +/// +constexpr uint32_t bit_to_byte_count(const int32_t bit_count) noexcept +{ + return static_cast((bit_count + 7) / 8); +} + +}} // namespace charls::test + +// ReSharper disable CppInconsistentNaming +namespace Microsoft { namespace VisualStudio { namespace CppUnitTestFramework { +// ReSharper restore CppInconsistentNaming template<> inline std::wstring ToString(const charls::jpegls_errc& q) @@ -35,23 +79,23 @@ RETURN_WIDE_STRING(static_cast(q)); } -} -} -} // namespace Microsoft::VisualStudio::CppUnitTestFramework +}}} // namespace Microsoft::VisualStudio::CppUnitTestFramework -namespace CharLSUnitTest { +namespace charls { namespace test { template -void assert_expect_exception(charls::jpegls_errc error_value, Functor functor) +void assert_expect_exception(const jpegls_errc error_value, Functor functor) { try { functor(); } - catch (const charls::jpegls_error& error) + catch (const jpegls_error& error) { Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(error_value == error.code()); + Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsNotNull(error.what()); + Microsoft::VisualStudio::CppUnitTestFramework::Assert::IsTrue(strlen(error.what()) > 0); return; } catch (...) @@ -62,4 +106,4 @@ Microsoft::VisualStudio::CppUnitTestFramework::Assert::Fail(); } -} // namespace CharLSUnitTest +}} // namespace charls::test diff -Nru charls-2.1.0+dfsg/unittest/version_test.cpp charls-2.2.0+dfsg/unittest/version_test.cpp --- charls-2.1.0+dfsg/unittest/version_test.cpp 2019-12-29 19:10:31.000000000 +0000 +++ charls-2.2.0+dfsg/unittest/version_test.cpp 2021-01-10 18:07:25.000000000 +0000 @@ -10,23 +10,20 @@ #include using Microsoft::VisualStudio::CppUnitTestFramework::Assert; -using namespace charls; using std::ostringstream; -// clang-format off - -namespace CharLSUnitTest { +namespace charls { namespace test { TEST_CLASS(version_test) { public: - TEST_METHOD(charls_get_version_string_test) + TEST_METHOD(charls_get_version_string_test) // NOLINT { - const char* version = charls_get_version_string(); + const char* version{charls_get_version_string()}; ostringstream expected_stream; expected_stream << CHARLS_VERSION_MAJOR << "." << CHARLS_VERSION_MINOR << "." << CHARLS_VERSION_PATCH; - const auto expected = expected_stream.str(); + const auto expected{expected_stream.str()}; Assert::IsTrue(strncmp(expected.c_str(), version, expected.length()) == 0); if (expected.length() != strlen(version)) @@ -35,7 +32,7 @@ } } - TEST_METHOD(charls_get_version_number_all) + TEST_METHOD(charls_get_version_number_all) // NOLINT { int32_t major; int32_t minor; @@ -48,7 +45,7 @@ Assert::AreEqual(CHARLS_VERSION_PATCH, patch); } - TEST_METHOD(charls_get_version_number_none) + TEST_METHOD(charls_get_version_number_none) // NOLINT { charls_get_version_number(nullptr, nullptr, nullptr); @@ -57,4 +54,4 @@ } }; -} // namespace CharLSUnitTest +}} // namespace charls::test