core/vgui/vgui_cache_wizard.cxx
Go to the documentation of this file.
00001 // This is core/vgui/vgui_cache_wizard.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \brief See vgui_cache_wizard.h for a description of this file.
00008 
00009 #include "vgui_cache_wizard.h"
00010 
00011 #include <vil1/vil1_crop.h>
00012 #include <vil1/vil1_pixel.h>
00013 
00014 #include <vcl_cassert.h>
00015 #include <vcl_iostream.h>
00016 
00017 #include <vgui/vgui_pixel.h>
00018 #include <vgui/vgui_macro.h>
00019 #include <vgui/internals/vgui_accelerate.h>
00020 
00021 #define DEFAULT_QUADRANT_WIDTH  256
00022 #define DEFAULT_QUADRANT_HEIGHT 256
00023 #define INVALID_TEXTURE_NAME -1
00024 
00025 #define mb_jigerry_pokery(a,b) ((a/b)+(a%b ? 1:0))
00026 #define mb_is_valid(x) (int(x) != INVALID_TEXTURE_NAME)
00027 
00028 bool debug = false;
00029 
00030 //: Constructor.
00031 // Set the image quadrant width/height.
00032 // Do use sensible values(i.e. powers of two) otherwise the cache
00033 // wizard won't work.
00034 vgui_cache_wizard::vgui_cache_wizard(int quadrant_width,
00035                                      int quadrant_height)
00036 {
00037   if (debug)
00038     vcl_cerr << __FILE__": this is the constructor\n";
00039     // *(int*)0 = 1;
00040 
00041   //: Get the maximum texture size.
00042   // Note that this function returns the worst case scenario
00043   // i.e. assumes the texture is 4bpp... So we are on the safe ground
00044   GLint max_texture_size;
00045   glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
00046   if (max_texture_size>256)
00047     max_texture_size = 256;
00048 
00049   if (quadrant_width<max_texture_size)
00050     quadrant_width_ = quadrant_width;
00051   else
00052     quadrant_width_ = max_texture_size;
00053   if (quadrant_height<max_texture_size)
00054     quadrant_height_ = quadrant_height;
00055   else
00056     quadrant_height_ = max_texture_size;
00057   // Assume 8M for now
00058   max_texture_num_ = (unsigned int)(8388608.0f/(quadrant_width_*quadrant_height_*4.0f))*3;
00059   // Generate the names for those textures
00060   texture_names_ = new GLuint[max_texture_num_];
00061   glGenTextures(max_texture_num_,texture_names_);
00062 }
00063 
00064 //: Destructor
00065 vgui_cache_wizard::~vgui_cache_wizard()
00066 {
00067   vcl_list<GLuint>::iterator i = cache_queue_.begin();
00068   for (int j=0; i!=cache_queue_.end(); ++i,++j)
00069     texture_names_[j] = *i;
00070   glDeleteTextures(cache_queue_.size(),texture_names_);
00071   delete texture_names_;
00072 }
00073 
00074 vgui_cache_wizard *vgui_cache_wizard::Instance()
00075 {
00076   static vgui_cache_wizard *instance_ = new vgui_cache_wizard(DEFAULT_QUADRANT_WIDTH, DEFAULT_QUADRANT_HEIGHT);
00077   return instance_;
00078 }
00079 
00080 //: Loads an image
00081 int vgui_cache_wizard::load_image(vil1_image img)
00082 {
00083   // Check whether the image pointer is already in memory
00084   vcl_vector<wizard_image *>::iterator i = images_.begin();
00085   for (int j = 0; i!=images_.end(); ++i,++j)
00086     if ((*i)->first == img)
00087       return j;
00088 
00089   // Find out the size of image in quadrants
00090   int cx = mb_jigerry_pokery(img.width(),quadrant_width_);
00091   int cy = mb_jigerry_pokery(img.height(),quadrant_height_);
00092 
00093   dimension *dim = new dimension(cx,cy);
00094   dimensions_.push_back(dim);
00095   // Initialize
00096   image_cache_quadrants *mapping = new image_cache_quadrants;
00097   for (int i = 0;i<cy;i++)
00098     for (int j = 0;j<cx;j++)
00099       mapping->push_back(unsigned(INVALID_TEXTURE_NAME));
00100 
00101   wizard_image *wz = new wizard_image(img,mapping);
00102   images_.push_back(wz);
00103   return images_.size()-1;
00104 }
00105 
00106 //: Returns the texture names associated with the specified image region
00107 bool vgui_cache_wizard::get_section(int id,int x,int y,int width,int height,
00108                                     image_cache_quadrants *quadrants,
00109                                     dimension *pos,
00110                                     dimension *size)
00111 {
00112   // Find out which image it is
00113   wizard_image *wz = images_[id];
00114   vil1_image img = wz->first;
00115   image_cache_quadrants *icq = wz->second;
00116   dimension *d = dimensions_[id];
00117   // Work out which quadrant (x,y) lies in
00118   int qx_c = x/quadrant_width_;
00119   int qy_c = y/quadrant_height_;
00120   // Work out section width and height in quadrant units
00121   int qw_c = (x+width)/quadrant_width_-qx_c;//mb_jigerry_pokery(x+width,quadrant_width_);
00122   int qh_c = (y+height)/quadrant_height_-qy_c;//mb_jigerry_pokery(y+height,quadrant_height_);
00123   if (debug)
00124     vcl_cerr<<"X: "<<qx_c<<"Y: "<<qy_c<<"W:"<<qw_c<<" H:"<<qh_c<<vcl_endl;
00125   pos->first = qx_c;
00126   pos->second = qy_c;
00127   size->first = qw_c;
00128   size->second = qh_c;
00129 
00130   // This is the index of the upper left quadrant of the section
00131   int ul_q = qy_c*d->first+qx_c;
00132   glEnable(GL_TEXTURE_2D);
00133   vgui_macro_report_errors;
00134 
00135   for (int i = 0;i<=qh_c;i++)
00136     for (int j = 0;j<=qw_c;j++)
00137     {
00138       // (i,j) relative to the upper left corner of the section
00139       // needs to be translated into image coordinates
00140       int index = ul_q+i*d->first+j;
00141       // Check to see whether the quadrant is in cache
00142       if (index<0 || index>=int(icq->size()))
00143       {
00144         vcl_cerr << __FILE__ ": index out of range\n";
00145         return false;
00146       }
00147       if (mb_is_valid((*icq)[index]))
00148       {
00149         quadrants->push_back((*icq)[index]);
00150         vcl_list<GLuint>::iterator i;
00151         for (i = cache_queue_.begin();i!=cache_queue_.end() && (*i)!=(*icq)[index]; i++)
00152           ;
00153         cache_queue_.erase(i);
00154         cache_queue_.push_back((*icq)[index]);
00155       }
00156       else
00157       {
00158         // If the quadrant is not in cache load it by dumping least recently used
00159         // texture block if necessary
00160         GLuint texture_name;
00161         if (cache_queue_.size() == max_texture_num_)
00162         {
00163           // Time to dump LRU texture and use it for the quadrant of this image section
00164           texture_name = cache_queue_.front();
00165           vcl_cerr<<"Texture name: "<<texture_name<<vcl_endl;
00166           cache_queue_.pop_front();
00167           // Find the image where texture_name has been used and invalidate
00168           // that qudrangle
00169           for (vcl_vector<wizard_image *>::iterator i = images_.begin(); i!=images_.end();i++)
00170             for (image_cache_quadrants::iterator k = (*i)->second->begin(); k!=(*i)->second->end();k++)
00171               if ((*k) == texture_name)
00172               {
00173                 *k = GLuint(INVALID_TEXTURE_NAME);
00174                 vcl_cerr<<"Invalidated!\n";
00175               }
00176         }
00177         else
00178         {
00179           unsigned int k = 0;
00180           while (k<max_texture_num_ && texture_names_[k] == GLuint(-1)) ++k;
00181           texture_name = texture_names_[k];
00182           texture_names_[k] = GLuint(-1);
00183         }
00184 
00185         glBindTexture(GL_TEXTURE_2D,texture_name);
00186         TexImage2D_Brownie(vil1_crop(img,pos->first*quadrant_width_+j*quadrant_width_,
00187                                     pos->second*quadrant_height_+i*quadrant_height_,
00188                                     quadrant_width_,
00189                                     quadrant_height_));
00190         quadrants->push_back(texture_name);
00191         cache_queue_.push_back(texture_name);
00192         // Validate the appropriate quadrangle
00193         (*icq)[index] = texture_name;
00194       }
00195     }
00196   return true;
00197 }
00198 
00199 //--------------------------------------------------------------------------------
00200 //
00201 // pixel format conversions
00202 
00203 // 'pix' is the pixel type supplied by the image.
00204 // 'wh'  is a string describing the pixel type.
00205 // uses: 'section_ok', 'image', 'data', 'x', 'y', 'w', 'h', 'allocw', 'alloch'
00206 #define fsm_macro_begin(pix, wh) \
00207 pix *data = new pix[ img.width()*img.height() ]; /* note: this buffer is w-by-h, not allocw-by-alloch */ \
00208 section_ok = img.get_section( data, 0,0, img.width(),img.height() ); \
00209 char const *what = wh; \
00210 /* bool found = false; */ /* used to allow semicolons after macro calls */ \
00211 if (false) {}
00212 
00213 // 'fmt' is the GLenum format passed to glDrawPixels()
00214 // 'typ' is the GLenum type passed to glDrawPixels()
00215 // 'sto' is the pixel type to store the section as. it must agree with 'fmt' and 'typ'.
00216 // uses: 'what', 'data', 'the_pixels', 'w', 'h'
00217 #define fsm_macro_magic(fmt, typ, sto) \
00218 if (format==fmt && type==typ) { \
00219   if (debug) \
00220     vcl_cerr << __FILE__ ": converting " << what << " image to " #fmt "," #typ " format\n"; \
00221   if (!the_pixels) \
00222     the_pixels = new sto[img.width()*img.height()]; \
00223   vgui_pixel_convert_span(data, static_cast<sto*>(the_pixels), \
00224                           img.width()*img.height()); \
00225 }
00226 
00227 // you *must* call this -- to deallocate the temp buffer.
00228 #define fsm_macro_end \
00229 delete [] data; \
00230 assert(section_ok);
00231 
00232 void vgui_cache_wizard::TexImage2D_Brownie(vil1_image img)
00233 {
00234   void *the_pixels = 0;
00235   bool section_ok;
00236   // FIXME: the calls to fsm_macro_magic() are identical for each image pixel type.
00237   // They could be coalesced to reduce code maintenance.
00238 
00239   // 8bit greyscale
00240   GLenum format,type;
00241   vgui_accelerate::instance()->vgui_choose_cache_format(&format,&type);
00242 
00243   vil1_pixel_format_t pixel_format = vil1_pixel_format(img);
00244 
00245   if (pixel_format == VIL1_BYTE)
00246   {
00247     fsm_macro_begin(GLubyte, "8 bit greyscale");
00248     fsm_macro_magic(GL_RGB,      GL_UNSIGNED_BYTE,        vgui_pixel_rgb888);
00249     fsm_macro_magic(GL_RGBA,     GL_UNSIGNED_BYTE,        vgui_pixel_rgba8888);
00250 #if defined(GL_UNSIGNED_SHORT_5_6_5)
00251     fsm_macro_magic(GL_RGB,      GL_UNSIGNED_SHORT_5_6_5, vgui_pixel_rgb565);
00252 #endif
00253 #if defined(GL_BGRA)
00254     fsm_macro_magic(GL_BGRA,     GL_UNSIGNED_BYTE,        vgui_pixel_bgra8888);
00255 #endif
00256 #if defined(GL_EXT_abgr) || defined(GL_ABGR_EXT)
00257     fsm_macro_magic(GL_ABGR_EXT, GL_UNSIGNED_BYTE,        vgui_pixel_abgr8888);
00258 #endif
00259     fsm_macro_end;
00260   }
00261 
00262   // 24bit rgb
00263   else if (pixel_format == VIL1_RGB_BYTE)
00264   {
00265     fsm_macro_begin(vgui_pixel_rgb888, "24 bit RGB");
00266     fsm_macro_magic(GL_RGB,      GL_UNSIGNED_BYTE,        vgui_pixel_rgb888);
00267     fsm_macro_magic(GL_RGBA,     GL_UNSIGNED_BYTE,        vgui_pixel_rgba8888);
00268 #if defined(GL_UNSIGNED_SHORT_5_6_5)
00269     fsm_macro_magic(GL_RGB,      GL_UNSIGNED_SHORT_5_6_5, vgui_pixel_rgb565);
00270 #endif
00271 #if defined(GL_BGRA)
00272     fsm_macro_magic(GL_BGRA,     GL_UNSIGNED_BYTE,        vgui_pixel_bgra8888);
00273 #endif
00274 #if defined(GL_EXT_abgr) || defined(GL_ABGR_EXT)
00275     fsm_macro_magic(GL_ABGR_EXT, GL_UNSIGNED_BYTE,        vgui_pixel_abgr8888);
00276 #endif
00277     fsm_macro_end;
00278   }
00279 
00280   // 32bit rgba
00281   else if (pixel_format == VIL1_RGBA_BYTE)
00282   {
00283     fsm_macro_begin(vgui_pixel_rgba8888, "32 bit RGBA");
00284     fsm_macro_magic(GL_RGB,      GL_UNSIGNED_BYTE,        vgui_pixel_rgb888);
00285     fsm_macro_magic(GL_RGBA,     GL_UNSIGNED_BYTE,        vgui_pixel_rgba8888);
00286 #if defined(GL_UNSIGNED_SHORT_5_6_5)
00287     fsm_macro_magic(GL_RGB,      GL_UNSIGNED_SHORT_5_6_5, vgui_pixel_rgb565);
00288 #endif
00289 #if defined(GL_BGRA)
00290     fsm_macro_magic(GL_BGRA,     GL_UNSIGNED_BYTE,        vgui_pixel_bgra8888);
00291 #endif
00292 #if defined(GL_EXT_abgr) || defined(GL_ABGR_EXT)
00293     fsm_macro_magic(GL_ABGR_EXT, GL_UNSIGNED_BYTE,        vgui_pixel_abgr8888);
00294 #endif
00295     fsm_macro_end;
00296   }
00297 
00298   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
00299   vgui_macro_report_errors;
00300 
00301   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
00302   vgui_macro_report_errors;
00303 
00304   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00305   vgui_macro_report_errors;
00306 
00307   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00308   vgui_macro_report_errors;
00309 
00310   // decal
00311   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
00312   vgui_macro_report_errors;
00313 
00314   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00315   if (debug) vcl_cerr<<"Loading texture...";
00316   glTexImage2D(GL_TEXTURE_2D, // target
00317                0,             // level
00318                3,             // internalformat (use only RGB. ignore alpha channel)
00319                img.width(),   // NB: must be power of 2
00320                img.height(),  // NB: must be power of 2
00321                0,             // border FIXME
00322                format,
00323                type,
00324                the_pixels);
00325   vgui_macro_report_errors;
00326 }