00001 /* 00002 * 00003 * Copyright (C) 2002-2005, OFFIS 00004 * 00005 * This software and supporting documentation were developed by 00006 * 00007 * Kuratorium OFFIS e.V. 00008 * Healthcare Information and Communication Systems 00009 * Escherweg 2 00010 * D-26121 Oldenburg, Germany 00011 * 00012 * THIS SOFTWARE IS MADE AVAILABLE, AS IS, AND OFFIS MAKES NO WARRANTY 00013 * REGARDING THE SOFTWARE, ITS PERFORMANCE, ITS MERCHANTABILITY OR 00014 * FITNESS FOR ANY PARTICULAR USE, FREEDOM FROM ANY COMPUTER DISEASES OR 00015 * ITS CONFORMITY TO ANY SPECIFICATION. THE ENTIRE RISK AS TO QUALITY AND 00016 * PERFORMANCE OF THE SOFTWARE IS WITH THE USER. 00017 * 00018 * Module: dcmdata 00019 * 00020 * Author: Marco Eichelberg 00021 * 00022 * Purpose: RLE compressor 00023 * 00024 * Last Update: $Author: onken $ 00025 * Update Date: $Date: 2005/12/16 09:04:47 $ 00026 * CVS/RCS Revision: $Revision: 1.12 $ 00027 * Status: $State: Exp $ 00028 * 00029 * CVS/RCS Log at end of file 00030 * 00031 */ 00032 00033 #ifndef DCRLEENC_H 00034 #define DCRLEENC_H 00035 00036 #include "dcmtk/config/osconfig.h" 00037 #include "dcmtk/ofstd/oflist.h" /* for class OFList<> */ 00038 00039 #define INCLUDE_CSTRING 00040 #include "dcmtk/ofstd/ofstdinc.h" 00041 00042 #define DcmRLEEncoder_BLOCKSIZE 16384 00043 00044 00049 class DcmEncoderOutputStream 00050 { 00051 public: 00056 virtual void write(const unsigned char *buf, size_t bufsize) =0; 00057 00060 virtual ~DcmEncoderOutputStream() {} 00061 00062 }; 00063 00064 00068 class DcmRLEEncoder 00069 { 00070 public: 00071 00075 DcmRLEEncoder(int doPad) 00076 : fail_(0) 00077 , pad_(doPad) 00078 , currentBlock_(new unsigned char[DcmRLEEncoder_BLOCKSIZE]) 00079 , offset_(0) 00080 , blockList_() 00081 , RLE_buff_(new unsigned char[132]) 00082 , RLE_prev_(-1) 00083 , RLE_pcount_(0) 00084 , RLE_bindex_(1) 00085 { 00086 if ((! RLE_buff_)||(! currentBlock_)) fail_ = 1; 00087 else RLE_buff_[0] = 0; 00088 } 00089 00091 ~DcmRLEEncoder() 00092 { 00093 delete[] currentBlock_; 00094 delete[] RLE_buff_; 00095 OFListIterator(unsigned char *) first = blockList_.begin(); 00096 OFListIterator(unsigned char *) last = blockList_.end(); 00097 while (first != last) 00098 { 00099 delete[] *first; 00100 first = blockList_.erase(first); 00101 } 00102 } 00103 00108 inline void add(unsigned char ch) 00109 { 00110 if (! fail_) // if fail_ is true, just ignore input 00111 { 00112 // if the current byte equals the last byte read 00113 // (which is initialized with the "impossible" value -1), 00114 // just increase the repeat counter 00115 if (OFstatic_cast(int, ch) == RLE_prev_) RLE_pcount_++; 00116 else 00117 { 00118 // byte is different from last byte read. 00119 // flush replicate run if necessary 00120 switch (RLE_pcount_) 00121 { 00122 case 0: 00123 // happens only after construction or flush() 00124 break; 00125 case 2: 00126 // two bytes in repeat buffer. Convert to literal run 00127 RLE_buff_[RLE_bindex_++] = OFstatic_cast(unsigned char, RLE_prev_); 00128 // no break. Fall-through into next case statement is intended. 00129 case 1: 00130 // one (or two) bytes in repeat buffer. Convert to literal run 00131 RLE_buff_[RLE_bindex_++] = OFstatic_cast(unsigned char, RLE_prev_); 00132 break; 00133 default: 00134 // more than two bytes in repeat buffer. Convert to replicate run 00135 if (RLE_bindex_ > 1) 00136 { 00137 // there is a literal run in the buffer that must be flushed 00138 // before the replicate run. Flush literal run now. 00139 RLE_buff_[0] = OFstatic_cast(unsigned char, RLE_bindex_-2); 00140 move(RLE_bindex_); 00141 } 00142 // this is the byte value for the repeat run 00143 RLE_buff_[1] = OFstatic_cast(unsigned char, RLE_prev_); 00144 // write as many repeat runs as necessary 00145 for (; RLE_pcount_>0; RLE_pcount_-=128) 00146 { 00147 // different PackBit schemes exist. The original from which 00148 // this code is derived used 0x80 | (RLE_pcount_ - 1) 00149 // to represent replicate runs. 00150 // DICOM instead uses 257 - RLE_pcount_ 00151 if (RLE_pcount_ > 128) RLE_buff_[0] = 0x81; 00152 else RLE_buff_[0] = OFstatic_cast(unsigned char, 257 - RLE_pcount_); 00153 move(2); 00154 } 00155 // now the buffer is guaranteed to be empty 00156 RLE_buff_[0] = 0; 00157 RLE_bindex_ = 1; 00158 break; 00159 } 00160 00161 // if we have 128 or more bytes in the literal run, flush buffer 00162 if (RLE_bindex_ > 129) 00163 { 00164 RLE_buff_[0] = 127; 00165 move(129); 00166 RLE_bindex_ -= 128; 00167 if (RLE_bindex_ > 1) 00168 RLE_buff_[1] = RLE_buff_[129]; 00169 if (RLE_bindex_ > 2) 00170 RLE_buff_[2] = RLE_buff_[130]; 00171 } 00172 00173 // current byte is stored in RLE_prev_, RLE_pcount_ is 1. 00174 RLE_prev_ = ch; 00175 RLE_pcount_ = 1; 00176 } 00177 } 00178 } 00179 00185 inline void add(const unsigned char *buf, size_t bufcount) 00186 { 00187 if (buf) 00188 { 00189 while (bufcount--) add(*buf++); 00190 } 00191 } 00192 00199 inline void flush() 00200 { 00201 if (! fail_) // if fail_ is true, do nothing 00202 { 00203 // if there are max 1 bytes in the repeat counter, convert to literal run 00204 if (RLE_pcount_ < 2) 00205 { 00206 for (; RLE_pcount_>0; --RLE_pcount_) RLE_buff_[RLE_bindex_++] = OFstatic_cast(unsigned char, RLE_prev_); 00207 } 00208 00209 // if we have 128 or more bytes in the literal run, flush buffer 00210 if (RLE_bindex_ > 129) 00211 { 00212 RLE_buff_[0] = 127; 00213 move(129); 00214 RLE_bindex_ -= 128; 00215 if (RLE_bindex_ > 1) 00216 RLE_buff_[1] = RLE_buff_[129]; 00217 if (RLE_bindex_ > 2) 00218 RLE_buff_[2] = RLE_buff_[130]; 00219 } 00220 00221 // if there is still a literal run in the buffer, flush literal run 00222 if (RLE_bindex_ > 1) 00223 { 00224 RLE_buff_[0] = OFstatic_cast(unsigned char, RLE_bindex_-2); 00225 move(RLE_bindex_); 00226 } 00227 00228 // if there is a remaining repeat run, flush this one as well 00229 if (RLE_pcount_ >= 2) 00230 { 00231 RLE_buff_[1] = OFstatic_cast(unsigned char, RLE_prev_); 00232 // write as many repeat runs as necessary 00233 for (; RLE_pcount_>0; RLE_pcount_-=128) 00234 { 00235 // different PackBit schemes exist. The original from which 00236 // this code is derived used 0x80 | (RLE_pcount_ - 1) 00237 // to represent replicate runs. 00238 // DICOM instead uses 257 - RLE_pcount_ 00239 if (RLE_pcount_ > 128) RLE_buff_[0] = 0x81; 00240 else RLE_buff_[0] = OFstatic_cast(unsigned char, 257 - RLE_pcount_); 00241 move(2); 00242 } 00243 } 00244 00245 // now the buffer is guaranteed to be empty, re-initialize 00246 RLE_buff_[0] = 0; 00247 RLE_prev_ = -1; 00248 RLE_pcount_ = 0; 00249 RLE_bindex_ = 1; 00250 } 00251 } 00252 00260 inline size_t size() const 00261 { 00262 size_t result = blockList_.size() * DcmRLEEncoder_BLOCKSIZE + offset_; 00263 if (pad_ && (result & 1)) result++; // enforce even number of bytes 00264 return result; 00265 } 00266 00270 inline OFBool fail() const 00271 { 00272 if (fail_) return OFTrue; else return OFFalse; 00273 } 00274 00279 inline void write(void *target) const 00280 { 00281 if ((!fail_) && target) 00282 { 00283 unsigned char *current = NULL; 00284 unsigned char *target8 = OFstatic_cast(unsigned char *, target); 00285 OFListConstIterator(unsigned char *) first = blockList_.begin(); 00286 OFListConstIterator(unsigned char *) last = blockList_.end(); 00287 while (first != last) 00288 { 00289 current = *first; 00290 memcpy(target8, current, DcmRLEEncoder_BLOCKSIZE); 00291 target8 += DcmRLEEncoder_BLOCKSIZE; 00292 ++first; 00293 } 00294 if (offset_ > 0) 00295 { 00296 memcpy(target8, currentBlock_, offset_); 00297 } 00298 00299 // pad to even number of bytes if necessary 00300 if (pad_ && ((blockList_.size() * DcmRLEEncoder_BLOCKSIZE + offset_) & 1)) 00301 { 00302 target8 += offset_; 00303 *target8 = 0; 00304 } 00305 } 00306 } 00307 00312 inline void write(DcmEncoderOutputStream& os) const 00313 { 00314 if (!fail_) 00315 { 00316 OFListConstIterator(unsigned char *) first = blockList_.begin(); 00317 OFListConstIterator(unsigned char *) last = blockList_.end(); 00318 while (first != last) 00319 { 00320 os.write(*first, DcmRLEEncoder_BLOCKSIZE); 00321 ++first; 00322 } 00323 if (offset_ > 0) 00324 { 00325 os.write(currentBlock_, offset_); 00326 } 00327 00328 // pad to even number of bytes if necessary 00329 if (pad_ && ((blockList_.size() * DcmRLEEncoder_BLOCKSIZE + offset_) & 1)) 00330 { 00331 unsigned char c = 0; 00332 os.write(&c, 1); 00333 } 00334 } 00335 } 00336 00337 private: 00338 00340 DcmRLEEncoder(const DcmRLEEncoder&); 00341 00343 DcmRLEEncoder& operator=(const DcmRLEEncoder&); 00344 00350 inline void move(size_t numberOfBytes) 00351 { 00352 size_t i=0; 00353 while (i < numberOfBytes) 00354 { 00355 if (offset_ == DcmRLEEncoder_BLOCKSIZE) 00356 { 00357 blockList_.push_back(currentBlock_); 00358 currentBlock_ = new unsigned char[DcmRLEEncoder_BLOCKSIZE]; 00359 offset_ = 0; 00360 if (! currentBlock_) // out of memory 00361 { 00362 fail_ = 1; 00363 break; // exit while loop 00364 } 00365 } 00366 currentBlock_[offset_++] = RLE_buff_[i++]; 00367 } 00368 } 00369 00370 /* member variables */ 00371 00377 int fail_; 00378 00383 int pad_; 00384 00389 unsigned char *currentBlock_; 00390 00395 size_t offset_; 00396 00401 OFList<unsigned char *> blockList_; 00402 00406 unsigned char *RLE_buff_; 00407 00412 int RLE_prev_; 00413 00417 int RLE_pcount_; 00418 00421 unsigned int RLE_bindex_; 00422 00423 }; 00424 00425 #endif 00426 00427 00428 /* 00429 * CVS/RCS Log 00430 * $Log: dcrleenc.h,v $ 00431 * Revision 1.12 2005/12/16 09:04:47 onken 00432 * - Added virtual (dummy) destructor to avoid compiler warnings 00433 * 00434 * Revision 1.11 2005/12/08 16:28:38 meichel 00435 * Changed include path schema for all DCMTK header files 00436 * 00437 * Revision 1.10 2004/01/16 14:06:20 joergr 00438 * Removed acknowledgements with e-mail addresses from CVS log. 00439 * 00440 * Revision 1.9 2003/08/14 09:00:56 meichel 00441 * Adapted type casts to new-style typecast operators defined in ofcast.h 00442 * 00443 * Revision 1.8 2003/06/12 18:21:24 joergr 00444 * Modified code to use const_iterators where appropriate (required for STL). 00445 * 00446 * Revision 1.7 2003/06/12 13:32:59 joergr 00447 * Fixed inconsistent API documentation reported by Doxygen. 00448 * 00449 * Revision 1.6 2003/03/21 13:06:46 meichel 00450 * Minor code purifications for warnings reported by MSVC in Level 4 00451 * 00452 * Revision 1.5 2002/11/27 12:07:22 meichel 00453 * Adapted module dcmdata to use of new header file ofstdinc.h 00454 * 00455 * Revision 1.4 2002/07/18 12:16:52 joergr 00456 * Replaced return statement by break in a while loop of an inline function (not 00457 * supported by Sun CC 2.0.1). 00458 * 00459 * Revision 1.3 2002/07/08 07:02:50 meichel 00460 * RLE codec now includes <string.h>, needed for memcpy on Win32 00461 * 00462 * Revision 1.2 2002/06/27 15:15:42 meichel 00463 * Modified RLE encoder to make it usable for other purposes than 00464 * DICOM encoding as well (e.g. PostScript, TIFF) 00465 * 00466 * Revision 1.1 2002/06/06 14:52:37 meichel 00467 * Initial release of the new RLE codec classes 00468 * and the dcmcrle/dcmdrle tools in module dcmdata 00469 * 00470 * 00471 */