00001 /* 00002 * 00003 * Copyright (C) 2002-2010, OFFIS e.V. 00004 * All rights reserved. See COPYRIGHT file for details. 00005 * 00006 * This software and supporting documentation were developed by 00007 * 00008 * OFFIS e.V. 00009 * R&D Division Health 00010 * Escherweg 2 00011 * D-26121 Oldenburg, Germany 00012 * 00013 * 00014 * Module: dcmdata 00015 * 00016 * Author: Marco Eichelberg 00017 * 00018 * Purpose: RLE compressor 00019 * 00020 * Last Update: $Author: joergr $ 00021 * Update Date: $Date: 2010-10-14 13:15:42 $ 00022 * CVS/RCS Revision: $Revision: 1.13 $ 00023 * Status: $State: Exp $ 00024 * 00025 * CVS/RCS Log at end of file 00026 * 00027 */ 00028 00029 #ifndef DCRLEENC_H 00030 #define DCRLEENC_H 00031 00032 #include "dcmtk/config/osconfig.h" 00033 #include "dcmtk/ofstd/oflist.h" /* for class OFList<> */ 00034 00035 #define INCLUDE_CSTRING 00036 #include "dcmtk/ofstd/ofstdinc.h" 00037 00038 #define DcmRLEEncoder_BLOCKSIZE 16384 00039 00040 00045 class DcmEncoderOutputStream 00046 { 00047 public: 00052 virtual void write(const unsigned char *buf, size_t bufsize) =0; 00053 00056 virtual ~DcmEncoderOutputStream() {} 00057 00058 }; 00059 00060 00064 class DcmRLEEncoder 00065 { 00066 public: 00067 00071 DcmRLEEncoder(int doPad) 00072 : fail_(0) 00073 , pad_(doPad) 00074 , currentBlock_(new unsigned char[DcmRLEEncoder_BLOCKSIZE]) 00075 , offset_(0) 00076 , blockList_() 00077 , RLE_buff_(new unsigned char[132]) 00078 , RLE_prev_(-1) 00079 , RLE_pcount_(0) 00080 , RLE_bindex_(1) 00081 { 00082 if ((! RLE_buff_)||(! currentBlock_)) fail_ = 1; 00083 else RLE_buff_[0] = 0; 00084 } 00085 00087 ~DcmRLEEncoder() 00088 { 00089 delete[] currentBlock_; 00090 delete[] RLE_buff_; 00091 OFListIterator(unsigned char *) first = blockList_.begin(); 00092 OFListIterator(unsigned char *) last = blockList_.end(); 00093 while (first != last) 00094 { 00095 delete[] *first; 00096 first = blockList_.erase(first); 00097 } 00098 } 00099 00104 inline void add(unsigned char ch) 00105 { 00106 if (! fail_) // if fail_ is true, just ignore input 00107 { 00108 // if the current byte equals the last byte read 00109 // (which is initialized with the "impossible" value -1), 00110 // just increase the repeat counter 00111 if (OFstatic_cast(int, ch) == RLE_prev_) RLE_pcount_++; 00112 else 00113 { 00114 // byte is different from last byte read. 00115 // flush replicate run if necessary 00116 switch (RLE_pcount_) 00117 { 00118 case 0: 00119 // happens only after construction or flush() 00120 break; 00121 case 2: 00122 // two bytes in repeat buffer. Convert to literal run 00123 RLE_buff_[RLE_bindex_++] = OFstatic_cast(unsigned char, RLE_prev_); 00124 // no break. Fall-through into next case statement is intended. 00125 case 1: 00126 // one (or two) bytes in repeat buffer. Convert to literal run 00127 RLE_buff_[RLE_bindex_++] = OFstatic_cast(unsigned char, RLE_prev_); 00128 break; 00129 default: 00130 // more than two bytes in repeat buffer. Convert to replicate run 00131 if (RLE_bindex_ > 1) 00132 { 00133 // there is a literal run in the buffer that must be flushed 00134 // before the replicate run. Flush literal run now. 00135 RLE_buff_[0] = OFstatic_cast(unsigned char, RLE_bindex_-2); 00136 move(RLE_bindex_); 00137 } 00138 // this is the byte value for the repeat run 00139 RLE_buff_[1] = OFstatic_cast(unsigned char, RLE_prev_); 00140 // write as many repeat runs as necessary 00141 for (; RLE_pcount_>0; RLE_pcount_-=128) 00142 { 00143 // different PackBit schemes exist. The original from which 00144 // this code is derived used 0x80 | (RLE_pcount_ - 1) 00145 // to represent replicate runs. 00146 // DICOM instead uses 257 - RLE_pcount_ 00147 if (RLE_pcount_ > 128) RLE_buff_[0] = 0x81; 00148 else RLE_buff_[0] = OFstatic_cast(unsigned char, 257 - RLE_pcount_); 00149 move(2); 00150 } 00151 // now the buffer is guaranteed to be empty 00152 RLE_buff_[0] = 0; 00153 RLE_bindex_ = 1; 00154 break; 00155 } 00156 00157 // if we have 128 or more bytes in the literal run, flush buffer 00158 if (RLE_bindex_ > 129) 00159 { 00160 RLE_buff_[0] = 127; 00161 move(129); 00162 RLE_bindex_ -= 128; 00163 if (RLE_bindex_ > 1) 00164 RLE_buff_[1] = RLE_buff_[129]; 00165 if (RLE_bindex_ > 2) 00166 RLE_buff_[2] = RLE_buff_[130]; 00167 } 00168 00169 // current byte is stored in RLE_prev_, RLE_pcount_ is 1. 00170 RLE_prev_ = ch; 00171 RLE_pcount_ = 1; 00172 } 00173 } 00174 } 00175 00181 inline void add(const unsigned char *buf, size_t bufcount) 00182 { 00183 if (buf) 00184 { 00185 while (bufcount--) add(*buf++); 00186 } 00187 } 00188 00195 inline void flush() 00196 { 00197 if (! fail_) // if fail_ is true, do nothing 00198 { 00199 // if there are max 1 bytes in the repeat counter, convert to literal run 00200 if (RLE_pcount_ < 2) 00201 { 00202 for (; RLE_pcount_>0; --RLE_pcount_) RLE_buff_[RLE_bindex_++] = OFstatic_cast(unsigned char, RLE_prev_); 00203 } 00204 00205 // if we have 128 or more bytes in the literal run, flush buffer 00206 if (RLE_bindex_ > 129) 00207 { 00208 RLE_buff_[0] = 127; 00209 move(129); 00210 RLE_bindex_ -= 128; 00211 if (RLE_bindex_ > 1) 00212 RLE_buff_[1] = RLE_buff_[129]; 00213 if (RLE_bindex_ > 2) 00214 RLE_buff_[2] = RLE_buff_[130]; 00215 } 00216 00217 // if there is still a literal run in the buffer, flush literal run 00218 if (RLE_bindex_ > 1) 00219 { 00220 RLE_buff_[0] = OFstatic_cast(unsigned char, RLE_bindex_-2); 00221 move(RLE_bindex_); 00222 } 00223 00224 // if there is a remaining repeat run, flush this one as well 00225 if (RLE_pcount_ >= 2) 00226 { 00227 RLE_buff_[1] = OFstatic_cast(unsigned char, RLE_prev_); 00228 // write as many repeat runs as necessary 00229 for (; RLE_pcount_>0; RLE_pcount_-=128) 00230 { 00231 // different PackBit schemes exist. The original from which 00232 // this code is derived used 0x80 | (RLE_pcount_ - 1) 00233 // to represent replicate runs. 00234 // DICOM instead uses 257 - RLE_pcount_ 00235 if (RLE_pcount_ > 128) RLE_buff_[0] = 0x81; 00236 else RLE_buff_[0] = OFstatic_cast(unsigned char, 257 - RLE_pcount_); 00237 move(2); 00238 } 00239 } 00240 00241 // now the buffer is guaranteed to be empty, re-initialize 00242 RLE_buff_[0] = 0; 00243 RLE_prev_ = -1; 00244 RLE_pcount_ = 0; 00245 RLE_bindex_ = 1; 00246 } 00247 } 00248 00256 inline size_t size() const 00257 { 00258 size_t result = blockList_.size() * DcmRLEEncoder_BLOCKSIZE + offset_; 00259 if (pad_ && (result & 1)) result++; // enforce even number of bytes 00260 return result; 00261 } 00262 00266 inline OFBool fail() const 00267 { 00268 if (fail_) return OFTrue; else return OFFalse; 00269 } 00270 00275 inline void write(void *target) const 00276 { 00277 if ((!fail_) && target) 00278 { 00279 unsigned char *current = NULL; 00280 unsigned char *target8 = OFstatic_cast(unsigned char *, target); 00281 OFListConstIterator(unsigned char *) first = blockList_.begin(); 00282 OFListConstIterator(unsigned char *) last = blockList_.end(); 00283 while (first != last) 00284 { 00285 current = *first; 00286 memcpy(target8, current, DcmRLEEncoder_BLOCKSIZE); 00287 target8 += DcmRLEEncoder_BLOCKSIZE; 00288 ++first; 00289 } 00290 if (offset_ > 0) 00291 { 00292 memcpy(target8, currentBlock_, offset_); 00293 } 00294 00295 // pad to even number of bytes if necessary 00296 if (pad_ && ((blockList_.size() * DcmRLEEncoder_BLOCKSIZE + offset_) & 1)) 00297 { 00298 target8 += offset_; 00299 *target8 = 0; 00300 } 00301 } 00302 } 00303 00308 inline void write(DcmEncoderOutputStream& os) const 00309 { 00310 if (!fail_) 00311 { 00312 OFListConstIterator(unsigned char *) first = blockList_.begin(); 00313 OFListConstIterator(unsigned char *) last = blockList_.end(); 00314 while (first != last) 00315 { 00316 os.write(*first, DcmRLEEncoder_BLOCKSIZE); 00317 ++first; 00318 } 00319 if (offset_ > 0) 00320 { 00321 os.write(currentBlock_, offset_); 00322 } 00323 00324 // pad to even number of bytes if necessary 00325 if (pad_ && ((blockList_.size() * DcmRLEEncoder_BLOCKSIZE + offset_) & 1)) 00326 { 00327 unsigned char c = 0; 00328 os.write(&c, 1); 00329 } 00330 } 00331 } 00332 00333 private: 00334 00336 DcmRLEEncoder(const DcmRLEEncoder&); 00337 00339 DcmRLEEncoder& operator=(const DcmRLEEncoder&); 00340 00346 inline void move(size_t numberOfBytes) 00347 { 00348 size_t i=0; 00349 while (i < numberOfBytes) 00350 { 00351 if (offset_ == DcmRLEEncoder_BLOCKSIZE) 00352 { 00353 blockList_.push_back(currentBlock_); 00354 currentBlock_ = new unsigned char[DcmRLEEncoder_BLOCKSIZE]; 00355 offset_ = 0; 00356 if (! currentBlock_) // out of memory 00357 { 00358 fail_ = 1; 00359 break; // exit while loop 00360 } 00361 } 00362 currentBlock_[offset_++] = RLE_buff_[i++]; 00363 } 00364 } 00365 00366 /* member variables */ 00367 00373 int fail_; 00374 00379 int pad_; 00380 00385 unsigned char *currentBlock_; 00386 00391 size_t offset_; 00392 00397 OFList<unsigned char *> blockList_; 00398 00402 unsigned char *RLE_buff_; 00403 00408 int RLE_prev_; 00409 00413 int RLE_pcount_; 00414 00417 unsigned int RLE_bindex_; 00418 00419 }; 00420 00421 #endif 00422 00423 00424 /* 00425 * CVS/RCS Log 00426 * $Log: dcrleenc.h,v $ 00427 * Revision 1.13 2010-10-14 13:15:42 joergr 00428 * Updated copyright header. Added reference to COPYRIGHT file. 00429 * 00430 * Revision 1.12 2005/12/16 09:04:47 onken 00431 * - Added virtual (dummy) destructor to avoid compiler warnings 00432 * 00433 * Revision 1.11 2005/12/08 16:28:38 meichel 00434 * Changed include path schema for all DCMTK header files 00435 * 00436 * Revision 1.10 2004/01/16 14:06:20 joergr 00437 * Removed acknowledgements with e-mail addresses from CVS log. 00438 * 00439 * Revision 1.9 2003/08/14 09:00:56 meichel 00440 * Adapted type casts to new-style typecast operators defined in ofcast.h 00441 * 00442 * Revision 1.8 2003/06/12 18:21:24 joergr 00443 * Modified code to use const_iterators where appropriate (required for STL). 00444 * 00445 * Revision 1.7 2003/06/12 13:32:59 joergr 00446 * Fixed inconsistent API documentation reported by Doxygen. 00447 * 00448 * Revision 1.6 2003/03/21 13:06:46 meichel 00449 * Minor code purifications for warnings reported by MSVC in Level 4 00450 * 00451 * Revision 1.5 2002/11/27 12:07:22 meichel 00452 * Adapted module dcmdata to use of new header file ofstdinc.h 00453 * 00454 * Revision 1.4 2002/07/18 12:16:52 joergr 00455 * Replaced return statement by break in a while loop of an inline function (not 00456 * supported by Sun CC 2.0.1). 00457 * 00458 * Revision 1.3 2002/07/08 07:02:50 meichel 00459 * RLE codec now includes <string.h>, needed for memcpy on Win32 00460 * 00461 * Revision 1.2 2002/06/27 15:15:42 meichel 00462 * Modified RLE encoder to make it usable for other purposes than 00463 * DICOM encoding as well (e.g. PostScript, TIFF) 00464 * 00465 * Revision 1.1 2002/06/06 14:52:37 meichel 00466 * Initial release of the new RLE codec classes 00467 * and the dcmcrle/dcmdrle tools in module dcmdata 00468 * 00469 * 00470 */