/* * * Copyright (C) 1997-2020, OFFIS e.V. * All rights reserved. See COPYRIGHT file for details. * * This software and supporting documentation were developed by * * OFFIS e.V. * R&D Division Health * Escherweg 2 * D-26121 Oldenburg, Germany * * * Module: dcmdata * * Author: Andreas Barth * * Purpose: * class DcmPolymorphOBOW for Tags that can change their VR * between OB and OW (e.g. Tag PixelData, OverlayData). This class shall * not be used directly in applications. No identification exists. * */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/dcmdata/dcvrpobw.h" DcmPolymorphOBOW::DcmPolymorphOBOW( const DcmTag & tag, const Uint32 len) : DcmOtherByteOtherWord(tag, len), changeVR(OFFalse), currentVR(EVR_OW) { if (getTag().getEVR() == EVR_ox || getTag().getEVR() == EVR_px || getTag().getEVR() == EVR_lt) setTagVR(EVR_OW); } DcmPolymorphOBOW::DcmPolymorphOBOW(const DcmPolymorphOBOW & oldObj) : DcmOtherByteOtherWord(oldObj) , changeVR(oldObj.changeVR) , currentVR(oldObj.currentVR) { } DcmPolymorphOBOW::~DcmPolymorphOBOW() { } DcmPolymorphOBOW &DcmPolymorphOBOW::operator=(const DcmPolymorphOBOW & obj) { if (this != &obj) { DcmOtherByteOtherWord::operator=(obj); changeVR = obj.changeVR; currentVR = obj.currentVR; } return *this; } int DcmPolymorphOBOW::compare(const DcmElement& rhs) const { /* check tag and VR */ int result = DcmElement::compare(rhs); if (result != 0) { return result; } /* cast away constness (dcmdata is not const correct...) */ DcmPolymorphOBOW* myThis = NULL; DcmPolymorphOBOW* myRhs = NULL; myThis = OFconst_cast(DcmPolymorphOBOW*, this); myRhs = OFstatic_cast(DcmPolymorphOBOW*, OFconst_cast(DcmElement*, &rhs)); /* compare length */ Uint32 myLength = myThis->getLength(); Uint32 rhsLength = myRhs->getLength(); if (myLength < rhsLength) return -1; else if (myLength > rhsLength) return 1; /* finally check whether values are the same */ else { // Get values, always compare in Little Endian byte order (only relevant for OW) void* myValue = myThis->getValue(EBO_LittleEndian); void* rhsValue = myRhs->getValue(EBO_LittleEndian); result = memcmp(myValue, rhsValue, myLength); if (result < 0) return -1; else if (result > 0) return 1; else return 0; } /* we never get here */ } OFCondition DcmPolymorphOBOW::copyFrom(const DcmObject& rhs) { if (this != &rhs) { if (rhs.ident() != ident()) return EC_IllegalCall; *this = OFstatic_cast(const DcmPolymorphOBOW &, rhs); } return EC_Normal; } OFCondition DcmPolymorphOBOW::getUint8Array( Uint8 * & bytes) { errorFlag = EC_Normal; OFBool bchangeVR = OFFalse; if (currentVR == EVR_OW) { if (getByteOrder() == EBO_BigEndian) { swapValueField(sizeof(Uint16)); setByteOrder(EBO_LittleEndian); } if (getTag().getEVR() == EVR_OW) { bchangeVR = OFTrue; setTagVR(EVR_OB); currentVR = EVR_OB; } } bytes = OFstatic_cast(Uint8 *, this -> getValue()); if (bchangeVR) setTagVR(EVR_OW); return errorFlag; } OFCondition DcmPolymorphOBOW::getUint16Array( Uint16 * & words) { errorFlag = EC_Normal; OFBool bchangeVR = OFFalse; if (currentVR == EVR_OB) { setByteOrder(EBO_LittleEndian); currentVR = EVR_OW; if (getTag().getEVR() == EVR_OB) { setTagVR(EVR_OW); bchangeVR = OFTrue; } } words = OFstatic_cast(Uint16 *, this -> getValue()); if (bchangeVR) setTagVR(EVR_OB); return errorFlag; } OFCondition DcmPolymorphOBOW::createUint8Array( const Uint32 numBytes, Uint8 * & bytes) { currentVR = EVR_OB; setTagVR(EVR_OB); errorFlag = createEmptyValue(OFstatic_cast(Uint32, sizeof(Uint8) * OFstatic_cast(size_t, numBytes))); setByteOrder(gLocalByteOrder); if (EC_Normal == errorFlag) bytes = OFstatic_cast(Uint8 *, this->getValue()); else bytes = NULL; return errorFlag; } OFCondition DcmPolymorphOBOW::createUint16Array( const Uint32 numWords, Uint16 * & words) { // Check whether input would lead to a buffer allocation of more than // 4 GB for a value, which is not possible in DICOM. The biggest input // parameter value permitted is 2147483647, since 2147483647*2 is still // < 2^32-1 (4 GB). if (numWords > 2147483647) { errorFlag = EC_TooManyBytesRequested; return errorFlag; } currentVR = EVR_OW; setTagVR(EVR_OW); errorFlag = createEmptyValue(OFstatic_cast(Uint32, sizeof(Uint16) * OFstatic_cast(size_t, numWords))); setByteOrder(gLocalByteOrder); if (EC_Normal == errorFlag) words = OFstatic_cast(Uint16 *, this->getValue()); else words = NULL; return errorFlag; } OFCondition DcmPolymorphOBOW::putUint8Array( const Uint8 * byteValue, const unsigned long numBytes) { errorFlag = EC_Normal; currentVR = getTag().getEVR(); if (numBytes) { if (byteValue) { // Check if more than 4 GB is requested, which is the maximum // length DICOM can handle. Take into account that the alignValue() // call adds a byte if an odd length is provided, thus, 4294967295 // would not work. if (numBytes > 4294967294UL) { errorFlag = EC_TooManyBytesRequested; return errorFlag; } errorFlag = putValue(byteValue, OFstatic_cast(Uint32, sizeof(Uint8) * OFstatic_cast(size_t, numBytes))); if (errorFlag == EC_Normal) { if (getTag().getEVR() == EVR_OW && getByteOrder() == EBO_BigEndian) setByteOrder(EBO_LittleEndian); this -> alignValue(); } } else errorFlag = EC_CorruptedData; } else this -> putValue(NULL, 0); return errorFlag; } OFCondition DcmPolymorphOBOW::putUint16Array( const Uint16 * wordValue, const unsigned long numWords) { errorFlag = EC_Normal; currentVR = getTag().getEVR(); if (numWords) { if (wordValue) { // Check whether input would lead to a buffer allocation of more than // 4 GB for a value, which is not possible in DICOM. The biggest input // parameter value permitted is 2147483647, since 2147483647*2 is still // < 2^32-1 (4 GB). if (numWords > 2147483647) { errorFlag = EC_TooManyBytesRequested; return EC_TooManyBytesRequested; } errorFlag = putValue(wordValue, OFstatic_cast(Uint32, sizeof(Uint16) * OFstatic_cast(size_t, numWords))); if (errorFlag == EC_Normal && getTag().getEVR() == EVR_OB && getByteOrder() == EBO_BigEndian) { swapValueField(sizeof(Uint16)); setByteOrder(EBO_LittleEndian); } } else errorFlag = EC_CorruptedData; } else errorFlag = this -> putValue(NULL, 0); return errorFlag; } OFCondition DcmPolymorphOBOW::read( DcmInputStream & inStream, const E_TransferSyntax ixfer, const E_GrpLenEncoding glenc, const Uint32 maxReadLength) { OFCondition l_error = DcmOtherByteOtherWord::read(inStream, ixfer, glenc, maxReadLength); if (getTransferState() == ERW_ready) currentVR = getTag().getEVR(); return l_error; } void DcmPolymorphOBOW::transferEnd() { changeVR = OFFalse; DcmOtherByteOtherWord::transferEnd(); } void DcmPolymorphOBOW::transferInit() { changeVR = OFFalse; DcmOtherByteOtherWord::transferInit(); } OFCondition DcmPolymorphOBOW::write( DcmOutputStream &outStream, const E_TransferSyntax oxfer, const E_EncodingType enctype, DcmWriteCache *wcache) { DcmXfer oXferSyn(oxfer); if (getTransferState() == ERW_init) { if (getTag().getEVR() == EVR_OB && oXferSyn.isImplicitVR()) { // This element was read or created as OB, but we are writing in // implicit VR transfer syntax (which always uses OW). Therefore, // change the VR associated with the tag to OW. setTagVR(EVR_OW); // If the data is currently in OB representation in memory, // adjust the VR to OW and update the current byte order. // OB data is equivalent to OW data in little endian byte order. if (currentVR == EVR_OB) { setByteOrder(EBO_LittleEndian); currentVR = EVR_OW; } // remember that we have changed the VR associated with the tag changeVR = OFTrue; } else if (getTag().getEVR() == EVR_OW && currentVR == EVR_OB) { // the element was originally read/created as OW // but is currently in OB format. Change back to OW. // OB data is equivalent to OW data in little endian byte order. setByteOrder(EBO_LittleEndian); currentVR = EVR_OW; } } errorFlag = DcmOtherByteOtherWord::write(outStream, oxfer, enctype, wcache); if (getTransferState() == ERW_ready && changeVR) { // Change the VR associated with the tag // (not the current VR!) back from OW to OB setTagVR(EVR_OB); } return errorFlag; } OFCondition DcmPolymorphOBOW::writeSignatureFormat( DcmOutputStream &outStream, const E_TransferSyntax oxfer, const E_EncodingType enctype, DcmWriteCache *wcache) { DcmXfer oXferSyn(oxfer); if (getTransferState() == ERW_init) { if (getTag().getEVR() == EVR_OB && oXferSyn.isImplicitVR()) { // This element was read or created as OB, but we are writing in // implicit VR transfer syntax (which always uses OW). Therefore, // change the VR associated with the tag to OW. setTagVR(EVR_OW); // If the data is currently in OB representation in memory, // adjust the VR to OW and update the current byte order. // OB data is equivalent to OW data in little endian byte order. if (currentVR == EVR_OB) { setByteOrder(EBO_LittleEndian); currentVR = EVR_OW; } // remember that we have changed the VR associated with the tag changeVR = OFTrue; } else if (getTag().getEVR() == EVR_OW && currentVR == EVR_OB) { // the element was originally read/created as OW // but is currently in OB format. Change back to OW. // OB data is equivalent to OW data in little endian byte order. setByteOrder(EBO_LittleEndian); currentVR = EVR_OW; } } errorFlag = DcmOtherByteOtherWord::writeSignatureFormat(outStream, oxfer, enctype, wcache); if (getTransferState() == ERW_ready && changeVR) { // Change the VR associated with the tag // (not the current VR!) back from OW to OB setTagVR(EVR_OB); } return errorFlag; }