/* * * 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 DcmPixelData * */ #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */ #include "dcmtk/dcmdata/dcpixel.h" #include "dcmtk/dcmdata/dccodec.h" #include "dcmtk/dcmdata/dcpixseq.h" #include "dcmtk/dcmdata/dcdeftag.h" #include "dcmtk/dcmdata/dcitem.h" #include "dcmtk/dcmdata/dcpxitem.h" #include "dcmtk/dcmdata/dcjson.h" // // class DcmRepresentationEntry // DcmRepresentationEntry::DcmRepresentationEntry( const E_TransferSyntax rt, const DcmRepresentationParameter *rp, DcmPixelSequence * ps) : repType(rt), repParam(NULL), pixSeq(ps) { if (rp) repParam = rp->clone(); } DcmRepresentationEntry::DcmRepresentationEntry( const DcmRepresentationEntry & oldEntry) : repType(oldEntry.repType), repParam(NULL), pixSeq(NULL) { if (oldEntry.repParam) repParam = oldEntry.repParam->clone(); pixSeq = new DcmPixelSequence(*(oldEntry.pixSeq)); } DcmRepresentationEntry::~DcmRepresentationEntry() { delete repParam; delete pixSeq; } OFBool DcmRepresentationEntry::operator==(const DcmRepresentationEntry & x) const { return (repType == x.repType) && ((x.repParam == NULL && repParam == NULL) || ((x.repParam != NULL) && (repParam != NULL) && (*(x.repParam) == *repParam))); } // // class DcmPixelData // // Constructors / Deconstructors DcmPixelData::DcmPixelData( const DcmTag & tag, const Uint32 len) : DcmPolymorphOBOW(tag, len), repList(), repListEnd(), original(), current(), existUnencapsulated(OFFalse), alwaysUnencapsulated(OFFalse), unencapsulatedVR(EVR_UNKNOWN), pixelSeqForWrite(NULL) { repListEnd = repList.end(); current = original = repListEnd; if ((getTag().getEVR() == EVR_ox) || (getTag().getEVR() == EVR_px)) setTagVR(EVR_OW); unencapsulatedVR = getTag().getEVR(); recalcVR(); } DcmPixelData::DcmPixelData( const DcmPixelData & oldPixelData) : DcmPolymorphOBOW(oldPixelData), repList(), repListEnd(), original(), current(), existUnencapsulated(oldPixelData.existUnencapsulated), alwaysUnencapsulated(oldPixelData.alwaysUnencapsulated), unencapsulatedVR(oldPixelData.unencapsulatedVR), pixelSeqForWrite(NULL) { repListEnd = repList.end(); original = repListEnd; current = original; recalcVR(); DcmRepresentationListConstIterator oldEnd(oldPixelData.repList.end()); for (DcmRepresentationListConstIterator it(oldPixelData.repList.begin()); it != oldEnd; ++it) { DcmRepresentationEntry * repEnt = new DcmRepresentationEntry(**it); repList.push_back(repEnt); if (it == oldPixelData.original) original = --repList.end(); if (it == oldPixelData.current) { current = --repList.end(); recalcVR(); } } } DcmPixelData::~DcmPixelData() { for (DcmRepresentationListIterator it(repList.begin()); it != repListEnd; ++it) { delete *it; *it = NULL; } } DcmPixelData &DcmPixelData::operator=(const DcmPixelData &obj) { if (this != &obj) { DcmPolymorphOBOW::operator=(obj); existUnencapsulated = obj.existUnencapsulated; alwaysUnencapsulated = obj.alwaysUnencapsulated; unencapsulatedVR = obj.unencapsulatedVR; pixelSeqForWrite = NULL; repList.clear(); repListEnd = repList.end(); original = repListEnd; current = original; recalcVR(); DcmRepresentationListConstIterator oldEnd(obj.repList.end()); DcmRepresentationListConstIterator it(obj.repList.begin()); while (it != oldEnd) { DcmRepresentationEntry *repEnt = new DcmRepresentationEntry(**it); repList.push_back(repEnt); if (it == obj.original) original = --repList.end(); if (it == current) { current = --repList.end(); recalcVR(); } ++it; } } return *this; } // methods in alphabetical order Uint32 DcmPixelData::calcElementLength( const E_TransferSyntax xfer, const E_EncodingType enctype) { DcmXfer xferSyn(xfer); errorFlag = EC_Normal; Uint32 elementLength = 0; if (xferSyn.isEncapsulated() && (! writeUnencapsulated(xfer))) { DcmRepresentationListIterator found; errorFlag = findConformingEncapsulatedRepresentation(xfer, NULL, found); if (errorFlag == EC_Normal) elementLength = (*found)->pixSeq->calcElementLength(xfer, enctype); } else if (existUnencapsulated) elementLength = DcmPolymorphOBOW::calcElementLength(xfer, enctype); else errorFlag = EC_RepresentationNotFound; return elementLength; } OFBool DcmPixelData::canChooseRepresentation( const E_TransferSyntax repType, const DcmRepresentationParameter * repParam) { OFBool result = OFFalse; DcmXfer toType(repType); const DcmRepresentationEntry findEntry(repType, repParam, NULL); DcmRepresentationListIterator resultIt(repListEnd); // find out whether we have the desired target representation available. Three possibilities: // 1. we have uncompressed data, and target is uncompressed (conversion between uncompressed always possible) // 2. we have uncompressed and want compressed, but we are forced to write uncompressed anyway // 3. we want to go to compressed, and already have the desired representation available if ((!toType.isEncapsulated() && existUnencapsulated) || (toType.isEncapsulated() && writeUnencapsulated(repType) && existUnencapsulated) || (toType.isEncapsulated() && findRepresentationEntry(findEntry, resultIt) == EC_Normal)) { // representation found result = OFTrue; } // otherwise let's see whether we know how to convert to the target representation else { // representation not found, check if we have a codec that can create the // desired representation. if (original == repListEnd) { // we have uncompressed data, check whether we know how to go from uncompressed to desired compression result = DcmCodecList::canChangeCoding(EXS_LittleEndianExplicit, toType.getXfer()); } else if (toType.isEncapsulated()) { // we have already compressed data, check whether we know how to transcode result = DcmCodecList::canChangeCoding((*original)->repType, toType.getXfer()); if (!result) { // direct transcoding is not possible. Check if we can decode and then encode. result = canChooseRepresentation(EXS_LittleEndianExplicit, NULL); if (result) result = DcmCodecList::canChangeCoding(EXS_LittleEndianExplicit, toType.getXfer()); } } else { // target transfer syntax is uncompressed, look whether decompression is possible result = DcmCodecList::canChangeCoding((*original)->repType, EXS_LittleEndianExplicit); } } return result; } OFBool DcmPixelData::canWriteXfer( const E_TransferSyntax newXfer, const E_TransferSyntax /*oldXfer*/) { DcmXfer newXferSyn(newXfer); DcmRepresentationListIterator found; OFBool result = existUnencapsulated && (!newXferSyn.isEncapsulated() || writeUnencapsulated(newXfer)); if (!result && newXferSyn.isEncapsulated()) result = (findConformingEncapsulatedRepresentation(newXferSyn, NULL, found) == EC_Normal); return result; } OFCondition DcmPixelData::chooseRepresentation( const E_TransferSyntax repType, const DcmRepresentationParameter * repParam, DcmStack & pixelStack) { OFCondition l_error = EC_CannotChangeRepresentation; DcmXfer toType(repType); const DcmRepresentationEntry findEntry(repType, repParam, NULL); DcmRepresentationListIterator result(repListEnd); if ((!toType.isEncapsulated() && existUnencapsulated) || (toType.isEncapsulated() && findRepresentationEntry(findEntry, result) == EC_Normal)) { // representation found current = result; recalcVR(); l_error = EC_Normal; } else { if (original == repListEnd) l_error = encode(EXS_LittleEndianExplicit, NULL, NULL, toType, repParam, pixelStack); else if (toType.isEncapsulated()) l_error = encode((*original)->repType, (*original)->repParam, (*original)->pixSeq, toType, repParam, pixelStack); else l_error = decode((*original)->repType, (*original)->repParam, (*original)->pixSeq, pixelStack); } if (l_error.bad() && toType.isEncapsulated() && existUnencapsulated && writeUnencapsulated(repType)) // Encoding failed so this will be written out unencapsulated l_error = EC_Normal; return l_error; } int DcmPixelData::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...) DcmPixelData* myThis = NULL; DcmPixelData* myRhs = NULL; myThis = OFconst_cast(DcmPixelData*, this); myRhs = OFstatic_cast(DcmPixelData*, OFconst_cast(DcmElement*, &rhs)); if (myThis->existUnencapsulated && myRhs->existUnencapsulated) { // we have uncompressed representations, which can be compared using DcmPolymorphOBOW::compare return DcmPolymorphOBOW::compare(rhs); } // both do not have uncompressed data, we must compare compressed ones. // check both have a current representation at all. if ((myThis->current == myThis->repList.end()) && (myRhs->current != myRhs->repList.end())) return -1; if ((myThis->current != myThis->repList.end()) && (myRhs->current == myRhs->repList.end())) return 1; if ((myThis->current == myThis->repList.end()) && (myRhs->current == myRhs->repList.end())) { // if one of both have uncompressed data at least, that one is considered "bigger" if (myThis->existUnencapsulated) return 1; if (myRhs->existUnencapsulated) return -1; else return 0; } // both have compressed data: compare current representation (only) if ((myThis->current != myThis->repList.end()) && (myRhs->current != myRhs->repList.end()) ) { E_TransferSyntax myRep = (*(myThis->current))->repType; E_TransferSyntax rhsRep = (*(myRhs->current))->repType; DcmXfer myXfer(myRep); DcmXfer rhsXfer(rhsRep); // if both transfer syntaxes are different, we have to perform more checks to // find out whether the related pixel data is comparable; this is the case // for all uncompressed transfer syntaxes, except Big Endian with OW data // since it uses a different memory layout, and we do not want to byte-swap // the values for the comparison. if (myRep != rhsRep) { return 1; } else { // For compressed, compare pixel items bytewise DcmPixelSequence* myPix = (*(myThis->current))->pixSeq; DcmPixelSequence* rhsPix = (*(myRhs->current))->pixSeq; if (!myPix && rhsPix) return -1; if (myPix && !rhsPix) return 1; if (!myPix && !rhsPix) return 0; // Check number of pixel items long unsigned int myNumPix = myPix->card(); long unsigned int rhsNumPix = rhsPix->card(); if (myNumPix < rhsNumPix) return -1; if (myNumPix > rhsNumPix) return 1; // loop over pixel items, both have the same number of pixel items for (unsigned long n = 0; n < myNumPix; n++) { DcmPixelItem* myPixItem = NULL; DcmPixelItem* rhsPixItem = NULL; if (myPix->getItem(myPixItem, n).good() && rhsPix->getItem(rhsPixItem, n).good()) { // compare them value by value, using DcmOtherByteOtherWord::compare() method result = myPixItem->compare(*rhsPixItem); if (result != 0) { return result; } } else { DCMDATA_ERROR("Internal error: Could not get pixel item #" << n << " from Pixel Sequence"); return 1; } } return 0; } } // if one of both have a current representation; consider that one "bigger". // if none has a current one, consider both equal (neither uncompressed or compressed data present). else { if (myThis->current != myThis->repList.end()) return 1; if (myRhs->current != myRhs->repList.end()) return -1; else return 0; } } OFCondition DcmPixelData::copyFrom(const DcmObject& rhs) { if (this != &rhs) { if (rhs.ident() != ident()) return EC_IllegalCall; *this = OFstatic_cast(const DcmPixelData &, rhs); } return EC_Normal; } void DcmPixelData::clearRepresentationList( DcmRepresentationListIterator leaveInList) { /* define iterators to go through all representations in the list */ DcmRepresentationListIterator it(repList.begin()); DcmRepresentationListIterator del; /* as long as we have not encountered the end of the */ /* representation list, go through all representations */ while (it != repListEnd) { /* if this representation shall not be left in the list */ if (it != leaveInList) { /* delete representation and move it to the next representation */ delete *it; del = it++; repList.erase(del); } /* else leave this representation in the list and just go to the next */ else ++it; } } OFCondition DcmPixelData::decode( const DcmXfer & fromType, const DcmRepresentationParameter * fromParam, DcmPixelSequence * fromPixSeq, DcmStack & pixelStack) { if (existUnencapsulated) return EC_Normal; OFBool removeOldPixelRepresentation = OFFalse; OFCondition l_error = DcmCodecList::decode(fromType, fromParam, fromPixSeq, *this, pixelStack, removeOldPixelRepresentation); if (l_error.good()) { existUnencapsulated = OFTrue; current = repListEnd; setVR(EVR_OW); recalcVR(); // the codec has indicated that the image pixel module has been modified // in a way that may affect the validity of the old representation of pixel data. // Thus, we cannot just switch back to the old representation. // Thus, remove old representation(s). if (removeOldPixelRepresentation) removeAllButCurrentRepresentations(); } else { DcmPolymorphOBOW::putUint16Array(NULL,0); existUnencapsulated = OFFalse; } return l_error; } OFCondition DcmPixelData::encode( const DcmXfer & fromType, const DcmRepresentationParameter * fromParam, DcmPixelSequence * fromPixSeq, const DcmXfer & toType, const DcmRepresentationParameter *toParam, DcmStack & pixelStack) { OFCondition l_error = EC_CannotChangeRepresentation; if (toType.isEncapsulated()) { DcmPixelSequence * toPixSeq = NULL; OFBool removeOldPixelRepresentation = OFFalse; if (fromType.isEncapsulated()) { l_error = DcmCodecList::encode(fromType.getXfer(), fromParam, fromPixSeq, toType.getXfer(), toParam, toPixSeq, pixelStack, removeOldPixelRepresentation); } else { Uint16 * pixelData; l_error = DcmPolymorphOBOW::getUint16Array(pixelData); Uint32 length = DcmPolymorphOBOW::getLength(); if (l_error == EC_Normal) { l_error = DcmCodecList::encode(fromType.getXfer(), pixelData, length, toType.getXfer(), toParam, toPixSeq, pixelStack, removeOldPixelRepresentation); } } if (l_error.good()) { current = insertRepresentationEntry( new DcmRepresentationEntry(toType.getXfer(), toParam, toPixSeq)); recalcVR(); // the codec has indicated that the image pixel module has been modified // in a way that may affect the validity of the old representation of pixel data. // Thus, we cannot just switch back to the old representation, but have // to actually decode in this case. Thus, remove old representation(s). if (removeOldPixelRepresentation) removeAllButCurrentRepresentations(); } else delete toPixSeq; // if it was possible to convert one encapsulated syntax into // another directly try it using decoding and encoding! if (l_error.bad() && fromType.isEncapsulated()) { l_error = decode(fromType, fromParam, fromPixSeq, pixelStack); if (l_error.good()) l_error = encode(EXS_LittleEndianExplicit, NULL, NULL, toType, toParam, pixelStack); } } return l_error; } OFCondition DcmPixelData::findRepresentationEntry( const DcmRepresentationEntry & findEntry, DcmRepresentationListIterator & result) { result = repList.begin(); while(result != repListEnd && (*result)->repType < findEntry.repType) ++result; DcmRepresentationListIterator it(result); while(it != repListEnd && **it != findEntry) ++it; if (it == repListEnd || **it != findEntry) return EC_RepresentationNotFound; else { result = it; return EC_Normal; } } OFCondition DcmPixelData::findConformingEncapsulatedRepresentation( const DcmXfer & repTypeSyn, const DcmRepresentationParameter * repParam, DcmRepresentationListIterator & result) { E_TransferSyntax repType = repTypeSyn.getXfer(); result = repListEnd; OFCondition l_error = EC_RepresentationNotFound; // we are looking for an encapsulated representation // of this pixel data element which meets both // the transfer syntax and (if given) the representation // parameter (i.e. quality factor for lossy JPEG). if (repTypeSyn.isEncapsulated()) { // first we check the current (active) representation if any. if ((current != repListEnd) && ((*current)->repType == repType) && ((repParam==NULL) || (((*current)->repParam != NULL)&&(*(*current)->repParam == *repParam)))) { result = current; l_error = EC_Normal; } else { // now we check all representations DcmRepresentationListIterator it(repList.begin()); OFBool found = OFFalse; while (!found && (it != repListEnd)) { if ((*it)->repType == repType) { if ((repParam == NULL) || (((*it)->repParam != NULL)&&(*(*it)->repParam == *repParam))) { // repParam is NULL or matches the one we are comparing with found = OFTrue; result = it; l_error = EC_Normal; } else ++it; } else ++it; } } } return l_error; } OFCondition DcmPixelData::getEncapsulatedRepresentation( const E_TransferSyntax repType, const DcmRepresentationParameter * repParam, DcmPixelSequence * & pixSeq) { DcmRepresentationListIterator found; DcmRepresentationEntry findEntry(repType, repParam, NULL); if (findRepresentationEntry(findEntry, found) == EC_Normal) { pixSeq = (*found)->pixSeq; return EC_Normal; } return EC_RepresentationNotFound; } OFBool DcmPixelData::hasRepresentation( const E_TransferSyntax repType, const DcmRepresentationParameter * repParam) { DcmXfer repTypeSyn(repType); DcmRepresentationListIterator found; if (!repTypeSyn.isEncapsulated() && existUnencapsulated) return OFTrue; else if (repTypeSyn.isEncapsulated()) return findConformingEncapsulatedRepresentation(repTypeSyn, repParam, found).good(); return OFFalse; } Uint32 DcmPixelData::getLength(const E_TransferSyntax xfer, const E_EncodingType enctype) { DcmXfer xferSyn(xfer); errorFlag = EC_Normal; Uint32 valueLength = 0; if (xferSyn.isEncapsulated() && !writeUnencapsulated(xfer)) { DcmRepresentationListIterator foundEntry; errorFlag = findConformingEncapsulatedRepresentation( xferSyn, NULL, foundEntry); if (errorFlag == EC_Normal) valueLength = (*foundEntry)->pixSeq->getLength(xfer, enctype); } else if (existUnencapsulated) valueLength = DcmPolymorphOBOW::getLength(xfer, enctype); else errorFlag = EC_RepresentationNotFound; return valueLength; } void DcmPixelData::getCurrentRepresentationKey( E_TransferSyntax & repType, const DcmRepresentationParameter * & repParam) { if (current != repListEnd) { repType = (*current)->repType; repParam = (*current)->repParam; } else { repType = EXS_LittleEndianExplicit; repParam = NULL; } } void DcmPixelData::getOriginalRepresentationKey( E_TransferSyntax & repType, const DcmRepresentationParameter * & repParam) { if (original != repListEnd) { repType = (*original)->repType; repParam = (*original)->repParam; } else { repType = EXS_LittleEndianExplicit; repParam = NULL; } } DcmRepresentationListIterator DcmPixelData::insertRepresentationEntry( DcmRepresentationEntry * repEntry) { DcmRepresentationListIterator insertedEntry; DcmRepresentationListIterator result; if (findRepresentationEntry(*repEntry, result).good()) { // this type of representation entry was already present in the list if (repEntry != *result) { insertedEntry = repList.insert(result, repEntry); // delete old entry from representation list delete *result; repList.erase(result); } } else insertedEntry = repList.insert(result,repEntry); return insertedEntry; } void DcmPixelData::print( STD_NAMESPACE ostream &out, const size_t flags, const int level, const char *pixelFileName, size_t *pixelCounter) { if (current == repListEnd) printPixel(out, flags, level, pixelFileName, pixelCounter); else (*current)->pixSeq->print(out, flags, level, pixelFileName, pixelCounter); } OFCondition DcmPixelData::putUint8Array( const Uint8 * byteValue, const unsigned long length) { // clear RepresentationList clearRepresentationList(repListEnd); OFCondition l_error = DcmPolymorphOBOW::putUint8Array(byteValue, length); original = current = repListEnd; recalcVR(); existUnencapsulated = OFTrue; return l_error; } OFCondition DcmPixelData::putUint16Array( const Uint16 * wordValue, const unsigned long length) { // clear RepresentationList clearRepresentationList(repListEnd); OFCondition l_error = DcmPolymorphOBOW::putUint16Array(wordValue, length); original = current = repListEnd; recalcVR(); existUnencapsulated = OFTrue; return l_error; } OFCondition DcmPixelData::createUint8Array( const Uint32 numBytes, Uint8 * & bytes) { OFCondition l_error = DcmPolymorphOBOW::createUint8Array(numBytes, bytes); existUnencapsulated = OFTrue; return l_error; } OFCondition DcmPixelData::createUint16Array( const Uint32 numWords, Uint16 * & words) { OFCondition l_error = DcmPolymorphOBOW::createUint16Array(numWords, words); existUnencapsulated = OFTrue; return l_error; } OFCondition DcmPixelData::createValueFromTempFile( DcmInputStreamFactory *factory, const Uint32 length, const E_ByteOrder byteOrder) { OFCondition l_error = DcmPolymorphOBOW::createValueFromTempFile(factory, length, byteOrder); existUnencapsulated = OFTrue; return l_error; } void DcmPixelData::putOriginalRepresentation( const E_TransferSyntax repType, const DcmRepresentationParameter * repParam, DcmPixelSequence * pixSeq) { // delete RepresentationList clearRepresentationList(repListEnd); // delete unencapsulated representation DcmPolymorphOBOW::putUint16Array(NULL,0); existUnencapsulated = OFFalse; // insert new Representation current = original = insertRepresentationEntry( new DcmRepresentationEntry(repType, repParam, pixSeq)); recalcVR(); } OFCondition DcmPixelData::read( DcmInputStream & inStream, const E_TransferSyntax ixfer, const E_GrpLenEncoding glenc, const Uint32 maxReadLength) { /* if this element's transfer state shows ERW_notInitialized, this is an illegal call */ if (getTransferState() == ERW_notInitialized) errorFlag = EC_IllegalCall; else { /* if this is not an illegal call, go ahead */ /* if the transfer state is ERW_init, we need to prepare the reading of the pixel */ /* data from the stream: remove all representations from the representation list. */ if (getTransferState() == ERW_init) clearRepresentationList(repListEnd); /* create a DcmXfer object based on the transfer syntax which was passed */ DcmXfer ixferSyn(ixfer); /* determine if the pixel data is captured in native (uncompressed) or encapsulated * (compressed) format. We only derive this information from the length field * which is set to undefined length for encapsulated data because even in * compressed transfer syntaxes the Icon Image Sequence may contain an * uncompressed image. */ if (getLengthField() == DCM_UndefinedLength) { /* the pixel data is captured in encapsulated (compressed) format */ /* if the transfer state is ERW_init, we need to prepare */ /* the reading of the pixel data from the stream. */ if (getTransferState() == ERW_init) { current = insertRepresentationEntry( new DcmRepresentationEntry( ixfer, NULL, new DcmPixelSequence(getTag(), getLengthField()))); recalcVR(); original = current; existUnencapsulated = OFFalse; setTransferState(ERW_inWork); if (! ixferSyn.isEncapsulated()) { /* Special case: we have encountered a compressed image * although we're decoding an uncompressed transfer syntax. * This could be a compressed image stored without meta-header. * For now, we just accept the data element; however, any attempt * to write the dataset will fail because no suitable decoder * is known. */ } } /* conduct the reading process */ errorFlag = (*current)->pixSeq->read(inStream, ixfer, glenc, maxReadLength); /* if the errorFlag equals EC_Normal, all pixel data has been */ /* read; hence, the transfer state has to be set to ERW_ready */ if (errorFlag == EC_Normal) setTransferState(ERW_ready); } else { /* the pixel data is captured in native (uncompressed) format */ /* if the transfer state is ERW_init, we need to prepare */ /* the reading of the pixel data from the stream. */ if (getTransferState() == ERW_init) { current = original = repListEnd; unencapsulatedVR = getTag().getEVR(); recalcVR(); existUnencapsulated = OFTrue; if (ixferSyn.isEncapsulated()) { /* Special case: we have encountered an uncompressed image * although we're decoding an encapsulated transfer syntax. * This is probably an icon image. */ alwaysUnencapsulated = OFTrue; } } /* conduct the reading process */ errorFlag = DcmPolymorphOBOW::read(inStream, ixfer, glenc, maxReadLength); } } /* return result value */ return errorFlag; } void DcmPixelData::removeAllButCurrentRepresentations() { clearRepresentationList(current); if (current != repListEnd && existUnencapsulated) { DcmPolymorphOBOW::putUint16Array(NULL,0); existUnencapsulated = OFFalse; } original = current; } void DcmPixelData::removeAllButOriginalRepresentations() { clearRepresentationList(original); if (original != repListEnd && existUnencapsulated) { DcmPolymorphOBOW::putUint16Array(NULL,0); existUnencapsulated = OFFalse; } current = original; recalcVR(); } OFCondition DcmPixelData::removeOriginalRepresentation( const E_TransferSyntax repType, const DcmRepresentationParameter * repParam) { OFCondition l_error = EC_Normal; DcmXfer repTypeSyn(repType); if (!repTypeSyn.isEncapsulated()) { if (original != repListEnd) { if (current == original) { current = repListEnd; recalcVR(); } repList.erase(original); original = repListEnd; } else l_error = EC_IllegalCall; } else { DcmRepresentationListIterator result; DcmRepresentationEntry findEntry(repType, repParam, NULL); if (findRepresentationEntry(findEntry, result) == EC_Normal) { if (result != original) { if (current == original) { current = result; recalcVR(); } if (original == repListEnd) { DcmPolymorphOBOW::putUint16Array(NULL, 0); existUnencapsulated = OFFalse; } else repList.erase(original); original = result; } else l_error = EC_IllegalCall; } else l_error = EC_RepresentationNotFound; } return l_error; } OFCondition DcmPixelData::removeRepresentation( const E_TransferSyntax repType, const DcmRepresentationParameter * repParam) { OFCondition l_error = EC_Normal; DcmXfer repTypeSyn(repType); if (!repTypeSyn.isEncapsulated()) { if (original != repListEnd && existUnencapsulated) { DcmPolymorphOBOW::putUint16Array(NULL, 0); existUnencapsulated = OFFalse; } else l_error = EC_CannotChangeRepresentation; } else { DcmRepresentationListIterator result; DcmRepresentationEntry findEntry(repType, repParam, NULL); if (findRepresentationEntry(findEntry, result) == EC_Normal) { if (original != result) repList.erase(result); else l_error = EC_CannotChangeRepresentation; } else l_error = EC_RepresentationNotFound; } return l_error; } OFCondition DcmPixelData::setCurrentRepresentationParameter( const DcmRepresentationParameter * repParam) { if (current != repListEnd) { if (repParam == NULL) (*current)->repParam = NULL; else (*current)->repParam = repParam->clone(); return EC_Normal; } return EC_RepresentationNotFound; } OFCondition DcmPixelData::setVR(DcmEVR vr) { unencapsulatedVR = vr; return DcmPolymorphOBOW::setVR(vr); } void DcmPixelData::transferEnd() { DcmPolymorphOBOW::transferEnd(); for (DcmRepresentationListIterator it(repList.begin()); it != repListEnd; ++it) (*it)->pixSeq->transferEnd(); } void DcmPixelData::transferInit() { DcmPolymorphOBOW::transferInit(); for (DcmRepresentationListIterator it(repList.begin()); it != repListEnd; ++it) (*it)->pixSeq->transferInit(); } OFCondition DcmPixelData::write( DcmOutputStream &outStream, const E_TransferSyntax oxfer, const E_EncodingType enctype, DcmWriteCache *wcache) { errorFlag = EC_Normal; if (getTransferState() == ERW_notInitialized) errorFlag = EC_IllegalCall; else { // check if the output transfer syntax is encapsulated // and we are not requested to write an uncompressed dataset // for example because this is within an Icon Image Sequence DcmXfer xferSyn(oxfer); if (xferSyn.isEncapsulated() && (! writeUnencapsulated(oxfer))) { // write encapsulated representation (i.e., compressed image) if (getTransferState() == ERW_init) { DcmRepresentationListIterator found; // find a compressed image matching the output transfer syntax errorFlag = findConformingEncapsulatedRepresentation(xferSyn, NULL, found); if (errorFlag == EC_Normal) { current = found; recalcVR(); pixelSeqForWrite = (*found)->pixSeq; setTransferState(ERW_inWork); } } // write compressed image if (errorFlag == EC_Normal && pixelSeqForWrite) errorFlag = pixelSeqForWrite->write(outStream, oxfer, enctype, wcache); if (errorFlag == EC_Normal) setTransferState(ERW_ready); } else if (existUnencapsulated) { // we're supposed to write an uncompressed image, and we happen to have one available. current = repListEnd; recalcVR(); // write uncompressed image errorFlag = DcmPolymorphOBOW::write(outStream, oxfer, enctype, wcache); } else if ((getValue() == NULL) && (current == repListEnd)) { // the PixelData is empty. Write an empty element. errorFlag = DcmPolymorphOBOW::write(outStream, oxfer, enctype, wcache); } else errorFlag = EC_RepresentationNotFound; } return errorFlag; } OFCondition DcmPixelData::writeXML( STD_NAMESPACE ostream &out, const size_t flags) { if (current == repListEnd) { errorFlag = DcmPolymorphOBOW::writeXML(out, flags); } else { /* pixel sequence (encapsulated data) */ errorFlag = (*current)->pixSeq->writeXML(out, flags); } return errorFlag; } OFCondition DcmPixelData::writeSignatureFormat( DcmOutputStream &outStream, const E_TransferSyntax oxfer, const E_EncodingType enctype, DcmWriteCache *wcache) { errorFlag = EC_Normal; if (getTransferState() == ERW_notInitialized) errorFlag = EC_IllegalCall; else if (getTag().isSignable()) { DcmXfer xferSyn(oxfer); if (xferSyn.isEncapsulated() && (! writeUnencapsulated(oxfer))) { if (getTransferState() == ERW_init) { DcmRepresentationListIterator found; errorFlag = findConformingEncapsulatedRepresentation(xferSyn, NULL, found); if (errorFlag == EC_Normal) { current = found; recalcVR(); pixelSeqForWrite = (*found)->pixSeq; setTransferState(ERW_inWork); } } if (errorFlag == EC_Normal && pixelSeqForWrite) errorFlag = pixelSeqForWrite->writeSignatureFormat(outStream, oxfer, enctype, wcache); if (errorFlag == EC_Normal) setTransferState(ERW_ready); } else if (existUnencapsulated) { current = repListEnd; recalcVR(); errorFlag = DcmPolymorphOBOW::writeSignatureFormat(outStream, oxfer, enctype, wcache); } else if (getValue() == NULL) { errorFlag = DcmPolymorphOBOW::writeSignatureFormat(outStream, oxfer, enctype, wcache); } else errorFlag = EC_RepresentationNotFound; } else errorFlag = EC_Normal; return errorFlag; } OFCondition DcmPixelData::loadAllDataIntoMemory(void) { if (current == repListEnd) return DcmElement::loadAllDataIntoMemory(); else return (*current)->pixSeq->loadAllDataIntoMemory(); } void DcmPixelData::setNonEncapsulationFlag(OFBool flag) { alwaysUnencapsulated = flag; } OFCondition DcmPixelData::getUncompressedFrame( DcmItem *dataset, Uint32 frameNo, Uint32& startFragment, void *buffer, Uint32 bufSize, OFString& decompressedColorModel, DcmFileCache *cache) { if ((dataset == NULL) || (buffer == NULL)) return EC_IllegalCall; Sint32 numberOfFrames = 1; dataset->findAndGetSint32(DCM_NumberOfFrames, numberOfFrames); // don't fail if absent if (numberOfFrames < 1) numberOfFrames = 1; Uint32 frameSize; OFCondition result = getUncompressedFrameSize(dataset, frameSize); if (result.bad()) return result; // determine the minimum buffer size, which may be frame size plus one pad byte if frame size is odd. // We need this extra byte, because the image might be in a different // endianness than our host cpu. In this case the decoder will swap // the data to the host byte order which could overflow the buffer. Uint32 minBufSize = frameSize; if (minBufSize & 1) ++minBufSize; if (bufSize < minBufSize) return EC_IllegalCall; // check frame number if (frameNo >= OFstatic_cast(Uint32, numberOfFrames)) return EC_IllegalCall; if (existUnencapsulated) { // we already have an uncompressed version of the pixel data // either in memory or in file. We can directly access this using // DcmElement::getPartialValue. result = getPartialValue(buffer, frameNo * frameSize, frameSize, cache); if (result.good()) result = dataset->findAndGetOFString(DCM_PhotometricInterpretation, decompressedColorModel); } else { // we only have a compressed version of the pixel data. // Identify a codec for decompressing the frame. result = DcmCodecList::decodeFrame( (*original)->repType, (*original)->repParam, (*original)->pixSeq, dataset, frameNo, startFragment, buffer, bufSize, decompressedColorModel); } return result; } OFCondition DcmPixelData::getDecompressedColorModel( DcmItem *dataset, OFString &decompressedColorModel) { OFCondition result = EC_IllegalParameter; if (dataset != NULL) { if (existUnencapsulated) { // we already have an uncompressed version of the pixel data either in memory or in file, // so just retrieve the color model from the given dataset result = dataset->findAndGetOFString(DCM_PhotometricInterpretation, decompressedColorModel); if (result == EC_TagNotFound) { DCMDATA_WARN("DcmPixelData: Mandatory element PhotometricInterpretation " << DCM_PhotometricInterpretation << " is missing"); result = EC_MissingAttribute; } else if (result.bad()) { DCMDATA_WARN("DcmPixelData: Cannot retrieve value of element PhotometricInterpretation " << DCM_PhotometricInterpretation << ": " << result.text()); } else if (decompressedColorModel.empty()) { DCMDATA_WARN("DcmPixelData: No value for mandatory element PhotometricInterpretation " << DCM_PhotometricInterpretation); result = EC_MissingValue; } } else { // we only have a compressed version of the pixel data. // Identify a codec for determining the color model. result = DcmCodecList::determineDecompressedColorModel( (*original)->repType, (*original)->repParam, (*original)->pixSeq, dataset, decompressedColorModel); } } return result; } OFBool DcmPixelData::writeUnencapsulated(const E_TransferSyntax xfer) { // There are three cases under which a dataset is written out // unencapsulated: // // - It was already read unencapsulated (handled via alwaysUnencapsulated) // - We were told to do so (handled via alwaysUnencapsulated) // - This is not the pixel data element on the main level and it exists // unencapsulated. if (alwaysUnencapsulated) return OFTrue; if (DcmXfer(xfer).isEncapsulated()) { DcmRepresentationListIterator found; OFCondition cond = findConformingEncapsulatedRepresentation(xfer, NULL, found); if (cond.good()) { // We found a suitable encapsulated representation, so encapsulate // this element in the output. return OFFalse; } } return existUnencapsulated && isNested(); } OFCondition DcmPixelData::writeJson(STD_NAMESPACE ostream &out, DcmJsonFormat &format) { // check if we have an empty uncompressed value field. // We never encode that as BulkDataURI. OFBool emptyValue = OFFalse; if ((current == repListEnd) && existUnencapsulated && (getLengthField() == 0)) { emptyValue = OFTrue; } // now check if the pixel data will be written as // BulkDataURI, which is possible for both uncompressed // and encapsulated pixel data. OFString value; if ((! emptyValue) && format.asBulkDataURI(getTag(), value)) { /* write JSON Opener */ writeJsonOpener(out, format); /* return defined BulkDataURI */ format.printBulkDataURIPrefix(out); DcmJsonFormat::printString(out, value); /* write JSON Closer */ writeJsonCloser(out, format); return EC_Normal; } // No bulk data URI, we're supposed to write as InlineBinary. // This is only defined for uncompressed data, not for any of the // encapsulated encodings. // check the current pixel data representation if ((current == repListEnd) && existUnencapsulated) { // current pixel data representation is uncompressed (and available). /* write JSON Opener */ writeJsonOpener(out, format); /* for an empty value field, we do not need to do anything */ if (getLengthField() > 0) { /* encode binary data as Base64 */ format.printInlineBinaryPrefix(out); out << "\""; /* adjust byte order to little endian */ Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue(EBO_LittleEndian)); OFStandard::encodeBase64(out, byteValues, OFstatic_cast(size_t, getLengthField())); out << "\""; } /* write JSON Closer */ writeJsonCloser(out, format); return EC_Normal; } // pixel data is encapsulated, return error return EC_CannotWriteJsonInlineBinary; }