Mercurial > hg > orthanc
changeset 3839:5bba4d249422 transcoding
integration mainline->transcoding
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 15 Apr 2020 22:03:21 +0200 |
parents | 4fde7933e504 (current diff) 95083d2f6819 (diff) |
children | 44bfcfdf42e8 |
files | Core/DicomNetworking/DicomStoreUserConnection.h OrthancServer/ServerContext.cpp Resources/CMake/DcmtkConfiguration.cmake UnitTestsSources/FromDcmtkTests.cpp |
diffstat | 23 files changed, 473 insertions(+), 55 deletions(-) [+] |
line wrap: on
line diff
--- a/Core/DicomNetworking/DicomAssociation.cpp Fri Apr 10 16:36:02 2020 +0200 +++ b/Core/DicomNetworking/DicomAssociation.cpp Wed Apr 15 22:03:21 2020 +0200 @@ -209,7 +209,7 @@ { return; // Already open } - + // Timeout used during association negociation and ASC_releaseAssociation() uint32_t acseTimeout = parameters.GetTimeout(); if (acseTimeout == 0)
--- a/Core/DicomNetworking/DicomControlUserConnection.cpp Fri Apr 10 16:36:02 2020 +0200 +++ b/Core/DicomNetworking/DicomControlUserConnection.cpp Wed Apr 15 22:03:21 2020 +0200 @@ -431,6 +431,15 @@ } + DicomControlUserConnection::DicomControlUserConnection(const std::string& localAet, + const RemoteModalityParameters& remote) : + parameters_(localAet, remote), + association_(new DicomAssociation) + { + SetupPresentationContexts(); + } + + bool DicomControlUserConnection::Echo() { association_->Open(parameters_);
--- a/Core/DicomNetworking/DicomControlUserConnection.h Fri Apr 10 16:36:02 2020 +0200 +++ b/Core/DicomNetworking/DicomControlUserConnection.h Wed Apr 15 22:03:21 2020 +0200 @@ -65,6 +65,9 @@ const DicomMap& fields); public: + DicomControlUserConnection(const std::string& localAet, + const RemoteModalityParameters& remote); + DicomControlUserConnection(const DicomAssociationParameters& params); const DicomAssociationParameters& GetParameters() const
--- a/Core/DicomNetworking/DicomStoreUserConnection.cpp Fri Apr 10 16:36:02 2020 +0200 +++ b/Core/DicomNetworking/DicomStoreUserConnection.cpp Wed Apr 15 22:03:21 2020 +0200 @@ -34,10 +34,14 @@ #include "../PrecompiledHeaders.h" #include "DicomStoreUserConnection.h" +#include "../DicomParsing/FromDcmtkBridge.h" +#include "../DicomParsing/ParsedDicomFile.h" +#include "../Logging.h" +#include "../OrthancException.h" #include "DicomAssociation.h" -#include "../Logging.h" -#include "../OrthancException.h" +#include <dcmtk/dcmdata/dcdeftag.h> + namespace Orthanc { @@ -237,4 +241,119 @@ association_->Open(parameters_); return LookupPresentationContext(presentationContextId, sopClassUid, transferSyntax); } + + + void DicomStoreUserConnection::Store(std::string& sopClassUid, + std::string& sopInstanceUid, + DcmDataset& dataset, + const std::string& moveOriginatorAET, + uint16_t moveOriginatorID) + { + OFString a, b; + if (!dataset.findAndGetOFString(DCM_SOPClassUID, a).good() || + !dataset.findAndGetOFString(DCM_SOPInstanceUID, b).good()) + { + throw OrthancException(ErrorCode_NoSopClassOrInstance, + "Unable to determine the SOP class/instance for C-STORE with AET " + + parameters_.GetRemoteApplicationEntityTitle()); + } + + sopClassUid.assign(a.c_str()); + sopInstanceUid.assign(b.c_str()); + + DicomTransferSyntax transferSyntax; + if (!FromDcmtkBridge::LookupOrthancTransferSyntax( + transferSyntax, dataset.getOriginalXfer())) + { + throw OrthancException(ErrorCode_InternalError, + "Unknown transfer syntax from DCMTK"); + } + + // Figure out which accepted presentation context should be used + uint8_t presID; + if (!NegotiatePresentationContext(presID, sopClassUid.c_str(), transferSyntax)) + { + throw OrthancException(ErrorCode_InternalError, + "No valid presentation context was negotiated upfront"); + } + + // Prepare the transmission of data + T_DIMSE_C_StoreRQ request; + memset(&request, 0, sizeof(request)); + request.MessageID = association_->GetDcmtkAssociation().nextMsgID++; + strncpy(request.AffectedSOPClassUID, sopClassUid.c_str(), DIC_UI_LEN); + request.Priority = DIMSE_PRIORITY_MEDIUM; + request.DataSetType = DIMSE_DATASET_PRESENT; + strncpy(request.AffectedSOPInstanceUID, sopInstanceUid.c_str(), DIC_UI_LEN); + + if (!moveOriginatorAET.empty()) + { + strncpy(request.MoveOriginatorApplicationEntityTitle, + moveOriginatorAET.c_str(), DIC_AE_LEN); + request.opts = O_STORE_MOVEORIGINATORAETITLE; + + request.MoveOriginatorID = moveOriginatorID; // The type DIC_US is an alias for uint16_t + request.opts |= O_STORE_MOVEORIGINATORID; + } + + // Finally conduct transmission of data + T_DIMSE_C_StoreRSP response; + DcmDataset* statusDetail = NULL; + DicomAssociation::CheckCondition( + DIMSE_storeUser(&association_->GetDcmtkAssociation(), presID, &request, + NULL, &dataset, /*progressCallback*/ NULL, NULL, + /*opt_blockMode*/ (GetParameters().HasTimeout() ? DIMSE_NONBLOCKING : DIMSE_BLOCKING), + /*opt_dimse_timeout*/ GetParameters().GetTimeout(), + &response, &statusDetail, NULL), + GetParameters(), "C-STORE"); + + if (statusDetail != NULL) + { + delete statusDetail; + } + + /** + * New in Orthanc 1.6.0: Deal with failures during C-STORE. + * http://dicom.nema.org/medical/dicom/current/output/chtml/part04/sect_B.2.3.html#table_B.2-1 + **/ + + if (response.DimseStatus != 0x0000 && // Success + response.DimseStatus != 0xB000 && // Warning - Coercion of Data Elements + response.DimseStatus != 0xB007 && // Warning - Data Set does not match SOP Class + response.DimseStatus != 0xB006) // Warning - Elements Discarded + { + char buf[16]; + sprintf(buf, "%04X", response.DimseStatus); + throw OrthancException(ErrorCode_NetworkProtocol, + "C-STORE SCU to AET \"" + + GetParameters().GetRemoteApplicationEntityTitle() + + "\" has failed with DIMSE status 0x" + buf); + } + } + + + void DicomStoreUserConnection::Store(std::string& sopClassUid, + std::string& sopInstanceUid, + ParsedDicomFile& parsed, + const std::string& moveOriginatorAET, + uint16_t moveOriginatorID) + { + Store(sopClassUid, sopInstanceUid, *parsed.GetDcmtkObject().getDataset(), + moveOriginatorAET, moveOriginatorID); + } + + + void DicomStoreUserConnection::Store(std::string& sopClassUid, + std::string& sopInstanceUid, + const void* buffer, + size_t size, + const std::string& moveOriginatorAET, + uint16_t moveOriginatorID) + { + std::unique_ptr<DcmFileFormat> dicom( + FromDcmtkBridge::LoadFromMemoryBuffer(buffer, size)); + + Store(sopClassUid, sopInstanceUid, *dicom->getDataset(), + moveOriginatorAET, moveOriginatorID); + } }
--- a/Core/DicomNetworking/DicomStoreUserConnection.h Fri Apr 10 16:36:02 2020 +0200 +++ b/Core/DicomNetworking/DicomStoreUserConnection.h Wed Apr 15 22:03:21 2020 +0200 @@ -41,6 +41,8 @@ #include <stdint.h> // For uint8_t +class DcmDataset; + namespace Orthanc { /** @@ -62,6 +64,7 @@ **/ class DicomAssociation; // Forward declaration for PImpl design pattern + class ParsedDicomFile; class DicomStoreUserConnection : public boost::noncopyable { @@ -78,7 +81,7 @@ // Return "false" if there is not enough room remaining in the association bool ProposeStorageClass(const std::string& sopClassUid, const std::set<DicomTransferSyntax>& syntaxes); - + public: DicomStoreUserConnection(const DicomAssociationParameters& params); @@ -120,12 +123,34 @@ void PrepareStorageClass(const std::string& sopClassUid, DicomTransferSyntax syntax); + // Should only be used if transcoding + // TODO => to private bool LookupPresentationContext(uint8_t& presentationContextId, const std::string& sopClassUid, DicomTransferSyntax transferSyntax); + // TODO => to private bool NegotiatePresentationContext(uint8_t& presentationContextId, const std::string& sopClassUid, DicomTransferSyntax transferSyntax); + + void Store(std::string& sopClassUid, + std::string& sopInstanceUid, + DcmDataset& dataset, + const std::string& moveOriginatorAET, + uint16_t moveOriginatorID); + + void Store(std::string& sopClassUid, + std::string& sopInstanceUid, + ParsedDicomFile& parsed, + const std::string& moveOriginatorAET, + uint16_t moveOriginatorID); + + void Store(std::string& sopClassUid, + std::string& sopInstanceUid, + const void* buffer, + size_t size, + const std::string& moveOriginatorAET, + uint16_t moveOriginatorID); }; }
--- a/Core/HttpServer/HttpServer.cpp Fri Apr 10 16:36:02 2020 +0200 +++ b/Core/HttpServer/HttpServer.cpp Wed Apr 15 22:03:21 2020 +0200 @@ -170,7 +170,7 @@ - class ChunkStore + class ChunkStore : public boost::noncopyable { private: typedef std::list<ChunkedFile*> Content; @@ -308,7 +308,7 @@ struct mg_connection *connection, const std::string& contentLength) { - int length; + int length; try { length = boost::lexical_cast<int>(contentLength); @@ -904,7 +904,6 @@ } } - if (!found && server.HasHandler()) {
--- a/Core/Images/PamReader.cpp Fri Apr 10 16:36:02 2020 +0200 +++ b/Core/Images/PamReader.cpp Wed Apr 15 22:03:21 2020 +0200 @@ -42,6 +42,7 @@ # include "../SystemToolbox.h" #endif +#include <stdlib.h> // For malloc/free #include <boost/algorithm/string/find.hpp> #include <boost/lexical_cast.hpp> @@ -200,7 +201,30 @@ } size_t offset = content_.size() - pitch * height; - AssignWritable(format, width, height, pitch, &content_[offset]); + + { + intptr_t bufferAddr = reinterpret_cast<intptr_t>(&content_[offset]); + if((bufferAddr % 8) == 0) + LOG(TRACE) << "PamReader::ParseContent() image address = " << bufferAddr; + else + LOG(TRACE) << "PamReader::ParseContent() image address = " << bufferAddr << " (not a multiple of 8!)"; + } + + // if we want to enforce alignment, we need to use a freshly allocated + // buffer, since we have no alignment guarantees on the original one + if (enforceAligned_) + { + if (alignedImageBuffer_ != NULL) + free(alignedImageBuffer_); + alignedImageBuffer_ = malloc(pitch * height); + memcpy(alignedImageBuffer_, &content_[offset], pitch* height); + content_ = ""; + AssignWritable(format, width, height, pitch, alignedImageBuffer_); + } + else + { + AssignWritable(format, width, height, pitch, &content_[offset]); + } // Byte swapping if needed if (bytesPerChannel != 1 && @@ -231,7 +255,7 @@ at SAFE_HEAP_LOAD_i32_2_2 (wasm-function[251132]:39) at __ZN7Orthanc9PamReader12ParseContentEv (wasm-function[11457]:8088) - Web Assenmbly IS LITTLE ENDIAN! + Web Assembly IS LITTLE ENDIAN! Perhaps in htobe16 ? */ @@ -274,4 +298,12 @@ content_.assign(reinterpret_cast<const char*>(buffer), size); ParseContent(); } + + PamReader::~PamReader() + { + if (alignedImageBuffer_ != NULL) + { + free(alignedImageBuffer_); + } + } }
--- a/Core/Images/PamReader.h Fri Apr 10 16:36:02 2020 +0200 +++ b/Core/Images/PamReader.h Wed Apr 15 22:03:21 2020 +0200 @@ -46,9 +46,41 @@ private: void ParseContent(); + /** + Whether we want to use the default malloc alignment in the image buffer, + at the expense of an extra copy + */ + bool enforceAligned_; + + /** + This is actually a copy of wrappedContent_, but properly aligned. + + It is only used if the enforceAligned parameter is set to true in the + constructor. + */ + void* alignedImageBuffer_; + + /** + Points somewhere in the content_ buffer. + */ + ImageAccessor wrappedContent_; + + /** + Raw content (file bytes or answer from the server, for instance). + */ std::string content_; public: + /** + See doc for field enforceAligned_ + */ + PamReader(bool enforceAligned = false) : + enforceAligned_(enforceAligned), + alignedImageBuffer_(NULL) + { + } + + virtual ~PamReader(); #if ORTHANC_SANDBOXED == 0 void ReadFromFile(const std::string& filename);
--- a/NEWS Fri Apr 10 16:36:02 2020 +0200 +++ b/NEWS Wed Apr 15 22:03:21 2020 +0200 @@ -19,6 +19,7 @@ * Fix lookup form in Orthanc Explorer (wildcards not allowed in StudyDate) * Fix signature of "OrthancPluginRegisterStorageCommitmentScpCallback()" in plugins SDK * Error reporting on failure while initializing SSL +* Fix unit test ParsedDicomFile.ToJsonFlags2 on big-endian architectures * Upgraded dependencies for static builds (notably on Windows): - civetweb 1.12 - openssl 1.1.1f
--- a/OrthancServer/DicomInstanceToStore.h Fri Apr 10 16:36:02 2020 +0200 +++ b/OrthancServer/DicomInstanceToStore.h Wed Apr 15 22:03:21 2020 +0200 @@ -44,7 +44,7 @@ { class ParsedDicomFile; - class DicomInstanceToStore + class DicomInstanceToStore : public boost::noncopyable { public: typedef std::map<std::pair<ResourceType, MetadataType>, std::string> MetadataMap;
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Fri Apr 10 16:36:02 2020 +0200 +++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp Wed Apr 15 22:03:21 2020 +0200 @@ -35,6 +35,8 @@ #include "OrthancRestApi.h" #include "../../Core/Cache/SharedArchive.h" +#include "../../Core/DicomNetworking/DicomAssociation.h" +#include "../../Core/DicomNetworking/DicomControlUserConnection.h" #include "../../Core/DicomParsing/FromDcmtkBridge.h" #include "../../Core/Logging.h" #include "../../Core/SerializationToolbox.h" @@ -80,8 +82,7 @@ try { - DicomUserConnection connection(localAet, remote); - connection.Open(); + DicomControlUserConnection connection(localAet, remote); if (connection.Echo()) { @@ -127,7 +128,7 @@ static void FindPatient(DicomFindAnswers& result, - DicomUserConnection& connection, + DicomControlUserConnection& connection, const DicomMap& fields) { // Only keep the filters from "fields" that are related to the patient @@ -138,7 +139,7 @@ static void FindStudy(DicomFindAnswers& result, - DicomUserConnection& connection, + DicomControlUserConnection& connection, const DicomMap& fields) { // Only keep the filters from "fields" that are related to the study @@ -153,7 +154,7 @@ } static void FindSeries(DicomFindAnswers& result, - DicomUserConnection& connection, + DicomControlUserConnection& connection, const DicomMap& fields) { // Only keep the filters from "fields" that are related to the series @@ -168,7 +169,7 @@ } static void FindInstance(DicomFindAnswers& result, - DicomUserConnection& connection, + DicomControlUserConnection& connection, const DicomMap& fields) { // Only keep the filters from "fields" that are related to the instance @@ -203,8 +204,7 @@ DicomFindAnswers answers(false); { - DicomUserConnection connection(localAet, remote); - connection.Open(); + DicomControlUserConnection connection(localAet, remote); FindPatient(answers, connection, fields); } @@ -238,8 +238,7 @@ DicomFindAnswers answers(false); { - DicomUserConnection connection(localAet, remote); - connection.Open(); + DicomControlUserConnection connection(localAet, remote); FindStudy(answers, connection, fields); } @@ -274,8 +273,7 @@ DicomFindAnswers answers(false); { - DicomUserConnection connection(localAet, remote); - connection.Open(); + DicomControlUserConnection connection(localAet, remote); FindSeries(answers, connection, fields); } @@ -311,8 +309,7 @@ DicomFindAnswers answers(false); { - DicomUserConnection connection(localAet, remote); - connection.Open(); + DicomControlUserConnection connection(localAet, remote); FindInstance(answers, connection, fields); } @@ -350,8 +347,7 @@ RemoteModalityParameters remote = MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); - DicomUserConnection connection(localAet, remote); - connection.Open(); + DicomControlUserConnection connection(localAet, remote); DicomFindAnswers patients(false); FindPatient(patients, connection, m); @@ -804,7 +800,7 @@ DicomMap answer; parent.GetHandler().GetAnswer(answer, index); - // This switch-case mimics "DicomUserConnection::Move()" + // This switch-case mimics "DicomControlUserConnection::Move()" switch (parent.GetHandler().GetLevel()) { case ResourceType_Patient: @@ -1031,8 +1027,7 @@ const RemoteModalityParameters source = MyGetModalityUsingSymbolicName(call.GetUriComponent("id", "")); - DicomUserConnection connection(localAet, source); - connection.Open(); + DicomControlUserConnection connection(localAet, source); for (Json::Value::ArrayIndex i = 0; i < request[KEY_RESOURCES].size(); i++) { @@ -1315,8 +1310,7 @@ DicomFindAnswers answers(true); { - DicomUserConnection connection(localAet, remote); - connection.Open(); + DicomControlUserConnection connection(localAet, remote); connection.FindWorklist(answers, *query); } @@ -1486,11 +1480,11 @@ context.GetStorageCommitmentReports().Store( transactionUid, new StorageCommitmentReports::Report(remoteAet)); - DicomUserConnection scu(localAet, remote); - + DicomAssociationParameters parameters(localAet, remote); + std::vector<std::string> a(sopClassUids.begin(), sopClassUids.end()); std::vector<std::string> b(sopInstanceUids.begin(), sopInstanceUids.end()); - scu.RequestStorageCommitment(transactionUid, a, b); + DicomAssociation::RequestStorageCommitment(parameters, transactionUid, a, b); } Json::Value result = Json::objectValue;
--- a/OrthancServer/ServerContext.cpp Fri Apr 10 16:36:02 2020 +0200 +++ b/OrthancServer/ServerContext.cpp Wed Apr 15 22:03:21 2020 +0200 @@ -691,7 +691,7 @@ { #if ENABLE_DICOM_CACHE == 0 static std::unique_ptr<IDynamicObject> p; - p.reset(provider_.Provide(instancePublicId)); + p.reset(that_.provider_.Provide(instancePublicId)); dicom_ = dynamic_cast<ParsedDicomFile*>(p.get()); #else dicom_ = &dynamic_cast<ParsedDicomFile&>(that_.dicomCache_.Access(instancePublicId));
--- a/Resources/CMake/Compiler.cmake Fri Apr 10 16:36:02 2020 +0200 +++ b/Resources/CMake/Compiler.cmake Wed Apr 15 22:03:21 2020 +0200 @@ -1,6 +1,7 @@ # This file sets all the compiler-related flags -if (CMAKE_CROSSCOMPILING OR +if ((CMAKE_CROSSCOMPILING AND NOT + "${CMAKE_SYSTEM_VERSION}" STREQUAL "CrossToolNg") OR "${CMAKE_SYSTEM_VERSION}" STREQUAL "LinuxStandardBase") # Cross-compilation necessarily implies standalone and static build SET(STATIC_BUILD ON)
--- a/Resources/CMake/DcmtkConfiguration.cmake Fri Apr 10 16:36:02 2020 +0200 +++ b/Resources/CMake/DcmtkConfiguration.cmake Wed Apr 15 22:03:21 2020 +0200 @@ -148,13 +148,34 @@ else() - # The following line allows to manually add libraries at the - # command-line, which is necessary for Ubuntu/Debian packages - set(tmp "${DCMTK_LIBRARIES}") - include(FindDCMTK) - list(APPEND DCMTK_LIBRARIES "${tmp}") + if (CMAKE_CROSSCOMPILING AND + "${CMAKE_SYSTEM_VERSION}" STREQUAL "CrossToolNg") + + CHECK_INCLUDE_FILE_CXX(dcmtk/dcmdata/dcfilefo.h HAVE_DCMTK_H) + if (NOT HAVE_DCMTK_H) + message(FATAL_ERROR "Please install the libdcmtk-dev package") + endif() + + CHECK_LIBRARY_EXISTS(dcmdata "dcmDataDict" "" HAVE_DCMTK_LIB) + if (NOT HAVE_DCMTK_LIB) + message(FATAL_ERROR "Please install the libdcmtk package") + endif() - include_directories(${DCMTK_INCLUDE_DIRS}) + find_path(DCMTK_INCLUDE_DIRS dcmtk/config/osconfig.h + /usr/include + ) + + link_libraries(dcmdata dcmnet dcmjpeg oflog ofstd) + + else() + # The following line allows to manually add libraries at the + # command-line, which is necessary for Ubuntu/Debian packages + set(tmp "${DCMTK_LIBRARIES}") + include(FindDCMTK) + list(APPEND DCMTK_LIBRARIES "${tmp}") + + include_directories(${DCMTK_INCLUDE_DIRS}) + endif() add_definitions( -DHAVE_CONFIG_H=1 @@ -225,6 +246,13 @@ message(FATAL_ERROR "Cannot locate the DICOM dictionary on this system") endif() + if (CMAKE_CROSSCOMPILING AND + "${CMAKE_SYSTEM_VERSION}" STREQUAL "CrossToolNg") + # Remove the sysroot prefix + file(RELATIVE_PATH tmp ${CMAKE_FIND_ROOT_PATH} ${DCMTK_DICTIONARY_DIR_AUTO}) + set(DCMTK_DICTIONARY_DIR_AUTO /${tmp} CACHE INTERNAL "") + endif() + message("Autodetected path to the DICOM dictionaries: ${DCMTK_DICTIONARY_DIR_AUTO}") add_definitions(-DDCMTK_DICTIONARY_DIR="${DCMTK_DICTIONARY_DIR_AUTO}") else()
--- a/Resources/CMake/GoogleTestConfiguration.cmake Fri Apr 10 16:36:02 2020 +0200 +++ b/Resources/CMake/GoogleTestConfiguration.cmake Wed Apr 15 22:03:21 2020 +0200 @@ -2,15 +2,15 @@ find_path(GOOGLE_TEST_DEBIAN_SOURCES_DIR NAMES src/gtest-all.cc PATHS - /usr/src/gtest - /usr/src/googletest/googletest + ${CROSSTOOL_NG_IMAGE}/usr/src/gtest + ${CROSSTOOL_NG_IMAGE}/usr/src/googletest/googletest PATH_SUFFIXES src ) find_path(GOOGLE_TEST_DEBIAN_INCLUDE_DIR NAMES gtest.h PATHS - /usr/include/gtest + ${CROSSTOOL_NG_IMAGE}/usr/include/gtest ) message("Path to the Debian Google Test sources: ${GOOGLE_TEST_DEBIAN_SOURCES_DIR}")
--- a/Resources/CMake/LibCurlConfiguration.cmake Fri Apr 10 16:36:02 2020 +0200 +++ b/Resources/CMake/LibCurlConfiguration.cmake Wed Apr 15 22:03:21 2020 +0200 @@ -262,7 +262,7 @@ check_struct_has_member("struct sockaddr_un" sun_path "sys/un.h" USE_UNIX_SOCKETS) - set(CMAKE_REQUIRED_INCLUDES "${CURL_SOURCES_DIR}/include") + list(APPEND CMAKE_REQUIRED_INCLUDES "${CURL_SOURCES_DIR}/include") set(CMAKE_EXTRA_INCLUDE_FILES "curl/system.h") check_type_size("curl_off_t" SIZEOF_CURL_OFF_T) @@ -312,6 +312,22 @@ ${CURL_SOURCES_DIR}/lib/curl_config.h ) endif() + +elseif (CMAKE_CROSSCOMPILING AND + "${CMAKE_SYSTEM_VERSION}" STREQUAL "CrossToolNg") + + CHECK_INCLUDE_FILE_CXX(curl/curl.h HAVE_CURL_H) + if (NOT HAVE_CURL_H) + message(FATAL_ERROR "Please install the libcurl-dev package") + endif() + + CHECK_LIBRARY_EXISTS(curl "curl_easy_init" "" HAVE_CURL_LIB) + if (NOT HAVE_CURL_LIB) + message(FATAL_ERROR "Please install the libcurl package") + endif() + + link_libraries(curl) + else() include(FindCURL) include_directories(${CURL_INCLUDE_DIRS})
--- a/Resources/CMake/LuaConfiguration.cmake Fri Apr 10 16:36:02 2020 +0200 +++ b/Resources/CMake/LuaConfiguration.cmake Wed Apr 15 22:03:21 2020 +0200 @@ -100,6 +100,32 @@ source_group(ThirdParty\\Lua REGULAR_EXPRESSION ${LUA_SOURCES_DIR}/.*) +elseif (CMAKE_CROSSCOMPILING AND + "${CMAKE_SYSTEM_VERSION}" STREQUAL "CrossToolNg") + + set(LUA_VERSIONS 5.3 5.2 5.1) + + unset(LUA_VERSION) + foreach(version IN ITEMS ${LUA_VERSIONS}) + CHECK_INCLUDE_FILE(lua${version}/lua.h HAVE_LUA${version}_H) + if (HAVE_LUA${version}_H) + set(LUA_VERSION ${version}) + break() + endif() + endforeach() + + if (NOT LUA_VERSION) + message(FATAL_ERROR "Please install the liblua-dev package") + endif() + + CHECK_LIBRARY_EXISTS(lua${LUA_VERSION} "lua_call" "${LUA_LIB_DIR}" HAVE_LUA_LIB) + if (NOT HAVE_LUA_LIB) + message(FATAL_ERROR "Please install the liblua package") + endif() + + include_directories(${CROSSTOOL_NG_IMAGE}/usr/include/lua${LUA_VERSION}) + link_libraries(lua${LUA_VERSION}) + else() include(FindLua)
--- a/Resources/CMake/OpenSslConfiguration.cmake Fri Apr 10 16:36:02 2020 +0200 +++ b/Resources/CMake/OpenSslConfiguration.cmake Wed Apr 15 22:03:21 2020 +0200 @@ -9,6 +9,26 @@ source_group(ThirdParty\\OpenSSL REGULAR_EXPRESSION ${OPENSSL_SOURCES_DIR}/.*) +elseif (CMAKE_CROSSCOMPILING AND + "${CMAKE_SYSTEM_VERSION}" STREQUAL "CrossToolNg") + + CHECK_INCLUDE_FILE_CXX(openssl/opensslv.h HAVE_OPENSSL_H) + if (NOT HAVE_OPENSSL_H) + message(FATAL_ERROR "Please install the libopenssl-dev package") + endif() + + CHECK_LIBRARY_EXISTS(crypto "OPENSSL_init" "" HAVE_OPENSSL_CRYPTO_LIB) + if (NOT HAVE_OPENSSL_CRYPTO_LIB) + message(FATAL_ERROR "Please install the libopenssl package") + endif() + + CHECK_LIBRARY_EXISTS(ssl "SSL_library_init" "" HAVE_OPENSSL_SSL_LIB) + if (NOT HAVE_OPENSSL_SSL_LIB) + message(FATAL_ERROR "Please install the libopenssl package") + endif() + + link_libraries(crypto ssl) + else() include(FindOpenSSL)
--- a/Resources/CMake/SQLiteConfiguration.cmake Fri Apr 10 16:36:02 2020 +0200 +++ b/Resources/CMake/SQLiteConfiguration.cmake Wed Apr 15 22:03:21 2020 +0200 @@ -41,12 +41,14 @@ source_group(ThirdParty\\SQLite REGULAR_EXPRESSION ${SQLITE_SOURCES_DIR}/.*) else() - CHECK_INCLUDE_FILE_CXX(sqlite3.h HAVE_SQLITE_H) + CHECK_INCLUDE_FILE(sqlite3.h HAVE_SQLITE_H) if (NOT HAVE_SQLITE_H) message(FATAL_ERROR "Please install the libsqlite3-dev package") endif() - find_path(SQLITE_INCLUDE_DIR sqlite3.h + find_path(SQLITE_INCLUDE_DIR + NAMES sqlite3.h + PATHS /usr/include /usr/local/include )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CrossToolchain.cmake Wed Apr 15 22:03:21 2020 +0200 @@ -0,0 +1,56 @@ +# +# $ CROSSTOOL_NG_ARCH=mips CROSSTOOL_NG_BOARD=malta CROSSTOOL_NG_IMAGE=/tmp/mips cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../Resources/CrossToolchain.cmake -DBUILD_CONNECTIVITY_CHECKS=OFF -DUSE_SYSTEM_CIVETWEB=OFF -DUSE_GOOGLE_TEST_DEBIAN_PACKAGE=ON -DUSE_SYSTEM_JSONCPP=OFF -DUSE_SYSTEM_UUID=OFF -DENABLE_DCMTK_JPEG_LOSSLESS=OFF -G Ninja && ninja +# + +INCLUDE(CMakeForceCompiler) + +SET(CROSSTOOL_NG_ROOT $ENV{CROSSTOOL_NG_ROOT} CACHE STRING "") +SET(CROSSTOOL_NG_ARCH $ENV{CROSSTOOL_NG_ARCH} CACHE STRING "") +SET(CROSSTOOL_NG_BOARD $ENV{CROSSTOOL_NG_BOARD} CACHE STRING "") +SET(CROSSTOOL_NG_SUFFIX $ENV{CROSSTOOL_NG_SUFFIX} CACHE STRING "") +SET(CROSSTOOL_NG_IMAGE $ENV{CROSSTOOL_NG_IMAGE} CACHE STRING "") + +IF ("${CROSSTOOL_NG_ROOT}" STREQUAL "") + SET(CROSSTOOL_NG_ROOT "/home/$ENV{USER}/x-tools") +ENDIF() + +IF ("${CROSSTOOL_NG_SUFFIX}" STREQUAL "") + SET(CROSSTOOL_NG_SUFFIX "linux-gnu") +ENDIF() + +SET(CROSSTOOL_NG_NAME ${CROSSTOOL_NG_ARCH}-${CROSSTOOL_NG_BOARD}-${CROSSTOOL_NG_SUFFIX}) +SET(CROSSTOOL_NG_BASE ${CROSSTOOL_NG_ROOT}/${CROSSTOOL_NG_NAME}) + +# the name of the target operating system +SET(CMAKE_SYSTEM_NAME Linux) +SET(CMAKE_SYSTEM_VERSION CrossToolNg) +SET(CMAKE_SYSTEM_PROCESSOR ${CROSSTOOL_NG_ARCH}) + +# which compilers to use for C and C++ +SET(CMAKE_C_COMPILER ${CROSSTOOL_NG_BASE}/bin/${CROSSTOOL_NG_NAME}-gcc) + +if (${CMAKE_VERSION} VERSION_LESS "3.6.0") + CMAKE_FORCE_CXX_COMPILER(${CROSSTOOL_NG_BASE}/bin/${CROSSTOOL_NG_NAME}-g++ GNU) +else() + SET(CMAKE_CXX_COMPILER ${CROSSTOOL_NG_BASE}/bin/${CROSSTOOL_NG_NAME}-g++) +endif() + +# here is the target environment located +SET(CMAKE_FIND_ROOT_PATH ${CROSSTOOL_NG_IMAGE}) +#SET(CMAKE_FIND_ROOT_PATH ${CROSSTOOL_NG_BASE}/${CROSSTOOL_NG_NAME}/sysroot) + +# adjust the default behaviour of the FIND_XXX() commands: +# search headers and libraries in the target environment, search +# programs in the host environment +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +SET(CMAKE_CROSSCOMPILING ON) +#SET(CROSS_COMPILER_PREFIX ${CROSSTOOL_NG_ARCH}-${CROSSTOOL_NG_SUFFIX}) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CROSSTOOL_NG_IMAGE}/usr/include -I${CROSSTOOL_NG_IMAGE}/usr/include/${CROSSTOOL_NG_ARCH}-${CROSSTOOL_NG_SUFFIX}" CACHE INTERNAL "" FORCE) +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I${CROSSTOOL_NG_IMAGE}/usr/include -I${CROSSTOOL_NG_IMAGE}/usr/include/${CROSSTOOL_NG_ARCH}-${CROSSTOOL_NG_SUFFIX}" CACHE INTERNAL "" FORCE) +SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--unresolved-symbols=ignore-in-shared-libs -L${CROSSTOOL_NG_BASE}/${CROSSTOOL_NG_NAME}/sysroot/usr/lib -L${CROSSTOOL_NG_IMAGE}/usr/lib -L${CROSSTOOL_NG_IMAGE}/usr/lib/${CROSSTOOL_NG_ARCH}-${CROSSTOOL_NG_SUFFIX} -L${CROSSTOOL_NG_IMAGE}/lib -L${CROSSTOOL_NG_IMAGE}/lib/${CROSSTOOL_NG_ARCH}-${CROSSTOOL_NG_SUFFIX}" CACHE INTERNAL "" FORCE) +SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--unresolved-symbols=ignore-in-shared-libs -L${CROSSTOOL_NG_BASE}/${CROSSTOOL_NG_NAME}/sysroot/usr/lib -L${CROSSTOOL_NG_IMAGE}/usr/lib -L${CROSSTOOL_NG_IMAGE}/usr/lib/${CROSSTOOL_NG_ARCH}-${CROSSTOOL_NG_SUFFIX} -L${CROSSTOOL_NG_IMAGE}/lib -L${CROSSTOOL_NG_IMAGE}/lib/${CROSSTOOL_NG_ARCH}-${CROSSTOOL_NG_SUFFIX}" CACHE INTERNAL "" FORCE)
--- a/Resources/LinuxStandardBaseToolchain.cmake Fri Apr 10 16:36:02 2020 +0200 +++ b/Resources/LinuxStandardBaseToolchain.cmake Wed Apr 15 22:03:21 2020 +0200 @@ -10,10 +10,10 @@ INCLUDE(CMakeForceCompiler) -SET(LSB_PATH $ENV{LSB_PATH}) -SET(LSB_CC $ENV{LSB_CC}) -SET(LSB_CXX $ENV{LSB_CXX}) -SET(LSB_TARGET_VERSION "4.0") +SET(LSB_PATH $ENV{LSB_PATH} CACHE STRING "") +SET(LSB_CC $ENV{LSB_CC} CACHE STRING "") +SET(LSB_CXX $ENV{LSB_CXX} CACHE STRING "") +SET(LSB_TARGET_VERSION "4.0" CACHE STRING "") IF ("${LSB_PATH}" STREQUAL "") SET(LSB_PATH "/opt/lsb")
--- a/UnitTestsSources/FromDcmtkTests.cpp Fri Apr 10 16:36:02 2020 +0200 +++ b/UnitTestsSources/FromDcmtkTests.cpp Wed Apr 15 22:03:21 2020 +0200 @@ -710,7 +710,14 @@ TEST(ParsedDicomFile, ToJsonFlags2) { ParsedDicomFile f(true); - f.Insert(DICOM_TAG_PIXEL_DATA, "Pixels", false, ""); + + { + // "ParsedDicomFile" uses Little Endian => 'B' (least significant + // byte) will be stored first in the memory buffer and in the + // file, then 'A'. Hence the expected "BA" value below. + Uint16 v[] = { 'A' * 256 + 'B', 0 }; + ASSERT_TRUE(f.GetDcmtkObject().getDataset()->putAndInsertUint16Array(DCM_PixelData, v, 2).good()); + } Json::Value v; f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_None, 0); @@ -729,7 +736,7 @@ ASSERT_EQ(6u, v.getMemberNames().size()); ASSERT_TRUE(v.isMember("7fe0,0010")); ASSERT_EQ(Json::stringValue, v["7fe0,0010"].type()); - ASSERT_EQ("Pixels", v["7fe0,0010"].asString()); + ASSERT_EQ("BA", v["7fe0,0010"].asString().substr(0, 2)); f.DatasetToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePixelData, 0); ASSERT_EQ(Json::objectValue, v.type()); @@ -739,7 +746,7 @@ std::string mime, content; ASSERT_TRUE(Toolbox::DecodeDataUriScheme(mime, content, v["7fe0,0010"].asString())); ASSERT_EQ("application/octet-stream", mime); - ASSERT_EQ("Pixels", content); + ASSERT_EQ("BA", content.substr(0, 2)); }
--- a/UnitTestsSources/ImageTests.cpp Fri Apr 10 16:36:02 2020 +0200 +++ b/UnitTestsSources/ImageTests.cpp Wed Apr 15 22:03:21 2020 +0200 @@ -410,6 +410,28 @@ } { + // true means "enforce alignment by using a temporary buffer" + Orthanc::PamReader r(true); + r.ReadFromMemory(s); + + ASSERT_EQ(r.GetFormat(), Orthanc::PixelFormat_Grayscale16); + ASSERT_EQ(r.GetWidth(), width); + ASSERT_EQ(r.GetHeight(), height); + + v = 0; + for (unsigned int y = 0; y < height; y++) + { + const uint16_t* p = reinterpret_cast<const uint16_t*> + ((const uint8_t*)r.GetConstBuffer() + y * r.GetPitch()); + ASSERT_EQ(p, r.GetConstRow(y)); + for (unsigned int x = 0; x < width; x++, p++, v++) + { + ASSERT_EQ(v, *p); + } + } + } + + { Orthanc::TemporaryFile tmp; tmp.Write(s); @@ -432,4 +454,30 @@ } } } + + { + Orthanc::TemporaryFile tmp; + tmp.Write(s); + + // true means "enforce alignment by using a temporary buffer" + Orthanc::PamReader r2(true); + r2.ReadFromFile(tmp.GetPath()); + + ASSERT_EQ(r2.GetFormat(), Orthanc::PixelFormat_Grayscale16); + ASSERT_EQ(r2.GetWidth(), width); + ASSERT_EQ(r2.GetHeight(), height); + + v = 0; + for (unsigned int y = 0; y < height; y++) + { + const uint16_t* p = reinterpret_cast<const uint16_t*> + ((const uint8_t*)r2.GetConstBuffer() + y * r2.GetPitch()); + ASSERT_EQ(p, r2.GetConstRow(y)); + for (unsigned int x = 0; x < width; x++, p++, v++) + { + ASSERT_EQ(*p, v); + } + } + } + }