vil_bmp.cxx
Go to the documentation of this file.
1 // This is core/vil/file_formats/vil_bmp.cxx
2 
3 #include <iostream>
4 #include <vector>
5 #include <cstring>
6 #include "vil_bmp.h"
7 
8 #include <cassert>
9 #ifdef _MSC_VER
10 # include <vcl_msvc_warnings.h>
11 #endif
12 #include <vil/vil_stream.h>
13 #include <vil/vil_property.h>
14 #include <vil/vil_memory_chunk.h>
15 #include <vil/vil_image_view.h>
16 #include <vil/vil_exception.h>
17 
18 #define where (std::cerr << __FILE__ " : " << __LINE__ << " : ")
19 
20 //--------------------------------------------------------------------------------
21 
23 {
24  // Attempt to read header
26  vs->seek(0L);
27  hdr.read(vs);
28 
29  if ( hdr.signature_valid() )
30  return new vil_bmp_image(vs);
31  else
32  return nullptr;
33 }
34 
36  unsigned nx,
37  unsigned ny,
38  unsigned nplanes,
39  vil_pixel_format format)
40 {
41  return new vil_bmp_image(vs, nx, ny, nplanes, format);
42 }
43 
44 char const* vil_bmp_format_tag = "bmp";
45 
46 char const* vil_bmp_file_format::tag() const
47 {
48  return vil_bmp_format_tag;
49 }
50 
51 /////////////////////////////////////////////////////////////////////////////
52 
53 char const* vil_bmp_image::file_format() const
54 {
55  return vil_bmp_format_tag;
56 }
57 
59  : is_(is)
60  , bit_map_start(-1L)
61 #if 0
62  , freds_colormap(0)
63  , local_color_map_(0)
64 #endif
65 {
66  is_->ref();
67  if (!read_header())
68  vil_exception_error(vil_exception_image_io("vil_bmp_image::read_header", "BMP", ""));
69 }
70 
71 bool vil_bmp_image::get_property(char const * tag, void * value) const
72 {
73  if (std::strcmp(vil_property_quantisation_depth, tag)==0)
74  {
75  if (value)
76  *static_cast<unsigned int*>(value) = core_hdr.bitsperpixel / nplanes();
77  return true;
78  }
79 
80  return false;
81 }
82 
83 vil_bmp_image::vil_bmp_image(vil_stream* vs, unsigned nx, unsigned ny,
84  unsigned nplanes, vil_pixel_format format):
85  is_(vs), bit_map_start(-1L)
86 {
87  if (format != VIL_PIXEL_FORMAT_BYTE)
88  {
90  //std::cerr << "Sorry -- pixel format " << format << " not yet supported\n";
91  return;
92  }
93  if(nplanes != 1 && nplanes != 3 && nplanes != 4)
94  {
95  vil_exception_warning(vil_exception_unsupported_operation("vil_bmp_image::vil_bmp_image: invalid number of planes"));
96  //std::cerr << "Sorry -- pixel format " << format << " not yet supported\n";
97  return;
98  }
99 
100  // FIXME - we only support 8, 24 bpp, and 32bpp; add support for 1, 4, and 16 bpp
101  assert(format == VIL_PIXEL_FORMAT_BYTE);
102  assert(nplanes == 1 || nplanes == 3 || nplanes == 4);
103 
104  is_->ref();
105 
106  // core_hdr.header_size is set up for us.
107  core_hdr.width = int(nx);
108  core_hdr.height = -int(ny); // use top-down approach
109  core_hdr.planes = 1;
110 
112 
113  write_header();
114 }
115 
117 {
118 #if 0
119  // we must get rid of the local_color_map_;
120  if (local_color_map_)
121  {
122  delete [] local_color_map_[0];
123  delete [] local_color_map_[1];
124  delete [] local_color_map_[2];
125  delete local_color_map_;
126  }
127 
128  if (freds_colormap)
129  {
130  delete [] freds_colormap[0];
131  delete [] freds_colormap[1];
132  delete [] freds_colormap[2];
133  delete [] freds_colormap[3];
134  delete [] freds_colormap;
135  freds_colormap = 0;
136  }
137 #endif
138 
139  is_->unref();
140 }
141 
142 unsigned vil_bmp_image::nplanes() const
143 {
144  return (core_hdr.bitsperpixel<24)?1:core_hdr.bitsperpixel/8; // FIXME
145 }
146 
147 unsigned vil_bmp_image::ni() const
148 {
149  return (core_hdr.width>=0)?core_hdr.width:-core_hdr.width; // width is signed integer
150 }
151 
152 unsigned vil_bmp_image::nj() const
153 {
154  return (core_hdr.height>=0)?core_hdr.height:-core_hdr.height; // height is signed integer
155 }
156 
158 {
159  // seek to beginning and read file header.
160  is_->seek(0L);
161  file_hdr.read(is_);
162  if ( ! file_hdr.signature_valid() )
163  {
164  where << "File is not a valid BMP file\n";
165  return false;
166  }
167 #ifdef DEBUG
168  file_hdr.print(std::cerr); // blather
169 #endif
170 
171  // read core header
172  core_hdr.read(is_);
173 #ifdef DEBUG
174  core_hdr.print(std::cerr); // blather
175 #endif
176  // allowed values for bitsperpixel are 1 4 8 16 24 32;
177  // currently we only support 8, 24, and 32 - FIXME
178  if ( core_hdr.bitsperpixel != 8 && core_hdr.bitsperpixel != 24 && core_hdr.bitsperpixel != 32 )
179  {
180  where << "BMP file has a non-supported pixel size of " << core_hdr.bitsperpixel << " bits\n";
181  return false;
182  }
183 
184  // determine whether or not there is an info header from
185  // the size field.
187  {
188  // no info header.
189  }
191  {
192  // probably an info header. read it now.
193  info_hdr.read(is_);
194 #ifdef DEBUG
195  info_hdr.print(std::cerr); // blather
196 #endif
197  if (info_hdr.compression)
198  {
199  where << "cannot cope with compression at the moment\n";
200  return false;
201  }
202  }
203  else
204  {
205  // urgh!
206  where << "dunno about header_size " << core_hdr.header_size << '\n';
207  return false;
208  }
209 
210  // skip colormap info
211  is_->seek(file_hdr.bitmap_offset); // === seek(is_->tell()+info_hdr.colormapsize);
212 #if 0
213  // color map nonsense
214  if (info_hdr.colormapsize ==0 && info_hdr.colorcount == 0)
215  {
216  // phew! no colour map.
217  }
218  else if (info_hdr.colormapsize == 256 && core_hdr.bitsperpixel == 8)
219  {
220  // In this case I know how to read the colormap because I have hexdumped an example.
221  // But I ignore the color map in the get_view( Fix params) routine because I don't care.
222  // fsm
223  typedef unsigned char uchar;
224  freds_colormap = new uchar *[4];
225  freds_colormap[0] = new uchar[256];
226  freds_colormap[1] = new uchar[256];
227  freds_colormap[2] = new uchar[256];
228  freds_colormap[3] = new uchar[256];
229  uchar bif[4];
230  for (int i=0; i<256; ++i)
231  {
232  is_->read(bif, sizeof(bif));
233  freds_colormap[0][i] = bif[0];
234  freds_colormap[1][i] = bif[1];
235  freds_colormap[2][i] = bif[2];
236  freds_colormap[3][i] = bif[3];
237  }
238  }
239  else
240  {
241  // dunno about this.
242  assert(false); // FIXME
243  }
244 #endif
245 
246  // old colormap reading code. it's not clear whether or not it worked -- fsm.
247 #if 0
248  // Determine the number of colors and set color map if necessary
249  int ccount=0;
250 
251  if (header.biClrUsed != 0)
252  ccount = header.biClrUsed;
253  else if (header.biBitCount != 24)
254  ccount = 1 << header.biBitCount;
255  else
256  {
257  }
258 
259  if (ccount != 0)
260  {
261  unsigned cmap_size;
262  if (header.biSize == sizeof(xBITMAPCOREHEADER))
263  cmap_size = ccount*3;
264  else
265  cmap_size = ccount*4;
266 
267  std::vector<uchar> cmap(cmap_size, 0); // use vector<> to avoid coreleak
268  if (is_->read(/* xxx */&cmap[0], 1024L) != 1024L)
269  {
270  std::cerr << "Error reading image palette\n";
271  return false;
272  }
273 
274  // SetColorNum(ccount);
275  // int ncolors = get_color_num();
276  int ncolors = ccount; // good guess
277  if (ncolors != 0)
278  {
279  int **color_map = new int*[3];
280  for (int i=0; i<3; ++i)
281  {
282  color_map[i] = new int[ncolors];
283  for (int j=0; j<ncolors; j++)
284  color_map[i][j] = (int) cmap[2-i+4*j];
285  }
286 
287  // SetColorMap(color_map); - TODO find out where to save a color map
288  local_color_map_=color_map;
289  }
290  }
291 
292  // TODO not obvious where the magic number is read
293 #endif
294 
295  // remember the position of the start of the bitmap data
296  bit_map_start = is_->tell();
297 #ifdef DEBUG
298  where << "bit_map_start = " << bit_map_start << '\n'; // blather
299 #endif
300  return bit_map_start == (int)file_hdr.bitmap_offset; // I think they're supposed to be the same -- fsm.
301 }
302 
304 {
305 #ifdef DEBUG
306  std::cerr << "Writing BMP header\n"
307  << ni() << 'x' << nj() << '@'
308  << nplanes() << 'x' <<
310 #endif
311 
312  int rowlen = ni() * nplanes() *
314  rowlen += (3-(rowlen+3)%4); // round up to a multiple of 4
315  int data_size = nj() * rowlen;
316 
317  if (nplanes() == 1)
323  core_hdr.header_size = 40;
324  // already set
325  //core_hdr.width = ni();
326  //core_hdr.height = nj();
329  info_hdr.bitmap_size = data_size;
330 
331 #ifdef DEBUG
332  file_hdr.print(std::cerr);
333  core_hdr.print(std::cerr); // blather
334  info_hdr.print(std::cerr);
335 #endif
336  is_->seek(0L);
337  file_hdr.write(is_);
338  core_hdr.write(is_);
339  info_hdr.write(is_);
340  if (nplanes() == 1) // Need to write a colourmap in this case
341  {
342  unsigned int const n = 1<<vil_pixel_format_sizeof_components(pixel_format())*8; // usually 256
343  auto* map = new vxl_byte[n*4];
344  vxl_byte* ptr = map;
345  for (unsigned int i=0; i<n; ++i, ptr+=4)
346  {
347  for (unsigned int j=0; j<3; ++j)
348  {
349  auto c = (unsigned char)i;
350  ptr[j] = c;
351  //is_->write(&c,1L);
352  }
353  ptr[3] = 0; // unused byte
354  }
355  is_->write(map, n*4);
356  delete [] map;
357  }
358  return true;
359 }
360 
361 //------------------------------------------------------------
363  unsigned x0, unsigned nx, unsigned y0, unsigned ny) const
364 {
365  if (x0+nx > ni() || y0+ny > nj())
366  {
367  vil_exception_warning(vil_exception_out_of_bounds("vil_bmp_image::get_copy_view"));
368  return nullptr;
369  }
370  //
371  unsigned const bytes_per_pixel = core_hdr.bitsperpixel / 8;
372  assert(core_hdr.bitsperpixel == 8 || core_hdr.bitsperpixel == 24 || core_hdr.bitsperpixel == 32 );
373  // FIXME - add support for 1, 4, and 16 bpp
374 
375  // actual number of bytes per raster in file.
376  unsigned const have_bytes_per_raster = ((bytes_per_pixel * core_hdr.width + 3)/4)*4;
377 
378  // number of bytes we want per raster.
379  unsigned long want_bytes_per_raster = nx*bytes_per_pixel;
380 
381  // The following line results in non-contiguous image view and waste a bit memory
382  // if (nx == ni()) want_bytes_per_raster = have_bytes_per_raster;
383 
384  vil_memory_chunk_sptr buf = new vil_memory_chunk(want_bytes_per_raster*ny, VIL_PIXEL_FORMAT_BYTE);
385 
386  std::ptrdiff_t top_left_y0_in_mem = 0;
387  std::ptrdiff_t ystep = want_bytes_per_raster;
388  unsigned int rows_to_skip = y0;
389  if( core_hdr.height > 0 )
390  {
391  // Bottom-up pass
392  // BMP images are stored with a flipped y-axis w.r.t. conventional
393  // pixel axes.
394  //
395  rows_to_skip = nj() - (y0+ny);
396  top_left_y0_in_mem = (ny-1)*want_bytes_per_raster;
397  ystep = -ystep;
398  }
399  else
400  {
401  // Top-bottom pass
402  // already init.
403  //top_left_y0_in_mem = 0;
404  //y0 = y0;
405  }
406 
407  // read each raster in turn. if the client wants the whole image, it may
408  // be faster to read() it all in one chunk, so long as the number of bytes
409  // per image raster is divisible by four (because the file rasters are
410  // padded at the ends).
411  vil_streampos bytes_read = 0;
412  if (nx == ni() && want_bytes_per_raster == have_bytes_per_raster)
413  {
414  is_->seek(bit_map_start + have_bytes_per_raster*rows_to_skip);
415  bytes_read = is_->read(reinterpret_cast<vxl_byte *>(buf->data()), want_bytes_per_raster *ny);
416  }
417  else
418  {
419  for (unsigned i=0; i<ny; ++i)
420  {
421  is_->seek(bit_map_start + have_bytes_per_raster*(i+rows_to_skip) + x0*bytes_per_pixel);
422  bytes_read += is_->read(reinterpret_cast<vxl_byte *>(buf->data()) + want_bytes_per_raster*i, want_bytes_per_raster);
423  }
424  }
425  if (bytes_read != vil_streampos(ny * want_bytes_per_raster))
426  {
428  vil_exception_corrupt_image_file("vil_bmp_image::get_copy_view", "BMP", ""));
429  return nullptr;
430  }
431 
432  unsigned np = 1;
433  std::ptrdiff_t plane_step = 1;
434  std::ptrdiff_t top_left_plane0_in_mem = 0;
435  if( core_hdr.bitsperpixel == 8 )
436  {
437  np = 1;
438  plane_step = 1;
439  top_left_plane0_in_mem = 0;
440  }
441  else if( core_hdr.bitsperpixel == 24 )
442  {
443  //return new vil_image_view<vxl_byte>(
444  // buf,
445  // reinterpret_cast<vxl_byte *>(buf->data())+(ny-1)*want_bytes_per_raster + nplanes()-1,
446  // nx, ny, nplanes(),
447  // nplanes(), -(long)want_bytes_per_raster, -1/*correspond to BB GG RR*/);
448  np = 3;
449  plane_step = -1; /*correspond to BB GG RR*/
450  top_left_plane0_in_mem = 2; // np-1
451  }
452  else if( core_hdr.bitsperpixel == 32 )
453  {
454  // re-organize channel ordering from BGRA to RGBA.
455  // In other words, swap B and R
456  assert( (want_bytes_per_raster & 3) == 0 ); // must be multiple of 4
457  auto* data = reinterpret_cast<vxl_byte *>(buf->data());
458  vxl_byte* const data_end = data+(want_bytes_per_raster*ny);
459  for(; data!=data_end; data+=4)
460  {
461  // memory layout for pixel color values:
462  // Form BB GG RR AA BB GG RR AA ....
463  // Change to RR GG BB AA RR GG BB AA ...
464  std::swap(data[0], data[2]);
465  }
466 
467  np = 4;
468  plane_step = 1;
469  top_left_plane0_in_mem = 0;
470  }
471  assert( np == nplanes() );
472 
473  return new vil_image_view<vxl_byte>(
474  buf,
475  reinterpret_cast<vxl_byte *>(buf->data())+top_left_plane0_in_mem+top_left_y0_in_mem,
476  nx, ny, np,
477  np, ystep, plane_step);
478 }
479 
480 
482  unsigned x0, unsigned y0)
483 {
484  if (!view_fits(view, x0, y0))
485  {
486  vil_exception_warning(vil_exception_out_of_bounds("vil_bmp_image::put_view"));
487  return false;
488  }
489 
490  if(view.pixel_format() != VIL_PIXEL_FORMAT_BYTE)
491  {
493  return false;
494  }
495  const auto & view2 = static_cast<const vil_image_view<vxl_byte> &>(view);
496 
497  unsigned const bypp = nplanes();
498  unsigned const rowlen = ni() * bypp;
499  unsigned const padlen = (3-(rowlen+3)%4); // round row length up to a multiple of 4
500  vxl_byte padding[3]={0, 0, 0};
501 
502  assert( core_hdr.height < 0 ); // we utilize only top-down scan
503 
504  if ((view2.planestep() == -1||nplanes()==1)&&
505  view2.istep()==(int)view2.nplanes())
506  {
507  for (unsigned y=0; y<view2.nj(); ++y)
508  {
509  is_->seek(bit_map_start+(y+y0)*(rowlen+padlen)+x0*bypp);
510  is_->write(&view2(0,y,view2.nplanes()-1), rowlen);
511  if (padlen !=0) is_->write(padding, padlen);
512  }
513  }
514  else if (nplanes()==3)
515  {
516  assert(nplanes()==3);
517  auto* buf = new vxl_byte[rowlen+padlen];
518  for (unsigned i=rowlen; i<rowlen+padlen; ++i) buf[i]=0;
519  for (unsigned j=0; j<view2.nj(); ++j)
520  {
521  vxl_byte* b = buf;
522  //unsigned int const negj = view2.nj()-j-1;
523  for (unsigned i=0; i<view2.ni(); ++i)
524  {
525  *(b++) = view2(i, j, 2); //B
526  *(b++) = view2(i, j, 1); //G
527  *(b++) = view2(i, j, 0); //R
528  }
529  is_->seek(bit_map_start+(j+y0)*(rowlen+padlen)+x0*bypp);
530  is_->write(buf, rowlen+padlen);
531  }
532  delete [] buf;
533  }
534  else /*nplanes()==4*/
535  {
536  assert(nplanes()==4);
537  auto* buf = new vxl_byte[rowlen+padlen];
538  for (unsigned i=rowlen; i<rowlen+padlen; ++i) buf[i]=0;
539  for (unsigned j=0; j<view2.nj(); ++j)
540  {
541  vxl_byte* b = buf;
542  //unsigned int const negj = view2.nj()-j-1;
543  for (unsigned i=0; i<view2.ni(); ++i)
544  {
545  *(b++) = view2(i, j, 2); //B
546  *(b++) = view2(i, j, 1); //G
547  *(b++) = view2(i, j, 0); //R
548  *(b++) = view2(i, j, 3); //A
549  }
550  is_->seek(bit_map_start+(j+y0)*(rowlen+padlen)+x0*bypp);
551  is_->write(buf, rowlen+padlen);
552  }
553  delete [] buf;
554  }
555  return true;
556 }
vil_stream * is_
Definition: vil_bmp.h:80
unsigned nj() const override
Dimensions: Planes x ni x nj.
Definition: vil_bmp.cxx:152
vil_bmp_image(vil_stream *is, unsigned ni, unsigned nj, unsigned nplanes, vil_pixel_format format)
Definition: vil_bmp.cxx:83
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.
char const * file_format() const override
Return a string describing the file format.
Definition: vil_bmp.cxx:53
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_bmp.cxx:22
virtual vil_streampos tell() const =0
Return file pointer.
Indicates that some operation is not supported.
Definition: vil_exception.h:99
unsigned nplanes() const override
Dimensions: planes x width x height x components.
Definition: vil_bmp.cxx:142
void write(vil_stream *) const
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
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_bmp.cxx:481
Indicates that an image load or save operation failed.
bool read_header()
Definition: vil_bmp.cxx:157
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.
Exceptions thrown by vil, and a mechanism for turning them off.
void print(std::ostream &) const
unsigned vil_pixel_format_sizeof_components(enum vil_pixel_format f)
Return the number of bytes used by each component of pixel format f.
void read(vil_stream *)
#define vil_property_quantisation_depth
The quantisation depth of pixel components.
Definition: vil_property.h:67
Stream interface for VIL image loaders.
Definition: vil_stream.h:21
bool write_header()
Definition: vil_bmp.cxx:303
void read(vil_stream *)
void write(vil_stream *) const
char const * tag() const override
Return a character string which uniquely identifies this format.
Definition: vil_bmp.cxx:46
virtual enum vil_pixel_format pixel_format() const =0
Return a description of the concrete data pixel type.
#define where
Definition: vil_bmp.cxx:18
Indicates that some reference was made to pixels beyond the bounds of an image.
Definition: vil_exception.h:81
vil_streampos bit_map_start
Definition: vil_bmp.h:90
Generic image implementation for BMP files.
Definition: vil_bmp.h:54
Indicates unexpected problems image file's data.
~vil_bmp_image() override
Definition: vil_bmp.cxx:116
Indicates that a function call failed because the pixel types were incompatible.
Definition: vil_exception.h:47
bool get_property(char const *tag, void *prop=nullptr) const override
Extra property information.
Definition: vil_bmp.cxx:71
enum vil_pixel_format pixel_format() const override
Pixel Format.
Definition: vil_bmp.h:67
void ref()
up/down the reference count.
Definition: vil_stream.h:45
void read(vil_stream *)
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.
There is no class or function called vil_property.
char const * vil_bmp_format_tag
Definition: vil_bmp.cxx:44
Ref. counted block of data on the heap.
void unref()
Definition: vil_stream.cxx:31
void print(std::ostream &) const
unsigned ni() const override
Dimensions: Planes x ni x nj.
Definition: vil_bmp.cxx:147
vxl_int_32 vil_streampos
Definition: vil_stream.h:16
void vil_exception_warning(T exception)
Throw an exception indicating a potential problem.
Definition: vil_exception.h:37
void vil_exception_error(T exception)
Throw an exception indicating a definite problem.
Definition: vil_exception.h:23
vil_bmp_info_header info_hdr
Definition: vil_bmp.h:89
void print(std::ostream &) const
vil_image_resource_sptr make_output_image(vil_stream *vs, unsigned nx, unsigned ny, unsigned nplanes, vil_pixel_format format) override
Definition: vil_bmp.cxx:35
void write(vil_stream *) const
vil_bmp_file_header file_hdr
Definition: vil_bmp.h:87
vil_bmp_core_header core_hdr
Definition: vil_bmp.h:88