diff --git a/include/DICOMRead.h b/include/DICOMRead.h
index c13fda0f70e..3d6c8cfaac1 100644
--- a/include/DICOMRead.h
+++ b/include/DICOMRead.h
@@ -27,6 +27,9 @@
#include "dicom_objects.h"
#include "condition.h"
+#include "nii_dicom.h"
+#include "nii_dicom_batch.h"
+
#define NUMBEROFTAGS 24
#define SHORTSIZE 16
#ifndef INTSIZE
@@ -59,6 +62,7 @@ typedef unsigned short int BOOL;
char *SDCMStatusFile = 0;
char *SDCMListFile = 0;
int UseDICOMRead2 = 1; // use new dicom reader by default
+int UseDICOMRead3 = 0;
/* These variables allow the user to change the first tag checked to
get the slice thickness. This is needed with siemens mag res
angiogram (MRAs) */
@@ -69,6 +73,7 @@ int AutoSliceResElTag = 0; // automatically determine which tag to use based on
extern char *SDCMStatusFile;
extern char *SDCMListFile;
extern int UseDICOMRead2;
+extern int UseDICOMRead3;
extern long SliceResElTag1;
extern long SliceResElTag2;
extern int AutoSliceResElTag;
@@ -286,6 +291,7 @@ int CompareDCMFileInfo(const void *a, const void *b);
int DCMCountFrames(DICOMInfo **dcmfi_list, int nlist);
int DCMSliceDir(DICOMInfo **dcmfi_list, int nlist);
MRI *DICOMRead2(const char *dcmfile, int LoadVolume);
+MRIFSSTRUCT *DICOMRead3(const char *dcmfile, int LoadVolume);
DCM_ELEMENT *GetElementFromFile(const char *dicomfile, long grpid, long elid);
int AllocElementData(DCM_ELEMENT *e);
diff --git a/mri_convert/CMakeLists.txt b/mri_convert/CMakeLists.txt
index 9844755fca3..e9dbb73d1d3 100644
--- a/mri_convert/CMakeLists.txt
+++ b/mri_convert/CMakeLists.txt
@@ -3,7 +3,7 @@ project(mri_convert)
# ATH: temporarily turning off this warning
add_compile_options(-Wno-self-assign)
-include_directories(${FS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/packages/dicom)
+include_directories(${FS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/packages/dicom ${CMAKE_SOURCE_DIR}/packages/dcm2niix)
add_executable(mri_convert mri_convert.cpp)
add_help(mri_convert mri_convert.help.xml)
diff --git a/mri_convert/mri_convert.cpp b/mri_convert/mri_convert.cpp
index e320b122735..924896216b8 100644
--- a/mri_convert/mri_convert.cpp
+++ b/mri_convert/mri_convert.cpp
@@ -1141,6 +1141,10 @@ int main(int argc, char *argv[])
forced_in_type = string_to_type(in_type_string);
force_in_type_flag = TRUE;
}
+ else if (strcmp(argv[i], "-dicomread3") == 0)
+ {
+ UseDICOMRead3 = 1;
+ }
else if(strcmp(argv[i], "-dicomread2") == 0)
{
UseDICOMRead2 = 1;
diff --git a/mri_otl/CMakeLists.txt b/mri_otl/CMakeLists.txt
index f9ddbdef4fe..49490ba389b 100644
--- a/mri_otl/CMakeLists.txt
+++ b/mri_otl/CMakeLists.txt
@@ -1,6 +1,6 @@
project(mri_otl)
-include_directories(${FS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/packages/dicom)
+include_directories(${FS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/packages/dicom ${CMAKE_SOURCE_DIR}/packages/dcm2niix)
add_executable(list_otl_labels list_otl_labels.cpp)
target_link_libraries(list_otl_labels utils)
diff --git a/mri_parse_sdcmdir/CMakeLists.txt b/mri_parse_sdcmdir/CMakeLists.txt
index 89b659a093a..6e985dc21bb 100644
--- a/mri_parse_sdcmdir/CMakeLists.txt
+++ b/mri_parse_sdcmdir/CMakeLists.txt
@@ -1,6 +1,6 @@
project(mri_parse_sdcmdir)
-include_directories(${FS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/packages/dicom)
+include_directories(${FS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/packages/dicom ${CMAKE_SOURCE_DIR}/packages/dcm2niix)
add_executable(mri_parse_sdcmdir mri_parse_sdcmdir.cpp)
target_link_libraries(mri_parse_sdcmdir utils)
diff --git a/mri_probedicom/CMakeLists.txt b/mri_probedicom/CMakeLists.txt
index 6994bc91f78..8896927a420 100644
--- a/mri_probedicom/CMakeLists.txt
+++ b/mri_probedicom/CMakeLists.txt
@@ -6,6 +6,7 @@ if(OPENGL_FOUND)
${FS_INCLUDE_DIRS}
${CMAKE_SOURCE_DIR}/packages/dicom
${CMAKE_SOURCE_DIR}/packages/glut
+ ${CMAKE_SOURCE_DIR}/packages/dcm2niix
${X11_INCLUDE_DIR}
)
diff --git a/mri_watershed/CMakeLists.txt b/mri_watershed/CMakeLists.txt
index 445e923ab78..6e9001a19d2 100644
--- a/mri_watershed/CMakeLists.txt
+++ b/mri_watershed/CMakeLists.txt
@@ -1,6 +1,6 @@
project(mri_watershed)
-include_directories(${FS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/packages/dicom)
+include_directories(${FS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR}/packages/dicom ${CMAKE_SOURCE_DIR}/packages/dcm2niix)
add_executable(mri_watershed mri_watershed.cpp)
add_help(mri_watershed mri_watershed.help.xml)
diff --git a/packages/CMakeLists.txt b/packages/CMakeLists.txt
index 8fd9e75cf05..0b912f0bbf6 100644
--- a/packages/CMakeLists.txt
+++ b/packages/CMakeLists.txt
@@ -13,6 +13,7 @@ add_subdirectories(
xml2
minc
dicom
+ dcm2niix
cephes
netcdf
tetgen
diff --git a/packages/dcm2niix/CMakeLists.txt b/packages/dcm2niix/CMakeLists.txt
new file mode 100644
index 00000000000..df054a10fd1
--- /dev/null
+++ b/packages/dcm2niix/CMakeLists.txt
@@ -0,0 +1,233 @@
+cmake_minimum_required(VERSION 2.8.11)
+
+project(dcm2niix)
+
+# Option Choose whether to use static runtime
+include(ucm.cmake)
+option(USE_STATIC_RUNTIME "Use static runtime" ON)
+if(USE_STATIC_RUNTIME)
+ ucm_set_runtime(STATIC)
+else()
+ ucm_set_runtime(DYNAMIC)
+endif()
+
+# Basic CMake build settings
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "Release" CACHE STRING
+ "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel")
+endif()
+
+if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
+ # using Clang
+ add_definitions(-fno-caret-diagnostics)
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-dead_strip")
+elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU")
+ # using GCC
+ if(NOT (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 7.1.0))
+ add_definitions(-Wno-format-overflow) # available since GCC 7.1.0
+ endif()
+ if(NOT (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4.5.0))
+ add_definitions(-Wno-unused-result) # available since GCC 4.5.0
+ endif()
+ if(NOT (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 4.8.0))
+ add_definitions(-fno-diagnostics-show-caret) # available since GCC 4.8.0
+ endif()
+elseif(MSVC)
+ # using Visual Studio C++
+ add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4018") # '<': signed/unsigned mismatch
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4068") # unknown pragma
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4101") # unreferenced local variable
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244") # 'initializing': conversion from 'double' to 'int', possible loss of data
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267") # 'initializing': conversion from 'size_t' to 'int', possible loss of data
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4305") # 'argument': truncation from 'double' to 'float'
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4308") # negative integral constant converted to unsigned type
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4334") # '<<': result of 32-bit shift implicitly converted to 64 bits
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4800") # 'uint32_t' : forcing value to bool 'true' or 'false'
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819") # The file contains a character that cannot be represented in the current code page
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4996") # 'access': The POSIX name for this item is deprecated
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8388608") # set "Stack Reserve Size" to 8MB (default value is 1MB)
+endif()
+
+# Compiler dependent flags
+include (CheckCXXCompilerFlag)
+if(UNIX)
+ check_cxx_compiler_flag(-march=armv8-a+crc ARM_CRC)
+ if(ARM_CRC)
+ # wrong answer for Apple Silicon: check_cxx_compiler_flag(-msse2 HAS_SSE2)
+ else()
+ check_cxx_compiler_flag(-msse2 HAS_SSE2)
+ if(HAS_SSE2)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2 -mfpmath=sse")
+ endif()
+ endif()
+endif()
+
+set(PROGRAMS dcm2niix)
+
+set(DCM2NIIX_SRCS
+ main_console.cpp
+ nii_dicom.cpp
+ jpg_0XC3.cpp
+ ujpeg.cpp
+ nifti1_io_core.cpp
+ nii_foreign.cpp
+ nii_ortho.cpp
+ nii_dicom_batch.cpp)
+
+option(USE_JPEGLS "Build with JPEG-LS support using CharLS" OFF)
+if(USE_JPEGLS)
+ add_definitions(-DmyEnableJPEGLS)
+ if(MSVC)
+ add_definitions(-DCHARLS_STATIC)
+ endif()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
+
+ set(CHARLS_SRCS
+ charls/jpegls.cpp
+ charls/jpegmarkersegment.cpp
+ charls/interface.cpp
+ charls/jpegstreamwriter.cpp
+ charls/jpegstreamreader.cpp)
+ add_executable(dcm2niix ${DCM2NIIX_SRCS} ${CHARLS_SRCS})
+else()
+ add_executable(dcm2niix ${DCM2NIIX_SRCS})
+endif()
+
+set(ZLIB_IMPLEMENTATION "Miniz" CACHE STRING "Choose zlib implementation.")
+set_property(CACHE ZLIB_IMPLEMENTATION PROPERTY STRINGS "Miniz;System;Custom")
+if(NOT ${ZLIB_IMPLEMENTATION} STREQUAL "Miniz")
+ if(NOT ${ZLIB_IMPLEMENTATION} STREQUAL "System")
+ set(ZLIB_ROOT ${ZLIB_ROOT} CACHE PATH "Specify custom zlib root directory.")
+ if(NOT ZLIB_ROOT)
+ message(FATAL_ERROR "ZLIB_ROOT needs to be set to locate custom zlib!")
+ endif()
+ endif()
+ find_package(ZLIB REQUIRED)
+ add_definitions(-DmyDisableMiniZ)
+ target_include_directories(dcm2niix PRIVATE ${ZLIB_INCLUDE_DIRS})
+ target_link_libraries(dcm2niix ${ZLIB_LIBRARIES})
+endif()
+
+option(USE_TURBOJPEG "Use TurboJPEG to decode classic JPEG" OFF)
+if(USE_TURBOJPEG)
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(TURBOJPEG REQUIRED libturbojpeg)
+ add_definitions(-DmyTurboJPEG)
+ target_include_directories(dcm2niix PRIVATE ${TURBOJPEG_INCLUDEDIR})
+ target_link_libraries(dcm2niix ${TURBOJPEG_LIBRARIES})
+endif()
+
+option(USE_JASPER "Build with JPEG2000 support using Jasper" OFF)
+if(USE_JASPER)
+ find_package(Jasper REQUIRED)
+ add_definitions(-DmyEnableJasper)
+ target_include_directories(dcm2niix PRIVATE ${JASPER_INCLUDE_DIR})
+ target_link_libraries(dcm2niix ${JASPER_LIBRARIES})
+endif()
+
+option(USE_OPENJPEG "Build with JPEG2000 support using OpenJPEG" OFF)
+if(USE_OPENJPEG)
+ set(OpenJPEG_DIR "${OpenJPEG_DIR}" CACHE PATH "Path to OpenJPEG configuration file" FORCE)
+
+ find_package(OpenJPEG REQUIRED)
+
+ if(WIN32)
+ if(BUILD_SHARED_LIBS)
+ add_definitions(-DOPJ_EXPORTS)
+ else()
+ add_definitions(-DOPJ_STATIC)
+ endif()
+ endif()
+
+ target_include_directories(dcm2niix PRIVATE ${OPENJPEG_INCLUDE_DIRS})
+ target_link_libraries(dcm2niix ${OPENJPEG_LIBRARIES})
+else ()
+ add_definitions(-DmyDisableOpenJPEG)
+endif()
+
+option(BATCH_VERSION "Build dcm2niibatch for multiple conversions" OFF)
+if(BATCH_VERSION)
+ set(DCM2NIIBATCH_SRCS
+ main_console_batch.cpp
+ nii_dicom.cpp
+ jpg_0XC3.cpp
+ ujpeg.cpp
+ nifti1_io_core.cpp
+ nii_foreign.cpp
+ nii_ortho.cpp
+ nii_dicom_batch.cpp)
+
+ if(USE_JPEGLS)
+ add_executable(dcm2niibatch ${DCM2NIIBATCH_SRCS} ${CHARLS_SRCS})
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+ add_executable(dcm2niibatch ${DCM2NIIBATCH_SRCS})
+ endif()
+
+ set(YAML-CPP_DIR ${YAML-CPP_DIR} CACHE PATH "Path to yaml-cpp configuration file" FORCE)
+
+ find_package(YAML-CPP REQUIRED)
+ target_include_directories(dcm2niibatch PRIVATE ${YAML_CPP_INCLUDE_DIR})
+ target_link_libraries(dcm2niibatch ${YAML_CPP_LIBRARIES})
+
+ if(ZLIB_FOUND)
+ target_include_directories(dcm2niibatch PRIVATE ${ZLIB_INCLUDE_DIRS})
+ target_link_libraries(dcm2niibatch ${ZLIB_LIBRARIES})
+ endif()
+
+ if(TURBOJPEG_FOUND)
+ target_include_directories(dcm2niibatch PRIVATE ${TURBOJPEG_INCLUDEDIR})
+ target_link_libraries(dcm2niibatch ${TURBOJPEG_LIBRARIES})
+ endif()
+
+ if(JASPER_FOUND)
+ target_include_directories(dcm2niibatch PRIVATE ${JASPER_INCLUDE_DIR})
+ target_link_libraries(dcm2niibatch ${JASPER_LIBRARIES})
+ endif()
+
+ if(OPENJPEG_FOUND)
+ target_include_directories(dcm2niibatch PRIVATE ${OPENJPEG_INCLUDE_DIRS})
+ target_link_libraries(dcm2niibatch ${OPENJPEG_LIBRARIES})
+ endif()
+
+ list(APPEND PROGRAMS dcm2niibatch)
+endif()
+
+
+if(APPLE)
+ message("-- Adding Apple plist")
+ set_target_properties(dcm2niix PROPERTIES LINK_FLAGS "-Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_SOURCE_DIR}/Info.plist")
+ #Apple notarization requires a Info.plist
+ # For .app bundles, the Info.plist is a separate file, for executables it is appended as a section
+ #you can check that the Info.plist section has been inserted with either of these commands
+ # otool -l ./dcm2niix | grep info_plist -B1 -A10
+ # launchctl plist ./dcm2niix
+endif()
+
+### start of addition for FREESURFER
+# we should not need this if we use target_compile_definitions()
+#target_link_libraries(dcm2niix nifti ${ZLIB_LIBRARIES})
+
+set(LIBRARY dcm2niixfs)
+add_library(dcm2niixfs STATIC
+ dcm2fsWrapper.cpp
+ nii_dicom.cpp
+ jpg_0XC3.cpp
+ ujpeg.cpp
+ nifti1_io_core.cpp
+ nii_foreign.cpp
+ nii_ortho.cpp
+ nii_dicom_batch.cpp)
+target_compile_definitions(dcm2niixfs PUBLIC -DMGH_FREESURFER -DUSING_MGH_NIFTI_IO)
+
+set(DCM2NIIXFSEXE dcm2niixfsexe)
+add_executable(${DCM2NIIXFSEXE} dcm2fsmain.cpp)
+target_link_libraries(${DCM2NIIXFSEXE} dcm2niixfs nifti ${ZLIB_LIBRARIES})
+### end of addition for FREESURFER
+
+install(TARGETS ${PROGRAMS} DESTINATION bin)
+### additions for FREESURFER
+install(TARGETS ${LIBRARY} DESTINATION bin)
+install(TARGETS ${DCM2NIIXFSEXE} DESTINATION bin)
diff --git a/packages/dcm2niix/Info.plist b/packages/dcm2niix/Info.plist
new file mode 100644
index 00000000000..d470b8ed58d
--- /dev/null
+++ b/packages/dcm2niix/Info.plist
@@ -0,0 +1,24 @@
+
+
+
+
+ CFBundleExecutable
+ dcm2niix
+ CFBundleIdentifier
+ com.mricro.dcm2niix
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ dcm2niix
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+ CFBundlePackageType
+ APPL
+
+
diff --git a/packages/dcm2niix/charls/License.txt b/packages/dcm2niix/charls/License.txt
new file mode 100644
index 00000000000..cb5c2b29920
--- /dev/null
+++ b/packages/dcm2niix/charls/License.txt
@@ -0,0 +1,29 @@
+https://github.com/team-charls/charls
+
+Copyright (c) 2007-2010, Jan de Vaan
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the name of my employer, nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/dcm2niix/charls/README.md b/packages/dcm2niix/charls/README.md
new file mode 100644
index 00000000000..82f45adc778
--- /dev/null
+++ b/packages/dcm2niix/charls/README.md
@@ -0,0 +1,82 @@
+[](https://ci.appveyor.com/project/vbaderks/charls/branch/master)
+[](https://travis-ci.org/team-charls/charls)
+
+# CharLS
+
+CharLS is a C++ implementation of the JPEG-LS standard for lossless and near-lossless image compression and decompression.
+JPEG-LS is a low-complexity image compression standard that matches JPEG 2000 compression ratios.
+
+# CharLS and dcm2niix
+
+[CharLS](https://github.com/team-charls/charls) is an optional module for dcm2niix. If included, it allows dcm2niix to handle the [JPEG-LS transfer syntaxes](https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage#DICOM_Transfer_Syntaxes_and_Compressed_Images). The included code was downloaded from the CharLS website on 6 June 2018.
+
+It is worth noting that DICOM can specify three different [lossless forms of the JPEG](http://www.mccauslandcenter.sc.edu/crnl/tools/jpeg-formats) image standard. CharLS is used only for the JPEG-LS form (transfer syntaxes 1.2.840.10008.1.2.4.80/81; ISO/IEC 14495-1:1999 ITU-T.87). In contrast, dcm2niix uses bespoke code to handle the older JPEG-Lossless (1.2.840.10008.1.2.4.57/70; ISO/IEC 10918-1:1994 ITU-T.81). Finally, dcm2niix handles the very complex JPEG-2000 lossless (1.2.840.10008.1.2.4.90/91; ISO/IEC 15444-1:2004 ITU-T.800) using the [OpenJPEG](https://github.com/uclouvain/openjpeg) library.
+
+To enable support you will need to include the `myEnableJPEGLS` compiler flag as well as a few file sin the `charls` folder. You will also need to specify `-std=c++14` and use a compiler that supports c++14 or later. Therefore, a minimal compile should look like this:
+
+`g++ -I. -std=c++14 -DmyEnableJPEGLS charls/jpegls.cpp charls/jpegmarkersegment.cpp charls/interface.cpp charls/jpegstreamwriter.cpp charls/jpegstreamreader.cpp main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -o dcm2niix -DmyDisableOpenJPEG`
+
+The option `myEnableJPEGLS` specifies the latest version of CharLS (currently version 2). Alternatively, you can specify `myEnableJPEGLS1` to compile for CharLS version 1. This older code is not included with dcm2niix, but you can [download it from Github](https://github.com/team-charls/charls/tree/1.x-master). Note that CharLS version 1 is designed for c++03:
+
+`g++ -I. -std=c++03 -DmyEnableJPEGLS1 charls1/header.cpp charls1/jpegls.cpp charls1/jpegmarkersegment.cpp charls1/interface.cpp charls1/jpegstreamwriter.cpp main_console.cpp nii_foreign.cpp nii_dicom.cpp jpg_0XC3.cpp ujpeg.cpp nifti1_io_core.cpp nii_ortho.cpp nii_dicom_batch.cpp -o dcm2niix -DmyDisableOpenJPEG`
+
+Note that in these examples we have disabled OpenJPEG's JPEG2000 support. In reality, you will probably want to support both JPEG2000 and JPEG-LS, this will allow you to convert a broader range of images, and JPEG2000 includes lossy variations that do not have analogues for JPEG-LS. For details on adding the JPEG2000 module see this [compile page](https://github.com/rordenlab/dcm2niix/blob/master/COMPILE.md).
+
+# JPEG-LS versus other lossless JPEG codecs
+
+You can use gdcmconv to compare the performance of the ancient JPEG-lossless (gdcmconv -J; default mode for dcmcjpeg), JPEG-LS (gdcmconv -L) and JPEG2000-lossess (gdcmconv -K). Below is a sample test looking at 800 DICOM CT scans - with a raw size of 425mb which dcm2niix can convert in 1.6 seconds. The table shows that JPEG-LS reduces the file sizes to 137mb (0.39 original size), but that decompression takes 7.2 times longer. In contrast, the complicated JPEG2000 achieves only slightly better compression but is much slower to decompress.
+
+| CT | Size | Speed |
+| ----------------------------------------- | -----:| -----:|
+| Raw 1.2.840.10008.1.2.1 | 1.00 | 1.0 |
+| JPEG-lossless 1.2.840.10008.1.2.4.70 | 0.37 | 6.4 |
+| JPEG-LS 1.2.840.10008.1.2.4.80 | 0.32 | 7.2 |
+| JPEG2000 lossless 1.2.840.10008.1.2.4.90 | 0.31 | 60.1 |
+
+Below is a sample test looking at 1092 DICOM MRI scans with a Siemens Prisma with 16-bit output (many older systems use 12-bit output which would presumably provide more compression potential) - with a raw size of 425mb which dcm2niix can convert in 4.7 seconds. Note that the MRI scans show poorer compression for all techniques.
+
+| MRI | Size | Speed |
+| ----------------------------------------- | -----:| -----:|
+| Raw 1.2.840.10008.1.2.1 | 1.00 | 1.0 |
+| JPEG-lossless 1.2.840.10008.1.2.4.70 | 0.65 | 5.0 |
+| JPEG-LS 1.2.840.10008.1.2.4.80 | 0.60 | 7.7 |
+| JPEG2000 lossless 1.2.840.10008.1.2.4.90 | 0.61 | 71.6 |
+
+The tables above describe illustrate the speed for decompression. With respect to compression, the MRI images take 54 seconds to compress as JPEG-lossless, 72 seconds to compress as JPEG-LS and 212 seconds for JPEG2000 lossless. These tests support the notion that JPEG-LS provides similar compression to JPEG2000 lossless with much faster compression and decompression. In fairness, it should be noted that all these tests use open source OpenJPEG library for JPEG2000 compression and decompression. This library is known to be robust but [slow compared to proprietary libraries](https://blog.hexagongeospatial.com/jpeg2000-quirks/).
+
+## Features
+
+* C++14 library implementation with a binary C interface for maximum interoperability.Note: a C++03 compatible implementation is maintained in the 1.x-master branch.
+* Supports Windows, Linux and Solaris in 32 bit and 64 bit.
+* Includes an adapter assembly for .NET based languages.
+* Excellent compression and decompression performance.
+
+## About JPEG-LS
+
+JPEG-LS (ISO/IEC 14495-1:1999 / ITU-T.87) is an image compression standard derived from the Hewlett Packard LOCO algorithm. JPEG-LS has low complexity (meaning fast compression) and high compression ratios, similar to the JPEG 2000 lossless ratios. JPEG-LS is more similar to the old Lossless JPEG than to JPEG 2000, but interestingly the two different techniques result in vastly different performance characteristics.
+Wikipedia on lossless JPEG and JPEG-LS:
+Tip: the ITU makes their version of the JPEG-LS standard (ITU-T.87) freely available for download, the text is identical with the ISO version.
+
+## About this software
+
+This project's goal is to provide a full implementation of the ISO/IEC 14495-1:1999, "Lossless and near-lossless compression of continuous-tone still images: Baseline" standard. This library is written from scratch in portable C++. The master branch uses modern C++14. The 1.x branch is maintained in C++03. All mainstream JPEG-LS features are implemented by this library.
+According to preliminary test results published on http://imagecompression.info/gralic, CharLS is about *twice as fast* as the original HP code, and beats both JPEG-XR and JPEG 2000 by a factor 3.
+
+### Limitations
+
+* No support for (optional) JPEG restart markers (RST). These markers are rarely used in practice.
+* No support for the SPIFF file header.
+* No support for oversize image dimension. Maximum supported image dimensions are [1, 65535] by [1, 65535].
+* After releasing the original baseline standrd 14495-1:1999, ISO released an extension to the JPEG-LS standard called ISO/IEC 14495-2:2003: "Lossless and near-lossless compression of continuous-tone still images: Extensions". CharLS doesn't support these extensions.
+
+## Supported platforms
+
+The code is regularly compiled/tested on Windows and 64 bit Linux. Additionally, the code has been successfully tested on Linux Intel/AMD 32/64 bit (slackware, debian, gentoo), Solaris SPARC systems, Intel based Macs and Windows CE (ARM CPU, emulated), where the less common compilers may require minor code edits. It leverages C++ language features (templates, traits) to create optimized code, which generally perform best with recent compilers. If you compile with GCC, 64 bit code performs substantially better.
+
+## 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](http://knivez.homelinux.org/) wrote an initial version of the color transfroms 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.
+
+## Legal
+
+The code in this project is available through a BSD style license, allowing use of the code in commercial closed source applications if you wish. **All** the code in this project is written from scratch, and not based on other JPEG-LS implementations. Be aware that Hewlett Packard claims to own patents that apply to JPEG-LS implementations, but they license it for free for conformant JPEG-LS implementations. Read more at before you use this if you use this code for commercial purposes.
diff --git a/packages/dcm2niix/charls/charls.h b/packages/dcm2niix/charls/charls.h
new file mode 100644
index 00000000000..031536779ca
--- /dev/null
+++ b/packages/dcm2niix/charls/charls.h
@@ -0,0 +1,91 @@
+/*
+ (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+*/
+
+
+#ifndef CHARLS_CHARLS
+#define CHARLS_CHARLS
+
+#include "publictypes.h"
+
+// Windows and building CharLS DLL itself.
+#if defined(WIN32) && defined(CHARLS_DLL_BUILD)
+#define CHARLS_IMEXPORT(returntype) __declspec(dllexport) returntype __stdcall // NOLINT
+#endif
+
+// Non-windows (static linking)
+#if !defined(CHARLS_IMEXPORT) && !defined(_WIN32)
+#define CHARLS_IMEXPORT(returntype) returntype
+#endif
+
+// Windows static linking
+#if !defined(CHARLS_IMEXPORT) && defined(CHARLS_STATIC)
+#define CHARLS_IMEXPORT(returntype) returntype
+#endif
+
+// Windows DLL
+#if !defined(CHARLS_IMEXPORT) && defined(CHARLS_DLL)
+#define CHARLS_IMEXPORT(returntype) __declspec(dllimport) returntype __stdcall
+#endif
+
+#if !defined(CHARLS_IMEXPORT)
+#error Please #define CHARLS_STATIC or CHARLS_DLL before including "charls.h" to indicate if CharLS is built as a static library or as a dll.
+#endif
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#else
+#include
+#endif
+
+///
+/// Encodes a byte array with pixel data to a JPEG-LS encoded (compressed) byte array.
+///
+/// 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_IMEXPORT(CharlsApiResultType) 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.
+///
+/// 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_IMEXPORT(CharlsApiResultType) JpegLsReadHeader(const void* compressedData, size_t compressedLength,
+ struct JlsParameters* params, char* errorMessage);
+
+///
+/// Encodes a JPEG-LS encoded byte array to uncompressed pixel data byte array.
+///
+/// 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_IMEXPORT(CharlsApiResultType) JpegLsDecode(void* destination, size_t destinationLength,
+ const void* source, size_t sourceLength, const struct JlsParameters* params, char* errorMessage);
+
+CHARLS_IMEXPORT(CharlsApiResultType) JpegLsDecodeRect(void* uncompressedData, size_t uncompressedLength,
+ const void* compressedData, size_t compressedLength,
+ struct JlsRect roi, const struct JlsParameters* info, char* errorMessage);
+
+#ifdef __cplusplus
+}
+
+CHARLS_IMEXPORT(CharlsApiResultType) JpegLsEncodeStream(ByteStreamInfo compressedStreamInfo, size_t& pcbyteWritten, ByteStreamInfo rawStreamInfo, const JlsParameters& params, char* errorMessage);
+CHARLS_IMEXPORT(CharlsApiResultType) JpegLsDecodeStream(ByteStreamInfo rawStream, ByteStreamInfo compressedStream, const JlsParameters* info, char* errorMessage);
+CHARLS_IMEXPORT(CharlsApiResultType) JpegLsReadHeaderStream(ByteStreamInfo rawStreamInfo, JlsParameters* params, char* errorMessage);
+
+#endif
+
+#endif
diff --git a/packages/dcm2niix/charls/colortransform.h b/packages/dcm2niix/charls/colortransform.h
new file mode 100644
index 00000000000..e5ec0c6547b
--- /dev/null
+++ b/packages/dcm2niix/charls/colortransform.h
@@ -0,0 +1,196 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_COLORTRANSFORM
+#define CHARLS_COLORTRANSFORM
+
+#include "util.h"
+
+// This file defines simple classes that define (lossless) color transforms.
+// They are invoked in processline.h to convert between decoded values and the internal line buffers.
+// Color transforms work best for computer generated images, but are outside the official JPEG-LS specifications.
+
+template
+struct TransformNoneImpl
+{
+ static_assert(std::is_integral::value, "Integral required.");
+
+ using size_type = T;
+
+ FORCE_INLINE Triplet operator()(int v1, int v2, int v3) const noexcept
+ {
+ return Triplet(v1, v2, v3);
+ }
+};
+
+
+template
+struct TransformNone : TransformNoneImpl
+{
+ static_assert(std::is_integral::value, "Integral required.");
+
+ using Inverse = TransformNoneImpl;
+};
+
+
+template
+struct TransformHp1
+{
+ static_assert(std::is_integral::value, "Integral required.");
+
+ using size_type = T;
+
+ struct Inverse
+ {
+ explicit Inverse(const TransformHp1&) noexcept
+ {
+ }
+
+ FORCE_INLINE Triplet operator()(int v1, int v2, int v3) const noexcept
+ {
+ return Triplet(v1 + v2 - Range / 2, v2, v3 + v2 - Range / 2);
+ }
+ };
+
+ FORCE_INLINE Triplet operator()(int red, int green, int blue) const noexcept
+ {
+ Triplet hp1;
+ hp1.v2 = static_cast(green);
+ hp1.v1 = static_cast(red - green + Range / 2);
+ hp1.v3 = static_cast(blue - green + Range / 2);
+ return hp1;
+ }
+
+private:
+ static constexpr size_t Range = 1 << (sizeof(T) * 8);
+};
+
+
+template
+struct TransformHp2
+{
+ static_assert(std::is_integral::value, "Integral required.");
+
+ using size_type = T;
+
+ struct Inverse
+ {
+ explicit Inverse(const TransformHp2&) noexcept
+ {
+ }
+
+ FORCE_INLINE Triplet operator()(int v1, int v2, int v3) const noexcept
+ {
+ 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
+ return rgb;
+ }
+ };
+
+ FORCE_INLINE Triplet operator()(int red, int green, int blue) const noexcept
+ {
+ return Triplet(red - green + Range / 2, green, blue - ((red + green) >> 1) - Range / 2);
+ }
+
+private:
+ static constexpr size_t Range = 1 << (sizeof(T) * 8);
+};
+
+
+template
+struct TransformHp3
+{
+ static_assert(std::is_integral::value, "Integral required.");
+
+ using size_type = T;
+
+ struct Inverse
+ {
+ explicit Inverse(const TransformHp3&) noexcept
+ {
+ }
+
+ FORCE_INLINE Triplet operator()(int v1, int v2, 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
+ return rgb;
+ }
+ };
+
+ FORCE_INLINE Triplet operator()(int red, int green, int blue) const noexcept
+ {
+ 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;
+ return hp3;
+ }
+
+private:
+ 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
+{
+ using size_type = typename Transform::size_type;
+
+ struct Inverse
+ {
+ 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;
+};
+
+
+#endif
diff --git a/packages/dcm2niix/charls/constants.h b/packages/dcm2niix/charls/constants.h
new file mode 100644
index 00000000000..10f9337f28d
--- /dev/null
+++ b/packages/dcm2niix/charls/constants.h
@@ -0,0 +1,17 @@
+//
+// Copyright CharLS Team, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_CONSTANTS
+#define CHARLS_CONSTANTS
+
+// 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.
+const int DefaultThreshold1 = 3; // BASIC_T1
+const int DefaultThreshold2 = 7; // BASIC_T2
+const int DefaultThreshold3 = 21; // BASIC_T3
+
+const int DefaultResetValue = 64; // Default RESET value as defined in ISO/IEC 14495-1, table C.2
+
+#endif
diff --git a/packages/dcm2niix/charls/context.h b/packages/dcm2niix/charls/context.h
new file mode 100644
index 00000000000..3c6d8bdf038
--- /dev/null
+++ b/packages/dcm2niix/charls/context.h
@@ -0,0 +1,117 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+
+#ifndef CHARLS_CONTEXT
+#define CHARLS_CONTEXT
+
+
+#include
+
+
+//
+// Purpose: a JPEG-LS context with it's current statistics.
+//
+struct JlsContext
+{
+ int32_t A;
+ int32_t B;
+ int16_t C;
+ int16_t N;
+
+ JlsContext() noexcept :
+ A(),
+ B(),
+ C(),
+ N(1)
+ {
+ }
+
+
+ explicit JlsContext(int32_t a) noexcept :
+ A(a),
+ B(0),
+ C(0),
+ N(1)
+ {
+ }
+
+
+ FORCE_INLINE int32_t GetErrorCorrection(int32_t k) const noexcept
+ {
+ if (k != 0)
+ return 0;
+
+ return BitWiseSign(2 * B + N - 1);
+ }
+
+
+ FORCE_INLINE void UpdateVariables(int32_t errorValue, int32_t NEAR, int32_t NRESET) noexcept
+ {
+ 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;
+
+ ASSERT(a < 65536 * 256);
+ ASSERT(std::abs(b) < 65536 * 256);
+
+ if (n == NRESET)
+ {
+ a = a >> 1;
+ b = b >> 1;
+ n = n >> 1;
+ }
+
+ A = a;
+ n = n + 1;
+ N = static_cast(n);
+
+ if (b + n <= 0)
+ {
+ b = b + n;
+ if (b <= -n)
+ {
+ b = -n + 1;
+ }
+ C = C - (C > -128);
+ }
+ else if (b > 0)
+ {
+ b = b - n;
+ if (b > 0)
+ {
+ b = 0;
+ }
+ C = C + (C < 127);
+ }
+ B = b;
+
+ ASSERT(N != 0);
+ }
+
+
+ FORCE_INLINE int32_t GetGolomb() const noexcept
+ {
+ 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++)
+ {
+ ASSERT(k <= 32);
+ }
+ return k;
+ }
+};
+
+#endif
diff --git a/packages/dcm2niix/charls/contextrunmode.h b/packages/dcm2niix/charls/contextrunmode.h
new file mode 100644
index 00000000000..b3f32cfe899
--- /dev/null
+++ b/packages/dcm2niix/charls/contextrunmode.h
@@ -0,0 +1,109 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_CONTEXTRUNMODE
+#define CHARLS_CONTEXTRUNMODE
+
+#include
+
+// Implements statistical modeling for the run mode context.
+// Computes model dependent parameters like the Golomb code lengths
+struct CContextRunMode
+{
+ // Note: members are sorted based on their size.
+ int32_t A;
+ int32_t _nRItype;
+ uint8_t _nReset;
+ uint8_t N;
+ uint8_t Nn;
+
+ CContextRunMode() noexcept
+ : A(),
+ _nRItype(),
+ _nReset(),
+ N(),
+ Nn()
+ {
+ }
+
+
+ CContextRunMode(int32_t a, int32_t nRItype, int32_t nReset) noexcept
+ : A(a),
+ _nRItype(nRItype),
+ _nReset(static_cast(nReset)),
+ N(1),
+ Nn(0)
+ {
+ }
+
+
+ FORCE_INLINE int32_t GetGolomb() const noexcept
+ {
+ const int32_t TEMP = A + (N >> 1) * _nRItype;
+ int32_t Ntest = N;
+ int32_t k = 0;
+ for (; Ntest < TEMP; k++)
+ {
+ Ntest <<= 1;
+ ASSERT(k <= 32);
+ }
+ return k;
+ }
+
+
+ void UpdateVariables(int32_t Errval, int32_t EMErrval) noexcept
+ {
+ if (Errval < 0)
+ {
+ Nn = Nn + 1;
+ }
+ A = A + ((EMErrval + 1 - _nRItype) >> 1);
+ if (N == _nReset)
+ {
+ A = A >> 1;
+ N = N >> 1;
+ Nn = Nn >> 1;
+ }
+ N = N + 1;
+ }
+
+
+ FORCE_INLINE int32_t ComputeErrVal(int32_t temp, int32_t k) const noexcept
+ {
+ const bool map = temp & 1;
+ const int32_t errvalabs = (temp + static_cast(map)) / 2;
+
+ if ((k != 0 || (2 * Nn >= N)) == map)
+ {
+ ASSERT(map == ComputeMap(-errvalabs, k));
+ return -errvalabs;
+ }
+
+ ASSERT(map == ComputeMap(errvalabs, k));
+ return errvalabs;
+ }
+
+
+ bool ComputeMap(int32_t Errval, int32_t k) const noexcept
+ {
+ if ((k == 0) && (Errval > 0) && (2 * Nn < N))
+ return true;
+
+ if ((Errval < 0) && (2 * Nn >= N))
+ return true;
+
+ if ((Errval < 0) && (k != 0))
+ return true;
+
+ return false;
+ }
+
+
+ FORCE_INLINE bool ComputeMapNegativeE(int32_t k) const noexcept
+ {
+ return k != 0 || (2 * Nn >= N);
+ }
+};
+
+#endif
diff --git a/packages/dcm2niix/charls/decoderstrategy.h b/packages/dcm2niix/charls/decoderstrategy.h
new file mode 100644
index 00000000000..b80d6744b81
--- /dev/null
+++ b/packages/dcm2niix/charls/decoderstrategy.h
@@ -0,0 +1,311 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_DECODERSTATEGY
+#define CHARLS_DECODERSTATEGY
+
+
+#include "util.h"
+#include "processline.h"
+#include
+
+// Purpose: Implements encoding to stream of bits. In encoding mode JpegLsCodec inherits from EncoderStrategy
+class DecoderStrategy
+{
+public:
+ explicit DecoderStrategy(const JlsParameters& params) :
+ _params(params),
+ _byteStream(nullptr),
+ _readCache(0),
+ _validBits(0),
+ _position(nullptr),
+ _nextFFPosition(nullptr),
+ _endPosition(nullptr)
+ {
+ }
+
+ virtual ~DecoderStrategy() = 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 JpegLSPresetCodingParameters& presets) = 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;
+ }
+
+ _nextFFPosition = FindNextFF();
+ MakeValid();
+ }
+
+ void AddBytesFromStream()
+ {
+ if (!_byteStream || _byteStream->sgetc() == std::char_traits::eof())
+ return;
+
+ const std::size_t count = _endPosition - _position;
+
+ if (count > 64)
+ return;
+
+ for (std::size_t i = 0; i < count; ++i)
+ {
+ _buffer[i] = _position[i];
+ }
+ const std::size_t offset = _buffer.data() - _position;
+
+ _position += offset;
+ _endPosition += offset;
+ _nextFFPosition += offset;
+
+ const std::streamsize readbytes = _byteStream->sgetn(reinterpret_cast(_endPosition), _buffer.size() - count);
+ _endPosition += readbytes;
+ }
+
+ FORCE_INLINE void Skip(int32_t length) noexcept
+ {
+ _validBits -= length;
+ _readCache = _readCache << length;
+ }
+
+ static void OnLineBegin(int32_t /*cpixel*/, void* /*ptypeBuffer*/, int32_t /*pixelStride*/) noexcept
+ {
+ }
+
+ void OnLineEnd(int32_t pixelCount, const void* ptypeBuffer, int32_t pixelStride) const
+ {
+ _processLine->NewLineDecoded(ptypeBuffer, pixelCount, pixelStride);
+ }
+
+ void EndScan()
+ {
+ if ((*_position) != 0xFF)
+ {
+ ReadBit();
+
+ if ((*_position) != 0xFF)
+ throw charls_error(charls::ApiResult::TooMuchCompressedData);
+ }
+
+ if (_readCache != 0)
+ throw charls_error(charls::ApiResult::TooMuchCompressedData);
+ }
+
+ FORCE_INLINE bool OptimizedRead() noexcept
+ {
+ // Easy & fast: if there is no 0xFF byte in sight, we can read without bit stuffing
+ if (_position < _nextFFPosition - (sizeof(bufType)-1))
+ {
+ _readCache |= FromBigEndian::Read(_position) >> _validBits;
+ const int bytesToRead = (bufType_bit_count - _validBits) >> 3;
+ _position += bytesToRead;
+ _validBits += bytesToRead * 8;
+ ASSERT(static_cast(_validBits) >= bufType_bit_count - 8);
+ return true;
+ }
+ return false;
+ }
+
+ void MakeValid()
+ {
+ ASSERT(static_cast(_validBits) <=bufType_bit_count - 8);
+
+ if (OptimizedRead())
+ return;
+
+ AddBytesFromStream();
+
+ do
+ {
+ if (_position >= _endPosition)
+ {
+ if (_validBits <= 0)
+ throw charls_error(charls::ApiResult::InvalidCompressedData);
+
+ return;
+ }
+
+ const bufType valnew = _position[0];
+
+ if (valnew == 0xFF)
+ {
+ // JPEG bit stream rule: no FF may be followed by 0x80 or higher
+ if (_position == _endPosition - 1 || (_position[1] & 0x80) != 0)
+ {
+ if (_validBits <= 0)
+ throw charls_error(charls::ApiResult::InvalidCompressedData);
+
+ return;
+ }
+ }
+
+ _readCache |= valnew << (bufType_bit_count - 8 - _validBits);
+ _position += 1;
+ _validBits += 8;
+
+ if (valnew == 0xFF)
+ {
+ _validBits--;
+ }
+ }
+ while (static_cast(_validBits) < bufType_bit_count - 8);
+
+ _nextFFPosition = FindNextFF();
+ }
+
+ uint8_t* FindNextFF() const noexcept
+ {
+ auto positionNextFF = _position;
+
+ while (positionNextFF < _endPosition)
+ {
+ if (*positionNextFF == 0xFF)
+ break;
+
+ positionNextFF++;
+ }
+
+ return positionNextFF;
+ }
+
+ uint8_t* GetCurBytePos() const noexcept
+ {
+ int32_t validBits = _validBits;
+ uint8_t* compressedBytes = _position;
+
+ for (;;)
+ {
+ const int32_t cbitLast = compressedBytes[-1] == 0xFF ? 7 : 8;
+
+ if (validBits < cbitLast)
+ return compressedBytes;
+
+ validBits -= cbitLast;
+ compressedBytes--;
+ }
+ }
+
+ FORCE_INLINE int32_t ReadValue(int32_t length)
+ {
+ if (_validBits < length)
+ {
+ MakeValid();
+ if (_validBits < length)
+ throw charls_error(charls::ApiResult::InvalidCompressedData);
+ }
+
+ ASSERT(length != 0 && length <= _validBits);
+ ASSERT(length < 32);
+ const auto result = static_cast(_readCache >> (bufType_bit_count - length));
+ Skip(length);
+ return result;
+ }
+
+ FORCE_INLINE int32_t PeekByte()
+ {
+ if (_validBits < 8)
+ {
+ MakeValid();
+ }
+
+ return static_cast(_readCache >> (bufType_bit_count - 8));
+ }
+
+ FORCE_INLINE bool ReadBit()
+ {
+ if (_validBits <= 0)
+ {
+ MakeValid();
+ }
+
+ const bool bSet = (_readCache & (static_cast(1) << (bufType_bit_count - 1))) != 0;
+ Skip(1);
+ return bSet;
+ }
+
+ FORCE_INLINE int32_t Peek0Bits()
+ {
+ if (_validBits < 16)
+ {
+ MakeValid();
+ }
+ bufType valTest = _readCache;
+
+ for (int32_t count = 0; count < 16; count++)
+ {
+ if ((valTest & (static_cast(1) << (bufType_bit_count - 1))) != 0)
+ return count;
+
+ valTest <<= 1;
+ }
+ return -1;
+ }
+
+ FORCE_INLINE int32_t ReadHighbits()
+ {
+ const int32_t count = Peek0Bits();
+ if (count >= 0)
+ {
+ Skip(count + 1);
+ return count;
+ }
+ Skip(15);
+
+ for (int32_t highbits = 15; ; highbits++)
+ {
+ if (ReadBit())
+ return highbits;
+ }
+ }
+
+ int32_t ReadLongValue(int32_t length)
+ {
+ if (length <= 24)
+ return ReadValue(length);
+
+ return (ReadValue(length - 24) << 24) + ReadValue(24);
+ }
+
+protected:
+ JlsParameters _params;
+ std::unique_ptr _processLine;
+
+private:
+ using bufType = std::size_t;
+ static constexpr size_t bufType_bit_count = sizeof(bufType) * 8;
+
+ std::vector _buffer;
+ std::basic_streambuf* _byteStream;
+
+ // decoding
+ bufType _readCache;
+ int32_t _validBits;
+ uint8_t* _position;
+ uint8_t* _nextFFPosition;
+ uint8_t* _endPosition;
+};
+
+
+#endif
diff --git a/packages/dcm2niix/charls/defaulttraits.h b/packages/dcm2niix/charls/defaulttraits.h
new file mode 100644
index 00000000000..433c839828d
--- /dev/null
+++ b/packages/dcm2niix/charls/defaulttraits.h
@@ -0,0 +1,147 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+
+#ifndef CHARLS_DEFAULTTRAITS
+#define CHARLS_DEFAULTTRAITS
+
+
+#include "util.h"
+#include "constants.h"
+#include
+#include
+
+
+// Default traits that support all JPEG LS parameters: custom limit, near, maxval (not power of 2)
+
+// 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
+
+WARNING_SUPPRESS(26432)
+
+template
+struct DefaultTraits
+{
+ using SAMPLE = sample;
+ using PIXEL = pixel;
+
+ 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;
+
+ 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)
+ {
+ }
+
+ 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&&) = default;
+ DefaultTraits& operator=(const DefaultTraits&) = delete;
+ DefaultTraits& operator=(DefaultTraits&&) = delete;
+
+ FORCE_INLINE int32_t ComputeErrVal(int32_t e) const noexcept
+ {
+ return ModuloRange(Quantize(e));
+ }
+
+ FORCE_INLINE SAMPLE ComputeReconstructedSample(int32_t Px, int32_t ErrVal) const noexcept
+ {
+ return FixReconstructedValue(Px + DeQuantize(ErrVal));
+ }
+
+ FORCE_INLINE bool IsNear(int32_t lhs, int32_t rhs) const noexcept
+ {
+ return std::abs(lhs - rhs) <= NEAR;
+ }
+
+ bool IsNear(Triplet lhs, 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;
+ }
+
+ FORCE_INLINE int32_t CorrectPrediction(int32_t Pxc) const noexcept
+ {
+ if ((Pxc & MAXVAL) == Pxc)
+ return Pxc;
+
+ return (~(Pxc >> (int32_t_bit_count-1))) & MAXVAL;
+ }
+
+ ///
+ /// Returns the value of errorValue modulo RANGE. ITU.T.87, A.4.5 (code segment A.9)
+ ///
+ FORCE_INLINE int32_t ModuloRange(int32_t errorValue) const noexcept
+ {
+ ASSERT(std::abs(errorValue) <= RANGE);
+
+ if (errorValue < 0)
+ {
+ errorValue += RANGE;
+ }
+ if (errorValue >= (RANGE + 1) / 2)
+ {
+ errorValue -= RANGE;
+ }
+
+ ASSERT(-RANGE / 2 <= errorValue && errorValue <= (RANGE / 2) - 1);
+ return errorValue;
+ }
+
+private:
+ int32_t Quantize(int32_t Errval) const noexcept
+ {
+ if (Errval > 0)
+ return (Errval + NEAR) / (2 * NEAR + 1);
+
+ return - (NEAR - Errval) / (2 * NEAR + 1);
+ }
+
+ FORCE_INLINE int32_t DeQuantize(int32_t Errval) const noexcept
+ {
+ return Errval * (2 * NEAR + 1);
+ }
+
+ FORCE_INLINE SAMPLE FixReconstructedValue(int32_t val) const noexcept
+ {
+ if (val < -NEAR)
+ {
+ val = val + RANGE * (2 * NEAR + 1);
+ }
+ else if (val > MAXVAL + NEAR)
+ {
+ val = val - RANGE * (2 * NEAR + 1);
+ }
+
+ return static_cast(CorrectPrediction(val));
+ }
+};
+
+WARNING_UNSUPPRESS()
+
+#endif
diff --git a/packages/dcm2niix/charls/encoderstrategy.h b/packages/dcm2niix/charls/encoderstrategy.h
new file mode 100644
index 00000000000..fa7c24dd419
--- /dev/null
+++ b/packages/dcm2niix/charls/encoderstrategy.h
@@ -0,0 +1,200 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_ENCODERSTRATEGY
+#define CHARLS_ENCODERSTRATEGY
+
+#include "processline.h"
+#include "decoderstrategy.h"
+
+
+// Purpose: Implements encoding to stream of bits. In encoding mode JpegLsCodec inherits from EncoderStrategy
+class EncoderStrategy
+{
+
+public:
+ explicit EncoderStrategy(const JlsParameters& params) :
+ _params(params),
+ _bitBuffer(0),
+ _freeBitCount(sizeof(_bitBuffer) * 8),
+ _compressedLength(0),
+ _position(nullptr),
+ _isFFWritten(false),
+ _bytesWritten(0),
+ _compressedStream(nullptr)
+ {
+ }
+
+ virtual ~EncoderStrategy() = default;
+
+ EncoderStrategy(const EncoderStrategy&) = delete;
+ EncoderStrategy(EncoderStrategy&&) = delete;
+ EncoderStrategy& operator=(const EncoderStrategy&) = delete;
+ EncoderStrategy& operator=(EncoderStrategy&&) = delete;
+
+ virtual std::unique_ptr CreateProcess(ByteStreamInfo rawStreamInfo) = 0;
+ virtual void SetPresets(const JpegLSPresetCodingParameters& presets) = 0;
+ virtual std::size_t EncodeScan(std::unique_ptr rawData, ByteStreamInfo& compressedData) = 0;
+
+ int32_t PeekByte();
+
+ void OnLineBegin(int32_t cpixel, void* ptypeBuffer, int32_t pixelStride) const
+ {
+ _processLine->NewLineRequested(ptypeBuffer, cpixel, pixelStride);
+ }
+
+ static void OnLineEnd(int32_t /*cpixel*/, void* /*ptypeBuffer*/, int32_t /*pixelStride*/) noexcept
+ {
+ }
+
+protected:
+
+ void Init(ByteStreamInfo& compressedStream)
+ {
+ _freeBitCount = sizeof(_bitBuffer) * 8;
+ _bitBuffer = 0;
+
+ if (compressedStream.rawStream)
+ {
+ _compressedStream = compressedStream.rawStream;
+ _buffer.resize(4000);
+ _position = _buffer.data();
+ _compressedLength = _buffer.size();
+ }
+ else
+ {
+ _position = compressedStream.rawData;
+ _compressedLength = compressedStream.count;
+ }
+ }
+
+ void AppendToBitStream(int32_t bits, int32_t bitCount)
+ {
+ ASSERT(bitCount < 32 && bitCount >= 0);
+ ASSERT((!_qdecoder) || (bitCount == 0 && bits == 0) ||( _qdecoder->ReadLongValue(bitCount) == bits));
+#ifndef NDEBUG
+ const int mask = (1u << (bitCount)) - 1;
+ ASSERT((bits | mask) == mask); // Not used bits must be set to zero.
+#endif
+
+ _freeBitCount -= bitCount;
+ if (_freeBitCount >= 0)
+ {
+ _bitBuffer |= bits << _freeBitCount;
+ }
+ else
+ {
+ // Add as much bits in the remaining space as possible and flush.
+ _bitBuffer |= bits >> -_freeBitCount;
+ Flush();
+
+ // A second flush may be required if extra marker detect bits were needed and not all bits could be written.
+ if (_freeBitCount < 0)
+ {
+ _bitBuffer |= bits >> -_freeBitCount;
+ Flush();
+ }
+
+ ASSERT(_freeBitCount >= 0);
+ _bitBuffer |= bits << _freeBitCount;
+ }
+ }
+
+ void EndScan()
+ {
+ 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)
+ {
+ OverFlow();
+ }
+ }
+
+ void OverFlow()
+ {
+ if (!_compressedStream)
+ throw charls_error(charls::ApiResult::CompressedBufferTooSmall);
+
+ 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 charls_error(charls::ApiResult::CompressedBufferTooSmall);
+
+ _position = _buffer.data();
+ _compressedLength = _buffer.size();
+ }
+
+ void Flush()
+ {
+ if (_compressedLength < 4)
+ {
+ OverFlow();
+ }
+
+ for (int i = 0; i < 4; ++i)
+ {
+ if (_freeBitCount >= 32)
+ break;
+
+ if (_isFFWritten)
+ {
+ // 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;
+ }
+ else
+ {
+ *_position = static_cast(_bitBuffer >> 24);
+ _bitBuffer = _bitBuffer << 8;
+ _freeBitCount += 8;
+ }
+
+ _isFFWritten = *_position == 0xFF;
+ _position++;
+ _compressedLength--;
+ _bytesWritten++;
+ }
+ }
+
+ std::size_t GetLength() const noexcept
+ {
+ return _bytesWritten - (_freeBitCount - 32) / 8;
+ }
+
+ FORCE_INLINE void AppendOnesToBitStream(int32_t length)
+ {
+ AppendToBitStream((1 << length) - 1, length);
+ }
+
+ std::unique_ptr _qdecoder;
+
+ JlsParameters _params;
+ std::unique_ptr _processLine;
+
+private:
+ unsigned int _bitBuffer;
+ int32_t _freeBitCount;
+ std::size_t _compressedLength;
+
+ // encoding
+ uint8_t* _position;
+ bool _isFFWritten;
+ std::size_t _bytesWritten;
+
+ std::vector _buffer;
+ std::basic_streambuf* _compressedStream;
+};
+
+#endif
diff --git a/packages/dcm2niix/charls/interface.cpp b/packages/dcm2niix/charls/interface.cpp
new file mode 100644
index 00000000000..c833706943f
--- /dev/null
+++ b/packages/dcm2niix/charls/interface.cpp
@@ -0,0 +1,245 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#include "charls.h"
+#include "util.h"
+#include "jpegstreamreader.h"
+#include "jpegstreamwriter.h"
+#include "jpegmarkersegment.h"
+#include
+
+using namespace charls;
+
+namespace
+{
+
+void VerifyInput(const ByteStreamInfo& uncompressedStream, const JlsParameters& parameters)
+{
+ if (!uncompressedStream.rawStream && !uncompressedStream.rawData)
+ throw charls_error(ApiResult::InvalidJlsParameters, "rawStream or rawData needs to reference to something");
+
+ if (parameters.width < 1 || parameters.width > 65535)
+ throw charls_error(ApiResult::InvalidJlsParameters, "width needs to be in the range [1, 65535]");
+
+ if (parameters.height < 1 || parameters.height > 65535)
+ throw charls_error(ApiResult::InvalidJlsParameters, "height needs to be in the range [1, 65535]");
+
+ if (parameters.bitsPerSample < 2 || parameters.bitsPerSample > 16)
+ throw charls_error(ApiResult::InvalidJlsParameters, "bitspersample needs to be in the range [2, 16]");
+
+ if (!(parameters.interleaveMode == InterleaveMode::None || parameters.interleaveMode == InterleaveMode::Sample || parameters.interleaveMode == InterleaveMode::Line))
+ throw charls_error(ApiResult::InvalidJlsParameters, "interleaveMode needs to be set to a value of {None, Sample, Line}");
+
+ if (parameters.components < 1 || parameters.components > 255)
+ throw charls_error(ApiResult::InvalidJlsParameters, "components needs to be in the range [1, 255]");
+
+ if (uncompressedStream.rawData)
+ {
+ if (uncompressedStream.count < static_cast(parameters.height) * parameters.width * parameters.components * (parameters.bitsPerSample > 8 ? 2 : 1))
+ throw charls_error(ApiResult::InvalidJlsParameters, "uncompressed size does not match with the other parameters");
+ }
+
+ switch (parameters.components)
+ {
+ case 3:
+ break;
+ case 4:
+ if (parameters.interleaveMode == InterleaveMode::Sample)
+ throw charls_error(ApiResult::InvalidJlsParameters, "interleaveMode cannot be set to Sample in combination with components = 4");
+ break;
+ default:
+ if (parameters.interleaveMode != InterleaveMode::None)
+ throw charls_error(ApiResult::InvalidJlsParameters, "interleaveMode can only be set to None in combination with components = 1");
+ break;
+ }
+}
+
+
+ApiResult ResultAndErrorMessage(ApiResult result, char* errorMessage) noexcept
+{
+ if (errorMessage)
+ {
+ errorMessage[0] = 0;
+ }
+
+ return result;
+}
+
+
+ApiResult ResultAndErrorMessageFromException(char* errorMessage)
+{
+ try
+ {
+ // re-trow the exception.
+ throw;
+ }
+ catch (const charls_error& error)
+ {
+ if (errorMessage)
+ {
+ ASSERT(strlen(error.what()) < ErrorMessageSize);
+ strcpy(errorMessage, error.what());
+ }
+
+ return static_cast(error.code().value());
+ }
+ catch (...)
+ {
+ return ResultAndErrorMessage(ApiResult::UnexpectedFailure, errorMessage);
+ }
+}
+
+} // namespace
+
+
+CHARLS_IMEXPORT(ApiResult) JpegLsEncodeStream(ByteStreamInfo compressedStreamInfo, size_t& pcbyteWritten,
+ ByteStreamInfo rawStreamInfo, const struct JlsParameters& params, char* errorMessage)
+{
+ try
+ {
+ VerifyInput(rawStreamInfo, params);
+
+ JlsParameters info = params;
+ if (info.stride == 0)
+ {
+ info.stride = info.width * ((info.bitsPerSample + 7)/8);
+ if (info.interleaveMode != InterleaveMode::None)
+ {
+ info.stride *= info.components;
+ }
+ }
+
+ JpegStreamWriter writer;
+ if (info.jfif.version)
+ {
+ writer.AddSegment(JpegMarkerSegment::CreateJpegFileInterchangeFormatSegment(info.jfif));
+ }
+
+ writer.AddSegment(JpegMarkerSegment::CreateStartOfFrameSegment(info.width, info.height, info.bitsPerSample, info.components));
+
+ if (info.colorTransformation != ColorTransformation::None)
+ {
+ writer.AddColorTransform(info.colorTransformation);
+ }
+
+ if (info.interleaveMode == InterleaveMode::None)
+ {
+ const int32_t cbyteComp = info.width * info.height * ((info.bitsPerSample + 7) / 8);
+ for (int32_t component = 0; component < info.components; ++component)
+ {
+ writer.AddScan(rawStreamInfo, info);
+ SkipBytes(rawStreamInfo, cbyteComp);
+ }
+ }
+ else
+ {
+ writer.AddScan(rawStreamInfo, info);
+ }
+
+ writer.Write(compressedStreamInfo);
+ pcbyteWritten = writer.GetBytesWritten();
+
+ return ResultAndErrorMessage(ApiResult::OK, errorMessage);
+ }
+ catch (...)
+ {
+ return ResultAndErrorMessageFromException(errorMessage);
+ }
+}
+
+
+CHARLS_IMEXPORT(ApiResult) JpegLsDecodeStream(ByteStreamInfo rawStream, ByteStreamInfo compressedStream, const JlsParameters* info, char* errorMessage)
+{
+ try
+ {
+ JpegStreamReader reader(compressedStream);
+
+ if (info)
+ {
+ reader.SetInfo(*info);
+ }
+
+ reader.Read(rawStream);
+
+ return ResultAndErrorMessage(ApiResult::OK, errorMessage);
+ }
+ catch (...)
+ {
+ return ResultAndErrorMessageFromException(errorMessage);
+ }
+}
+
+
+CHARLS_IMEXPORT(ApiResult) JpegLsReadHeaderStream(ByteStreamInfo rawStreamInfo, JlsParameters* params, char* errorMessage)
+{
+ try
+ {
+ JpegStreamReader reader(rawStreamInfo);
+ reader.ReadHeader();
+ reader.ReadStartOfScan(true);
+ *params = reader.GetMetadata();
+
+ return ResultAndErrorMessage(ApiResult::OK, errorMessage);
+ }
+ catch (...)
+ {
+ return ResultAndErrorMessageFromException(errorMessage);
+ }
+}
+
+extern "C"
+{
+ CHARLS_IMEXPORT(ApiResult) JpegLsEncode(void* destination, size_t destinationLength, size_t* bytesWritten, const void* source, size_t sourceLength, const struct JlsParameters* params, char* errorMessage)
+ {
+ if (!destination || !bytesWritten || !source || !params)
+ return ApiResult::InvalidJlsParameters;
+
+ const ByteStreamInfo rawStreamInfo = FromByteArrayConst(source, sourceLength);
+ const ByteStreamInfo compressedStreamInfo = FromByteArray(destination, destinationLength);
+
+ return JpegLsEncodeStream(compressedStreamInfo, *bytesWritten, rawStreamInfo, *params, errorMessage);
+ }
+
+
+ CHARLS_IMEXPORT(ApiResult) JpegLsReadHeader(const void* compressedData, size_t compressedLength, JlsParameters* params, char* errorMessage)
+ {
+ return JpegLsReadHeaderStream(FromByteArrayConst(compressedData, compressedLength), params, errorMessage);
+ }
+
+
+ CHARLS_IMEXPORT(ApiResult) JpegLsDecode(void* destination, size_t destinationLength, const void* source, size_t sourceLength, const struct JlsParameters* params, char* errorMessage)
+ {
+ const ByteStreamInfo compressedStream = FromByteArrayConst(source, sourceLength);
+ const ByteStreamInfo rawStreamInfo = FromByteArray(destination, destinationLength);
+
+ return JpegLsDecodeStream(rawStreamInfo, compressedStream, params, errorMessage);
+ }
+
+
+ CHARLS_IMEXPORT(ApiResult) JpegLsDecodeRect(void* uncompressedData, size_t uncompressedLength, const void* compressedData, size_t compressedLength,
+ JlsRect roi, const JlsParameters* info, char* errorMessage)
+ {
+ try
+ {
+ const ByteStreamInfo compressedStream = FromByteArrayConst(compressedData, compressedLength);
+ JpegStreamReader reader(compressedStream);
+
+ const ByteStreamInfo rawStreamInfo = FromByteArray(uncompressedData, uncompressedLength);
+
+ if (info)
+ {
+ reader.SetInfo(*info);
+ }
+
+ reader.SetRect(roi);
+ reader.Read(rawStreamInfo);
+
+ return ResultAndErrorMessage(ApiResult::OK, errorMessage);
+ }
+ catch (...)
+ {
+ return ResultAndErrorMessageFromException(errorMessage);
+ }
+ }
+}
diff --git a/packages/dcm2niix/charls/jlscodecfactory.h b/packages/dcm2niix/charls/jlscodecfactory.h
new file mode 100644
index 00000000000..53384de311c
--- /dev/null
+++ b/packages/dcm2niix/charls/jlscodecfactory.h
@@ -0,0 +1,23 @@
+//
+// (C) CharLS Team 2014, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_JLSCODECFACTORY
+#define CHARLS_JLSCODECFACTORY
+
+#include
+
+struct JlsParameters;
+struct JpegLSPresetCodingParameters;
+
+template
+class JlsCodecFactory
+{
+public:
+ std::unique_ptr CreateCodec(const JlsParameters& params, const JpegLSPresetCodingParameters& presets);
+
+private:
+ std::unique_ptr CreateOptimizedCodec(const JlsParameters& params);
+};
+
+#endif
diff --git a/packages/dcm2niix/charls/jpegimagedatasegment.h b/packages/dcm2niix/charls/jpegimagedatasegment.h
new file mode 100644
index 00000000000..980efc0a5a1
--- /dev/null
+++ b/packages/dcm2niix/charls/jpegimagedatasegment.h
@@ -0,0 +1,29 @@
+//
+// (C) CharLS Team 2014, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_JPEGIMAGEDATASEGMENT
+#define CHARLS_JPEGIMAGEDATASEGMENT
+
+#include "jpegsegment.h"
+#include "jpegstreamwriter.h"
+
+class JpegImageDataSegment : public JpegSegment
+{
+public:
+ JpegImageDataSegment(ByteStreamInfo rawStream, const JlsParameters& params, int componentCount) noexcept :
+ _componentCount(componentCount),
+ _rawStreamInfo(rawStream),
+ _params(params)
+ {
+ }
+
+ void Serialize(JpegStreamWriter& streamWriter) override;
+
+private:
+ int _componentCount;
+ ByteStreamInfo _rawStreamInfo;
+ JlsParameters _params;
+};
+
+#endif
diff --git a/packages/dcm2niix/charls/jpegls.cpp b/packages/dcm2niix/charls/jpegls.cpp
new file mode 100644
index 00000000000..72deb7dfb65
--- /dev/null
+++ b/packages/dcm2niix/charls/jpegls.cpp
@@ -0,0 +1,185 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#include "util.h"
+#include "decoderstrategy.h"
+#include "encoderstrategy.h"
+#include "lookuptable.h"
+#include "losslesstraits.h"
+#include "defaulttraits.h"
+#include "jlscodecfactory.h"
+#include "jpegstreamreader.h"
+#include
+
+using namespace charls;
+
+// 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
+{
+
+signed char QuantizeGratientOrg(const JpegLSPresetCodingParameters& preset, int32_t NEAR, 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) return -1;
+ if (Di <= NEAR) return 0;
+ if (Di < preset.Threshold1) return 1;
+ if (Di < preset.Threshold2) return 2;
+ if (Di < preset.Threshold3) return 3;
+
+ return 4;
+}
+
+
+std::vector CreateQLutLossless(int32_t cbit)
+{
+ const JpegLSPresetCodingParameters preset = ComputeDefault((1u << static_cast(cbit)) - 1, 0);
+ const int32_t range = preset.MaximumSampleValue + 1;
+
+ std::vector lut(static_cast(range) * 2);
+
+ for (int32_t diff = -range; diff < range; diff++)
+ {
+ lut[static_cast(range) + diff] = QuantizeGratientOrg(preset, 0,diff);
+ }
+ return lut;
+}
+
+template
+std::unique_ptr create_codec(const Traits& traits, const JlsParameters& params)
+{
+ return std::make_unique>(traits, params);
+}
+
+
+} // namespace
+
+
+class charls_category : public std::error_category
+{
+public:
+ const char* name() const noexcept override
+ {
+ return "charls";
+ }
+
+ std::string message(int /* errval */) const override
+ {
+ return "CharLS error";
+ }
+};
+
+const std::error_category& charls_error::CharLSCategoryInstance() noexcept
+{
+ static charls_category instance;
+ return instance;
+}
+
+
+// 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) };
+
+// Lookup tables: sample differences to bin indexes.
+std::vector rgquant8Ll = CreateQLutLossless(8);
+std::vector rgquant10Ll = CreateQLutLossless(10);
+std::vector rgquant12Ll = CreateQLutLossless(12);
+std::vector rgquant16Ll = CreateQLutLossless(16);
+
+
+template
+std::unique_ptr JlsCodecFactory::CreateCodec(const JlsParameters& params, const JpegLSPresetCodingParameters& presets)
+{
+ std::unique_ptr codec;
+
+ if (presets.ResetValue == 0 || presets.ResetValue == DefaultResetValue)
+ {
+ codec = CreateOptimizedCodec(params);
+ }
+
+ if (!codec)
+ {
+ if (params.bitsPerSample <= 8)
+ {
+ DefaultTraits traits((1 << params.bitsPerSample) - 1, params.allowedLossyError, presets.ResetValue);
+ traits.MAXVAL = presets.MaximumSampleValue;
+ codec = std::make_unique, Strategy>>(traits, params);
+ }
+ else
+ {
+ DefaultTraits traits((1 << params.bitsPerSample) - 1, params.allowedLossyError, presets.ResetValue);
+ traits.MAXVAL = presets.MaximumSampleValue;
+ codec = std::make_unique, Strategy>>(traits, params);
+ }
+ }
+
+ codec->SetPresets(presets);
+ return codec;
+}
+
+template
+std::unique_ptr JlsCodecFactory::CreateOptimizedCodec(const JlsParameters& params)
+{
+ if (params.interleaveMode == InterleaveMode::Sample && params.components != 3)
+ return nullptr;
+
+#ifndef DISABLE_SPECIALIZATIONS
+
+ // optimized lossless versions common formats
+ if (params.allowedLossyError == 0)
+ {
+ if (params.interleaveMode == InterleaveMode::Sample)
+ {
+ if (params.bitsPerSample == 8)
+ return create_codec(LosslessTraits, 8>(), params);
+ }
+ else
+ {
+ switch (params.bitsPerSample)
+ {
+ case 8: return create_codec(LosslessTraits(), params);
+ case 12: return create_codec(LosslessTraits(), params);
+ case 16: return create_codec(LosslessTraits(), params);
+ default:
+ break;
+ }
+ }
+ }
+
+#endif
+
+ const int maxval = (1u << static_cast(params.bitsPerSample)) - 1;
+
+ if (params.bitsPerSample <= 8)
+ {
+ if (params.interleaveMode == InterleaveMode::Sample)
+ return create_codec(DefaultTraits >(maxval, params.allowedLossyError), params);
+
+ return create_codec(DefaultTraits((1u << params.bitsPerSample) - 1, params.allowedLossyError), params);
+ }
+ if (params.bitsPerSample <= 16)
+ {
+ if (params.interleaveMode == InterleaveMode::Sample)
+ return create_codec(DefaultTraits >(maxval, params.allowedLossyError), params);
+
+ return create_codec(DefaultTraits(maxval, params.allowedLossyError), params);
+ }
+ return nullptr;
+}
+
+
+template class JlsCodecFactory;
+template class JlsCodecFactory;
diff --git a/packages/dcm2niix/charls/jpegmarkercode.h b/packages/dcm2niix/charls/jpegmarkercode.h
new file mode 100644
index 00000000000..69aa85efbd3
--- /dev/null
+++ b/packages/dcm2niix/charls/jpegmarkercode.h
@@ -0,0 +1,40 @@
+//
+// (C) CharLS Team 2014, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#pragma once
+
+#include
+
+// JPEG Marker codes have the pattern 0xFFaa in a JPEG byte stream.
+// The valid 'aa' options are defined by several ITU / IEC standards:
+// 0x00, 0x01, 0xFE, 0xC0-0xDF are defined in ITU T.81/IEC 10918-1
+// 0xF0 - 0xF6 are defined in ITU T.84/IEC 10918-3 JPEG extensions
+// 0xF7 - 0xF8 are defined in ITU T.87/IEC 14495-1 JPEG LS
+// 0x4F - 0x6F, 0x90 - 0x93 are defined in JPEG 2000 IEC 15444-1
+enum class JpegMarkerCode : 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.
+
+ // The following markers are defined in ITU T.81 | ISO IEC 10918-1.
+ 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.
+ StartOfFrameExtendedArithemtic = 0xC9, // SOF_9: Marks the start of a extended sequential arithmetic encoded frame.
+ StartOfFrameProgressiveArithemtic = 0xCA, // SOF_10: Marks the start of a progressive arithmetic encoded frame.
+ StartOfFrameLosslessArithemtic = 0xCB, // SOF_11: Marks the start of a lossless arithmetic encoded frame.
+
+ 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.
+
+ ApplicationData0 = 0xE0, // APP0: Application data 0: used for JFIF header.
+ ApplicationData7 = 0xE7, // APP7: Application data 7: color-space.
+ ApplicationData8 = 0xE8, // APP8: Application data 8: colorXForm.
+ Comment = 0xFE // COM: Comment block.
+};
diff --git a/packages/dcm2niix/charls/jpegmarkersegment.cpp b/packages/dcm2niix/charls/jpegmarkersegment.cpp
new file mode 100644
index 00000000000..35551aed921
--- /dev/null
+++ b/packages/dcm2niix/charls/jpegmarkersegment.cpp
@@ -0,0 +1,116 @@
+//
+// (C) CharLS Team 2014, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#include "jpegmarkersegment.h"
+#include "jpegmarkercode.h"
+#include "util.h"
+#include
+#include
+
+using namespace charls;
+
+std::unique_ptr JpegMarkerSegment::CreateStartOfFrameSegment(int width, int height, int bitsPerSample, int componentCount)
+{
+ ASSERT(width >= 0 && width <= UINT16_MAX);
+ ASSERT(height >= 0 && height <= UINT16_MAX);
+ ASSERT(bitsPerSample > 0 && bitsPerSample <= UINT8_MAX);
+ ASSERT(componentCount > 0 && componentCount <= (UINT8_MAX - 1));
+
+ // Create a Frame Header as defined in T.87, C.2.2 and T.81, B.2.2
+ std::vector content;
+ content.push_back(static_cast(bitsPerSample)); // P = Sample precision
+ push_back(content, static_cast(height)); // Y = Number of lines
+ push_back(content, static_cast(width)); // X = Number of samples per line
+
+ // Components
+ content.push_back(static_cast(componentCount)); // Nf = Number of image components in frame
+ for (auto component = 0; component < componentCount; ++component)
+ {
+ // Component Specification parameters
+ content.push_back(static_cast(component + 1)); // Ci = Component identifier
+ content.push_back(0x11); // Hi + Vi = Horizontal sampling factor + Vertical sampling factor
+ content.push_back(0); // Tqi = Quantization table destination selector (reserved for JPEG-LS, should be set to 0)
+ }
+
+ return std::make_unique(JpegMarkerCode::StartOfFrameJpegLS, move(content));
+}
+
+
+std::unique_ptr JpegMarkerSegment::CreateJpegFileInterchangeFormatSegment(const JfifParameters& params)
+{
+ ASSERT(params.units == 0 || params.units == 1 || params.units == 2);
+ ASSERT(params.Xdensity > 0);
+ ASSERT(params.Ydensity > 0);
+ ASSERT(params.Xthumbnail >= 0 && params.Xthumbnail < 256);
+ ASSERT(params.Ythumbnail >= 0 && params.Ythumbnail < 256);
+
+ // Create a JPEG APP0 segment in the JPEG File Interchange Format (JFIF), v1.02
+ std::vector content { 'J', 'F', 'I', 'F', '\0' };
+ push_back(content, static_cast(params.version));
+ content.push_back(static_cast(params.units));
+ push_back(content, static_cast(params.Xdensity));
+ push_back(content, static_cast(params.Ydensity));
+
+ // thumbnail
+ content.push_back(static_cast(params.Xthumbnail));
+ content.push_back(static_cast(params.Ythumbnail));
+ if (params.Xthumbnail > 0)
+ {
+ if (params.thumbnail)
+ throw charls_error(ApiResult::InvalidJlsParameters, "params.Xthumbnail is > 0 but params.thumbnail == null_ptr");
+
+ content.insert(content.end(), static_cast(params.thumbnail),
+ static_cast(params.thumbnail) + static_cast(3) * params.Xthumbnail * params.Ythumbnail);
+ }
+
+ return std::make_unique(JpegMarkerCode::ApplicationData0, move(content));
+}
+
+
+std::unique_ptr JpegMarkerSegment::CreateJpegLSPresetParametersSegment(const JpegLSPresetCodingParameters& params)
+{
+ std::vector content;
+
+ // Parameter ID. 0x01 = JPEG-LS preset coding parameters.
+ content.push_back(1);
+
+ push_back(content, static_cast(params.MaximumSampleValue));
+ push_back(content, static_cast(params.Threshold1));
+ push_back(content, static_cast(params.Threshold2));
+ push_back(content, static_cast(params.Threshold3));
+ push_back(content, static_cast(params.ResetValue));
+
+ return std::make_unique(JpegMarkerCode::JpegLSPresetParameters, move(content));
+}
+
+
+std::unique_ptr JpegMarkerSegment::CreateColorTransformSegment(ColorTransformation transformation)
+{
+ return std::make_unique(
+ JpegMarkerCode::ApplicationData8,
+ std::vector { 'm', 'r', 'f', 'x', static_cast(transformation) });
+}
+
+
+std::unique_ptr JpegMarkerSegment::CreateStartOfScanSegment(int componentIndex, int componentCount, int allowedLossyError, InterleaveMode interleaveMode)
+{
+ ASSERT(componentIndex >= 0);
+ ASSERT(componentCount > 0);
+
+ // Create a Scan Header as defined in T.87, C.2.3 and T.81, B.2.3
+ std::vector content;
+
+ content.push_back(static_cast(componentCount));
+ for (auto i = 0; i < componentCount; ++i)
+ {
+ content.push_back(static_cast(componentIndex + i));
+ content.push_back(0); // Mapping table selector (0 = no table)
+ }
+
+ content.push_back(static_cast(allowedLossyError)); // NEAR parameter
+ content.push_back(static_cast(interleaveMode)); // ILV parameter
+ content.push_back(0); // transformation
+
+ return std::make_unique(JpegMarkerCode::StartOfScan, move(content));
+}
diff --git a/packages/dcm2niix/charls/jpegmarkersegment.h b/packages/dcm2niix/charls/jpegmarkersegment.h
new file mode 100644
index 00000000000..568cf9a4328
--- /dev/null
+++ b/packages/dcm2niix/charls/jpegmarkersegment.h
@@ -0,0 +1,76 @@
+//
+// (C) CharLS Team 2014, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_JPEGMARKERSEGMENT
+#define CHARLS_JPEGMARKERSEGMENT
+
+#include "jpegsegment.h"
+#include "jpegstreamwriter.h"
+#include
+#include
+#include
+
+
+enum class JpegMarkerCode : uint8_t;
+
+
+class JpegMarkerSegment : public JpegSegment
+{
+public:
+ ///
+ /// Creates 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.
+ static std::unique_ptr CreateStartOfFrameSegment(int width, int height, int bitsPerSample, int componentCount);
+
+ ///
+ /// Creates a JPEG File Interchange (APP1 + jfif) segment.
+ ///
+ /// Parameters to write into the JFIF segment.
+ static std::unique_ptr CreateJpegFileInterchangeFormatSegment(const JfifParameters& params);
+
+ ///
+ /// Creates a JPEG-LS preset parameters (LSE) segment.
+ ///
+ /// Parameters to write into the JPEG-LS preset segment.
+ static std::unique_ptr CreateJpegLSPresetParametersSegment(const JpegLSPresetCodingParameters& params);
+
+ ///
+ /// Creates a color transformation (APP8) segment.
+ ///
+ /// Parameters to write into the JFIF segment.
+ static std::unique_ptr CreateColorTransformSegment(charls::ColorTransformation transformation);
+
+ ///
+ /// Creates a JPEG-LS Start Of Scan (SOS) segment.
+ ///
+ /// The component index of the scan segment or the start index if component count > 1.
+ /// 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.
+ static std::unique_ptr CreateStartOfScanSegment(int componentIndex, int componentCount, int allowedLossyError, charls::InterleaveMode interleaveMode);
+
+ JpegMarkerSegment(JpegMarkerCode markerCode, std::vector&& content) :
+ _markerCode(markerCode),
+ _content(content)
+ {
+ }
+
+ void Serialize(JpegStreamWriter& streamWriter) override
+ {
+ streamWriter.WriteByte(0xFF);
+ streamWriter.WriteByte(static_cast(_markerCode));
+ streamWriter.WriteWord(static_cast(_content.size() + 2));
+ streamWriter.WriteBytes(_content);
+ }
+
+private:
+ JpegMarkerCode _markerCode;
+ std::vector _content;
+};
+
+#endif
diff --git a/packages/dcm2niix/charls/jpegsegment.h b/packages/dcm2niix/charls/jpegsegment.h
new file mode 100644
index 00000000000..4e52c59c567
--- /dev/null
+++ b/packages/dcm2niix/charls/jpegsegment.h
@@ -0,0 +1,28 @@
+//
+// (C) CharLS Team 2014, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_JPEGSEGMENT
+#define CHARLS_JPEGSEGMENT
+
+class JpegStreamWriter;
+
+//
+// Purpose: base class for segments that can be written to JPEG streams.
+//
+class JpegSegment
+{
+public:
+ virtual ~JpegSegment() = default;
+ virtual void Serialize(JpegStreamWriter& streamWriter) = 0;
+
+ JpegSegment(const JpegSegment&) = delete;
+ JpegSegment(JpegSegment&&) = delete;
+ JpegSegment& operator=(const JpegSegment&) = delete;
+ JpegSegment& operator=(JpegSegment&&) = delete;
+
+protected:
+ JpegSegment() = default;
+};
+
+#endif
diff --git a/packages/dcm2niix/charls/jpegstreamreader.cpp b/packages/dcm2niix/charls/jpegstreamreader.cpp
new file mode 100644
index 00000000000..052d3ec67f3
--- /dev/null
+++ b/packages/dcm2niix/charls/jpegstreamreader.cpp
@@ -0,0 +1,409 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#include "jpegstreamreader.h"
+#include "util.h"
+#include "jpegstreamwriter.h"
+#include "jpegimagedatasegment.h"
+#include "jpegmarkercode.h"
+#include "decoderstrategy.h"
+#include "encoderstrategy.h"
+#include "jlscodecfactory.h"
+#include "constants.h"
+#include
+#include
+#include
+
+using namespace charls;
+
+extern template class JlsCodecFactory;
+extern template class JlsCodecFactory;
+
+namespace {
+
+
+// JFIF\0
+uint8_t jfifID[] = { 'J', 'F', 'I', 'F', '\0' };
+
+
+/// Clamping function as defined by ISO/IEC 14495-1, Figure C.3
+int32_t clamp(int32_t i, int32_t j, int32_t maximumSampleValue) noexcept
+{
+ if (i > maximumSampleValue || i < j)
+ return j;
+
+ return i;
+}
+
+
+ApiResult CheckParameterCoherent(const JlsParameters& params) noexcept
+{
+ if (params.bitsPerSample < 2 || params.bitsPerSample > 16)
+ return ApiResult::ParameterValueNotSupported;
+
+ if (params.interleaveMode < InterleaveMode::None || params.interleaveMode > InterleaveMode::Sample)
+ return ApiResult::InvalidCompressedData;
+
+ switch (params.components)
+ {
+ case 4: return params.interleaveMode == InterleaveMode::Sample ? ApiResult::ParameterValueNotSupported : ApiResult::OK;
+ case 3: return ApiResult::OK;
+ case 0: return ApiResult::InvalidJlsParameters;
+
+ default: return params.interleaveMode != InterleaveMode::None ? ApiResult::ParameterValueNotSupported : ApiResult::OK;
+ }
+}
+
+} // namespace
+
+
+JpegLSPresetCodingParameters ComputeDefault(int32_t maximumSampleValue, int32_t allowedLossyError) noexcept
+{
+ JpegLSPresetCodingParameters preset;
+
+ const int32_t factor = (std::min(maximumSampleValue, 4095) + 128) / 256;
+ const int threshold1 = clamp(factor * (DefaultThreshold1 - 2) + 2 + 3 * allowedLossyError, allowedLossyError + 1, maximumSampleValue);
+ const int threshold2 = clamp(factor * (DefaultThreshold2 - 3) + 3 + 5 * allowedLossyError, threshold1, maximumSampleValue); //-V537
+
+ preset.Threshold1 = threshold1;
+ preset.Threshold2 = threshold2;
+ preset.Threshold3 = clamp(factor * (DefaultThreshold3 - 4) + 4 + 7 * allowedLossyError, threshold2, maximumSampleValue);
+ preset.MaximumSampleValue = maximumSampleValue;
+ preset.ResetValue = DefaultResetValue;
+ return preset;
+}
+
+
+void JpegImageDataSegment::Serialize(JpegStreamWriter& streamWriter)
+{
+ JlsParameters info = _params;
+ info.components = _componentCount;
+ auto codec = JlsCodecFactory().CreateCodec(info, _params.custom);
+ std::unique_ptr processLine(codec->CreateProcess(_rawStreamInfo));
+ ByteStreamInfo compressedData = streamWriter.OutputStream();
+ const size_t cbyteWritten = codec->EncodeScan(move(processLine), compressedData);
+ streamWriter.Seek(cbyteWritten);
+}
+
+
+JpegStreamReader::JpegStreamReader(ByteStreamInfo byteStreamInfo) noexcept :
+ _byteStream(byteStreamInfo),
+ _params(),
+ _rect()
+{
+}
+
+
+void JpegStreamReader::Read(ByteStreamInfo rawPixels)
+{
+ ReadHeader();
+
+ const auto result = CheckParameterCoherent(_params);
+ if (result != ApiResult::OK)
+ throw charls_error(result);
+
+ if (_rect.Width <= 0)
+ {
+ _rect.Width = _params.width;
+ _rect.Height = _params.height;
+ }
+
+ const int64_t bytesPerPlane = static_cast(_rect.Width) * _rect.Height * ((_params.bitsPerSample + 7)/8);
+
+ if (rawPixels.rawData && static_cast(rawPixels.count) < bytesPerPlane * _params.components)
+ throw charls_error(ApiResult::UncompressedBufferTooSmall);
+
+ int componentIndex = 0;
+
+ while (componentIndex < _params.components)
+ {
+ ReadStartOfScan(componentIndex == 0);
+
+ std::unique_ptr qcodec = JlsCodecFactory().CreateCodec(_params, _params.custom);
+ std::unique_ptr processLine(qcodec->CreateProcess(rawPixels));
+ qcodec->DecodeScan(move(processLine), _rect, _byteStream);
+ SkipBytes(rawPixels, static_cast(bytesPerPlane));
+
+ if (_params.interleaveMode != InterleaveMode::None)
+ return;
+
+ componentIndex += 1;
+ }
+}
+
+
+void JpegStreamReader::ReadNBytes(std::vector& dst, int byteCount)
+{
+ for (int i = 0; i < byteCount; ++i)
+ {
+ dst.push_back(static_cast(ReadByte()));
+ }
+}
+
+
+void JpegStreamReader::ReadHeader()
+{
+ if (ReadNextMarker() != JpegMarkerCode::StartOfImage)
+ throw charls_error(ApiResult::InvalidCompressedData);
+
+ for (;;)
+ {
+ const JpegMarkerCode marker = ReadNextMarker();
+ if (marker == JpegMarkerCode::StartOfScan)
+ return;
+
+ const int32_t cbyteMarker = ReadWord();
+ const int bytesRead = ReadMarker(marker) + 2;
+
+ const int paddingToRead = cbyteMarker - bytesRead;
+ if (paddingToRead < 0)
+ throw charls_error(ApiResult::InvalidCompressedData);
+
+ for (int i = 0; i < paddingToRead; ++i)
+ {
+ ReadByte();
+ }
+ }
+}
+
+
+JpegMarkerCode JpegStreamReader::ReadNextMarker()
+{
+ auto byte = ReadByte();
+ if (byte != 0xFF)
+ {
+ std::ostringstream message;
+ message << std::setfill('0');
+ message << "Expected JPEG Marker start byte 0xFF but the byte value was 0x" << std::hex << std::uppercase
+ << std::setw(2) << static_cast(byte);
+ throw charls_error(ApiResult::MissingJpegMarkerStart, message.str());
+ }
+
+ // 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 == 0xFF);
+
+ return static_cast(byte);
+}
+
+
+int JpegStreamReader::ReadMarker(JpegMarkerCode marker)
+{
+ // ISO/IEC 14495-1, ITU-T Recommendation T.87, C.1.1. defines the following markers valid for a JPEG-LS byte stream:
+ // SOF55, LSE, SOI, EOI, SOS, DNL, DRI, RSTm, APPn, COM.
+ // All other markers shall not be present.
+ switch (marker)
+ {
+ case JpegMarkerCode::StartOfFrameJpegLS:
+ return ReadStartOfFrame();
+
+ case JpegMarkerCode::Comment:
+ return ReadComment();
+
+ case JpegMarkerCode::JpegLSPresetParameters:
+ return ReadPresetParameters();
+
+ case JpegMarkerCode::ApplicationData0:
+ return 0;
+
+ case JpegMarkerCode::ApplicationData7:
+ return ReadColorSpace();
+
+ case JpegMarkerCode::ApplicationData8:
+ return ReadColorXForm();
+
+ case JpegMarkerCode::StartOfFrameBaselineJpeg:
+ case JpegMarkerCode::StartOfFrameExtendedSequential:
+ case JpegMarkerCode::StartOfFrameProgressive:
+ case JpegMarkerCode::StartOfFrameLossless:
+ case JpegMarkerCode::StartOfFrameDifferentialSequential:
+ case JpegMarkerCode::StartOfFrameDifferentialProgressive:
+ case JpegMarkerCode::StartOfFrameDifferentialLossless:
+ case JpegMarkerCode::StartOfFrameExtendedArithemtic:
+ case JpegMarkerCode::StartOfFrameProgressiveArithemtic:
+ case JpegMarkerCode::StartOfFrameLosslessArithemtic:
+ {
+ std::ostringstream message;
+ message << "JPEG encoding with marker " << static_cast(marker) << " is not supported.";
+ throw charls_error(ApiResult::UnsupportedEncoding, message.str());
+ }
+
+ // Other tags not supported (among which DNL DRI)
+ default:
+ {
+ std::ostringstream message;
+ message << "Unknown JPEG marker " << static_cast(marker) << " encountered.";
+ throw charls_error(ApiResult::UnknownJpegMarker, message.str());
+ }
+ }
+}
+
+
+int JpegStreamReader::ReadPresetParameters()
+{
+ const int type = ReadByte();
+
+ switch (type)
+ {
+ case 1:
+ {
+ _params.custom.MaximumSampleValue = ReadWord();
+ _params.custom.Threshold1 = ReadWord();
+ _params.custom.Threshold2 = ReadWord();
+ _params.custom.Threshold3 = ReadWord();
+ _params.custom.ResetValue = ReadWord();
+ return 11;
+ }
+
+ case 2: // mapping table specification
+ case 3: // mapping table continuation
+ case 4: // X and Y parameters greater than 16 bits are defined.
+ {
+ std::ostringstream message;
+ message << "JPEG-LS preset parameters with type " << static_cast(type) << " are not supported.";
+ throw charls_error(ApiResult::UnsupportedEncoding, message.str());
+ }
+ default:
+ {
+ std::ostringstream message;
+ message << "JPEG-LS preset parameters with invalid type " << static_cast(type) << " encountered.";
+ throw charls_error(ApiResult::InvalidJlsParameters, message.str());
+ }
+ }
+}
+
+
+void JpegStreamReader::ReadStartOfScan(bool firstComponent)
+{
+ if (!firstComponent)
+ {
+ if (ReadByte() != 0xFF)
+ throw charls_error(ApiResult::MissingJpegMarkerStart);
+ if (static_cast(ReadByte()) != JpegMarkerCode::StartOfScan)
+ throw charls_error(ApiResult::InvalidCompressedData);// TODO: throw more specific error code.
+ }
+ int length = ReadByte();
+ length = length * 256 + ReadByte(); // TODO: do something with 'length' or remove it.
+
+ const int componentCount = ReadByte();
+ if (componentCount != 1 && componentCount != _params.components)
+ throw charls_error(ApiResult::ParameterValueNotSupported);
+
+ for (int i = 0; i < componentCount; ++i)
+ {
+ ReadByte();
+ ReadByte();
+ }
+ _params.allowedLossyError = ReadByte();
+ _params.interleaveMode = static_cast(ReadByte());
+ if (!(_params.interleaveMode == InterleaveMode::None || _params.interleaveMode == InterleaveMode::Line || _params.interleaveMode == InterleaveMode::Sample))
+ throw charls_error(ApiResult::InvalidCompressedData);// TODO: throw more specific error code.
+ if (ReadByte() != 0)
+ throw charls_error(ApiResult::InvalidCompressedData);// TODO: throw more specific error code.
+
+ if(_params.stride == 0)
+ {
+ const int width = _rect.Width != 0 ? _rect.Width : _params.width;
+ const int components = _params.interleaveMode == InterleaveMode::None ? 1 : _params.components;
+ _params.stride = components * width * ((_params.bitsPerSample + 7) / 8);
+ }
+}
+
+
+int JpegStreamReader::ReadComment() noexcept
+{
+ return 0;
+}
+
+
+void JpegStreamReader::ReadJfif()
+{
+ for(int i = 0; i < static_cast(sizeof(jfifID)); i++)
+ {
+ if(jfifID[i] != ReadByte())
+ return;
+ }
+ _params.jfif.version = ReadWord();
+
+ // DPI or DPcm
+ _params.jfif.units = ReadByte();
+ _params.jfif.Xdensity = ReadWord();
+ _params.jfif.Ydensity = ReadWord();
+
+ // thumbnail
+ _params.jfif.Xthumbnail = ReadByte();
+ _params.jfif.Ythumbnail = ReadByte();
+ if(_params.jfif.Xthumbnail > 0 && _params.jfif.thumbnail)
+ {
+ std::vector tempbuff(static_cast(_params.jfif.thumbnail),
+ static_cast(_params.jfif.thumbnail) + static_cast(3) * _params.jfif.Xthumbnail * _params.jfif.Ythumbnail);
+ ReadNBytes(tempbuff, 3*_params.jfif.Xthumbnail*_params.jfif.Ythumbnail);
+ }
+}
+
+
+int JpegStreamReader::ReadStartOfFrame()
+{
+ _params.bitsPerSample = ReadByte();
+ _params.height = ReadWord();
+ _params.width = ReadWord();
+ _params.components= ReadByte();
+ return 6;
+}
+
+
+uint8_t JpegStreamReader::ReadByte()
+{
+ if (_byteStream.rawStream)
+ return static_cast(_byteStream.rawStream->sbumpc());
+
+ if (_byteStream.count == 0)
+ throw charls_error(ApiResult::CompressedBufferTooSmall);
+
+ const uint8_t value = _byteStream.rawData[0];
+ SkipBytes(_byteStream, 1);
+ return value;
+}
+
+
+int JpegStreamReader::ReadWord()
+{
+ const int i = ReadByte() * 256;
+ return i + ReadByte();
+}
+
+
+int JpegStreamReader::ReadColorSpace() const noexcept
+{
+ return 0;
+}
+
+
+int JpegStreamReader::ReadColorXForm()
+{
+ std::vector sourceTag;
+ ReadNBytes(sourceTag, 4);
+
+ if (strncmp(sourceTag.data(), "mrfx", 4) != 0)
+ return 4;
+
+ const auto xform = ReadByte();
+ switch (xform)
+ {
+ case static_cast(ColorTransformation::None):
+ case static_cast(ColorTransformation::HP1):
+ case static_cast(ColorTransformation::HP2):
+ case static_cast(ColorTransformation::HP3):
+ _params.colorTransformation = static_cast(xform);
+ 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 charls_error(ApiResult::ImageTypeNotSupported);
+ default:
+ throw charls_error(ApiResult::InvalidCompressedData);
+ }
+}
diff --git a/packages/dcm2niix/charls/jpegstreamreader.h b/packages/dcm2niix/charls/jpegstreamreader.h
new file mode 100644
index 00000000000..6d14b268fe6
--- /dev/null
+++ b/packages/dcm2niix/charls/jpegstreamreader.h
@@ -0,0 +1,75 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+#ifndef CHARLS_JPEGSTREAMREADER
+#define CHARLS_JPEGSTREAMREADER
+
+#include "publictypes.h"
+#include
+#include
+
+
+enum class JpegMarkerCode : uint8_t;
+struct JlsParameters;
+class JpegCustomParameters;
+
+
+JpegLSPresetCodingParameters ComputeDefault(int32_t maximumSampleValue, int32_t allowedLossyError) noexcept;
+
+
+//
+// JpegStreamReader: minimal implementation to read a JPEG byte stream.
+//
+class JpegStreamReader
+{
+public:
+ explicit JpegStreamReader(ByteStreamInfo byteStreamInfo) noexcept;
+
+ const JlsParameters& GetMetadata() const noexcept
+ {
+ return _params;
+ }
+
+ const JpegLSPresetCodingParameters& GetCustomPreset() const noexcept
+ {
+ return _params.custom;
+ }
+
+ void Read(ByteStreamInfo rawPixels);
+ void ReadHeader();
+
+ void SetInfo(const JlsParameters& params) noexcept
+ {
+ _params = params;
+ }
+
+ void SetRect(const JlsRect& rect) noexcept
+ {
+ _rect = rect;
+ }
+
+ void ReadStartOfScan(bool firstComponent);
+ uint8_t ReadByte();
+
+private:
+ JpegMarkerCode ReadNextMarker();
+ int ReadPresetParameters();
+ static int ReadComment() noexcept;
+ int ReadStartOfFrame();
+ int ReadWord();
+ void ReadNBytes(std::vector& dst, int byteCount);
+ int ReadMarker(JpegMarkerCode marker);
+
+ void ReadJfif();
+
+ // Color Transform Application Markers & Code Stream (HP extension)
+ int ReadColorSpace() const noexcept;
+ int ReadColorXForm();
+
+ ByteStreamInfo _byteStream;
+ JlsParameters _params;
+ JlsRect _rect;
+};
+
+
+#endif
diff --git a/packages/dcm2niix/charls/jpegstreamwriter.cpp b/packages/dcm2niix/charls/jpegstreamwriter.cpp
new file mode 100644
index 00000000000..a0d0103044a
--- /dev/null
+++ b/packages/dcm2niix/charls/jpegstreamwriter.cpp
@@ -0,0 +1,91 @@
+//
+// (C) CharLS Team 2014, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+
+#include "jpegstreamwriter.h"
+#include "jpegimagedatasegment.h"
+#include "jpegmarkercode.h"
+#include "jpegmarkersegment.h"
+#include "jpegstreamreader.h"
+#include "util.h"
+#include
+
+using namespace charls;
+
+namespace
+{
+
+bool IsDefault(const JpegLSPresetCodingParameters& custom) noexcept
+{
+ if (custom.MaximumSampleValue != 0)
+ return false;
+
+ if (custom.Threshold1 != 0)
+ return false;
+
+ if (custom.Threshold2 != 0)
+ return false;
+
+ if (custom.Threshold3 != 0)
+ return false;
+
+ if (custom.ResetValue != 0)
+ return false;
+
+ return true;
+}
+
+}
+
+
+JpegStreamWriter::JpegStreamWriter() noexcept
+ : _data(),
+ _byteOffset(0),
+ _lastCompenentIndex(0)
+{
+}
+
+
+void JpegStreamWriter::AddColorTransform(ColorTransformation transformation)
+{
+ AddSegment(JpegMarkerSegment::CreateColorTransformSegment(transformation));
+}
+
+
+size_t JpegStreamWriter::Write(const ByteStreamInfo& info)
+{
+ _data = info;
+
+ WriteMarker(JpegMarkerCode::StartOfImage);
+
+ for (size_t i = 0; i < _segments.size(); ++i)
+ {
+ _segments[i]->Serialize(*this);
+ }
+
+ WriteMarker(JpegMarkerCode::EndOfImage);
+
+ return _byteOffset;
+}
+
+
+void JpegStreamWriter::AddScan(const ByteStreamInfo& info, const JlsParameters& params)
+{
+ if (!IsDefault(params.custom))
+ {
+ AddSegment(JpegMarkerSegment::CreateJpegLSPresetParametersSegment(params.custom));
+ }
+ else if (params.bitsPerSample > 12)
+ {
+ const JpegLSPresetCodingParameters preset = ComputeDefault((1 << params.bitsPerSample) - 1, params.allowedLossyError);
+ AddSegment(JpegMarkerSegment::CreateJpegLSPresetParametersSegment(preset));
+ }
+
+ // Note: it is a common practice to start to count components by index 1.
+ _lastCompenentIndex += 1;
+ const int componentCount = params.interleaveMode == InterleaveMode::None ? 1 : params.components;
+ AddSegment(JpegMarkerSegment::CreateStartOfScanSegment(_lastCompenentIndex, componentCount, params.allowedLossyError, params.interleaveMode));
+
+ AddSegment(std::make_unique(info, params, componentCount));
+}
diff --git a/packages/dcm2niix/charls/jpegstreamwriter.h b/packages/dcm2niix/charls/jpegstreamwriter.h
new file mode 100644
index 00000000000..dd8ffdb66c4
--- /dev/null
+++ b/packages/dcm2niix/charls/jpegstreamwriter.h
@@ -0,0 +1,111 @@
+//
+// (C) CharLS Team 2014, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_JPEGSTREAMWRITER
+#define CHARLS_JPEGSTREAMWRITER
+
+#include "util.h"
+#include "jpegsegment.h"
+#include
+#include
+
+enum class JpegMarkerCode : uint8_t;
+
+
+//
+// Purpose: 'Writer' class that can generate JPEG-LS file streams.
+//
+class JpegStreamWriter
+{
+ friend class JpegMarkerSegment;
+ friend class JpegImageDataSegment;
+
+public:
+ JpegStreamWriter() noexcept;
+
+ void AddSegment(std::unique_ptr segment)
+ {
+ _segments.push_back(std::move(segment));
+ }
+
+ void AddScan(const ByteStreamInfo& info, const JlsParameters& params);
+
+ void AddColorTransform(charls::ColorTransformation transformation);
+
+ std::size_t GetBytesWritten() const noexcept
+ {
+ return _byteOffset;
+ }
+
+ std::size_t GetLength() const noexcept
+ {
+ return _data.count - _byteOffset;
+ }
+
+ std::size_t Write(const ByteStreamInfo& info);
+
+private:
+ uint8_t* GetPos() const noexcept
+ {
+ return _data.rawData + _byteOffset;
+ }
+
+ ByteStreamInfo OutputStream() const noexcept
+ {
+ ByteStreamInfo data = _data;
+ data.count -= _byteOffset;
+ data.rawData += _byteOffset;
+ return data;
+ }
+
+ void WriteByte(uint8_t val)
+ {
+ if (_data.rawStream)
+ {
+ _data.rawStream->sputc(val);
+ }
+ else
+ {
+ if (_byteOffset >= _data.count)
+ throw charls_error(charls::ApiResult::CompressedBufferTooSmall);
+
+ _data.rawData[_byteOffset++] = val;
+ }
+ }
+
+ void WriteBytes(const std::vector& bytes)
+ {
+ for (std::size_t i = 0; i < bytes.size(); ++i)
+ {
+ WriteByte(bytes[i]);
+ }
+ }
+
+ void WriteWord(uint16_t value)
+ {
+ WriteByte(static_cast(value / 0x100));
+ WriteByte(static_cast(value % 0x100));
+ }
+
+ void WriteMarker(JpegMarkerCode marker)
+ {
+ WriteByte(0xFF);
+ WriteByte(static_cast(marker));
+ }
+
+ void Seek(std::size_t byteCount) noexcept
+ {
+ if (_data.rawStream)
+ return;
+
+ _byteOffset += byteCount;
+ }
+
+ ByteStreamInfo _data;
+ std::size_t _byteOffset;
+ int32_t _lastCompenentIndex;
+ std::vector> _segments;
+};
+
+#endif
diff --git a/packages/dcm2niix/charls/lookuptable.h b/packages/dcm2niix/charls/lookuptable.h
new file mode 100644
index 00000000000..5ef59eeddab
--- /dev/null
+++ b/packages/dcm2niix/charls/lookuptable.h
@@ -0,0 +1,75 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+
+#ifndef CHARLS_LOOKUPTABLE
+#define CHARLS_LOOKUPTABLE
+
+
+#include
+
+
+// Tables for fast decoding of short Golomb Codes.
+struct Code
+{
+ Code() noexcept :
+ _value(),
+ _length()
+ {
+ }
+
+ Code(int32_t value, int32_t length) noexcept :
+ _value(value),
+ _length(length)
+ {
+ }
+
+ int32_t GetValue() const noexcept
+ {
+ return _value;
+ }
+
+ int32_t GetLength() const noexcept
+ {
+ return _length;
+ }
+
+ int32_t _value;
+ int32_t _length;
+};
+
+
+class CTable
+{
+public:
+ static constexpr size_t byte_bit_count = 8;
+
+ CTable() noexcept
+ {
+ std::memset(_rgtype, 0, sizeof(_rgtype));
+ }
+
+ void AddEntry(uint8_t bvalue, Code c) noexcept
+ {
+ const int32_t length = c.GetLength();
+ ASSERT(static_cast(length) <= byte_bit_count);
+
+ for (int32_t i = 0; i < static_cast(1) << (byte_bit_count - length); ++i)
+ {
+ ASSERT(_rgtype[(bvalue << (byte_bit_count - length)) + i].GetLength() == 0);
+ _rgtype[(bvalue << (byte_bit_count - length)) + i] = c;
+ }
+ }
+
+ FORCE_INLINE const Code& Get(int32_t value) const noexcept
+ {
+ return _rgtype[value];
+ }
+
+private:
+ Code _rgtype[1 << byte_bit_count];
+};
+
+
+#endif
diff --git a/packages/dcm2niix/charls/losslesstraits.h b/packages/dcm2niix/charls/losslesstraits.h
new file mode 100644
index 00000000000..db620af2382
--- /dev/null
+++ b/packages/dcm2niix/charls/losslesstraits.h
@@ -0,0 +1,135 @@
+//
+// (C) Jan de Vaan 2007-2010, all rights reserved. See the accompanying "License.txt" for licensed use.
+//
+
+#ifndef CHARLS_LOSSLESSTRAITS
+#define CHARLS_LOSSLESSTRAITS
+
+#include "constants.h"
+
+// 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
+{
+ using SAMPLE = sample;
+
+ enum
+ {
+ NEAR = 0,
+ bpp = bitsperpixel,
+ qbpp = bitsperpixel,
+ RANGE = (1 << bpp),
+ MAXVAL= (1 << bpp) - 1,
+ LIMIT = 2 * (bitsperpixel + std::max(8, bitsperpixel)),
+ RESET = DefaultResetValue
+ };
+
+ static FORCE_INLINE int32_t ComputeErrVal(int32_t d) noexcept
+ {
+ return ModuloRange(d);
+ }
+
+ static FORCE_INLINE bool IsNear(int32_t lhs, 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
+ static FORCE_INLINE int32_t ModuloRange(int32_t errorValue) noexcept
+ {
+ return static_cast(errorValue << (int32_t_bit_count - bpp)) >> (int32_t_bit_count - bpp);
+ }
+
+ static FORCE_INLINE SAMPLE ComputeReconstructedSample(int32_t Px, int32_t ErrVal) noexcept
+ {
+ return static_cast(MAXVAL & (Px + ErrVal));
+ }
+
+ static FORCE_INLINE int32_t CorrectPrediction(int32_t Pxc) noexcept
+ {
+ if ((Pxc & MAXVAL) == Pxc)
+ return Pxc;
+
+ return (~(Pxc >> (int32_t_bit_count-1))) & MAXVAL;
+ }
+};
+
+
+template
+struct LosslessTraits : LosslessTraitsImpl
+{
+ using PIXEL = T;
+};
+
+
+template<>
+struct LosslessTraits : LosslessTraitsImpl
+{
+ using PIXEL = SAMPLE;
+
+ static FORCE_INLINE signed char ModRange(int32_t Errval) noexcept
+ {
+ return static_cast(Errval);
+ }
+
+ static FORCE_INLINE int32_t ComputeErrVal(int32_t d) noexcept
+ {
+ return static_cast(d);
+ }
+
+ static FORCE_INLINE uint8_t ComputeReconstructedSample(int32_t Px, int32_t ErrVal) noexcept
+ {
+ return static_cast