DYT/Tool/3rdParty_x64/include/dcmtk/dcmdata/dcrleenc.h
2024-11-22 23:19:31 +08:00

417 lines
12 KiB
C++

/*
*
* Copyright (C) 2002-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: Marco Eichelberg
*
* Purpose: RLE compressor
*
*/
#ifndef DCRLEENC_H
#define DCRLEENC_H
#include "dcmtk/config/osconfig.h"
#include "dcmtk/ofstd/oflist.h" /* for class OFList<> */
#define INCLUDE_CSTRING
#include "dcmtk/ofstd/ofstdinc.h"
#include "dcmtk/dcmdata/dcdefine.h"
#define DcmRLEEncoder_BLOCKSIZE 16384
/** abstract class that defines an interface through which
* encoder classes (such as DcmRLEEncoder) may export their
* encoded data
*/
class DCMTK_DCMDATA_EXPORT DcmEncoderOutputStream
{
public:
/** write the given buffer into the output stream
* @param buf pointer to buffer
* @param bufsize number of bytes in buffer
*/
virtual void write(const unsigned char *buf, size_t bufsize) =0;
/** Virtual Desctructor
*/
virtual ~DcmEncoderOutputStream() {}
};
/** this class implements an RLE compressor conforming to the DICOM standard.
* The class is loosely based on an implementation by Phil Norman.
*/
class DCMTK_DCMDATA_EXPORT DcmRLEEncoder
{
public:
/** default constructor
* @param doPad if true, RLE codec will pad output data to even number of bytes
*/
DcmRLEEncoder(int doPad)
: fail_(0)
, pad_(doPad)
, currentBlock_(new unsigned char[DcmRLEEncoder_BLOCKSIZE])
, offset_(0)
, blockList_()
, RLE_buff_(new unsigned char[132])
, RLE_prev_(-1)
, RLE_pcount_(0)
, RLE_bindex_(1)
{
if ((! RLE_buff_)||(! currentBlock_)) fail_ = 1;
else RLE_buff_[0] = 0;
}
/// destructor
~DcmRLEEncoder()
{
delete[] currentBlock_;
delete[] RLE_buff_;
OFListIterator(unsigned char *) first = blockList_.begin();
OFListIterator(unsigned char *) last = blockList_.end();
while (first != last)
{
delete[] *first;
first = blockList_.erase(first);
}
}
/** this method adds one byte to the byte stream to be compressed
* with the RLE compressor.
* @param ch byte to be added
*/
inline void add(unsigned char ch)
{
if (! fail_) // if fail_ is true, just ignore input
{
// if the current byte equals the last byte read
// (which is initialized with the "impossible" value -1),
// just increase the repeat counter
if (OFstatic_cast(int, ch) == RLE_prev_) RLE_pcount_++;
else
{
// byte is different from last byte read.
// flush replicate run if necessary
switch (RLE_pcount_)
{
case 0:
// happens only after construction or flush()
break;
case 2:
// two bytes in repeat buffer. Convert to literal run
RLE_buff_[RLE_bindex_++] = OFstatic_cast(unsigned char, RLE_prev_);
// no break. Fall-through into next case statement is intended.
case 1:
// one (or two) bytes in repeat buffer. Convert to literal run
RLE_buff_[RLE_bindex_++] = OFstatic_cast(unsigned char, RLE_prev_);
break;
default:
// more than two bytes in repeat buffer. Convert to replicate run
if (RLE_bindex_ > 1)
{
// there is a literal run in the buffer that must be flushed
// before the replicate run. Flush literal run now.
RLE_buff_[0] = OFstatic_cast(unsigned char, RLE_bindex_-2);
move(RLE_bindex_);
}
// this is the byte value for the repeat run
RLE_buff_[1] = OFstatic_cast(unsigned char, RLE_prev_);
// write as many repeat runs as necessary
for (; RLE_pcount_>0; RLE_pcount_-=128)
{
// different PackBit schemes exist. The original from which
// this code is derived used 0x80 | (RLE_pcount_ - 1)
// to represent replicate runs.
// DICOM instead uses 257 - RLE_pcount_
if (RLE_pcount_ > 128) RLE_buff_[0] = 0x81;
else RLE_buff_[0] = OFstatic_cast(unsigned char, 257 - RLE_pcount_);
move(2);
}
// now the buffer is guaranteed to be empty
RLE_buff_[0] = 0;
RLE_bindex_ = 1;
break;
}
// if we have 128 or more bytes in the literal run, flush buffer
if (RLE_bindex_ > 129)
{
RLE_buff_[0] = 127;
move(129);
RLE_bindex_ -= 128;
if (RLE_bindex_ > 1)
RLE_buff_[1] = RLE_buff_[129];
if (RLE_bindex_ > 2)
RLE_buff_[2] = RLE_buff_[130];
}
// current byte is stored in RLE_prev_, RLE_pcount_ is 1.
RLE_prev_ = ch;
RLE_pcount_ = 1;
}
}
}
/** this method adds a block of bytes to the byte stream to be
* compressed with the RLE compressor.
* @param buf buffer to be added
* @param bufcount number of bytes in buffer
*/
inline void add(const unsigned char *buf, size_t bufcount)
{
if (buf)
{
while (bufcount--) add(*buf++);
}
}
/** this method finalizes the compressed RLE stream, i.e. flushes all
* pending literal or repeat runs. This method can be called at any
* time; however, it must be called before size() or write()
* can be used. Intermediate calls should be avoided since they
* possibly decrease the compression ratio.
*/
inline void flush()
{
if (! fail_) // if fail_ is true, do nothing
{
// if there are max 1 bytes in the repeat counter, convert to literal run
if (RLE_pcount_ < 2)
{
for (; RLE_pcount_>0; --RLE_pcount_) RLE_buff_[RLE_bindex_++] = OFstatic_cast(unsigned char, RLE_prev_);
}
// if we have 128 or more bytes in the literal run, flush buffer
if (RLE_bindex_ > 129)
{
RLE_buff_[0] = 127;
move(129);
RLE_bindex_ -= 128;
if (RLE_bindex_ > 1)
RLE_buff_[1] = RLE_buff_[129];
if (RLE_bindex_ > 2)
RLE_buff_[2] = RLE_buff_[130];
}
// if there is still a literal run in the buffer, flush literal run
if (RLE_bindex_ > 1)
{
RLE_buff_[0] = OFstatic_cast(unsigned char, RLE_bindex_-2);
move(RLE_bindex_);
}
// if there is a remaining repeat run, flush this one as well
if (RLE_pcount_ >= 2)
{
RLE_buff_[1] = OFstatic_cast(unsigned char, RLE_prev_);
// write as many repeat runs as necessary
for (; RLE_pcount_>0; RLE_pcount_-=128)
{
// different PackBit schemes exist. The original from which
// this code is derived used 0x80 | (RLE_pcount_ - 1)
// to represent replicate runs.
// DICOM instead uses 257 - RLE_pcount_
if (RLE_pcount_ > 128) RLE_buff_[0] = 0x81;
else RLE_buff_[0] = OFstatic_cast(unsigned char, 257 - RLE_pcount_);
move(2);
}
}
// now the buffer is guaranteed to be empty, re-initialize
RLE_buff_[0] = 0;
RLE_prev_ = -1;
RLE_pcount_ = 0;
RLE_bindex_ = 1;
}
}
/** returns the size of compressed RLE stream in bytes.
* The size is guaranteed to be an even number of bytes (padded
* with a trailing zero byte as required by DICOM if necessary).
* This method may only be called after flush() has been executed
* to finalize the compressed stream.
* @return size of compressed stream, in bytes
*/
inline size_t size() const
{
size_t result = blockList_.size() * DcmRLEEncoder_BLOCKSIZE + offset_;
if (pad_ && (result & 1)) result++; // enforce even number of bytes
return result;
}
/** returns true if the RLE compressor has run out of memory. In this case,
* no output has been created.
*/
inline OFBool fail() const
{
if (fail_) return OFTrue; else return OFFalse;
}
/** copies the compressed RLE byte stream into a target array of at least
* size() bytes.
* @param target pointer to array of at least size() bytes, must not be NULL.
*/
inline void write(void *target) const
{
if ((!fail_) && target)
{
unsigned char *current = NULL;
unsigned char *target8 = OFstatic_cast(unsigned char *, target);
OFListConstIterator(unsigned char *) first = blockList_.begin();
OFListConstIterator(unsigned char *) last = blockList_.end();
while (first != last)
{
current = *first;
memcpy(target8, current, DcmRLEEncoder_BLOCKSIZE);
target8 += DcmRLEEncoder_BLOCKSIZE;
++first;
}
if (offset_ > 0)
{
memcpy(target8, currentBlock_, offset_);
}
// pad to even number of bytes if necessary
if (pad_ && ((blockList_.size() * DcmRLEEncoder_BLOCKSIZE + offset_) & 1))
{
target8 += offset_;
*target8 = 0;
}
}
}
/** copies the compressed RLE byte stream into an
* output stream
* @param os output stream
*/
inline void write(DcmEncoderOutputStream& os) const
{
if (!fail_)
{
OFListConstIterator(unsigned char *) first = blockList_.begin();
OFListConstIterator(unsigned char *) last = blockList_.end();
while (first != last)
{
os.write(*first, DcmRLEEncoder_BLOCKSIZE);
++first;
}
if (offset_ > 0)
{
os.write(currentBlock_, offset_);
}
// pad to even number of bytes if necessary
if (pad_ && ((blockList_.size() * DcmRLEEncoder_BLOCKSIZE + offset_) & 1))
{
unsigned char c = 0;
os.write(&c, 1);
}
}
}
private:
/// private undefined copy constructor
DcmRLEEncoder(const DcmRLEEncoder&);
/// private undefined copy assignment operator
DcmRLEEncoder& operator=(const DcmRLEEncoder&);
/** this method moves the given number of bytes from buff_
* to currentBlock_ and "flushes" currentBlock_ to
* blockList_ if necessary.
* @param numberOfBytes number of bytes to copy
*/
inline void move(size_t numberOfBytes)
{
size_t i=0;
while (i < numberOfBytes)
{
if (offset_ == DcmRLEEncoder_BLOCKSIZE)
{
blockList_.push_back(currentBlock_);
currentBlock_ = new unsigned char[DcmRLEEncoder_BLOCKSIZE];
offset_ = 0;
if (! currentBlock_) // out of memory
{
fail_ = 1;
break; // exit while loop
}
}
currentBlock_[offset_++] = RLE_buff_[i++];
}
}
/* member variables */
/** this flag indicates a failure of the RLE codec. Once a failure is
* flagged, the codec will consume all input and not produce any more
* output. A failure status can only be caused by an out-of-memory
* condition.
*/
int fail_;
/** this flag indicates whether the RLE codec must pad encoded
* data to an even number of bytes (as required by DICOM).
* True if padding is required, false otherwise
*/
int pad_;
/** this member points to a block of size DcmRLEEncoder_BLOCKSIZE
* (unless fail_ is true). This is the current block of data to
* which the RLE stream is written
*/
unsigned char *currentBlock_;
/** contains the number of bytes already written the the memory
* block pointed to by currentBlock_. Value is always less than
* DcmRLEEncoder_BLOCKSIZE.
*/
size_t offset_;
/** this member contains a list of memory blocks of size DcmRLEEncoder_BLOCKSIZE
* which already have been filled with encoded RLE data.
* The current block (pointed to by currentBlock_) is not contained in this list.
*/
OFList<unsigned char *> blockList_;
/** this member points to a buffer of 132 bytes that is used by the RLE
* encoding algorithm.
*/
unsigned char *RLE_buff_;
/** value of the last byte fed to the RLE compressor. This byte is not yet
* stored in the RLE_buff_ buffer.
* Type is int because this allows an "impossible" -1 as default value
*/
int RLE_prev_;
/** repeat counter, for RLE compressor
* may temporarily become negative, guaranteed to be >= 0 between method calls.
*/
int RLE_pcount_;
/** index of next unused byte in RLE_buff_.
*/
unsigned int RLE_bindex_;
};
#endif