core/vil/file_formats/vil_iris.cxx
Go to the documentation of this file.
00001 // This is core/vil/file_formats/vil_iris.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \author Joris Schouteden
00008 // \date 17 Feb 2000
00009 //
00010 // \verbatim
00011 //  Modifications
00012 //   Ported from vil1 by Peter Vanroose, 16 June 2003.
00013 //   12-Oct-2003 - PVr - being more careful with unsigned (bug fix for Alpha)
00014 // \endverbatim
00015 //
00016 //-----------------------------------------------------------------------------
00017 
00018 #include "vil_iris.h"
00019 
00020 #include <vcl_cassert.h>
00021 #include <vcl_cstring.h> // for memcpy()
00022 #include <vcl_iostream.h>
00023 
00024 #include <vil/vil_stream.h>
00025 #include <vil/vil_image_view.h>
00026 #include <vil/vil_exception.h>
00027 
00028 #include <vxl_config.h> // for vxl_byte, vxl_uint_32, ...
00029 
00030 static vxl_sint_16 get_short(vil_stream* file, int location = -1); // default -1 means: read at current position
00031 static vxl_uint_16 get_ushort(vil_stream* file, int location = -1);
00032 static char get_char(vil_stream* file, int location = -1);
00033 static vxl_sint_32 get_long(vil_stream* file, int location = -1);
00034 static void send_char(vil_stream* data, char s);
00035 static void send_short(vil_stream* data, vxl_sint_16 s);
00036 static void send_ushort(vil_stream* data, vxl_uint_16 s);
00037 static void send_long(vil_stream* data, vxl_sint_32 s);
00038 static void expandrow(unsigned char *optr, unsigned char *iptr, int z);
00039 
00040 char const* vil_iris_format_tag = "iris";
00041 
00042 vil_image_resource_sptr vil_iris_file_format::make_input_image(vil_stream* is)
00043 {
00044   is->seek(0L);
00045   if (is->file_size() < 84L) return 0;
00046   int colormap_;
00047 
00048   vxl_sint_16 magic_      = get_short(is);
00049   int storage_            = get_char(is);
00050   int bytes_per_component = get_char(is);
00051   int dimension_          = get_ushort(is);
00052   /*int ni_     =*/ get_ushort(is);
00053   /*int nj_     =*/ get_ushort(is);
00054   /*int nplanes_=*/ get_ushort(is);
00055   /*int pixmin_ =*/ get_long(is);
00056   /*int pixmax_ =*/ get_long(is);
00057 
00058   is->seek(24L);
00059   char imagename[81];
00060   if (is->read(imagename, 80L) != 80) return 0;
00061 
00062   colormap_ = get_long(is);
00063 
00064   if (magic_ != 474) return 0;
00065   if (storage_ != 0 && storage_ != 1) return 0;
00066   if (colormap_ == 3) return 0;
00067   if (dimension_ == 3 && colormap_ != 0) return 0;
00068   if (dimension_ > 3 || dimension_ < 1) return 0;
00069   if (bytes_per_component < 1 || bytes_per_component > 2) return 0;
00070 
00071   return new vil_iris_generic_image(is,imagename);
00072 }
00073 
00074 vil_image_resource_sptr vil_iris_file_format::make_output_image(vil_stream* is,
00075                                                                 unsigned int ni, unsigned int nj, unsigned int nplanes,
00076                                                                 vil_pixel_format format)
00077 {
00078   return new vil_iris_generic_image(is, ni, nj, nplanes, format);
00079 }
00080 
00081 char const* vil_iris_file_format::tag() const
00082 {
00083   return vil_iris_format_tag;
00084 }
00085 
00086 /////////////////////////////////////////////////////////////////////////////
00087 
00088 vil_iris_generic_image::vil_iris_generic_image(vil_stream* is, char const* imagename):
00089   starttab_(0), lengthtab_(0), is_(is)
00090 {
00091   is_->ref();
00092   read_header();
00093   vcl_strncpy(imagename_, imagename, 80);
00094 }
00095 
00096 bool vil_iris_generic_image::get_property(char const* /*tag*/, void* /*prop*/) const
00097 {
00098   // This is not an in-memory image type, nor is it read-only:
00099   return false;
00100 }
00101 
00102 char const* vil_iris_generic_image::file_format() const
00103 {
00104   return vil_iris_format_tag;
00105 }
00106 
00107 vil_iris_generic_image::vil_iris_generic_image(vil_stream* is,
00108                                                unsigned int ni, unsigned int nj, unsigned int nplanes,
00109                                                vil_pixel_format format)
00110   : starttab_(0), lengthtab_(0), is_(is), magic_(474), ni_(ni), nj_(nj),
00111     nplanes_(nplanes), format_(format), pixmin_(0),
00112     pixmax_(vil_pixel_format_sizeof_components(format)==1 ? 255 : 65535),
00113     storage_(0), dimension_(nplanes_==1 ? 2 : 3), colormap_(0)
00114 {
00115   is_->ref();
00116 
00117   if (vil_pixel_format_sizeof_components(format) <= 2)
00118   {
00119     vcl_strcpy(imagename_, "written by vil_iris_generic_image");
00120 
00121     if (nplanes_ != 1 && nplanes_ != 3 && nplanes_ != 4)
00122       vcl_cerr << __FILE__ ": Cannot write iris image, can only do grayscale or RGB(A)\n";
00123     write_header();
00124   }
00125   else vcl_cerr << __FILE__ ": Cannot write iris image, which needs 8 or 16 bits per component\n";
00126 }
00127 
00128 vil_iris_generic_image::~vil_iris_generic_image()
00129 {
00130   is_->unref();
00131   delete [] starttab_;
00132   delete [] lengthtab_;
00133 }
00134 
00135 bool vil_iris_generic_image::read_header()
00136 {
00137   is_->seek(0L);
00138 
00139   magic_ = get_short(is_, 0);
00140   if (magic_ != 474)
00141   {
00142     vcl_cerr << __FILE__ ": This is not an Iris RGB file: magic number is incorrect: "
00143              << magic_ << vcl_endl;
00144     return false;
00145   }
00146 
00147   storage_ = get_char(is_);
00148   if (storage_ != 0 && storage_ != 1)
00149   {
00150     vcl_cerr << __FILE__ ": This is not an Iris RGB file: storage must be RLE or VERBATIM\n";
00151     return false;
00152   }
00153 
00154   int bytes_per_component = get_char(is_);
00155   dimension_ = get_ushort(is_);
00156   ni_        = get_ushort(is_);
00157   nj_        = get_ushort(is_);
00158   nplanes_   = get_ushort(is_);
00159   pixmin_    = get_long(is_);
00160   pixmax_    = get_long(is_);
00161   format_ = bytes_per_component==1 ? VIL_PIXEL_FORMAT_BYTE :
00162             bytes_per_component==2 ? VIL_PIXEL_FORMAT_UINT_16 :
00163                                      VIL_PIXEL_FORMAT_UNKNOWN;
00164 
00165   // DUMMY1 starts at 20
00166   // image name starts at 24
00167 
00168   is_->seek(24L);
00169   is_->read(imagename_, 80L);
00170 
00171   // COLORMAP starts at 104
00172   colormap_ = get_long(is_);
00173 
00174   // _DUMMY2 starts at 108, ends at 512
00175 
00176   if (colormap_ == 3)
00177   {
00178     vcl_cerr << __FILE__ ": This is not an ordinary Iris RGB image but a colormap file which I cannot handle\n";
00179     return false;
00180   }
00181 
00182   if (dimension_ == 3 && colormap_ != 0)
00183   {
00184     vcl_cerr << __FILE__ ": Cannot handle Iris RGB file with colormap other than NORMAL\n";
00185     return false;
00186   }
00187 
00188   if (storage_)    // we got a RLE image
00189     read_offset_tables();
00190 
00191   return true;
00192 }
00193 
00194 
00195 bool vil_iris_generic_image::write_header()
00196 {
00197 #ifdef DEBUG
00198   vcl_cerr << __FILE__ ": vil_iris_generic_image::write_header()\n"
00199            << "Here we go :\n"
00200            << "magic_      = " << magic_    << vcl_endl
00201            << "storage_    = " << storage_ << vcl_endl
00202            << "format_     = " << format_ << vcl_endl
00203            << "dimension_  = " << dimension_ << vcl_endl
00204            << "ni_      = " << ni_ << vcl_endl
00205            << "nj_     = " << nj_ << vcl_endl
00206            << "nplanes_    = " << nplanes_ << vcl_endl
00207            << "pixmin_     = " << pixmin_ << vcl_endl
00208            << "pixmax_     = " << pixmax_ << vcl_endl
00209            << "colormap_   = " << colormap_ << vcl_endl
00210            << "imagename_  = " << imagename_ << vcl_endl
00211            << vcl_endl;
00212 #endif
00213 
00214   char dummy[410];
00215 
00216   send_short(is_, static_cast<vxl_sint_16>(magic_));
00217   send_char(is_, (char)storage_); // either VERBATIM (0) or RLE (1)
00218   send_char(is_, (char)vil_pixel_format_sizeof_components(format_));  // bytes per pixel per channel
00219   send_ushort(is_, static_cast<vxl_uint_16>(dimension_)); // either 1 (1 scanline), 2 (grey image), or 3 (colour)
00220   send_ushort(is_, static_cast<vxl_uint_16>(ni_));     // width
00221   send_ushort(is_, static_cast<vxl_uint_16>(nj_));     // height
00222   send_ushort(is_, static_cast<vxl_uint_16>(nplanes_));// nr of colour bands; typically 3 (RGB) or 4 (RGBA)
00223   send_long(is_, pixmin_);   // minimum pixel value; typically 0
00224   send_long(is_, pixmax_); // maximum pixel value; typically 255 if _PBC is 1
00225   is_->write(dummy, 4L);
00226   is_->write(imagename_, 80L); // null-terminated string
00227   send_long(is_, colormap_); // either NORMAL (0) (RGB), DITHERED (1) (R=3,G=3,B=2 bits),
00228                   // SCREEN (2) (obsolete) or COLORMAP (3) (hardware-specific).
00229 
00230   start_of_data_ = is_->tell();
00231 
00232   return is_->write(dummy, 404L) == 404L;
00233 }
00234 
00235 
00236 static inline void swap(void* p,int length)
00237 {
00238   char* t = (char*)p;
00239 #ifdef DEBUG
00240   if (length == sizeof(vxl_uint_32) && *(vxl_uint_32*)p != 0) {
00241     vcl_cerr << "Swapping " << *(vxl_uint_32*)p;
00242     if (length == sizeof(float))
00243       vcl_cerr << " (or " << *(float*)p << ')';
00244   }
00245 #endif
00246   for (int j=0;2*j<length;++j) { char c = t[j]; t[j] = t[length-j-1]; t[length-j-1] = c; }
00247 #ifdef DEBUG
00248   if (length == sizeof(vxl_uint_32) && *(vxl_uint_32*)p != 0) {
00249     vcl_cerr << " to " << *(vxl_uint_32*)p;
00250     if (length == sizeof(float))
00251       vcl_cerr << " (or " << *(float*)p << ')';
00252     vcl_cerr << '\n';
00253   }
00254 #endif
00255 }
00256 
00257 vil_image_view_base_sptr vil_iris_generic_image::get_copy_view( unsigned int x0, unsigned int xs,
00258                                                                 unsigned int y0, unsigned int ys) const
00259 {
00260   // I am not dealing with requests for memory outside the image
00261   // so just abort when getting any such request:
00262   assert(x0+xs<=ni_);
00263   assert(y0+ys<=nj_);
00264 
00265   if (storage_)
00266     return get_section_rle(x0,xs,y0,ys);
00267   else
00268     return get_section_verbatim(x0,xs,y0,ys);
00269 }
00270 
00271 
00272 vil_image_view_base_sptr vil_iris_generic_image::get_section_verbatim(unsigned int x0, unsigned int xs,
00273                                                                       unsigned int y0, unsigned int ys) const
00274 {
00275   unsigned int pix_size = vil_pixel_format_sizeof_components(format_);
00276   unsigned int row_len = xs * pix_size;
00277 
00278   vil_memory_chunk_sptr buf = new vil_memory_chunk(row_len*ys*nplanes_,format_);
00279   vxl_byte* ib = reinterpret_cast<vxl_byte*>(buf->data());
00280   vxl_uint_16* ob = reinterpret_cast<vxl_uint_16*>(buf->data());
00281   vxl_byte* cbi = ib;
00282 
00283   // for each channel
00284   for (unsigned int channel=0; channel<nplanes_; ++channel)
00285   {
00286     // for each row; storage is bottom row first!
00287     for (unsigned int row = nj_-y0-ys; row < nj_-y0; ++row,cbi+=row_len)
00288     {
00289       is_->seek(512L + (channel * ni_*nj_ + row * ni_ + x0) * pix_size);
00290       is_->read(cbi, row_len);
00291     }
00292   }
00293   if (VXL_LITTLE_ENDIAN && pix_size > 1) // IRIS image data is big-endian
00294     for (unsigned int i=0;i<xs*ys*nplanes_;++i)
00295       swap(ob+i,pix_size);
00296 
00297   // Note that jstep is negative!  Hence data ref pt is not ib but ib+xs*(ys-1)
00298   if (format_ == VIL_PIXEL_FORMAT_BYTE)
00299     return new vil_image_view<vxl_byte>(buf,ib+xs*(ys-1),xs,ys,nplanes_,1,-int(xs),xs*ys);
00300   else if (format_ == VIL_PIXEL_FORMAT_UINT_16)
00301     return new vil_image_view<vxl_uint_16>(buf,ob+xs*(ys-1),xs,ys,nplanes_,1,-int(xs),xs*ys);
00302   else
00303     return 0;
00304 }
00305 
00306 
00307 vil_image_view_base_sptr vil_iris_generic_image::get_section_rle(unsigned int x0, unsigned int xs,
00308                                                                  unsigned int y0, unsigned int ys) const
00309 {
00310   unsigned int pix_size = vil_pixel_format_sizeof_components(format_);
00311   unsigned int row_len = xs * pix_size;
00312 
00313   vil_memory_chunk_sptr buf = new vil_memory_chunk(row_len*ys*nplanes_,format_);
00314   vxl_byte* ib = reinterpret_cast<vxl_byte*>(buf->data());
00315   vxl_uint_16* ob = reinterpret_cast<vxl_uint_16*>(buf->data());
00316   vxl_byte* cbi = ib;
00317   unsigned char* exrow = new unsigned char[ni_];
00318 
00319   // for each channel
00320   for (unsigned int channel=0; channel<nplanes_; ++channel)
00321   {
00322     // for each row
00323     for (unsigned int rowno=nj_-y0-ys; rowno<nj_-y0; ++rowno,cbi+=row_len)
00324     {
00325       // find length and start position
00326       unsigned long rleoffset =  starttab_[rowno+channel*nj_];
00327       unsigned long rlelength = lengthtab_[rowno+channel*nj_];
00328 
00329       // read rle row into array
00330       unsigned char* rlerow = new unsigned char[rlelength];
00331       is_->seek(rleoffset);
00332       is_->read((void*)rlerow, rlelength);
00333 
00334       // decode rle row
00335       expandrow(exrow,rlerow,0);
00336       delete[] rlerow;
00337 
00338       // write expanded row in store
00339       vcl_memcpy(cbi,exrow+x0,xs);
00340     }
00341   }
00342   delete[] exrow;
00343   if (format_ == VIL_PIXEL_FORMAT_BYTE)
00344     return new vil_image_view<vxl_byte>(buf,ib+xs*(ys-1),xs,ys,nplanes_,1,-int(xs),xs*ys);
00345   else if (format_ == VIL_PIXEL_FORMAT_UINT_16)
00346     return new vil_image_view<vxl_uint_16>(buf,ob+xs*(ys-1),xs,ys,nplanes_,1,-int(xs),xs*ys);
00347   else
00348     return 0;
00349 }
00350 
00351 
00352 bool vil_iris_generic_image::put_view( vil_image_view_base const& buf, unsigned int x0, unsigned int y0)
00353 {
00354   assert(buf.pixel_format() == format_); // pixel formats of image and buffer must match
00355   if (!view_fits(buf, x0, y0))
00356   {
00357     vil_exception_warning(vil_exception_out_of_bounds("vil_iris_generic_image::put_view"));
00358     return false;
00359   }
00360 #ifdef DEBUG
00361   vcl_cerr << "vil_iris_image::put_view() : buf="
00362            << buf.ni()<<'x'<<buf.nj()<<'x'<< buf.nplanes()<<'p'
00363            << " at ("<<x0<<','<<y0<<")\n";
00364 #endif
00365   const vil_image_view<unsigned char>& buff = static_cast<vil_image_view<unsigned char> const&>(buf);
00366   const unsigned char* ob = buff.top_left_ptr();
00367   unsigned int pix_size = vil_pixel_format_sizeof_components(format_);
00368 
00369   vcl_size_t rowsize = pix_size*buf.ni();
00370   vcl_ptrdiff_t rowskip = pix_size*buff.jstep();
00371   vcl_size_t planeskip = pix_size*buff.planestep();
00372 
00373   if (VXL_LITTLE_ENDIAN && pix_size > 1) // IRIS image data is big-endian
00374   {
00375     // buffer for swapping bytes
00376     vxl_byte* tempbuf = new vxl_byte[rowsize];
00377     // for each channel
00378     for (unsigned int channel = 0; channel<nplanes_; ++channel) {
00379       ob += rowskip*buff.nj();
00380       // number of rows to write
00381       for (unsigned int y = nj_-y0-buf.nj(); y < nj_-y0; ++y) {
00382         ob -= rowskip;
00383         // skip to start of section
00384         is_->seek(512L + (channel * ni_*nj_ + y * ni_ + x0) * pix_size);
00385         // swap bytes before writing
00386         vcl_memcpy(tempbuf,ob,rowsize);
00387         for (unsigned int i=0;i<buf.ni();++i)
00388           swap(tempbuf+i*pix_size,pix_size);
00389         // write swapped bytes
00390         if ((vil_streampos)rowsize != is_->write(tempbuf, rowsize))
00391           vcl_cerr << "WARNING: " << __FILE__ << ":\n"
00392                    << " could not write "<<rowsize<<" bytes to stream;\n"
00393                    << " channel="<<channel<<", y="<<y<<'\n';
00394 #ifdef DEBUG
00395         else
00396           vcl_cerr << "written "<<rowsize<<" bytes to stream; channel="<<channel<<", y="<<y<<'\n';
00397 #endif
00398       }
00399       ob += planeskip;
00400     }
00401     delete[] tempbuf;
00402   }
00403   else // (VXL_BIG_ENDIAN || pix_size == 1)
00404   {
00405     // for each channel
00406     for (unsigned int channel = 0; channel<nplanes_; ++channel) {
00407       ob += rowskip*buff.nj();
00408       // number of rows to write
00409       for (unsigned int y = nj_-y0-buf.nj(); y < nj_-y0; ++y) {
00410         ob -= rowskip;
00411         // skip to start of section
00412         is_->seek(512L + (channel * ni_*nj_ + y * ni_ + x0) * pix_size);
00413         if ((vil_streampos)rowsize != is_->write(ob, rowsize))
00414           vcl_cerr << "WARNING: " << __FILE__ << ":\n"
00415                    << " could not write "<<rowsize<<" bytes to stream;\n"
00416                    << " channel="<<channel<<", y="<<y<<'\n';
00417 #ifdef DEBUG
00418         else
00419           vcl_cerr << "written "<<rowsize<<" bytes to stream; channel="<<channel<<", y="<<y<<'\n';
00420 #endif
00421       }
00422       ob += planeskip;
00423     }
00424   }
00425   return true;
00426 }
00427 
00428 bool vil_iris_generic_image::read_offset_tables()
00429 {
00430   unsigned int tablen = nj_ * nplanes_;
00431 
00432   starttab_  = new unsigned long[tablen];
00433   lengthtab_ = new unsigned long[tablen];
00434 
00435   for (unsigned int i=0; i<tablen; ++i) {
00436     starttab_[i] = get_long(is_,512+(i*4));
00437   }
00438 
00439   unsigned int lengthtab_offset =  512 + tablen*4;
00440 
00441   for (unsigned int i=0; i<tablen; ++i) {
00442     lengthtab_[i] = get_long(is_,lengthtab_offset+(i*4));
00443   }
00444 
00445   return true;
00446 }
00447 
00448 
00449 vxl_sint_16 get_short(vil_stream* file, int location)
00450 {
00451   if (location >= 0) file->seek(location);
00452 
00453   vxl_byte buff[2];
00454   file->read(buff, 2L);
00455 
00456   // Decode from two's complement to machine format
00457   vxl_uint_16 bits = static_cast<vxl_uint_16>(( buff[0] << 8 ) + buff[1]);
00458 
00459   if ( ( bits & 0x8000 ) != 0 )
00460     return static_cast<vxl_sint_16>(-( ~bits + 1 ));
00461   else
00462     return static_cast<vxl_sint_16>( bits );
00463 }
00464 
00465 
00466 char get_char(vil_stream* file, int location)
00467 {
00468   if (location >= 0) file->seek(location);
00469 
00470   char buff[1];
00471   file->read((void*)buff, 1L);
00472   return buff[0];
00473 }
00474 
00475 vxl_uint_16 get_ushort(vil_stream* file, int location)
00476 {
00477   if (location >= 0) file->seek(location);
00478 
00479   unsigned char buff[2];
00480   file->read((void*)buff, 2L);
00481   return static_cast<vxl_uint_16>((buff[0]<<8)+(buff[1]<<0));
00482 }
00483 
00484 vxl_sint_32 get_long(vil_stream* file, int location)
00485 {
00486   if (location >= 0) file->seek(location);
00487 
00488   vxl_byte buff[4];
00489   if (file->read((void*)buff, 4L) != 4L) return 0;
00490 
00491   // Decode from two's complement to machine format
00492   vxl_uint_32 bits = ( vxl_uint_32(buff[0]) << 24 ) |
00493                      ( vxl_uint_32(buff[1]) << 16 ) |
00494                      ( vxl_uint_32(buff[2]) <<  8 ) |
00495                                    buff[3];
00496 
00497   if ( ( bits & 0x80000000L ) != 0 )
00498     return -vxl_sint_32( ~bits + 1 );
00499   else
00500     return vxl_sint_32( bits );
00501 }
00502 
00503 
00504 void send_char(vil_stream* data, char s)
00505 {
00506   data->write(&s ,1L);
00507 }
00508 
00509 void send_short(vil_stream* data, vxl_sint_16 s)
00510 {
00511   vxl_uint_16 bits;
00512   if ( s < 0 ) {
00513     bits = static_cast<vxl_uint_16>(-s);
00514     bits = static_cast<vxl_uint_16>(~bits + 1);
00515   }
00516   else {
00517     bits = static_cast<vxl_uint_16>(s);
00518   }
00519 
00520   vxl_byte buff[2];
00521   buff[0] = static_cast<vxl_byte>((bits >>  8) & 0xff);
00522   buff[1] = static_cast<vxl_byte>( bits        & 0xff);
00523   data->write(buff, 2L);
00524 }
00525 
00526 void send_ushort(vil_stream* data, vxl_uint_16 s)
00527 {
00528   unsigned char buff[2];
00529   buff[0] = static_cast<unsigned char>((s >> 8) & 0xff);
00530   buff[1] = static_cast<unsigned char>( s       & 0xff);
00531   data->write(buff, 2L);
00532 }
00533 
00534 void send_long(vil_stream* data, vxl_sint_32 s)
00535 {
00536   // The write out the value as a two's complement number in MSB order
00537 
00538   vxl_uint_32 bits;
00539   if ( s < 0 ) {
00540     bits = -s;
00541     bits = ~bits + 1;
00542   }
00543   else {
00544     bits = s;
00545   }
00546 
00547   vxl_byte buff[4];
00548   buff[0] = static_cast<unsigned char>( (bits >> 24) & 0xff );
00549   buff[1] = static_cast<unsigned char>( (bits >> 16) & 0xff );
00550   buff[2] = static_cast<unsigned char>( (bits >>  8) & 0xff );
00551   buff[3] = static_cast<unsigned char>(  bits        & 0xff );
00552   data->write(buff, 4L);
00553 }
00554 
00555 void expandrow(unsigned char *optr, unsigned char *iptr, int z)
00556 {
00557   unsigned char pixel, count;
00558 
00559   optr += z;
00560   while (true)
00561   {
00562     pixel = *iptr++;
00563     if ( !(count = static_cast<unsigned char>(pixel & 0x7f)) )
00564       return;
00565     if (pixel & 0x80)
00566     {
00567       while (count--) { *optr = *iptr++; ++optr; }
00568     }
00569     else
00570     {
00571       pixel = *iptr++;
00572       while (count--) { *optr = pixel; ++optr; }
00573     }
00574   }
00575 }