core/vgui/vgui_vil_image_renderer.cxx
Go to the documentation of this file.
00001 // This is core/vgui/vgui_vil_image_renderer.cxx
00002 #include "vgui_vil_image_renderer.h"
00003 //:
00004 // \file
00005 // \author Amitha Perera
00006 // \brief  See vgui_vil_image_renderer.h for a description of this file.
00007 //
00008 // Cut-n-paste and modify from vil1_image_renderer.cxx
00009 
00010 #include <vcl_iostream.h>
00011 #include <vcl_cmath.h>
00012 #include <vul/vul_timer.h>
00013 #include <vil/vil_property.h>
00014 #include <vil/vil_image_resource.h>
00015 #include <vil/vil_pyramid_image_resource.h>
00016 #include <vil/vil_image_view.h>
00017 #include <vil/vil_new.h>
00018 #include <vgui/vgui_section_render.h>
00019 #include "vgui_gl.h"
00020 #include "vgui_macro.h"
00021 #include "vgui_section_buffer.h"
00022 #include "vgui_range_map.h"
00023 
00024 // Only check-in false:
00025 static const bool debug = false;
00026 #define trace if (true) { } else vcl_cerr
00027 
00028 //#define RENDER_TIMER
00029 // limit on buffer size 25 Mpix
00030 static const unsigned buf_limit = 25000000;
00031 
00032 vgui_vil_image_renderer::
00033 vgui_vil_image_renderer()
00034   : buffer_( 0 ),  buffer_params_(0), valid_buffer_(false),
00035     x0_(0), y0_(0), w_(1), h_(1), zx_(1.0f), zy_(1.0f), sni_(0), snj_(0)
00036 {
00037 }
00038 
00039 
00040 vgui_vil_image_renderer::
00041 ~vgui_vil_image_renderer()
00042 {
00043   delete buffer_;
00044 }
00045 
00046 void
00047 vgui_vil_image_renderer::
00048 set_image_resource( vil_image_resource_sptr const& image )
00049 {
00050   // delete old buffer. we could try to reuse it.
00051   delete buffer_;
00052   buffer_ = 0;
00053   valid_buffer_ = false;
00054   the_image_ = image;
00055   if ( the_image_ ) {
00056     trace << "image : " << the_image_ << vcl_flush;
00057   }
00058 }
00059 
00060 
00061 vil_image_resource_sptr
00062 vgui_vil_image_renderer::
00063 get_image_resource() const
00064 {
00065   return the_image_;
00066 }
00067 
00068 
00069 void
00070 vgui_vil_image_renderer::
00071 reread_image()
00072 {
00073   delete buffer_;
00074   buffer_ = 0;
00075   valid_buffer_ = false;
00076 }
00077 
00078 void vgui_vil_image_renderer::
00079 create_buffer(vgui_range_map_params_sptr const& rmp)
00080 {
00081   delete buffer_;
00082   unsigned ni = the_image_->ni(), nj = the_image_->nj();
00083   //If the image is too large only display the upper left corner
00084   //as a square region if possible
00085   unsigned dims =
00086     static_cast<unsigned>(vcl_sqrt(static_cast<double>(buf_limit)));
00087   if (ni*nj>buf_limit)
00088   {
00089     vcl_cerr << "In vgui_vil_image_renderer - image too large, " << ni << 'x' << nj <<", for complete buffer.\n"
00090              << "Rendering only the top left "<< dims << 'x' << dims << " corner\n";
00091     unsigned dims =
00092       static_cast<unsigned>(vcl_sqrt(static_cast<double>(buf_limit)));
00093     if (ni<dims)
00094       nj = (buf_limit/ni) -1 ;
00095     else {ni=dims; nj = dims;}
00096     if (nj<dims)
00097       ni = (buf_limit/nj) -1 ;
00098     else {ni=dims; nj = dims;}
00099   }
00100   buffer_ = new vgui_section_buffer( 0, 0, ni, nj, GL_NONE, GL_NONE );
00101   buffer_->apply( the_image_, rmp );
00102 
00103   buffer_params_ = rmp;
00104   valid_buffer_ = true;
00105 }
00106 //: creates a buffer for a portion of the image
00107 void vgui_vil_image_renderer::
00108 create_buffer(vgui_range_map_params_sptr const& rmp,
00109               unsigned x0, unsigned y0, unsigned x1, unsigned y1,
00110               float zoomx, float zoomy)
00111 {
00112   delete buffer_;
00113   buffer_ = new vgui_section_buffer( x0, y0, x1, y1, GL_NONE, GL_NONE );
00114   buffer_->set_zoom(zoomx, zoomy);
00115   buffer_->apply( the_image_, rmp );
00116 
00117   buffer_params_ = rmp;
00118   valid_buffer_ = true;
00119 }
00120 // Create a buffer corresponding to a pyramid zoom level
00121 void vgui_vil_image_renderer::
00122 create_buffer(vgui_range_map_params_sptr const& rmp,
00123               float zoomx, float zoomy,
00124               vil_image_resource_sptr const& ir)
00125 {
00126   delete buffer_;
00127   buffer_ = 0;
00128   if (!rmp||!ir)
00129     return;
00130 
00131   buffer_ = new vgui_section_buffer( 0, 0,
00132                                      ir->ni(), ir->nj(),
00133                                      GL_NONE, GL_NONE );
00134   buffer_->set_zoom(zoomx, zoomy);
00135   buffer_->apply( ir, rmp );
00136 
00137   buffer_params_ = rmp;
00138   valid_buffer_ = true;
00139 }
00140 
00141 void vgui_vil_image_renderer::
00142 draw_pixels()
00143 {
00144   buffer_->draw_as_image() || buffer_->draw_as_rectangle();
00145 }
00146 
00147 
00148 bool vgui_vil_image_renderer::
00149 render_directly(vgui_range_map_params_sptr const& rmp)
00150 {
00151   if (!rmp)
00152     return false;
00153   //check if range map params have changed. If so, the buffer is obsolete.
00154   if (!old_range_map_params(rmp))
00155     valid_buffer_ = false;
00156 
00157 
00158   //check consistency of range map and image
00159   if (!the_image_||the_image_->nplanes()!=rmp->n_components_)
00160     return false;
00161 
00162   //Use the hardware map instead of a pre-rendered, cached  buffer
00163   bool hmap = !(rmp->cache_mapped_pix_);
00164 
00165   //Extract the viewport parameters (currently displayed image region)
00166   unsigned i0=0, j0=0;
00167   unsigned ni =the_image_->ni(), nj=the_image_->nj();
00168   float zoomx = 1, zoomy = -1;
00169   pixel_view(i0, ni, j0, nj, zoomx, zoomy);
00170 
00171   // check if the viewport parameters changed from previous render
00172   // update parameter history
00173   bool vp_changed = false;
00174   if ((x0_ != i0) || (y0_ != j0) || (ni != w_) || (nj != h_) ||
00175       (zx_ != zoomx) || (zy_ != zoomy))  {
00176     vp_changed = true;
00177     x0_ = i0; y0_ = j0; w_ = ni; h_ = nj;
00178     zx_ = zoomx;  zy_ = zoomy;
00179   }
00180 
00181   //Check if the resource is a pyramid image
00182   // if so then the pyramid level with best match to displayed scale is used
00183   // to render the screen.  Much faster for large images
00184   float actual_scale = 1.0f;
00185   vil_pyramid_image_resource_sptr pyr;
00186   if (the_image_->get_property(vil_property_pyramid))
00187     pyr = (vil_pyramid_image_resource*)the_image_.ptr();
00188 
00189 
00190   vul_timer t;
00191   //we are guaranteed that the image and range map are present
00192   //further we know that pixel type unsigned char or unsigned short
00193   //OpenGL supports a table mapping, glPixelMapfv, which is an array of
00194   //float values in the range [0,1]. If the map is defined, then OpenGL
00195   //can read the image pixels directly from the image.
00196 
00197   vil_pixel_format format = the_image_->pixel_format();
00198   switch ( format )
00199   {
00200     case VIL_PIXEL_FORMAT_BYTE:
00201     {
00202       vgui_range_map<unsigned char> rm(*rmp);
00203       switch ( the_image_->nplanes() )
00204       {
00205         case 1:  // ===== 8 bit grey scale image ======
00206         {
00207           vbl_array_1d<float> fLmap = rm.fLmap();
00208           if (!fLmap.size())
00209             return false;
00210           if (vp_changed||(hmap&&!vbuf_)||(!hmap&&!valid_buffer_)){
00211             vil_image_view<unsigned char> view;
00212             if (pyr)//pyramid image
00213             {
00214               view = pyr->get_copy_view(i0, ni, j0, nj, zoomx, actual_scale);
00215               sni_ = view.ni(); snj_ = view.nj();
00216               zx_/=actual_scale;       zy_/=actual_scale;
00217               if (hmap)
00218                 vbuf_ = view.memory_chunk();
00219               else
00220               {
00221                 vil_image_resource_sptr ir =
00222                   vil_new_image_resource_of_view(view);
00223                 this->create_buffer(rmp, zx_, zy_, ir);
00224                 valid_buffer_ = true;
00225               }
00226             }//regular image
00227             else if (hmap){
00228               view = the_image_->get_view(i0,ni,j0,nj);
00229               sni_ = view.ni(); snj_ = view.nj();
00230               vbuf_= view.memory_chunk();
00231             } else//buffer of the visible viewport
00232                 this->create_buffer(rmp, i0, j0, ni, nj, zx_, zy_);
00233           }
00234 
00235           if (valid_buffer_&&!hmap)
00236           { // use buffer to render
00237             buffer_->draw_viewport_as_image();
00238             return true;
00239           }
00240           else // use hardware to render
00241             if (vbuf_&&vgui_view_render(vbuf_->data(), sni_, snj_,
00242                                         zx_, zy_, GL_LUMINANCE,
00243                                         GL_UNSIGNED_BYTE, hmap, &fLmap))
00244             {
00245 #ifdef RENDER_TIMER
00246               vcl_cout << "Directly Byte Luminance Rendered in "
00247                        << t.real() << "msecs\n";
00248 #endif
00249               valid_buffer_ = false;
00250               buffer_params_ = rmp;
00251               return true;
00252             }
00253           return false;
00254         } // end of 8 bit grey scale
00255 
00256         case 3: // ===== 8 bit RGB color image ======
00257         {
00258           vbl_array_1d<float> fRmap = rm.fRmap();
00259           vbl_array_1d<float> fGmap = rm.fGmap();
00260           vbl_array_1d<float> fBmap = rm.fBmap();
00261           if (!(fRmap.size()&&fGmap.size()&&fBmap.size()))
00262             return false;
00263           if (vp_changed||(hmap&&!vbuf_)||(!hmap&&!valid_buffer_)){
00264             vil_image_view<vil_rgb<unsigned char> > view;
00265             if (pyr)//pyramid image
00266             {
00267               view = pyr->get_copy_view(i0, ni, j0, nj, zoomx, actual_scale);
00268               sni_ = view.ni(); snj_ = view.nj();
00269               zx_/=actual_scale;       zy_/=actual_scale;
00270               if (hmap)
00271                 vbuf_ = view.memory_chunk();
00272               else
00273               {
00274                 vil_image_resource_sptr ir =
00275                   vil_new_image_resource_of_view(view);
00276                 this->create_buffer(rmp, zx_, zy_, ir);
00277                 valid_buffer_ = true;
00278               }
00279             }//regular image
00280           else if (hmap){
00281             view = the_image_->get_view(i0,ni,j0,nj);
00282             sni_ = view.ni(); snj_ = view.nj();
00283             vbuf_= view.memory_chunk();
00284           } else //buffer of the visible viewport
00285               this->create_buffer(rmp, i0, j0, ni, nj, zx_, zy_);}
00286 
00287           if (valid_buffer_&&!hmap)
00288           {// use buffer to render
00289             buffer_->draw_viewport_as_image();
00290             return true;
00291           }
00292           else // use hardware to render
00293             if (vbuf_&&vgui_view_render(vbuf_->data(),
00294                                         sni_, snj_,
00295                                         zx_, zy_,
00296                                         GL_RGB, GL_UNSIGNED_BYTE, hmap,
00297                                         0, &fRmap, &fGmap, &fBmap))
00298             {
00299 #ifdef RENDER_TIMER
00300               vcl_cout << "Directly Byte RGB Rendered in "
00301                        << t.real() << "msecs\n";
00302 #endif
00303               valid_buffer_ = false;
00304               buffer_params_ = rmp;
00305               return true;
00306             }
00307           return false;
00308         }// end of 8 bit RGB
00309 
00310 #if 0  // Case 4 is currently disabled in anticipation of handling
00311        // four-band multispectral images, where the display is
00312        // customized
00313         case 4:
00314         {
00315           vbl_array_1d<float> fRmap = rm.fRmap();
00316           vbl_array_1d<float> fGmap = rm.fGmap();
00317           vbl_array_1d<float> fBmap = rm.fBmap();
00318           vbl_array_1d<float> fAmap = rm.fAmap();
00319           if (!(fRmap.size()&&fGmap.size()&&fBmap.size()&&fAmap.size()))
00320             return false;
00321           if (valid_buffer_&&!hmap)
00322           {
00323             buffer_->draw_viewport_as_image();
00324             buffer_params_ = rmp;
00325           }
00326           else
00327             if (vbuf_&&vgui_view_render(reinterpret_cast<unsigned char*>(vbuf_->data()),
00328                                         sni_, snj_,
00329                                         zx_, zy_,
00330                                         GL_RGBA, GL_UNSIGNED_BYTE, hmap,
00331                                         0, &fRmap, &fGmap, &fBmap, &fAmap))
00332             {
00333 #ifdef RENDER_TIMER
00334               vcl_cout << "Directly Byte RGBA Rendered in "
00335                        << t.real() << "msecs\n";
00336 #endif
00337               valid_buffer_ = false;
00338               buffer_params_ = rmp;
00339               return true;
00340             }
00341           return false;
00342         }
00343 #endif
00344         default:
00345           return false;
00346       }
00347     }//end of VIL_PIXEL_FORMAT_BYTE
00348 
00349     case VIL_PIXEL_FORMAT_UINT_16: // ===== 16 bit grey scale image ======
00350     {
00351       vgui_range_map<unsigned short> rm(*rmp);
00352       switch ( the_image_->nplanes() )
00353       {
00354         case 1:
00355         {
00356           vbl_array_1d<float> fLmap = rm.fLmap();
00357           if (!fLmap.size())
00358             return false;
00359           if (vp_changed||(hmap&&!vbuf_)||(!hmap&&!valid_buffer_)){
00360             vil_image_view<unsigned short> view;
00361             if (pyr)//pyramid image
00362             {
00363               view = pyr->get_copy_view(i0, ni, j0, nj, zoomx, actual_scale);
00364               if (!view) return false;
00365               sni_ = view.ni(); snj_ = view.nj();
00366               zx_/=actual_scale;       zy_/=actual_scale;
00367               if (hmap)
00368                 vbuf_ = view.memory_chunk();
00369               else
00370               {
00371                 vil_image_resource_sptr ir =
00372                   vil_new_image_resource_of_view(view);
00373                 this->create_buffer(rmp, zx_, zy_, ir);
00374                 valid_buffer_ = true;
00375               }
00376             }//regular image
00377             else if (hmap) {
00378               view = the_image_->get_view(i0,ni,j0,nj);
00379               sni_ = view.ni(); snj_ = view.nj();
00380               vbuf_= view.memory_chunk();
00381             } else
00382               this->create_buffer(rmp, i0, j0, ni, nj, zx_, zy_);}
00383 
00384           if (valid_buffer_&&!hmap)
00385           {// render with buffer
00386             buffer_->draw_viewport_as_image();
00387             return true;
00388           }
00389           else // use hardware
00390             if ( vbuf_&&vgui_view_render(vbuf_->data(),
00391                                          sni_, snj_,
00392                                          zx_, zy_,
00393                                          GL_LUMINANCE, GL_UNSIGNED_SHORT,
00394                                          hmap, &fLmap))
00395             {
00396 #ifdef RENDER_TIMER
00397               vcl_cout << "ushort Luminance Map Hardware Rendered in "
00398                        << t.real() << "msecs\n";
00399 #endif
00400               valid_buffer_ = false;
00401               buffer_params_ = rmp;
00402               return true;
00403             }
00404           return false;
00405         }//end of case uint_16, 1 plane
00406 
00407         // This case arises for multi-spectral satellite images
00408         // The current display approach is to select 3 bands from
00409         // 4 to display as RBGA where A =0. (Later use a lookup table)
00410         case 4:  // =============== 16 bit RGBX ============
00411         {
00412           if (hmap)
00413             return false;//Can't do hardware mapping with 3 band selection
00414           if (vp_changed||!valid_buffer_){
00415             vil_image_view<vil_rgba<unsigned short> > view;
00416             if (pyr) {  //pyramid image
00417               view = pyr->get_copy_view(i0, ni, j0, nj, zoomx, actual_scale);
00418               sni_ = view.ni(); snj_ = view.nj();
00419               zx_/=actual_scale;       zy_/=actual_scale;
00420               vil_image_resource_sptr ir =
00421                 vil_new_image_resource_of_view(view);
00422               this->create_buffer(rmp, zx_, zy_, ir);
00423               valid_buffer_ = true;
00424             }else //regular image
00425               this->create_buffer(rmp, i0, j0, ni, nj, zx_, zy_);}
00426           if (valid_buffer_&&!hmap)
00427           {
00428             buffer_->draw_viewport_as_image();
00429             return true;
00430           }
00431           return false;
00432         }//end of case uint_16, 4 plane
00433         default:
00434           return false;
00435       }// end of uint_16, switch on planes
00436       return false; // this statement is never reached
00437     } // end of uint_16
00438     default:
00439       return false;
00440   }//end of switch on format
00441   return false; // this statement is never reached
00442 }
00443 
00444 void vgui_vil_image_renderer::
00445     render(vgui_range_map_params_sptr const& rmp)
00446 {
00447   if ( !the_image_ )
00448     return;
00449   //If the image can be mapped then there is no point in having a
00450   //GL buffer.  The image can be directly rendered by the hardware
00451   //using the range map.
00452   if (rmp&&rmp->use_glPixelMap_&&this->render_directly(rmp))
00453     return;
00454 
00455   //otherwise we have to render a cached section buffer
00456 
00457   // Delay sectioning until first render time. This allows the section
00458   // buffer to decide on a cache format which depends on the current GL
00459   // rendering context.
00460   if (!this->old_range_map_params(rmp)||!valid_buffer_)
00461     this->create_buffer(rmp);
00462 
00463   this->draw_pixels();
00464 }
00465 
00466 //: Are the range map params associated with the current buffer out of date?
00467 //  If so we have to refresh the buffer.
00468 bool vgui_vil_image_renderer::
00469     old_range_map_params(vgui_range_map_params_sptr const& rmp)
00470 {
00471   //Cases
00472 
00473   //1) Both the current params and the new params are null
00474   if (!buffer_params_&&!rmp)
00475     return true;
00476 
00477   //2) The current params are null and the new params are not
00478   if (!buffer_params_&&rmp)
00479     return false;
00480 
00481   //3) The current params are not null and the new params are
00482   if (buffer_params_&&!rmp)
00483     return false;
00484 
00485   //4) Both current params and the new params are not null.
00486   // Are they equal?
00487   if (buffer_params_&&rmp)
00488     return *buffer_params_==*rmp;
00489 
00490   return false;
00491 }