/*
*
* Copyright (C) 1994-2011, 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: Gerd Ehlers, Andreas Barth
*
* Purpose: Implementation of class DcmPixelSequence
*
*/
#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
#include "dcmtk/ofstd/ofstream.h"
#include "dcmtk/ofstd/ofuuid.h"
#include "dcmtk/dcmdata/dcpixseq.h"
#include "dcmtk/dcmdata/dcpxitem.h"
#include "dcmtk/dcmdata/dcitem.h"
#include "dcmtk/dcmdata/dcvr.h"
#include "dcmtk/dcmdata/dcdeftag.h"
// ********************************
DcmPixelSequence::DcmPixelSequence(const DcmTag &tag)
: DcmSequenceOfItems(tag, 0),
Xfer(EXS_Unknown)
{
setTagVR(EVR_OB);
setLengthField(DCM_UndefinedLength); // pixel sequences always use undefined length
}
DcmPixelSequence::DcmPixelSequence(const DcmTag &tag,
const Uint32 len)
: DcmSequenceOfItems(tag, len),
Xfer(EXS_Unknown)
{
setTagVR(EVR_OB);
setLengthField(DCM_UndefinedLength); // pixel sequences always use undefined length
}
DcmPixelSequence::DcmPixelSequence(const DcmPixelSequence &old)
: DcmSequenceOfItems(old),
Xfer(old.Xfer)
{
/* everything gets handled in DcmSequenceOfItems constructor */
}
DcmPixelSequence::~DcmPixelSequence()
{
}
DcmPixelSequence &DcmPixelSequence::operator=(const DcmPixelSequence &obj)
{
if (this != &obj)
{
DcmSequenceOfItems::operator=(obj);
Xfer = obj.Xfer;
}
return *this;
}
OFCondition DcmPixelSequence::copyFrom(const DcmObject& rhs)
{
if (this != &rhs)
{
if (rhs.ident() != ident()) return EC_IllegalCall;
*this = OFstatic_cast(const DcmPixelSequence &, rhs);
}
return EC_Normal;
}
// ********************************
void DcmPixelSequence::print(STD_NAMESPACE ostream &out,
const size_t flags,
const int level,
const char *pixelFileName,
size_t *pixelCounter)
{
/* print pixel sequence start line */
if (flags & DCMTypes::PF_showTreeStructure)
{
/* empty text */
printInfoLine(out, flags, level);
/* print pixel sequence content */
if (!itemList->empty())
{
/* print pixel items */
DcmObject *dO;
itemList->seek(ELP_first);
do {
dO = itemList->get();
dO->print(out, flags, level + 1, pixelFileName, pixelCounter);
} while (itemList->seek(ELP_next));
}
} else {
OFOStringStream oss;
oss << "(PixelSequence ";
if (getLengthField() != DCM_UndefinedLength)
oss << "with explicit length ";
oss << "#=" << card() << ")" << OFStringStream_ends;
OFSTRINGSTREAM_GETSTR(oss, tmpString)
printInfoLine(out, flags, level, tmpString);
OFSTRINGSTREAM_FREESTR(tmpString)
/* print pixel sequence content */
if (!itemList->empty())
{
DcmObject *dO;
itemList->seek(ELP_first);
do {
dO = itemList->get();
dO->print(out, flags, level + 1, pixelFileName, pixelCounter);
} while (itemList->seek(ELP_next));
}
/* print pixel sequence end line */
DcmTag delimItemTag(DCM_SequenceDelimitationItemTag);
if (getLengthField() == DCM_UndefinedLength)
printInfoLine(out, flags, level, "(SequenceDelimitationItem)", &delimItemTag);
else
printInfoLine(out, flags, level, "(SequenceDelimitationItem for re-encod.)", &delimItemTag);
}
}
// ********************************
OFCondition DcmPixelSequence::writeXML(STD_NAMESPACE ostream &out,
const size_t flags)
{
OFCondition l_error = EC_Normal;
if (flags & DCMTypes::XF_useNativeModel)
{
/* write XML start tag */
writeXMLStartTag(out, flags);
/* for an empty value field, we do not need to do anything */
if (getLengthField() > 0)
{
/* encode binary data as Base64 */
if (flags & DCMTypes::XF_encodeBase64)
{
out << "";
Uint8 *byteValues = OFstatic_cast(Uint8 *, getValue());
OFStandard::encodeBase64(out, byteValues, OFstatic_cast(size_t, getLengthField()));
out << "" << OFendl;
} else {
/* generate a new UID but the binary data is not (yet) written. */
OFUUID uuid;
out << "" << OFendl;
}
}
/* write XML end tag */
writeXMLEndTag(out, flags);
} else {
/* the DCMTK-specific XML format requires no special handling */
l_error = DcmSequenceOfItems::writeXML(out, flags);
}
return l_error;
}
// ********************************
Uint32 DcmPixelSequence::calcElementLength(const E_TransferSyntax xfer,
const E_EncodingType enctype)
{
// add 8 bytes for Sequence Delimitation Tag which always exists for Pixel Sequences
return DcmElement::calcElementLength(xfer, enctype) + 8;
}
// ********************************
OFCondition DcmPixelSequence::makeSubObject(DcmObject *&subObject,
const DcmTag &newTag,
const Uint32 newLength)
{
OFCondition l_error = EC_Normal;
DcmObject *newObject = NULL;
switch (newTag.getEVR())
{
case EVR_na:
if (newTag.getXTag() == DCM_Item)
newObject = new DcmPixelItem(newTag, newLength);
else if (newTag.getXTag() == DCM_SequenceDelimitationItem)
l_error = EC_SequEnd;
else if (newTag.getXTag() == DCM_ItemDelimitationItem)
l_error = EC_ItemEnd;
else
l_error = EC_InvalidTag;
break;
default:
newObject = new DcmPixelItem(newTag, newLength);
l_error = EC_CorruptedData;
break;
}
subObject = newObject;
return l_error;
}
// ********************************
OFCondition DcmPixelSequence::insert(DcmPixelItem *item,
unsigned long where)
{
errorFlag = EC_Normal;
if (item != NULL)
{
// special case: last position
if (where == DCM_EndOfListIndex)
{
// insert at end of list (avoid seeking)
itemList->append(item);
DCMDATA_TRACE("DcmPixelSequence::insert() Item at last position inserted");
} else {
// insert after "where"
itemList->seek_to(where);
itemList->insert(item);
DCMDATA_TRACE("DcmPixelSequence::insert() Item at position " << where << " inserted");
}
// check whether the new item already has a parent
if (item->getParent() != NULL)
{
DCMDATA_DEBUG("DcmPixelSequence::insert() PixelItem already has a parent: "
<< item->getParent()->getTag() << " VR=" << DcmVR(item->getParent()->getVR()).getVRName());
}
// remember the parent (i.e. the surrounding sequence)
item->setParent(this);
} else
errorFlag = EC_IllegalCall;
return errorFlag;
}
// ********************************
OFCondition DcmPixelSequence::getItem(DcmPixelItem *&item,
const unsigned long num)
{
errorFlag = EC_Normal;
item = OFstatic_cast(DcmPixelItem*, itemList->seek_to(num)); // read item from list
if (item == NULL)
errorFlag = EC_IllegalCall;
return errorFlag;
}
// ********************************
OFCondition DcmPixelSequence::remove(DcmPixelItem *&item,
const unsigned long num)
{
errorFlag = EC_Normal;
item = OFstatic_cast(DcmPixelItem*, itemList->seek_to(num)); // read item from list
if (item != NULL)
{
itemList->remove();
item->setParent(NULL); // forget about the parent
} else
errorFlag = EC_IllegalCall;
return errorFlag;
}
// ********************************
OFCondition DcmPixelSequence::remove(DcmPixelItem *item)
{
errorFlag = EC_IllegalCall;
if (!itemList->empty() && item != NULL)
{
DcmObject *dO;
itemList->seek(ELP_first);
do {
dO = itemList->get();
if (dO == item)
{
itemList->remove(); // remove element from list, but do no delete it
item->setParent(NULL); // forget about the parent
errorFlag = EC_Normal;
break;
}
} while (itemList->seek(ELP_next));
}
return errorFlag;
}
// ********************************
OFCondition DcmPixelSequence::changeXfer(const E_TransferSyntax newXfer)
{
if (Xfer == EXS_Unknown || canWriteXfer(newXfer, Xfer))
{
Xfer = newXfer;
return EC_Normal;
} else
return EC_IllegalCall;
}
// ********************************
OFBool DcmPixelSequence::canWriteXfer(const E_TransferSyntax newXfer,
const E_TransferSyntax oldXfer)
{
DcmXfer newXferSyn(newXfer);
return newXferSyn.isEncapsulated() && newXfer == oldXfer && oldXfer == Xfer;
}
// ********************************
OFCondition DcmPixelSequence::read(DcmInputStream &inStream,
const E_TransferSyntax ixfer,
const E_GrpLenEncoding glenc,
const Uint32 maxReadLength)
{
OFCondition l_error = changeXfer(ixfer);
if (l_error.good())
return DcmSequenceOfItems::read(inStream, ixfer, glenc, maxReadLength);
return l_error;
}
// ********************************
OFCondition DcmPixelSequence::write(DcmOutputStream &outStream,
const E_TransferSyntax oxfer,
const E_EncodingType /*enctype*/,
DcmWriteCache *wcache)
{
OFCondition l_error = changeXfer(oxfer);
if (l_error.good())
return DcmSequenceOfItems::write(outStream, oxfer, EET_UndefinedLength, wcache);
return l_error;
}
// ********************************
OFCondition DcmPixelSequence::writeSignatureFormat(DcmOutputStream &outStream,
const E_TransferSyntax oxfer,
const E_EncodingType /*enctype*/,
DcmWriteCache *wcache)
{
OFCondition l_error = changeXfer(oxfer);
if (l_error.good())
return DcmSequenceOfItems::writeSignatureFormat(outStream, oxfer, EET_UndefinedLength, wcache);
return l_error;
}
OFCondition DcmPixelSequence::storeCompressedFrame(DcmOffsetList &offsetList,
Uint8 *compressedData,
Uint32 compressedLen,
Uint32 fragmentSize)
{
if (compressedData == NULL)
return EC_IllegalCall;
OFCondition result = EC_Normal;
if (fragmentSize >= 0x400000)
fragmentSize = 0; // prevent overflow
else
fragmentSize <<= 10; // unit is kbytes
if (fragmentSize == 0)
fragmentSize = compressedLen;
Uint32 offset = 0;
Uint32 currentSize = 0;
Uint32 numFragments = 0;
DcmPixelItem *fragment = NULL;
while ((offset < compressedLen) && (result.good()))
{
fragment = new DcmPixelItem(DCM_PixelItemTag);
if (fragment == NULL)
result = EC_MemoryExhausted;
else
{
insert(fragment);
numFragments++;
currentSize = fragmentSize;
if (offset + currentSize > compressedLen)
currentSize = compressedLen - offset;
// if currentSize is odd this will be fixed during DcmOtherByteOtherWord::write()
result = fragment->putUint8Array(compressedData + offset, currentSize);
if (result.good())
offset += currentSize;
}
}
currentSize = offset + (numFragments << 3); // 8 bytes extra for each item header
// odd frame size requires padding, i.e. last fragment uses odd length pixel item
if (currentSize & 1)
currentSize++;
offsetList.push_back(currentSize);
return result;
}