# HG changeset patch # User Sebastien Jodogne # Date 1614197194 -3600 # Node ID 5774fe497ff29023467e1cf55f410f38effdd56a # Parent 93a51d228d802491904a3266aaa1c88f14900a90 fix decoding of images on big-endian architectures diff -r 93a51d228d80 -r 5774fe497ff2 OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp --- a/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp Wed Feb 24 15:07:54 2021 +0100 +++ b/OrthancFramework/Sources/DicomParsing/Internals/DicomImageDecoder.cpp Wed Feb 24 21:06:34 2021 +0100 @@ -91,6 +91,7 @@ #include #include #include +#include #if ORTHANC_ENABLE_DCMTK_JPEG_LOSSLESS == 1 # include @@ -122,6 +123,7 @@ namespace Orthanc { + static const Endianness ENDIANNESS = Toolbox::DetectEndianness(); static const DicomTag DICOM_TAG_CONTENT(0x07a1, 0x100a); static const DicomTag DICOM_TAG_COMPRESSION_TYPE(0x07a1, 0x1011); @@ -556,7 +558,25 @@ info.GetWidth() * GetBytesPerPixel(sourceFormat), buffer + frame * frameSize); - ImageProcessing::Convert(*target, sourceImage); + switch (ENDIANNESS) + { + case Endianness_Little: + ImageProcessing::Convert(*target, sourceImage); + break; + + case Endianness_Big: + { + // We cannot do byte swapping directly on the constant DcmDataset + std::unique_ptr copy(Image::Clone(sourceImage)); + ImageProcessing::SwapEndianness(*copy); + ImageProcessing::Convert(*target, *copy); + break; + } + + default: + throw OrthancException(ErrorCode_InternalError); + } + ImageProcessing::ShiftRight(*target, info.GetShift()); fastVersionSuccess = true; } @@ -660,6 +680,29 @@ } + static void UndoBigEndianSwapping(ImageAccessor& decoded) + { + if (ENDIANNESS == Endianness_Big && + decoded.GetFormat() == PixelFormat_Grayscale8) + { + /** + * Undo the call to "swapIfNecessary()" that is done in + * "dcmjpeg/libsrc/djcodecd.cc" and "dcmjpls/libsrc/djcodecd.cc" + * if "jpeg->bytesPerSample() == 1", presumably because DCMTK + * plans for DICOM-to-DICOM conversion + **/ + if (decoded.GetPitch() % 2 == 0) + { + swapBytes(decoded.GetBuffer(), decoded.GetPitch() * decoded.GetHeight(), sizeof(uint16_t)); + } + else + { + throw OrthancException(ErrorCode_InternalError, "Cannot swap the bytes of an image that has an odd width"); + } + } + } + + ImageAccessor* DicomImageDecoder::Decode(DcmDataset& dataset, unsigned int frame) { @@ -710,7 +753,9 @@ throw OrthancException(ErrorCode_InternalError); } - return ApplyCodec(*decoder, parameters, representationParameter, dataset, frame); + std::unique_ptr result(ApplyCodec(*decoder, parameters, representationParameter, dataset, frame)); + UndoBigEndianSwapping(*result); // New in Orthanc 1.9.1 to decode on big-endian architectures + return result.release(); } #endif @@ -771,8 +816,10 @@ default: throw OrthancException(ErrorCode_InternalError); } - - return ApplyCodec(*decoder, parameters, representationParameter, dataset, frame); + + std::unique_ptr result(ApplyCodec(*decoder, parameters, representationParameter, dataset, frame)); + UndoBigEndianSwapping(*result); // New in Orthanc 1.9.1 to decode on big-endian architectures + return result.release(); } #endif diff -r 93a51d228d80 -r 5774fe497ff2 OrthancFramework/Sources/Images/ImageProcessing.cpp --- a/OrthancFramework/Sources/Images/ImageProcessing.cpp Wed Feb 24 15:07:54 2021 +0100 +++ b/OrthancFramework/Sources/Images/ImageProcessing.cpp Wed Feb 24 21:06:34 2021 +0100 @@ -2636,4 +2636,71 @@ } } } + + + void ImageProcessing::SwapEndianness(ImageAccessor& image /* inplace */) + { + const unsigned int width = image.GetWidth(); + const unsigned int height = image.GetHeight(); + + switch (image.GetFormat()) + { + case PixelFormat_Grayscale8: + case PixelFormat_RGB24: + case PixelFormat_RGBA32: + case PixelFormat_BGRA32: + // No swapping required + break; + + case PixelFormat_Grayscale16: + case PixelFormat_SignedGrayscale16: + for (unsigned int y = 0; y < height; y++) + { + uint8_t* t = reinterpret_cast(image.GetRow(y)); + for (unsigned int x = 0; x < width; x++) + { + uint8_t a = t[0]; + t[0] = t[1]; + t[1] = a; + t += 2; + } + } + break; + + case PixelFormat_Grayscale32: + case PixelFormat_Float32: + for (unsigned int y = 0; y < height; y++) + { + uint8_t* t = reinterpret_cast(image.GetRow(y)); + for (unsigned int x = 0; x < width; x++) + { + uint8_t a = t[0]; + uint8_t b = t[1]; + t[0] = t[3]; + t[1] = t[2]; + t[2] = b; + t[3] = a; + t += 4; + } + } + break; + + case PixelFormat_RGB48: // uint16_t per channel + for (unsigned int y = 0; y < height; y++) + { + uint8_t* t = reinterpret_cast(image.GetRow(y)); + for (unsigned int x = 0; x < 3 * width; x++) + { + uint8_t a = t[0]; + t[0] = t[1]; + t[1] = a; + t += 2; + } + } + break; + + default: + throw OrthancException(ErrorCode_NotImplemented); + } + } } diff -r 93a51d228d80 -r 5774fe497ff2 OrthancFramework/Sources/Images/ImageProcessing.h --- a/OrthancFramework/Sources/Images/ImageProcessing.h Wed Feb 24 15:07:54 2021 +0100 +++ b/OrthancFramework/Sources/Images/ImageProcessing.h Wed Feb 24 21:06:34 2021 +0100 @@ -200,5 +200,7 @@ // https://en.wikipedia.org/wiki/YCbCr#JPEG_conversion static void ConvertJpegYCbCrToRgb(ImageAccessor& image /* inplace */); + + static void SwapEndianness(ImageAccessor& image /* inplace */); }; }