vil_ras.cxx
Go to the documentation of this file.
1 // This is core/vil/file_formats/vil_ras.cxx
2 //:
3 // \file
4 
5 #include <iostream>
6 #include <vector>
7 #include "vil_ras.h"
8 
9 #include <cassert>
10 #ifdef _MSC_VER
11 # include <vcl_msvc_warnings.h>
12 #endif
13 
14 #include <vil/vil_stream.h>
15 #include <vil/vil_image_resource.h>
16 #include <vil/vil_image_view.h>
17 #include <vil/vil_memory_chunk.h>
18 #include <vil/vil_exception.h>
19 
20 #include <vxl_config.h>
21 
22 char const* vil_ras_format_tag = "ras";
23 
24 
25 // ===========================================================================
26 // helper routines
27 
28 
29 namespace
30 {
31 #if VXL_LITTLE_ENDIAN
32  //: Change the byte order on a little endian machine.
33  // Do nothing on a big endian machine.
34  inline
35  void swap_endian( vxl_uint_32& word )
36  {
37  auto* bytes = reinterpret_cast<vxl_uint_8*>( &word );
38  vxl_uint_8 t = bytes[0];
39  bytes[0] = bytes[3];
40  bytes[3] = t;
41  t = bytes[1];
42  bytes[1] = bytes[2];
43  bytes[2] = t;
44  }
45 #endif
46 
47  //: Equivalent of ntoh
48  // Read a big-endian word from the stream, storing it in the
49  // native format.
50  bool read_uint_32( vil_stream* vs, vxl_uint_32& word )
51  {
52  if ( vs->read( &word, 4 ) < 4 )
53  return false;
54 #if VXL_LITTLE_ENDIAN
55  swap_endian( word );
56 #endif
57  return true;
58  }
59 
60  //: Equivalent of hton
61  // Write a host-format word as to a big-endian formatted stream.
62  bool write_uint_32( vil_stream* vs, vxl_uint_32 word )
63  {
64 #if VXL_LITTLE_ENDIAN
65  swap_endian( word );
66 #endif
67  return vs->write( &word, 4 ) == 4;
68  }
69 
70  //: Compute the length of the data.
71  // Compute the length of the data from the width, height and depth,
72  // accounting for any padding that may be necessary to keep the scan
73  // lines on 16-bit boundaries.
74  vxl_uint_32 compute_length( vxl_uint_32 w, vxl_uint_32 h, vxl_uint_32 d )
75  {
76  w *= (d/8);
77  w += (w%2);
78  return h*w;
79  }
80 
81  // From http://gmt.soest.hawaii.edu/gmt/doc/html/GMT_Docs/node111.html
82  // and other documents on the web.
83  const vxl_uint_8 RAS_MAGIC[] = { 0x59, 0xA6, 0x6A, 0x95 };
84  constexpr vxl_uint_32 RT_OLD = 0; //< Raw pixrect image in MSB-first order
85  constexpr vxl_uint_32 RT_STANDARD = 1; //< Raw pixrect image in MSB-first order
86  constexpr vxl_uint_32 RT_BYTE_ENCODED = 2; //< (Run-length compression of bytes)
87  constexpr vxl_uint_32 RT_FORMAT_RGB = 3; //< ([X]RGB instead of [X]BGR)
88  constexpr vxl_uint_32 RMT_NONE = 0; //< No colourmap (ras_maplength is expected to be 0)
89  constexpr vxl_uint_32 RMT_EQUAL_RGB = 1; //< (red[ras_maplength/3],green[],blue[])
90 }
91 
92 
93 // ===========================================================================
94 // vil_ras_file_format
95 
96 
100 {
101  // Check the magic number
102  vxl_uint_8 buf[4] = { 0, 0, 0, 0 };
103  vs->read( buf, 4 );
104  if ( ! ( buf[0] == RAS_MAGIC[0] && buf[1] == RAS_MAGIC[1] &&
105  buf[2] == RAS_MAGIC[2] && buf[3] == RAS_MAGIC[3] ) )
106  return nullptr;
107 
108  return new vil_ras_image( vs );
109 }
110 
111 
114  unsigned ni,
115  unsigned nj,
116  unsigned nplanes,
117  vil_pixel_format format)
118 {
119  return new vil_ras_image(vs, ni, nj, nplanes, format );
120 }
121 
122 
123 char const*
125 tag() const
126 {
127  return vil_ras_format_tag;
128 }
129 
130 
131 // ===========================================================================
132 // vil_ras_image
133 
134 
137  vs_(vs)
138 {
139  vs_->ref();
140  read_header();
141 }
142 
143 
144 bool
146 get_property( char const* /*tag*/, void* /*prop*/ ) const
147 {
148  // This is not an in-memory image type, nor is it read-only:
149  return false;
150 }
151 
152 
153 char const*
155 file_format() const
156 {
157  return vil_ras_format_tag;
158 }
159 
160 
163  unsigned ni,
164  unsigned nj,
165  unsigned nplanes,
166  vil_pixel_format format )
167 {
168  vs_ = vs; vs_->ref();
169  width_ = ni;
170  height_ = nj;
171 
173  if ( components_ != 1 && components_ != 3 )
174  {
175  std::cerr << __FILE__ << ": can't handle "
176  << nplanes << " x "
177  << vil_pixel_format_num_components( format ) << " components\n";
178  return;
179  }
180 
182 
183  if ( bits_per_component_ != 8 ) {
184  std::cerr << __FILE__ << ": can't handle " << bits_per_component_ << " bits per component\n";
185  return;
186  }
187 
189 
190  if (components_ == 3)
191  type_ = RT_FORMAT_RGB;
192  else
193  type_ = RT_STANDARD;
194  map_type_ = RMT_NONE;
195  map_length_ = 0;
196  length_ = compute_length( width_, height_, depth_ );
197  col_map_ = nullptr;
198 
199  write_header();
200 }
201 
202 
205 {
206  delete[] col_map_;
207  vs_->unref();
208 }
209 
210 
211 //: Read the header of a Sun raster file.
212 bool
215 {
216  // Go to start of file
217  vs_->seek(0);
218 
219  vxl_uint_8 buf[4];
220  if ( vs_->read(buf, 4) < 4 ) // at end-of-file?
221  return false;
222  if (! ( buf[0] == RAS_MAGIC[0] && buf[1] == RAS_MAGIC[1] &&
223  buf[2] == RAS_MAGIC[2] && buf[3] == RAS_MAGIC[3] ) )
224  return false; // magic number isn't correct
225 
226  if ( !( read_uint_32( vs_, width_ ) &&
227  read_uint_32( vs_, height_ ) &&
228  read_uint_32( vs_, depth_ ) &&
229  read_uint_32( vs_, length_ ) &&
230  read_uint_32( vs_, type_ ) &&
231  read_uint_32( vs_, map_type_ ) &&
232  read_uint_32( vs_, map_length_ ) ) )
233  return false;
234 
235  // Do consistency checks of the header
236  if ( map_type_ != RMT_NONE || depth_ == 24 ) {
237  components_ = 3;
238  } else {
239  components_ = 1;
240  }
241 
242  if (type_ != RT_OLD && type_ != RT_STANDARD &&
243  type_ != RT_BYTE_ENCODED && type_ != RT_FORMAT_RGB ) {
244  std::cerr << __FILE__ << ": unknown type " << type_ << std::endl;
245  return false;
246  }
247  if ( map_type_ != RMT_NONE && map_type_ != RMT_EQUAL_RGB ) {
248  std::cerr << __FILE__ << ": unknown map type " << map_type_ << std::endl;
249  return false;
250  }
251  if ( map_type_ == RMT_NONE && map_length_ != 0 ) {
252  std::cerr << __FILE__ << ": No colour map according to header, but there is a map!\n";
253  return false;
254  }
255 
256  if ( depth_ != 8 && !(depth_== 16&&components_==1) && depth_ != 24 ) {
257  std::cerr << __FILE__ << ": depth " << depth_ << " not implemented\n";
258  return false;
259  }
260 
261  // The "old" format always has length set to zero, so we should compute it ourselves.
262  if ( type_ == RT_OLD ) {
263  length_ = compute_length( width_, height_, depth_ );
264  }
265  if ( length_ == 0 ) {
266  std::cerr << __FILE__ << ": header says image has length zero\n";
267  return false;
268  }
269  if ( type_ != RT_BYTE_ENCODED && length_ != compute_length( width_, height_, depth_ ) ) {
270  std::cerr << __FILE__ << ": length " << length_ << " does not match wxhxd = "
271  << compute_length( width_, height_, depth_ ) << std::endl;
272  return false;
273  }
274  if ( map_length_ % 3 != 0 ) {
275  std::cerr << __FILE__ << ": color map length is not a multiple of 3\n";
276  return false;
277  }
278 
279  if ( map_length_ ) {
280  col_map_ = new vxl_uint_8[ map_length_ ];
282  } else {
283  col_map_ = nullptr;
284  }
285 
286  start_of_data_ = vs_->tell();
287 
289  if (components_==1)
291  return true;
292 }
293 
294 
295 bool
298 {
299  vs_->seek(0);
300 
301  vs_->write( RAS_MAGIC, 4 );
302 
303  // no color map for the files we write
304  assert( map_length_ == 0 );
305 
306  write_uint_32( vs_, width_ );
307  write_uint_32( vs_, height_ );
308  write_uint_32( vs_, depth_ );
309  write_uint_32( vs_, length_ );
310  write_uint_32( vs_, type_ );
311  write_uint_32( vs_, map_type_ );
312  write_uint_32( vs_, map_length_ );
313 
314  start_of_data_ = vs_->tell();
315 
316  return true;
317 }
318 
319 
320 unsigned
322 nplanes() const
323 {
324  return components_;
325 }
326 
327 
328 unsigned
330 ni() const
331 {
332  return width_;
333 }
334 
335 
336 unsigned
338 nj() const
339 {
340  return height_;
341 }
342 
346 {
347  if (bits_per_component_ == 8)
348  return VIL_PIXEL_FORMAT_BYTE;
349  if (bits_per_component_ == 16)
352 }
353 
356 get_copy_view( unsigned i0, unsigned ni,
357  unsigned j0, unsigned nj ) const
358 {
359  if ( type_ == RT_BYTE_ENCODED )
360  return nullptr; // not yet implemented
361 
362  unsigned file_bytes_per_pixel = (depth_+7)/8;
363  unsigned buff_bytes_per_pixel = components_ * ( (bits_per_component_+7)/8 );
364  unsigned file_byte_width = width_ * file_bytes_per_pixel;
365  file_byte_width += ( file_byte_width % 2 ); // each scan line ends on a 16bit boundary
366  vil_streampos file_byte_start = start_of_data_ + j0 * file_byte_width + i0 * file_bytes_per_pixel;
367  unsigned buff_byte_width = ni * buff_bytes_per_pixel;
369  vil_memory_chunk_sptr buf = new vil_memory_chunk(ni * nj * buff_bytes_per_pixel, fmt );
370 
371  auto* ib = reinterpret_cast<vxl_uint_8*>( buf->data() );
372 
373  if ( !col_map_ )
374  {
375  // No colourmap, so just read in the bytes.
376  // Make the component order RGB to avoid surprising the user.
377  for ( unsigned j = 0; j < nj; ++j ) {
378  vs_->seek( file_byte_start + j * file_byte_width );
379  vs_->read( ib + j * buff_byte_width, buff_byte_width );
380 
381  if ( type_ != RT_FORMAT_RGB && components_ == 3 ) {
382  vxl_uint_8* pixel = ib + j * buff_byte_width;
383  for ( unsigned i = 0; i < ni; ++i ) {
384  vxl_uint_8* rp = pixel+2;
385  vxl_uint_8 t = *pixel;
386  *pixel = *rp;
387  *rp = t;
388  pixel += 3;
389  }
390  }
391  }
392  }
393  else
394  {
395  assert( file_bytes_per_pixel == 1 && buff_bytes_per_pixel == 3 );
396  unsigned col_len = map_length_ / 3;
397  // Read a line, and map every index into an RGB triple
398  std::vector<vxl_uint_8> line( ni );
399  for ( unsigned j = 0; j < nj; ++j ) {
400  vs_->seek( file_byte_start + j * file_byte_width );
401  vs_->read( &line[0], line.size() );
402  vxl_uint_8* in_p = &line[0];
403  vxl_uint_8* out_p = ib + j * buff_byte_width;
404  for ( unsigned i=0; i < ni; ++i ) {
405  assert( *in_p < col_len );
406  *(out_p++) = col_map_[ *in_p ];
407  *(out_p++) = col_map_[ *in_p + col_len ];
408  *(out_p++) = col_map_[ *in_p + 2*col_len ];
409  ++in_p;
410  }
411  }
412  }
413  if (fmt==VIL_PIXEL_FORMAT_BYTE)
414  return new vil_image_view<vxl_byte>( buf, ib,
415  ni, nj, components_,
416  components_, components_*ni, 1 );
418  {
419  //Sun raster format is always written in big endian format so we may need to reverse the bytes
420 #if (VXL_LITTLE_ENDIAN)
421  vxl_byte s[2];
422  for (unsigned long is = 0; is<ni*nj*2; is+=2)
423  {
424  s[0]= *(ib+is);
425  s[1]= *(ib+is+1);
426  *(ib+is)=s[1];
427  *(ib+is+1)=s[0];
428  }
429 #endif
430  auto* sib = reinterpret_cast<vxl_uint_16*>(ib);
431  return new vil_image_view<vxl_uint_16>( buf, sib,
432  ni, nj, components_,
433  components_, components_*ni, 1 );
434  }
435  return nullptr;
436 }
437 
438 
439 bool
441 put_view( const vil_image_view_base& view, unsigned i0, unsigned j0 )
442 {
443  // Get a 3-plane view of the section
444  vil_image_view<vxl_uint_8> section( view );
445 
446  if ( ! this->view_fits( section, i0, j0 ) ) {
447  vil_exception_warning(vil_exception_out_of_bounds("vil_ras_image::put_view"));
448  return false;
449  }
450 
451  if ( section.nplanes() != components_ ) {
452  std::cerr << "ERROR: " << __FILE__ << ": data parameters of view don't match\n";
453  return false;
454  }
455 
456  if ( col_map_ ) {
457  std::cerr << __FILE__ << ": writing to file with a colour map is not implemented\n";
458  return false;
459  }
460  if ( type_ == RT_BYTE_ENCODED ) {
461  std::cerr << __FILE__ << ": writing to a run-length encoded file is not implemented\n";
462  return false;
463  }
464  if ( components_ == 3 && type_ != RT_FORMAT_RGB ) {
465  std::cerr << __FILE__ << ": writing BGR format is not implemented\n";
466  return false;
467  }
468 
469  // With the restrictions above, writing is simple. Just dump the bytes.
470 
471  unsigned file_bytes_per_pixel = (depth_+7)/8;
472  unsigned buff_bytes_per_pixel = components_ * ( (bits_per_component_+7)/8 );
473  unsigned file_byte_width = width_ * file_bytes_per_pixel;
474  file_byte_width += ( file_byte_width % 2 ); // each scan line ends on a 16bit boundary
475  vil_streampos file_byte_start = start_of_data_ + j0 * file_byte_width + i0 * file_bytes_per_pixel;
476  unsigned buff_byte_width = view.ni() * buff_bytes_per_pixel;
477 
478  assert( file_bytes_per_pixel == buff_bytes_per_pixel );
479  assert( file_byte_width >= buff_byte_width );
480 
481  // If we are writing full line widths, then make sure the padding
482  // byte is set to zero. Otherwise, assume that the current data is
483  // valid, and therefore that the padding byte is already zero.
484  //
485  std::vector<vxl_uint_8> data_buffer;
486  if ( file_byte_width == buff_byte_width+1 ) {
487  data_buffer.resize( file_byte_width );
488  data_buffer[ file_byte_width-1 ] = 0;
489  } else {
490  data_buffer.resize( buff_byte_width );
491  }
492 
493  for ( unsigned j = 0; j < section.nj(); ++j )
494  {
495  // Copy a line of the image into a contiguous buffer. No need to
496  // optimize for the case with section is also appropriately
497  // contiguous because the disk writing process will likely be the
498  // bottleneck.
499  //
500  vxl_uint_8* ptr = &data_buffer[0];
501  for ( unsigned i = i0; i < section.ni(); ++i ) {
502  for ( unsigned p = 0; p < section.nplanes(); ++p ) {
503  *ptr = section(i,j,p);
504  ++ptr;
505  }
506  }
507 
508  // Write the line to disk.
509  vs_->seek( file_byte_start + j * file_byte_width );
510  vs_->write( &data_buffer[0], data_buffer.size() );
511  }
512 
513  return true;
514 }
Stream interface for VIL image loaders.
An abstract base class of smart pointers to actual image data in memory.
vil_pixel_format
Describes the type of the concrete data.
virtual bool view_fits(const vil_image_view_base &im, unsigned i0, unsigned j0)
Check that a view will fit into the data at the given offset.
unsigned components_
Definition: vil_ras.h:43
virtual vil_streampos tell() const =0
Return file pointer.
vxl_uint_8 * col_map_
Definition: vil_ras.h:53
bool put_view(const vil_image_view_base &im, unsigned i0, unsigned j0) override
Put the data in this view back into the image source.
Definition: vil_ras.cxx:441
bool write_header()
Definition: vil_ras.cxx:297
bool get_property(char const *tag, void *prop=nullptr) const override
Extra property information.
Definition: vil_ras.cxx:146
vxl_uint_32 length_
Definition: vil_ras.h:49
virtual vil_streampos write(void const *buf, vil_streampos n)=0
Write n bytes from buf. Returns number of bytes written.
Concrete view of image data of type T held in memory.
Definition: vil_fwd.h:13
vil_streampos start_of_data_
Definition: vil_ras.h:46
~vil_ras_image() override
Definition: vil_ras.cxx:204
virtual void seek(vil_streampos position)=0
Goto file pointer.
virtual vil_streampos read(void *buf, vil_streampos n)=0
Read n bytes into buf. Returns number of bytes read.
vxl_uint_32 depth_
Definition: vil_ras.h:48
Exceptions thrown by vil, and a mechanism for turning them off.
char const * file_format() const override
Return a string describing the file format.
Definition: vil_ras.cxx:155
vxl_uint_32 type_
Definition: vil_ras.h:50
vil_image_resource_sptr make_output_image(vil_stream *vs, unsigned ni, unsigned nj, unsigned nplanes, vil_pixel_format format) override
Make a "generic_image" on which put_section may be applied.
Definition: vil_ras.cxx:113
unsigned vil_pixel_format_sizeof_components(enum vil_pixel_format f)
Return the number of bytes used by each component of pixel format f.
unsigned bits_per_component_
Definition: vil_ras.h:44
vxl_uint_32 map_type_
Definition: vil_ras.h:51
unsigned ni() const
Width.
Stream interface for VIL image loaders.
Definition: vil_stream.h:21
vil_stream * vs_
Definition: vil_ras.h:40
unsigned nj() const
Height.
Generic image implementation for Sun raster files.
Definition: vil_ras.h:38
Indicates that some reference was made to pixels beyond the bounds of an image.
Definition: vil_exception.h:81
vil_ras_image(vil_stream *is)
Definition: vil_ras.cxx:136
char const * vil_ras_format_tag
Definition: vil_ras.cxx:22
unsigned vil_pixel_format_num_components(enum vil_pixel_format f)
Return the number of components in pixel format f.
void ref()
up/down the reference count.
Definition: vil_stream.h:45
vil_image_view_base_sptr get_copy_view() const
Create a read/write view of a copy of all the data.
A base class reference-counting view of some image data.
Ref. counted block of data on the heap.
void unref()
Definition: vil_stream.cxx:31
bool read_header()
Read the header of a Sun raster file.
Definition: vil_ras.cxx:214
unsigned nplanes() const
Number of planes.
unsigned nplanes() const override
Dimensions: Planes x ni x nj.
Definition: vil_ras.cxx:322
vxl_uint_32 width_
Definition: vil_ras.h:41
vil_image_resource_sptr make_input_image(vil_stream *vs) override
Attempt to make a generic_image which will read from vil_stream vs.
Definition: vil_ras.cxx:99
vxl_int_32 vil_streampos
Definition: vil_stream.h:16
unsigned nj() const override
Dimensions: Planes x ni x nj.
Definition: vil_ras.cxx:338
void vil_exception_warning(T exception)
Throw an exception indicating a potential problem.
Definition: vil_exception.h:37
Representation of a generic image source or destination.
char const * tag() const override
Return a character string which uniquely identifies this format.
Definition: vil_ras.cxx:125
vil_pixel_format pixel_format() const override
Pixel Format.
Definition: vil_ras.cxx:345
vxl_uint_32 map_length_
Definition: vil_ras.h:52
vxl_uint_32 height_
Definition: vil_ras.h:42
unsigned ni() const override
Dimensions: Planes x ni x nj.
Definition: vil_ras.cxx:330