core/vil/file_formats/vil_ras.cxx
Go to the documentation of this file.
00001 // This is core/vil/file_formats/vil_ras.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 
00008 #include "vil_ras.h"
00009 
00010 #include <vcl_cassert.h>
00011 #include <vcl_iostream.h>
00012 #include <vcl_vector.h>
00013 
00014 #include <vil/vil_stream.h>
00015 #include <vil/vil_image_resource.h>
00016 #include <vil/vil_image_view.h>
00017 #include <vil/vil_memory_chunk.h>
00018 #include <vil/vil_exception.h>
00019 
00020 #include <vxl_config.h>
00021 
00022 char const* vil_ras_format_tag = "ras";
00023 
00024 
00025 // ===========================================================================
00026 //                                                             helper routines
00027 
00028 
00029 namespace
00030 {
00031 #if VXL_LITTLE_ENDIAN
00032   //: Change the byte order on a little endian machine.
00033   // Do nothing on a big endian machine.
00034   inline
00035   void swap_endian( vxl_uint_32& word )
00036   {
00037     vxl_uint_8* bytes = reinterpret_cast<vxl_uint_8*>( &word );
00038     vxl_uint_8 t = bytes[0];
00039     bytes[0] = bytes[3];
00040     bytes[3] = t;
00041     t = bytes[1];
00042     bytes[1] = bytes[2];
00043     bytes[2] = t;
00044   }
00045 #endif
00046 
00047   //: Equivalent of ntoh
00048   // Read a big-endian word from the stream, storing it in the
00049   // native format.
00050   bool read_uint_32( vil_stream* vs, vxl_uint_32& word )
00051   {
00052     if ( vs->read( &word, 4 ) < 4 )
00053       return false;
00054 #if VXL_LITTLE_ENDIAN
00055     swap_endian( word );
00056 #endif
00057     return true;
00058   }
00059 
00060   //: Equivalent of hton
00061   // Write a host-format word as to a big-endian formatted stream.
00062   bool write_uint_32( vil_stream* vs, vxl_uint_32 word )
00063   {
00064 #if VXL_LITTLE_ENDIAN
00065     swap_endian( word );
00066 #endif
00067     return vs->write( &word, 4 ) == 4;
00068   }
00069 
00070   //: Compute the length of the data.
00071   // Compute the length of the data from the width, height and depth,
00072   // accounting for any padding that may be necessary to keep the scan
00073   // lines on 16-bit boundaries.
00074   vxl_uint_32 compute_length( vxl_uint_32 w, vxl_uint_32 h, vxl_uint_32 d )
00075   {
00076     w *= (d/8);
00077     w += (w%2);
00078     return h*w;
00079   }
00080 
00081   // From http://gmt.soest.hawaii.edu/gmt/doc/html/GMT_Docs/node111.html
00082   // and other documents on the web.
00083   const vxl_uint_8 RAS_MAGIC[] = { 0x59, 0xA6, 0x6A, 0x95 };
00084   const vxl_uint_32 RT_OLD = 0;          //< Raw pixrect image in MSB-first order
00085   const vxl_uint_32 RT_STANDARD = 1;     //< Raw pixrect image in MSB-first order
00086   const vxl_uint_32 RT_BYTE_ENCODED = 2; //< (Run-length compression of bytes)
00087   const vxl_uint_32 RT_FORMAT_RGB = 3;   //< ([X]RGB instead of [X]BGR)
00088   const vxl_uint_32 RMT_NONE = 0;        //< No colourmap (ras_maplength is expected to be 0)
00089   const vxl_uint_32 RMT_EQUAL_RGB = 1;   //< (red[ras_maplength/3],green[],blue[])
00090 }
00091 
00092 
00093 // ===========================================================================
00094 //                                                        vil_ras_file_format
00095 
00096 
00097 vil_image_resource_sptr
00098 vil_ras_file_format::
00099 make_input_image( vil_stream* vs )
00100 {
00101   // Check the magic number
00102   vxl_uint_8 buf[4] = { 0, 0, 0, 0 };
00103   vs->read( buf, 4 );
00104   if ( ! ( buf[0] == RAS_MAGIC[0] && buf[1] == RAS_MAGIC[1] &&
00105            buf[2] == RAS_MAGIC[2] && buf[3] == RAS_MAGIC[3]  ) )
00106     return 0;
00107 
00108   return new vil_ras_image( vs );
00109 }
00110 
00111 
00112 vil_image_resource_sptr
00113 vil_ras_file_format::make_output_image( vil_stream* vs,
00114                                         unsigned ni,
00115                                         unsigned nj,
00116                                         unsigned nplanes,
00117                                         vil_pixel_format format)
00118 {
00119   return new vil_ras_image(vs, ni, nj, nplanes, format );
00120 }
00121 
00122 
00123 char const*
00124 vil_ras_file_format::
00125 tag() const
00126 {
00127   return vil_ras_format_tag;
00128 }
00129 
00130 
00131 // ===========================================================================
00132 //                                                              vil_ras_image
00133 
00134 
00135 vil_ras_image::
00136 vil_ras_image( vil_stream* vs ):
00137   vs_(vs)
00138 {
00139   vs_->ref();
00140   read_header();
00141 }
00142 
00143 
00144 bool
00145 vil_ras_image::
00146 get_property( char const* /*tag*/, void* /*prop*/ ) const
00147 {
00148   // This is not an in-memory image type, nor is it read-only:
00149   return false;
00150 }
00151 
00152 
00153 char const*
00154 vil_ras_image::
00155 file_format() const
00156 {
00157   return vil_ras_format_tag;
00158 }
00159 
00160 
00161 vil_ras_image::
00162 vil_ras_image(vil_stream* vs,
00163               unsigned ni,
00164               unsigned nj,
00165               unsigned nplanes,
00166               vil_pixel_format format )
00167 {
00168   vs_ = vs; vs_->ref();
00169   width_ = ni;
00170   height_ = nj;
00171 
00172   components_ = nplanes * vil_pixel_format_num_components( format );
00173   if ( components_ != 1 && components_ != 3 )
00174   {
00175     vcl_cerr << __FILE__ << ": can't handle "
00176              << nplanes << " x "
00177              << vil_pixel_format_num_components( format ) << " components\n";
00178     return;
00179   }
00180 
00181   bits_per_component_ = 8 * vil_pixel_format_sizeof_components( format );
00182 
00183   if ( bits_per_component_ != 8 ) {
00184     vcl_cerr << __FILE__ << ": can't handle " << bits_per_component_ << " bits per component\n";
00185     return;
00186   }
00187 
00188   depth_ = components_ * bits_per_component_;
00189 
00190   if (components_ == 3)
00191     type_ = RT_FORMAT_RGB;
00192   else
00193     type_ = RT_STANDARD;
00194   map_type_ = RMT_NONE;
00195   map_length_ = 0;
00196   length_ = compute_length( width_, height_, depth_ );
00197   col_map_ = 0;
00198 
00199   write_header();
00200 }
00201 
00202 
00203 vil_ras_image::
00204 ~vil_ras_image()
00205 {
00206   delete[] col_map_;
00207   vs_->unref();
00208 }
00209 
00210 
00211 //: Read the header of a Sun raster file.
00212 bool
00213 vil_ras_image::
00214 read_header()
00215 {
00216   // Go to start of file
00217   vs_->seek(0);
00218 
00219   vxl_uint_8 buf[4];
00220   if ( vs_->read(buf, 4) < 4 ) // at end-of-file?
00221     return false;
00222   if (! ( buf[0] == RAS_MAGIC[0] && buf[1] == RAS_MAGIC[1] &&
00223           buf[2] == RAS_MAGIC[2] && buf[3] == RAS_MAGIC[3]  ) )
00224     return false; // magic number isn't correct
00225 
00226   if ( !( read_uint_32( vs_, width_ ) &&
00227           read_uint_32( vs_, height_ ) &&
00228           read_uint_32( vs_, depth_ ) &&
00229           read_uint_32( vs_, length_ ) &&
00230           read_uint_32( vs_, type_ ) &&
00231           read_uint_32( vs_, map_type_ ) &&
00232           read_uint_32( vs_, map_length_ ) ) )
00233     return false;
00234 
00235   // Do consistency checks of the header
00236   if ( map_type_ != RMT_NONE || depth_ == 24 ) {
00237     components_ = 3;
00238   } else {
00239     components_ = 1;
00240   }
00241 
00242   if (type_ != RT_OLD && type_ != RT_STANDARD &&
00243       type_ != RT_BYTE_ENCODED && type_ != RT_FORMAT_RGB ) {
00244     vcl_cerr << __FILE__ << ": unknown type " << type_ << vcl_endl;
00245     return false;
00246   }
00247   if ( map_type_ != RMT_NONE && map_type_ != RMT_EQUAL_RGB ) {
00248     vcl_cerr << __FILE__ << ": unknown map type " << map_type_ << vcl_endl;
00249     return false;
00250   }
00251   if ( map_type_ == RMT_NONE && map_length_ != 0 ) {
00252     vcl_cerr << __FILE__ << ": No colour map according to header, but there is a map!\n";
00253     return false;
00254   }
00255 
00256   if ( depth_ != 8 && !(depth_== 16&&components_==1) && depth_ != 24 ) {
00257     vcl_cerr << __FILE__ << ": depth " << depth_ << " not implemented\n";
00258     return false;
00259   }
00260 
00261   // The "old" format always has length set to zero, so we should compute it ourselves.
00262   if ( type_ == RT_OLD ) {
00263     length_ = compute_length( width_, height_, depth_ );
00264   }
00265   if ( length_ == 0 ) {
00266     vcl_cerr << __FILE__ << ": header says image has length zero\n";
00267     return false;
00268   }
00269   if ( type_ != RT_BYTE_ENCODED && length_ != compute_length( width_, height_, depth_ ) ) {
00270     vcl_cerr << __FILE__ << ": length " << length_ << " does not match wxhxd = "
00271              << compute_length( width_, height_, depth_ ) << vcl_endl;
00272     return false;
00273   }
00274   if ( map_length_ % 3 != 0 ) {
00275     vcl_cerr << __FILE__ << ": color map length is not a multiple of 3\n";
00276     return false;
00277   }
00278 
00279   if ( map_length_ ) {
00280     col_map_ = new vxl_uint_8[ map_length_ ];
00281     vs_->read( col_map_, (vil_streampos)map_length_ );
00282   } else {
00283     col_map_ = 0;
00284   }
00285 
00286   start_of_data_ = vs_->tell();
00287 
00288   bits_per_component_ = 8;
00289   if (components_==1)
00290     bits_per_component_ = depth_;
00291   return true;
00292 }
00293 
00294 
00295 bool
00296 vil_ras_image::
00297 write_header()
00298 {
00299   vs_->seek(0);
00300 
00301   vs_->write( RAS_MAGIC, 4 );
00302 
00303   // no color map for the files we write
00304   assert( map_length_ == 0 );
00305 
00306   write_uint_32( vs_, width_ );
00307   write_uint_32( vs_, height_ );
00308   write_uint_32( vs_, depth_ );
00309   write_uint_32( vs_, length_ );
00310   write_uint_32( vs_, type_ );
00311   write_uint_32( vs_, map_type_ );
00312   write_uint_32( vs_, map_length_ );
00313 
00314   start_of_data_ = vs_->tell();
00315 
00316   return true;
00317 }
00318 
00319 
00320 unsigned
00321 vil_ras_image::
00322 nplanes() const
00323 {
00324   return components_;
00325 }
00326 
00327 
00328 unsigned
00329 vil_ras_image::
00330 ni() const
00331 {
00332   return width_;
00333 }
00334 
00335 
00336 unsigned
00337 vil_ras_image::
00338 nj() const
00339 {
00340   return height_;
00341 }
00342 
00343 vil_pixel_format
00344 vil_ras_image::
00345 pixel_format() const
00346 {
00347   if (bits_per_component_ == 8)
00348     return VIL_PIXEL_FORMAT_BYTE;
00349   if (bits_per_component_ == 16)
00350     return VIL_PIXEL_FORMAT_UINT_16;
00351   return VIL_PIXEL_FORMAT_UNKNOWN;
00352 }
00353 
00354 vil_image_view_base_sptr
00355 vil_ras_image::
00356 get_copy_view( unsigned i0, unsigned ni,
00357                unsigned j0, unsigned nj ) const
00358 {
00359   if ( type_ == RT_BYTE_ENCODED )
00360     return 0; // not yet implemented
00361 
00362   unsigned file_bytes_per_pixel = (depth_+7)/8;
00363   unsigned buff_bytes_per_pixel = components_ * ( (bits_per_component_+7)/8 );
00364   unsigned file_byte_width = width_ * file_bytes_per_pixel;
00365   file_byte_width += ( file_byte_width % 2 ); // each scan line ends on a 16bit boundary
00366   vil_streampos file_byte_start = start_of_data_ + j0 * file_byte_width + i0 * file_bytes_per_pixel;
00367   unsigned buff_byte_width = ni * buff_bytes_per_pixel;
00368   vil_pixel_format fmt = pixel_format();
00369   vil_memory_chunk_sptr buf = new vil_memory_chunk(ni * nj * buff_bytes_per_pixel, fmt );
00370 
00371   vxl_uint_8* ib = reinterpret_cast<vxl_uint_8*>( buf->data() );
00372 
00373   if ( !col_map_ )
00374   {
00375     // No colourmap, so just read in the bytes.
00376     // Make the component order RGB to avoid surprising the user.
00377     for ( unsigned j = 0; j < nj; ++j ) {
00378       vs_->seek( file_byte_start + j * file_byte_width );
00379       vs_->read( ib + j * buff_byte_width, buff_byte_width );
00380 
00381       if ( type_ != RT_FORMAT_RGB && components_ == 3 ) {
00382         vxl_uint_8* pixel = ib + j * buff_byte_width;
00383         for ( unsigned i = 0; i < ni; ++i ) {
00384           vxl_uint_8* rp = pixel+2;
00385           vxl_uint_8 t = *pixel;
00386           *pixel = *rp;
00387           *rp = t;
00388           pixel += 3;
00389         }
00390       }
00391     }
00392   }
00393   else
00394   {
00395     assert( file_bytes_per_pixel == 1 && buff_bytes_per_pixel == 3 );
00396     unsigned col_len = map_length_ / 3;
00397     // Read a line, and map every index into an RGB triple
00398     vcl_vector<vxl_uint_8> line( ni );
00399     for ( unsigned j = 0; j < nj; ++j ) {
00400       vs_->seek( file_byte_start + j * file_byte_width );
00401       vs_->read( &line[0], line.size() );
00402       vxl_uint_8* in_p = &line[0];
00403       vxl_uint_8* out_p = ib + j * buff_byte_width;
00404       for ( unsigned i=0; i < ni; ++i ) {
00405         assert( *in_p < col_len );
00406         *(out_p++) = col_map_[ *in_p ];
00407         *(out_p++) = col_map_[ *in_p + col_len ];
00408         *(out_p++) = col_map_[ *in_p + 2*col_len ];
00409         ++in_p;
00410       }
00411     }
00412   }
00413   if (fmt==VIL_PIXEL_FORMAT_BYTE)
00414     return new vil_image_view<vxl_byte>( buf, ib,
00415                                          ni, nj, components_,
00416                                          components_, components_*ni, 1 );
00417   if (fmt==VIL_PIXEL_FORMAT_UINT_16&&components_==1)
00418   {
00419     //Sun raster format is always written in big endian format so we may need to reverse the bytes
00420 #if (VXL_LITTLE_ENDIAN)
00421     vxl_byte s[2];
00422     for (unsigned long is = 0; is<ni*nj*2; is+=2)
00423     {
00424       s[0]= *(ib+is);
00425       s[1]= *(ib+is+1);
00426       *(ib+is)=s[1];
00427       *(ib+is+1)=s[0];
00428     }
00429 #endif
00430     vxl_uint_16* sib = reinterpret_cast<vxl_uint_16*>(ib);
00431     return new vil_image_view<vxl_uint_16>( buf, sib,
00432                                             ni, nj, components_,
00433                                             components_, components_*ni, 1 );
00434   }
00435   return 0;
00436 }
00437 
00438 
00439 bool
00440 vil_ras_image::
00441 put_view( const vil_image_view_base& view, unsigned i0, unsigned j0 )
00442 {
00443   // Get a 3-plane view of the section
00444   vil_image_view<vxl_uint_8> section( view );
00445 
00446   if ( ! this->view_fits( section, i0, j0 ) ) {
00447     vil_exception_warning(vil_exception_out_of_bounds("vil_ras_image::put_view"));
00448     return false;
00449   }
00450 
00451   if ( section.nplanes() != components_ ) {
00452     vcl_cerr << "ERROR: " << __FILE__ << ": data parameters of view don't match\n";
00453     return false;
00454   }
00455 
00456   if ( col_map_ ) {
00457     vcl_cerr << __FILE__ << ": writing to file with a colour map is not implemented\n";
00458     return false;
00459   }
00460   if ( type_ == RT_BYTE_ENCODED ) {
00461     vcl_cerr << __FILE__ << ": writing to a run-length encoded file is not implemented\n";
00462     return false;
00463   }
00464   if ( components_ == 3 && type_ != RT_FORMAT_RGB ) {
00465     vcl_cerr << __FILE__ << ": writing BGR format is not implemented\n";
00466     return false;
00467   }
00468 
00469   // With the restrictions above, writing is simple. Just dump the bytes.
00470 
00471   unsigned file_bytes_per_pixel = (depth_+7)/8;
00472   unsigned buff_bytes_per_pixel = components_ * ( (bits_per_component_+7)/8 );
00473   unsigned file_byte_width = width_ * file_bytes_per_pixel;
00474   file_byte_width += ( file_byte_width % 2 ); // each scan line ends on a 16bit boundary
00475   vil_streampos file_byte_start = start_of_data_ + j0 * file_byte_width + i0 * file_bytes_per_pixel;
00476   unsigned buff_byte_width = view.ni() * buff_bytes_per_pixel;
00477 
00478   assert( file_bytes_per_pixel == buff_bytes_per_pixel );
00479   assert( file_byte_width >= buff_byte_width );
00480 
00481   // If we are writing full line widths, then make sure the padding
00482   // byte is set to zero. Otherwise, assume that the current data is
00483   // valid, and therefore that the padding byte is already zero.
00484   //
00485   vcl_vector<vxl_uint_8> data_buffer;
00486   if ( file_byte_width == buff_byte_width+1 ) {
00487     data_buffer.resize( file_byte_width );
00488     data_buffer[ file_byte_width-1 ] = 0;
00489   } else {
00490     data_buffer.resize( buff_byte_width );
00491   }
00492 
00493   for ( unsigned j = 0; j < section.nj(); ++j )
00494   {
00495     // Copy a line of the image into a contiguous buffer. No need to
00496     // optimize for the case with section is also appropriately
00497     // contiguous because the disk writing process will likely be the
00498     // bottleneck.
00499     //
00500     vxl_uint_8* ptr = &data_buffer[0];
00501     for ( unsigned i = i0; i < section.ni(); ++i ) {
00502       for ( unsigned p = 0; p < section.nplanes(); ++p ) {
00503         *ptr = section(i,j,p);
00504         ++ptr;
00505       }
00506     }
00507 
00508     // Write the line to disk.
00509     vs_->seek( file_byte_start + j * file_byte_width );
00510     vs_->write( &data_buffer[0], data_buffer.size() );
00511   }
00512 
00513   return true;
00514 }