core/vil/file_formats/vil_bmp.cxx
Go to the documentation of this file.
00001 // This is core/vil/file_formats/vil_bmp.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 
00006 #include "vil_bmp.h"
00007 
00008 #include <vcl_cassert.h>
00009 #include <vcl_iostream.h>
00010 #include <vcl_vector.h>
00011 #include <vcl_cstring.h>
00012 #include <vil/vil_stream.h>
00013 #include <vil/vil_property.h>
00014 #include <vil/vil_memory_chunk.h>
00015 #include <vil/vil_image_view.h>
00016 #include <vil/vil_exception.h>
00017 
00018 #define where (vcl_cerr << __FILE__ " : " << __LINE__ << " : ")
00019 
00020 //--------------------------------------------------------------------------------
00021 
00022 vil_image_resource_sptr vil_bmp_file_format::make_input_image(vil_stream* vs)
00023 {
00024   // Attempt to read header
00025   vil_bmp_file_header hdr;
00026   vs->seek(0L);
00027   hdr.read(vs);
00028 
00029   if ( hdr.signature_valid() )
00030     return new vil_bmp_image(vs);
00031   else
00032     return 0;
00033 }
00034 
00035 vil_image_resource_sptr vil_bmp_file_format::make_output_image(vil_stream* vs,
00036                                                                unsigned nx,
00037                                                                unsigned ny,
00038                                                                unsigned nplanes,
00039                                                                vil_pixel_format format)
00040 {
00041   return new vil_bmp_image(vs, nx, ny, nplanes, format);
00042 }
00043 
00044 char const* vil_bmp_format_tag = "bmp";
00045 
00046 char const* vil_bmp_file_format::tag() const
00047 {
00048   return vil_bmp_format_tag;
00049 }
00050 
00051 /////////////////////////////////////////////////////////////////////////////
00052 
00053 char const* vil_bmp_image::file_format() const
00054 {
00055   return vil_bmp_format_tag;
00056 }
00057 
00058 vil_bmp_image::vil_bmp_image(vil_stream* is)
00059   : is_(is)
00060   , bit_map_start(-1L)
00061 #if 0
00062   , freds_colormap(0)
00063   , local_color_map_(0)
00064 #endif
00065 {
00066   is_->ref();
00067   if (!read_header())
00068     vil_exception_error(vil_exception_image_io("vil_bmp_image::read_header", "BMP", ""));
00069 }
00070 
00071 bool vil_bmp_image::get_property(char const * tag, void * value) const
00072 {
00073   if (vcl_strcmp(vil_property_quantisation_depth, tag)==0)
00074   {
00075     if (value)
00076       *static_cast<unsigned int*>(value) = core_hdr.bitsperpixel / nplanes();
00077     return true;
00078   }
00079 
00080   return false;
00081 }
00082 
00083 vil_bmp_image::vil_bmp_image(vil_stream* vs, unsigned nx, unsigned ny,
00084                              unsigned nplanes, vil_pixel_format format):
00085   is_(vs), bit_map_start(-1L)
00086 {
00087   if (format != VIL_PIXEL_FORMAT_BYTE)
00088     vcl_cerr << "Sorry -- pixel format " << format << " not yet supported\n";
00089   // FIXME - we only support 8, 24 bpp, and 32bpp; add support for 1, 4, and 16 bpp
00090   assert(format == VIL_PIXEL_FORMAT_BYTE);
00091   assert(nplanes == 1 || nplanes == 3 || nplanes == 4);
00092 
00093   is_->ref();
00094 
00095   // core_hdr.header_size is set up for us.
00096   core_hdr.width = nx;
00097   core_hdr.height = ny;
00098   core_hdr.planes = 1;
00099 
00100   core_hdr.bitsperpixel = 8 * nplanes;
00101 
00102   write_header();
00103 }
00104 
00105 vil_bmp_image::~vil_bmp_image()
00106 {
00107 #if 0
00108   // we must get rid of the local_color_map_;
00109   if (local_color_map_)
00110   {
00111     delete [] local_color_map_[0];
00112     delete [] local_color_map_[1];
00113     delete [] local_color_map_[2];
00114     delete local_color_map_;
00115   }
00116 
00117   if (freds_colormap)
00118   {
00119     delete [] freds_colormap[0];
00120     delete [] freds_colormap[1];
00121     delete [] freds_colormap[2];
00122     delete [] freds_colormap[3];
00123     delete [] freds_colormap;
00124     freds_colormap = 0;
00125   }
00126 #endif
00127 
00128   is_->unref();
00129 }
00130 
00131 bool vil_bmp_image::read_header()
00132 {
00133   // seek to beginning and read file header.
00134   is_->seek(0L);
00135   file_hdr.read(is_);
00136   if ( ! file_hdr.signature_valid() )
00137   {
00138     where <<  "File is not a valid BMP file\n";
00139     return false;
00140   }
00141 #ifdef DEBUG
00142   file_hdr.print(vcl_cerr); // blather
00143 #endif
00144 
00145   // read core header
00146   core_hdr.read(is_);
00147 #ifdef DEBUG
00148   core_hdr.print(vcl_cerr); // blather
00149 #endif
00150   // allowed values for bitsperpixel are 1 4 8 16 24 32;
00151   // currently we only support 8, 24, and 32 - FIXME
00152   if ( core_hdr.bitsperpixel != 8 && core_hdr.bitsperpixel != 24 && core_hdr.bitsperpixel != 32 )
00153   {
00154     where << "BMP file has a non-supported pixel size of " << core_hdr.bitsperpixel << " bits\n";
00155     return false;
00156   }
00157 
00158   // determine whether or not there is an info header from
00159   // the size field.
00160   if (core_hdr.header_size == vil_bmp_core_header::disk_size)
00161   {
00162     // no info header.
00163   }
00164   else if (core_hdr.header_size == vil_bmp_core_header::disk_size + vil_bmp_info_header::disk_size)
00165   {
00166     // probably an info header. read it now.
00167     info_hdr.read(is_);
00168 #ifdef DEBUG
00169     info_hdr.print(vcl_cerr); // blather
00170 #endif
00171     if (info_hdr.compression)
00172     {
00173       where << "cannot cope with compression at the moment\n";
00174       return false;
00175     }
00176   }
00177   else
00178   {
00179     // urgh!
00180     where << "dunno about header_size " << core_hdr.header_size << '\n';
00181     return false;
00182   }
00183 
00184   // skip colormap info
00185   is_->seek(file_hdr.bitmap_offset); // === seek(is_->tell()+info_hdr.colormapsize);
00186 #if 0
00187   // color map nonsense
00188   if (info_hdr.colormapsize ==0 && info_hdr.colorcount == 0)
00189   {
00190     // phew! no colour map.
00191   }
00192   else if (info_hdr.colormapsize == 256 && core_hdr.bitsperpixel == 8)
00193   {
00194     // In this case I know how to read the colormap because I have hexdumped an example.
00195     // But I ignore the color map in the get_view( Fix params) routine because I don't care.
00196     // fsm
00197     typedef unsigned char uchar;
00198     freds_colormap = new uchar *[4];
00199     freds_colormap[0] = new uchar[256];
00200     freds_colormap[1] = new uchar[256];
00201     freds_colormap[2] = new uchar[256];
00202     freds_colormap[3] = new uchar[256];
00203     uchar bif[4];
00204     for (int i=0; i<256; ++i)
00205     {
00206       is_->read(bif, sizeof(bif));
00207       freds_colormap[0][i] = bif[0];
00208       freds_colormap[1][i] = bif[1];
00209       freds_colormap[2][i] = bif[2];
00210       freds_colormap[3][i] = bif[3];
00211     }
00212   }
00213   else
00214   {
00215     // dunno about this.
00216     assert(false); // FIXME
00217   }
00218 #endif
00219 
00220   // old colormap reading code. it's not clear whether or not it worked -- fsm.
00221 #if 0
00222   // Determine the number of colors and set color map if necessary
00223   int ccount=0;
00224 
00225   if (header.biClrUsed != 0)
00226     ccount = header.biClrUsed;
00227   else if (header.biBitCount != 24)
00228     ccount = 1 << header.biBitCount;
00229   else
00230   {
00231   }
00232 
00233   if (ccount != 0)
00234   {
00235     unsigned cmap_size;
00236     if (header.biSize == sizeof(xBITMAPCOREHEADER))
00237       cmap_size = ccount*3;
00238     else
00239       cmap_size = ccount*4;
00240 
00241     vcl_vector<uchar> cmap(cmap_size, 0); // use vector<> to avoid coreleak
00242     if (is_->read(/* xxx */&cmap[0], 1024L) != 1024L)
00243     {
00244       vcl_cerr << "Error reading image palette\n";
00245       return false;
00246     }
00247 
00248     // SetColorNum(ccount);
00249     // int ncolors = get_color_num();
00250     int ncolors = ccount; // good guess
00251     if (ncolors != 0)
00252     {
00253       int **color_map = new int*[3];
00254       for (int i=0; i<3; ++i)
00255       {
00256         color_map[i] = new int[ncolors];
00257         for (int j=0; j<ncolors; j++)
00258           color_map[i][j] = (int) cmap[2-i+4*j];
00259       }
00260 
00261       // SetColorMap(color_map);  - TODO find out where to save a color map
00262       local_color_map_=color_map;
00263     }
00264   }
00265 
00266   // TODO not obvious where the magic number is read
00267 #endif
00268 
00269   // remember the position of the start of the bitmap data
00270   bit_map_start = is_->tell();
00271 #ifdef DEBUG
00272   where << "bit_map_start = " << bit_map_start << '\n'; // blather
00273 #endif
00274   return bit_map_start == (int)file_hdr.bitmap_offset; // I think they're supposed to be the same -- fsm.
00275 }
00276 
00277 bool vil_bmp_image::write_header()
00278 {
00279 #ifdef DEBUG
00280   vcl_cerr << "Writing BMP header\n"
00281            << ni() << 'x' << nj() << '@'
00282            << nplanes() << 'x' <<
00283            vil_pixel_format_sizeof_components(pixel_format()) << '\n';
00284 #endif
00285 
00286   int rowlen = ni() * nplanes() *
00287     vil_pixel_format_sizeof_components(pixel_format());
00288   rowlen += (3-(rowlen+3)%4); // round up to a multiple of 4
00289   int data_size = nj() * rowlen;
00290 
00291   if (nplanes() == 1)
00292     info_hdr.colorcount = info_hdr.colormapsize = 1<<
00293       vil_pixel_format_sizeof_components(pixel_format()) * 8;
00294   file_hdr.bitmap_offset = 54L + 4 * info_hdr.colormapsize;
00295   bit_map_start = file_hdr.bitmap_offset;
00296   file_hdr.file_size = file_hdr.bitmap_offset+data_size;
00297   core_hdr.header_size = 40;
00298   core_hdr.width = ni();
00299   core_hdr.height = nj();
00300   core_hdr.bitsperpixel = nplanes()*
00301     vil_pixel_format_sizeof_components(pixel_format()) * 8;
00302   info_hdr.bitmap_size = data_size;
00303 
00304 #ifdef DEBUG
00305   file_hdr.print(vcl_cerr);
00306   core_hdr.print(vcl_cerr); // blather
00307   info_hdr.print(vcl_cerr);
00308 #endif
00309   is_->seek(0L);
00310   file_hdr.write(is_);
00311   core_hdr.write(is_);
00312   info_hdr.write(is_);
00313   if (nplanes() == 1) // Need to write a colourmap in this case
00314     for (int i=0; i<(1<<vil_pixel_format_sizeof_components(pixel_format())*8); ++i)
00315       for (int j=0; j<4; ++j)
00316       {
00317         unsigned char c = (unsigned char)i;
00318         is_->write(&c,1L);
00319       }
00320 
00321   return true;
00322 }
00323 
00324 //------------------------------------------------------------
00325 vil_image_view_base_sptr vil_bmp_image::get_copy_view(
00326   unsigned x0, unsigned nx, unsigned y0, unsigned ny) const
00327 {
00328   if (x0+nx > ni() || y0+ny > nj())
00329   {
00330     vil_exception_warning(vil_exception_out_of_bounds("vil_bmp_image::get_copy_view"));
00331     return 0;
00332   }
00333   //
00334   unsigned const bytes_per_pixel = core_hdr.bitsperpixel / 8;
00335   assert(core_hdr.bitsperpixel == 8 || core_hdr.bitsperpixel == 24 || core_hdr.bitsperpixel == 32 );
00336   // FIXME - add support for 1, 4, and 16 bpp
00337 
00338   // actual number of bytes per raster in file.
00339   unsigned const have_bytes_per_raster = ((bytes_per_pixel * core_hdr.width + 3)/4)*4;
00340 
00341   // number of bytes we want per raster.
00342   unsigned long want_bytes_per_raster = nx*bytes_per_pixel;
00343 
00344   if (nx == ni()) want_bytes_per_raster =  have_bytes_per_raster;
00345 
00346   vil_memory_chunk_sptr buf = new vil_memory_chunk(want_bytes_per_raster*ny, VIL_PIXEL_FORMAT_BYTE);
00347 
00348   // BMP images are stored with a flipped y-axis w.r.t. conventional
00349   // pixel axes.
00350   //
00351   y0 = nj() - (y0+ny);
00352 
00353   // read each raster in turn. if the client wants the whole image, it may
00354   // be faster to read() it all in one chunk, so long as the number of bytes
00355   // per image raster is divisible by four (because the file rasters are
00356   // padded at the ends).
00357   vil_streampos bytes_read = 0;
00358   if (nx == ni())
00359   {
00360     is_->seek(bit_map_start + have_bytes_per_raster*y0);
00361     bytes_read = is_->read(reinterpret_cast<vxl_byte *>(buf->data()), want_bytes_per_raster *ny);
00362   }
00363   else
00364   {
00365     for (unsigned i=0; i<ny; ++i)
00366     {
00367       is_->seek(bit_map_start + have_bytes_per_raster*(i+y0) + x0*bytes_per_pixel);
00368       bytes_read += is_->read(reinterpret_cast<vxl_byte *>(buf->data()) + want_bytes_per_raster*i, want_bytes_per_raster);
00369     }
00370   }
00371   if (bytes_read != vil_streampos(ny * want_bytes_per_raster))
00372   {
00373     vil_exception_warning(
00374       vil_exception_corrupt_image_file("vil_bmp_image::get_copy_view", "BMP", ""));
00375     return 0;
00376   }
00377 
00378   vcl_ptrdiff_t plane_step = 1;
00379   if( core_hdr.bitsperpixel == 24 )
00380   {
00381     return new vil_image_view<vxl_byte>(
00382       buf,
00383       reinterpret_cast<vxl_byte *>(buf->data())+(ny-1)*want_bytes_per_raster + nplanes()-1,
00384       nx, ny, nplanes(),
00385       nplanes(), -(long)want_bytes_per_raster, -1/*correspond to BB GG RR*/);
00386   }
00387   else if( core_hdr.bitsperpixel == 32 )
00388   {
00389     // re-organize channel ordering from BGRA to RGBA. 
00390     // In other words,  swap B and R
00391     assert( (want_bytes_per_raster & 3) == 0 );  //  must be multiple of 4
00392     vxl_byte* data = reinterpret_cast<vxl_byte *>(buf->data());
00393     vxl_byte* const data_end = data+(want_bytes_per_raster*ny);
00394     for(; data!=data_end; data+=4)
00395     {
00396       // memory layout for pixel color values:
00397       // BB GG RR AA BB GG RR AA ....
00398       // Change to RR GG BB AA RR GG BB AA ...
00399       vcl_swap(data[0], data[2]);
00400     }
00401   }
00402 
00403   return new vil_image_view<vxl_byte>(
00404     buf,
00405     reinterpret_cast<vxl_byte *>(buf->data())+(ny-1)*want_bytes_per_raster,
00406     nx, ny, nplanes(),
00407     nplanes(), -(long)want_bytes_per_raster, plane_step);
00408 }
00409 
00410 
00411 bool vil_bmp_image::put_view(const vil_image_view_base& view,
00412                              unsigned x0, unsigned y0)
00413 {
00414   if (!view_fits(view, x0, y0))
00415   {
00416     vil_exception_warning(vil_exception_out_of_bounds("vil_bmp_image::put_view"));
00417     return false;
00418   }
00419 
00420   assert (view.pixel_format() == VIL_PIXEL_FORMAT_BYTE); // FIXME
00421   const vil_image_view<vxl_byte> & view2 = static_cast<const vil_image_view<vxl_byte> &>(view);
00422 
00423   unsigned const bypp = nplanes();
00424   unsigned const rowlen = ni() * bypp;
00425   unsigned const padlen = (3-(rowlen+3)%4); // round row length up to a multiple of 4
00426   vxl_byte padding[3]={0, 0, 0};
00427 
00428   if ((view2.planestep() == -1||nplanes()==1)&&
00429       view2.istep()==(int)view2.nplanes())
00430   {
00431     for (unsigned y=0; y<view2.nj(); ++y)
00432     {
00433       is_->seek(bit_map_start+(y+y0)*(rowlen+padlen)+x0*bypp);
00434       is_->write(&view2(0,view2.nj()-y-1,view2.nplanes()-1), rowlen);
00435       if (padlen !=0) is_->write(padding, padlen);
00436     }
00437   }
00438   else if (nplanes()==3)
00439   {
00440     assert(nplanes()==3);
00441     vxl_byte* buf = new vxl_byte[rowlen+padlen];
00442     for (unsigned i=rowlen; i<rowlen+padlen; ++i) buf[i]=0;
00443     for (unsigned j=0; j<view2.nj(); ++j)
00444     {
00445       vxl_byte* b = buf;
00446       unsigned int const negj = view2.nj()-j-1;
00447       for (unsigned i=0; i<view2.ni(); ++i)
00448       {
00449         *(b++) = view2(i, negj, 2);  //B
00450         *(b++) = view2(i, negj, 1);  //G
00451         *(b++) = view2(i, negj, 0);  //R
00452       }
00453       is_->seek(bit_map_start+(j+y0)*(rowlen+padlen)+x0*bypp);
00454       is_->write(buf, rowlen+padlen);
00455     }
00456     delete [] buf;
00457   }
00458   else /*nplanes()==4*/
00459   {
00460     assert(nplanes()==4);
00461     vxl_byte* buf = new vxl_byte[rowlen+padlen];
00462     for (unsigned i=rowlen; i<rowlen+padlen; ++i) buf[i]=0;
00463     for (unsigned j=0; j<view2.nj(); ++j)
00464     {
00465       vxl_byte* b = buf;
00466       unsigned int const negj = view2.nj()-j-1;
00467       for (unsigned i=0; i<view2.ni(); ++i)
00468       {
00469         *(b++) = view2(i, negj, 2);  //B
00470         *(b++) = view2(i, negj, 1);  //G
00471         *(b++) = view2(i, negj, 0);  //R
00472         *(b++) = view2(i, negj, 3);  //A
00473       }
00474       is_->seek(bit_map_start+(j+y0)*(rowlen+padlen)+x0*bypp);
00475       is_->write(buf, rowlen+padlen);
00476     }
00477     delete [] buf;
00478   }
00479   return true;
00480 }