TextureManager.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00023 #include <math.h>
00024 #include <iostream>
00025 
00026 #include <config.h>
00027 
00028 #ifdef __APPLE__
00029 #include "panoinc.h"
00030 #endif
00031 
00032 
00033 #include "ViewState.h"
00034 #include "TextureManager.h"
00035 #include "huginApp.h"
00036 #include "GLPreviewFrame.h"
00037 
00038 #include "vigra/stdimage.hxx"
00039 #include "vigra/resizeimage.hxx"
00040 #include "base_wx/wxImageCache.h"
00041 #include "photometric/ResponseTransform.h"
00042 #include "panodata/Mask.h"
00043 
00044 // The OpenGL Extension wrangler libray will find extensions and the latest
00045 // supported OpenGL version on all platforms.
00046 #if !defined Hugin_shared || !defined _WINDOWS
00047 #define GLEW_STATIC
00048 #endif
00049 #include <GL/glew.h>
00050 #include <wx/platform.h>
00051 
00052 #ifdef __WXMAC__
00053 #include <OpenGL/gl.h>
00054 #include <OpenGL/glu.h>
00055 #else
00056 #include <GL/gl.h>
00057 #include <GL/glu.h>
00058 #endif
00059 
00060 TextureManager::TextureManager(PT::Panorama *pano, ViewState *view_state_in)
00061 {
00062     m_pano = pano;
00063     photometric_correct = false;
00064     view_state = view_state_in;
00065 }
00066 
00067 TextureManager::~TextureManager()
00068 {
00069     // free up the textures
00070     textures.clear();
00071 }
00072 
00073 void TextureManager::DrawImage(unsigned int image_number,
00074                                unsigned int display_list)
00075 {
00076     // bind the texture that represents the given image number.
00077     std::map<TextureKey, TextureInfo>::iterator it;
00078     HuginBase::SrcPanoImage *img_p = view_state->GetSrcImage(image_number);
00079     TextureKey key(img_p, &photometric_correct);
00080     it = textures.find(key);
00081     DEBUG_ASSERT(it != textures.end());
00082     it->second.Bind();
00083     glColor4f(1.0,1.0,1.0,1.0);
00084     if (it->second.GetUseAlpha() || it->second.GetHasActiveMasks())
00085     {
00086         // use an alpha blend if there is a alpha channel or a mask for this image.
00087         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
00088         glEnable(GL_BLEND);
00089     }
00090     if (!photometric_correct)
00091     {
00092         // When using real time photometric correction, we multiply the colour
00093         // components to get the white balance and exposure correct.
00094         HuginBase::SrcPanoImage *img = view_state->GetSrcImage(image_number);    
00095         // we adjust the intensity by using a darker colour
00096         float es = viewer_exposure / img->getExposure();
00097         float scale[4] = {es / img->getWhiteBalanceRed(),
00098                           es,
00099                           es / img->getWhiteBalanceBlue(),
00100                           1.0};
00101         glColor3fv(scale);
00102         glCallList(display_list);
00103         // Since the intensity was clamped to 0.0 - 1.0, we might overdraw a
00104         // few times to make it brighter.
00105         // FIXME If the image has areas masked out, these will also be
00106         // brightened. It might be better to do using the texture, but this
00107         // way we can only add the texture to the frame buffer, (we can't double
00108         // the intensity multiple times) and there is a cost in processing the
00109         // texture. It also won't work properly on partially transparent places.
00110         if (scale[0] > 1.0 || scale[1] > 1.0 || scale[2] >  1.0)
00111         {
00112             view_state->GetTextureManager()->DisableTexture();
00113             glEnable(GL_BLEND);
00114             glBlendFunc(GL_DST_COLOR, GL_ONE);
00115             glColor4f(1.0, 1.0, 1.0, 1.0);
00116             // double the brightness for colour components until it is almost
00117             // right, however limit it incase it is really bright.
00118             bool r, g, b;
00119             unsigned short int count = 0;
00120             while ((   (r = (scale[0] > 2.0))
00121                    || (g = (scale[1] > 2.0))
00122                    || (b = (scale[2] > 2.0)))
00123                    && count < 9)
00124             {
00125                 glColor4f(r ? 1.0 : 0.0, g ? 1.0 : 0.0, b ? 1.0 : 0.0, 1.0);
00126                 glCallList(display_list);
00127                 if (r) scale[0] /= 2.0;
00128                 if (g) scale[1] /= 2.0;
00129                 if (b) scale[2] /= 2.0;
00130                 count++;
00131             }
00132             // now add on anything remaining.
00133             if (scale[0] > 1.0 || scale[1] > 1.0 || scale[2] >  1.0)
00134             {
00135                 // clamped to 0.0-1.0, so it won't get darker.
00136                 scale[0] -= 1.0; scale[1] -= 1.0; scale[2] -= 1.0;
00137                 glColor3fv(scale);
00138                 glCallList(display_list);
00139             }
00140             glEnable(GL_TEXTURE_2D);
00141             glDisable(GL_BLEND);
00142             glColor3f(1.0, 1.0, 1.0);
00143         }
00144     } else {
00145         // we've already corrected all the photometrics, just draw once normally
00146         glCallList(display_list);
00147         if (it->second.GetUseAlpha() || it->second.GetHasActiveMasks())
00148         {
00149             glDisable(GL_BLEND);
00150         }
00151     }
00152 }
00153 
00154 unsigned int TextureManager::GetTextureName(unsigned int image_number)
00155 {
00156     // bind the texture that represents the given image number.
00157     std::map<TextureKey, TextureInfo>::iterator it;
00158     HuginBase::SrcPanoImage *img_p = view_state->GetSrcImage(image_number);
00159     TextureKey key(img_p, &photometric_correct);
00160     it = textures.find(key);
00161     DEBUG_ASSERT(it != textures.end());
00162     return it->second.GetNumber();
00163 }
00164 
00165 void TextureManager::BindTexture(unsigned int image_number)
00166 {
00167     // bind the texture that represents the given image number.
00168     std::map<TextureKey, TextureInfo>::iterator it;
00169     HuginBase::SrcPanoImage *img_p = view_state->GetSrcImage(image_number);
00170     TextureKey key(img_p, &photometric_correct);
00171     it = textures.find(key);
00172     DEBUG_ASSERT(it != textures.end());
00173     it->second.Bind();
00174 }
00175 
00176 void TextureManager::DisableTexture(bool maskOnly)
00177 {
00178     if(view_state->GetSupportMultiTexture())
00179     {
00180         glActiveTexture(GL_TEXTURE1);
00181         glDisable(GL_TEXTURE_2D);
00182         glActiveTexture(GL_TEXTURE0);
00183         if(!maskOnly)
00184             glDisable(GL_TEXTURE_2D);
00185     }
00186     else
00187     {
00188         if(!maskOnly)
00189             glDisable(GL_TEXTURE_2D);
00190     };
00191 };
00192 
00193 void TextureManager::Begin()
00194 {
00195     if (!photometric_correct)
00196     {
00197         // find the exposure factor to scale by.
00198         viewer_exposure = 1.0 / pow(2.0,
00199                                     m_pano->getOptions().outputExposureValue);
00200     };
00201 };
00202 
00203 void TextureManager::End()
00204 {
00205 }
00206 
00207 void TextureManager::CheckUpdate()
00208 {
00209     // The images or their lenses have changed.
00210     // Find what size we should have the textures.
00211     // Note that one image changing does affect the rest, if an image suddenly
00212     // takes up more space, the others should take up less.
00213     unsigned int num_images = m_pano->getNrOfImages();
00214     if (num_images == 0)
00215     {
00216         textures.clear();
00217         return;
00218     }
00219     // if we are doing photometric correction, and someone changed the output
00220     // exposure, all of our images are at the wrong exposure.
00221     if (photometric_correct && view_state->RequireRecalculatePhotometric())
00222     {
00223         textures.clear();
00224     }
00225     HuginBase::PanoramaOptions *dest_img = view_state->GetOptions();
00226     // Recalculuate the ideal image density if required
00227     // TODO tidy up once it works.
00228     DEBUG_INFO("Updating texture sizes.");
00229     // find the total of fields of view of the images, in degrees squared
00230     // we assume each image has the same density across all it's pixels
00231     double total_fov = 0.0;
00232     for (unsigned int image_index = 0; image_index < num_images; image_index++)
00233     {
00234         HuginBase::SrcPanoImage *src = view_state->GetSrcImage(image_index);
00235         double aspect = double(src->getSize().height())
00236                                                / double(src->getSize().width());
00237         total_fov += src->getHFOV() * aspect;
00238     };
00239     // now find the ideal density
00240     texel_density = double(GetMaxTotalTexels()) / total_fov;
00241 
00242     // now recalculate the best image sizes
00243     // The actual texture size is the biggest one possible withouth scaling the
00244     // image up in any direction. We only specifiy mipmap levels we can fit in
00245     // a given amount of texture memory, while respecting the image's FOV.
00246     int texels_used = 0;
00247     double ideal_texels_used = 0.0;
00248     for (unsigned int image_index = 0; image_index < num_images; image_index++)
00249     {    
00250         // find this texture
00251         // if it has not been created before, it will be created now.
00252         std::map<TextureKey, TextureInfo>::iterator it;
00253         HuginBase::SrcPanoImage *img_p = view_state->GetSrcImage(image_index);
00254         TextureKey key(img_p, &photometric_correct);
00255         it = textures.find(key);
00256         TextureInfo *texinfo;
00257         /* This section would allow us to reuse textures generated when we want
00258          * to change the size. It is not used as it causes segmentation faults
00259          * under Ubuntu 8.04's "ati" graphics driver.
00260          */
00261       #if 0
00262         if (it == textures.end())
00263         {
00264             // We haven't seen this image before.
00265             // Find a size for it and make its texture.
00266             // store the power that 2 is raised to, not the actual size
00267             unsigned int max_tex_width_p = int(log2(img_p->getSize().width())),
00268                         max_tex_height_p = int(log2(img_p->getSize().height()));
00269             // check this is hardware supported.
00270             {
00271               unsigned int biggest = GetMaxTextureSizePower();
00272               if (biggest < max_tex_width_p) max_tex_width_p = biggest;
00273               if (biggest < max_tex_height_p) max_tex_height_p = biggest;
00274             }
00275             std::cout << "Texture size for image " << image_index << " is "
00276                       << (1 << max_tex_width_p) << " by "
00277                       << (1 << max_tex_height_p) << "\n";
00278             // create a new texinfo and store the texture details.
00279             std::cout << "About to create new TextureInfo for "
00280                       << img_p->getFilename()
00281                       << ".\n";
00282             std::pair<std::map<TextureKey, TextureInfo>::iterator, bool> ins;
00283             ins = textures.insert(std::pair<TextureKey, TextureInfo>
00284                                  (TextureKey(img_p, &photometric_correct),
00285                 // the key is used to identify the image with (or without)
00286                 // photometric correction parameters.
00287                               TextureInfo(max_tex_width_p, max_tex_height_p)
00288                             ));
00289             texinfo = &((ins.first)->second);
00290         }
00291         else
00292         {
00293             texinfo = &(it->second);
00294         }
00295                 
00296         // find the highest mipmap we want to use.
00297         double hfov = img_p->getHFOV(),
00298                aspect = double (texinfo->height) / double (texinfo->width),
00299                ideal_texels = texel_density * hfov * aspect,
00300                // we would like a mipmap with this size:
00301                ideal_tex_width = sqrt(ideal_texels / aspect),
00302                ideal_tex_height = aspect * ideal_tex_width;
00303         // Ideally this mipmap would bring us up to this many texels
00304         ideal_texels_used += ideal_texels;
00305         std::cout << "Ideal mip size: " << ideal_tex_width << " by "
00306                   << ideal_tex_height << "\n";
00307         // Find the smallest mipmap level that is at least this size.
00308         int max_mip_level = (texinfo->width_p > texinfo->height_p)
00309                             ? texinfo->width_p : texinfo->height_p;
00310         int mip_level = max_mip_level - ceil((ideal_tex_width > ideal_tex_height)
00311                         ? log2(ideal_tex_width) : log2(ideal_tex_height));
00312         // move to the next mipmap level if we are over budget.
00313         if ((texels_used + (1 << (texinfo->width_p + texinfo->height_p
00314                                   - mip_level * 2)))
00315             > ideal_texels_used)
00316         {
00317             // scale down
00318             mip_level ++;
00319         }
00320         // don't allow any mipmaps smaller than the 1 by 1 pixel one.
00321         if (mip_level > max_mip_level) mip_level = max_mip_level;
00322         // don't allow any mipmaps with a negative level of detail (scales up)
00323         if (mip_level < 0) mip_level = 0;
00324         // find the size of this level
00325         int mip_width_p = texinfo->width_p - mip_level,
00326             mip_height_p = texinfo->height_p - mip_level;
00327         // check if we have scaled down to a single line, and make sure we
00328         // limit the line's width to 1 pixel.
00329         if (mip_width_p < 0) mip_width_p = 0;
00330         if (mip_height_p < 0) mip_height_p = 0;
00331         
00332         // now count these texels as used- we are ignoring the smaller mip
00333         //   levels, they add 1/3 on to the size.
00334         texels_used += 1 << (mip_width_p + mip_height_p);
00335         std::cout << "biggest mipmap of image " << image_index << " is "
00336                   << (1 << mip_width_p) << " by " << (1 << mip_height_p)
00337                   << " (level " << mip_level <<").\n";
00338         std::cout << "Ideal texels used " << int(ideal_texels_used)
00339                   << ", actually used " << texels_used << ".\n\n";
00340         if (texinfo->min_lod != mip_level)
00341         {
00342             // maximum level required changed.
00343             if (texinfo->min_lod > mip_level)
00344             {
00345                 // generate more levels
00346                 texinfo->DefineLevels(mip_level,
00347                                       (texinfo->min_lod > max_mip_level) ?
00348                                       max_mip_level : texinfo->min_lod - 1,
00349                                       photometric_correct, dest_img,
00350                                       view_state->GetSrcImage(image_index));
00351             }
00352             texinfo->SetMaxLevel(mip_level);
00353             texinfo->min_lod = mip_level;
00354         }
00355     }
00356     #endif
00357     /* Instead of the above section, replace the whole texture when appropriate:
00358         */
00359         // Find a size for it
00360         double hfov = img_p->getHFOV(),
00361            aspect = double (img_p->getSize().height())
00362                                             / double (img_p->getSize().width()),
00363            ideal_texels = texel_density * hfov * aspect,
00364            // we would like a texture this size:
00365            ideal_tex_width = sqrt(ideal_texels / aspect),
00366            ideal_tex_height = aspect * ideal_tex_width;
00367         // shrink if bigger than the original, avoids scaling up excessively.
00368         if (ideal_tex_width > img_p->getSize().width())
00369                 ideal_tex_width = img_p->getSize().width();
00370         if (ideal_tex_height > img_p->getSize().height())
00371                 ideal_tex_height = img_p->getSize().height();
00372         // we will need to round up/down to a power of two
00373         // round up first, then shrink if over budget.
00374         // store the power that 2 is raised to, not the actual size
00375         unsigned int tex_width_p = int(log2(ideal_tex_width)) + 1,
00376                     tex_height_p = int(log2(ideal_tex_height)) + 1;
00377         // check this is hardware supported.
00378         {
00379           unsigned int biggest = GetMaxTextureSizePower();
00380           if (biggest < tex_width_p) tex_width_p = biggest;
00381           if (biggest < tex_height_p) tex_height_p = biggest;
00382         }
00383         
00384         // check if this is over budget.
00385         ideal_texels_used += ideal_texels; 
00386         // while the texture is over budget, shrink it
00387         while (  (texels_used + (1 << (tex_width_p + tex_height_p)))
00388             > ideal_texels_used)
00389         {
00390             // smaller aspect means the texture is wider.
00391             if ((double) (1 << tex_height_p) / (double) (1 << tex_width_p)
00392                < aspect)
00393             {
00394                 tex_width_p--;
00395             } else {
00396                 tex_height_p--;
00397             }
00398         }
00399         // we have a nice size
00400         texels_used += 1 << (tex_width_p + tex_height_p);
00401         if (   it == textures.end()
00402             || (it->second).width_p != tex_width_p
00403             || (it->second).height_p != tex_height_p)
00404         {
00405             // Either: 1. We haven't seen this image before
00406             //     or: 2. Our texture for this is image is the wrong size
00407             // ...therefore we make a new one the right size:
00408             //
00409             // remove duplicate key if exists
00410             TextureKey checkKey (img_p, &photometric_correct);
00411             if (textures.find(checkKey) != textures.end()) {
00412                 // Already exists in map, remove it first before adding a new one
00413                 textures.erase(checkKey);
00414             }
00415 
00416             std::pair<std::map<TextureKey, TextureInfo>::iterator, bool> ins;
00417             ins = textures.insert(std::pair<TextureKey, TextureInfo>
00418                                  (TextureKey(img_p, &photometric_correct),
00419                 // the key is used to identify the image with (or without)
00420                 // photometric correction parameters.
00421                               TextureInfo(view_state, tex_width_p, tex_height_p)
00422                             ));
00423            // create and upload the texture image
00424            texinfo = &((ins.first)->second);
00425            texinfo->DefineLevels(0, // minimum mip level
00426                                  // maximum mip level
00427                         tex_width_p > tex_height_p ? tex_width_p : tex_height_p,
00428                                 photometric_correct,
00429                                 *dest_img,
00430                                 *view_state->GetSrcImage(image_index));
00431            texinfo->DefineMaskTexture(*view_state->GetSrcImage(image_index));
00432         }
00433         else
00434         {
00435             if(view_state->RequireRecalculateMasks(image_index))
00436             {
00437                 //mask for this image has changed, also update only mask
00438                 (*it).second.UpdateMask(*view_state->GetSrcImage(image_index));
00439             };
00440         }
00441     }
00442     // We should remove any images' texture when it is no longer in the panorama
00443     // with the ati bug work around, we might make unneassry textures whenever 
00444     //if (photometric_correct || view_state->ImagesRemoved())
00445     {
00446         CleanTextures();
00447     }
00448 //    std::map<TextureKey, TextureInfo>::iterator it;
00449 //    for (it = textures.begin() ; it != textures.end() ; it++) {
00450 //        DEBUG_DEBUG("textures num " << it->second.GetNumber());
00451 //    }
00452 }
00453 
00454 void TextureManager::SetPhotometricCorrect(bool state)
00455 {
00456     // change the photometric correction state.
00457     if (state != photometric_correct)
00458     {
00459         photometric_correct = state;
00460         // We will need to recalculate all the images.
00461         /* TODO It may be possible to keep textures that have some identity
00462          * photometric transformation.
00463          * Be warned that when turning off photometric correction, two images
00464          * with the same filename will suddenly have the same key, which will
00465          * break the textures map, hence clearing now                         */
00466         textures.clear();
00467     }
00468 }
00469 
00470 unsigned int TextureManager::GetMaxTotalTexels()
00471 {
00472     // TODO: cut off at a sensible value for available hardware, otherwise set
00473     // to something like 4 times the size of the screen.
00474     // The value is guestimated as good for 1024*512 view where each point is
00475     // covered by 4 images.
00476     return 2097152;
00477     // Note: since we use mipmaps, the amount of actual maximum of pixels stored
00478     // will be 4/3 of this value. It should use a maximum of 8MB of video memory
00479     // for 8 bits per channel rgb images, 12MB if we include a mask.
00480     // Video memory is also used for two copies of the screen and any auxilary
00481     // buffers, and the meshes, so we should do fine with ~24MB of video memory.
00482 }
00483 
00484 unsigned int TextureManager::GetMaxTextureSizePower()
00485 {
00486     // get the maximum texture size supported by the hardware
00487     // note the value can be too small, it is for a square texture with borders.
00488     // we don't use borders, and the textures aren't always square.
00489     static unsigned int max_size_p = 0;
00490     if (max_size_p) return max_size_p; // don't ask openGL again.
00491     
00492     GLint max_size;
00493     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
00494     if (glGetError())
00495     {
00496       DEBUG_ERROR("Cannot find maximum texture size!");
00497       // opengl docs say 64 pixels square is the minimum size guranteed to be supported.
00498       return 6;
00499     }
00500     max_size_p = int(log2(max_size));
00501     DEBUG_INFO("Max texture size supported is " << max_size <<
00502                " (2^" << max_size_p << ")");
00503     return max_size_p;
00504 }
00505 
00506 void TextureManager::CleanTextures()
00507 {
00508     // clean up all the textures from removed images.
00509     // TODO can this be more efficient?
00510     unsigned int num_images = m_pano->getNrOfImages();
00511     bool retry = true;
00512     std::map<TextureKey, TextureInfo>::iterator tex;
00513     while (retry)
00514     {
00515       retry = false;
00516       for (tex = textures.begin(); tex != textures.end(); tex++)
00517       {
00518           bool found = false;
00519           
00520           // try and find an image with this key
00521           for (unsigned int img = 0; img < num_images; img++)
00522           {
00523               TextureKey ik(view_state->GetSrcImage(img), &photometric_correct);
00524               if (ik == tex->first)
00525               {
00526                   found = true;
00527                   break;
00528               }
00529           }
00530           // remove it if it was not found
00531           if (!found)
00532           {
00533               DEBUG_INFO("Removing old texture for " << tex->first.filename << ".");
00534               retry = true;
00535               textures.erase(tex);
00536               break;
00537           }
00538       }
00539     }
00540 }
00541 
00542 TextureManager::TextureInfo::TextureInfo(ViewState *new_view_state)
00543 {
00544     // we shouldn't be using this. It exists only to make std::map happy.
00545     DEBUG_ASSERT(0);
00546     m_viewState=new_view_state;
00547     has_active_masks=false;
00548     CreateTexture();
00549 }
00550 
00551 TextureManager::TextureInfo::TextureInfo(ViewState *new_view_state, unsigned int width_p_in,
00552                                          unsigned int height_p_in)
00553 {
00554     m_viewState=new_view_state;
00555     has_active_masks=false;
00556     width_p = width_p_in;
00557     height_p = height_p_in;
00558     width = 1 << width_p;
00559     height = 1 << height_p;
00560     CreateTexture();
00561 }
00562 
00563 void TextureManager::TextureInfo::CreateTexture()
00564 {
00565     // Get an number for an OpenGL texture
00566     glGenTextures(1, (GLuint*) &num);
00567     DEBUG_DEBUG("textures num created " << num);
00568     glGenTextures(1, (GLuint*) &numMask);
00569     // we want to generate all levels of detail, they are all undefined.
00570     min_lod = 1000;
00571     SetParameters();
00572 }
00573 
00574 void TextureManager::TextureInfo::SetParameters()
00575 {
00576     BindImageTexture();
00577     glEnable(GL_TEXTURE_2D);
00578     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
00579                     GL_LINEAR_MIPMAP_LINEAR);
00580     // we don't want the edges to repeat the other side of the texture
00581     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00582     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00583     // use anistropic filtering if supported. This is good because we are
00584     // sretching and distorting the textures rather a lot in places and still
00585     // want good image quality.
00586     static bool checked_anisotropic = false;
00587     static bool has_anisotropic;
00588     static float anisotropy;
00589     if (!checked_anisotropic)
00590     {
00591         // check if it is supported
00592         if (GLEW_EXT_texture_filter_anisotropic)
00593         {
00594             has_anisotropic = true;
00595             glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropy);
00596             DEBUG_INFO("Using anisotropic filtering at maximum value "
00597                       << anisotropy);
00598         } else {
00599             has_anisotropic = false;
00600             DEBUG_INFO("Anisotropic filtering is not available.");
00601         }
00602         checked_anisotropic = true;
00603     }
00604     if (has_anisotropic)
00605     {
00606         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
00607                         anisotropy);
00608     }
00609     if(m_viewState->GetSupportMultiTexture())
00610     {
00611         BindMaskTexture();
00612         glEnable(GL_TEXTURE_2D);
00613         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
00614         // we don't want the edges to repeat the other side of the texture
00615         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00616         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00617         if(has_anisotropic)
00618             glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,anisotropy);
00619     };
00620     GLenum error = glGetError();
00621     if (error != GL_NO_ERROR)
00622     {
00623         DEBUG_ERROR("GL Error when setting texture parameters: "
00624                     << gluErrorString(error) << ".");
00625     }
00626 }
00627 
00628 TextureManager::TextureInfo::~TextureInfo()
00629 {
00630     // free up the graphics system's memory for this texture
00631     DEBUG_DEBUG("textures num deleting " <<  num);
00632     glDeleteTextures(1, (GLuint*) &num);
00633     glDeleteTextures(1, (GLuint*) &numMask);
00634 }
00635 
00636 void TextureManager::TextureInfo::Bind()
00637 {
00638     BindImageTexture();
00639     BindMaskTexture();
00640     if(m_viewState->GetSupportMultiTexture())
00641     {
00642         if(has_active_masks)
00643             glEnable(GL_TEXTURE_2D);
00644         else
00645             glDisable(GL_TEXTURE_2D);
00646         glActiveTexture(GL_TEXTURE0);
00647     };
00648 }
00649 
00650 void TextureManager::TextureInfo::BindImageTexture()
00651 {
00652     if(m_viewState->GetSupportMultiTexture())
00653     {
00654         glActiveTexture(GL_TEXTURE0);
00655         glBindTexture(GL_TEXTURE_2D, num);
00656     }
00657     else
00658         glBindTexture(GL_TEXTURE_2D, num);
00659 };
00660 void TextureManager::TextureInfo::BindMaskTexture()
00661 {
00662     if(m_viewState->GetSupportMultiTexture())
00663     {
00664         glActiveTexture(GL_TEXTURE1);
00665         glEnable(GL_TEXTURE_2D);
00666         glBindTexture(GL_TEXTURE_2D, numMask);
00667     }
00668 };
00669 
00670 // Note min and max refer to the mipmap levels, not the sizes of them. min has
00671 // the biggest size.
00672 void TextureManager::TextureInfo::DefineLevels(int min,
00673                                                int max,
00674                                                bool photometric_correct,
00675                                      const HuginBase::PanoramaOptions &dest_img,
00676                                          const HuginBase::SrcPanoImage &src_img)
00677 {
00678     // This might take a while, so show a busy cursor.
00679     //FIXME: busy cursor creates weird problem with calling checkupdate function again and messing up the textures
00680 //    wxBusyCursor busy_cursor;
00681     // activate the texture so we can change it.
00682     BindImageTexture();
00683     // find the highest allowable mip level
00684     int max_mip_level = (width_p > height_p) ? width_p : height_p;
00685     if (max > max_mip_level) max = max_mip_level;
00686     
00687     // add more detail textures. We need to get the biggest one first.
00688     // find the original image to scale down.
00689     // TODO cache full texture to disk after scaling?
00690     // TODO use small image if don't need bigger?
00691     // It is also possible to use HDR textures, but I can't see the point using
00692     // them as the only difference on an LDR display would be spending extra 
00693     // time reading the texture and converting the numbers. (float and uint16)
00694     // remove some cache items if we are using lots of memory:
00695     ImageCache::getInstance().softFlush();
00696     DEBUG_INFO("Loading image");
00697     std::string img_name = src_img.getFilename();
00698     ImageCache::EntryPtr entry = ImageCache::getInstance().getImageIfAvailable(img_name);
00699     if (!entry.get())
00700     {
00701         // Image isn't loaded yet. Request it for later.
00702         m_imageRequest = ImageCache::getInstance().requestAsyncImage(img_name);
00703         // call this function with the same parameters after the image loads
00704         m_imageRequest->ready.connect(0, 
00705             boost::bind(&TextureManager::TextureInfo::DefineLevels, this,
00706                         min, max, photometric_correct, dest_img, src_img));
00707         // After that, redraw the preview.
00708         m_imageRequest->ready.connect(1,
00709             boost::bind(&GLPreviewFrame::redrawPreview,
00710                         huginApp::getMainFrame()->getGLPreview()));
00711         
00712         // make a temporary placeholder image.
00713         GLubyte placeholder_image[64][64][4];
00714         for (int i = 0; i < 64; i++) {
00715             for (int j = 0; j < 64; j++) {
00716                 // checkboard pattern
00717                 GLubyte c = (i/8+j/8)%2 ? 63 : 191;
00718                 placeholder_image[i][j][0] = c;
00719                 placeholder_image[i][j][1] = c;
00720                 placeholder_image[i][j][2] = c;
00721                 // alpha is low, so the placeholder is mostly transparent.
00722                 placeholder_image[i][j][3] = 63;
00723             }
00724         }
00725         gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA8, 64, 64,
00726                                GL_RGBA, GL_UNSIGNED_BYTE,
00727                                placeholder_image);
00728         SetParameters();
00729         return;
00730     }
00731     // forget the request if we made one before.
00732     m_imageRequest = ImageCache::RequestPtr();
00733     DEBUG_INFO("Converting to 8 bits");
00734     boost::shared_ptr<vigra::BRGBImage> img = entry->get8BitImage();
00735     boost::shared_ptr<vigra::BImage> mask = entry->mask;
00736     // first make the biggest mip level.
00737     int wo = 1 << (width_p - min), ho = 1 << (height_p - min);
00738     if (wo < 1) wo = 1; if (ho < 1) ho = 1;
00739     // use Vigra to resize image
00740     DEBUG_INFO("Scaling image");
00741     vigra::BRGBImage out_img(wo, ho);
00742     // also read in the mask. OpenGL requires that the mask is in the same array
00743     // as the colour data, but the ImageCache doesn't work in this way.
00744     has_mask = mask->width()  && mask->height();
00745     vigra::UInt8Image *out_alpha;
00746     if (has_mask) out_alpha = new vigra::UInt8Image(wo, ho);
00747     if (wo < 2 || ho < 2)
00748     {
00749         // too small for vigra to scale
00750         // we still need to define some mipmap levels though, so use only (0, 0)
00751         for (int h = 0; h < ho; h++)
00752         {
00753             for (int w = 0; w < wo; w++)
00754             {
00755                 out_img[h][w] = (*img)[0][0];
00756                 if (has_mask) (*out_alpha)[h][w] = (*mask)[0][0];
00757             }
00758         }
00759     } else {
00760         // I think this takes to long, although it should be prettier.
00761         /*vigra::resizeImageLinearInterpolation(srcImageRange(*img),
00762                                                destImageRange(out_img));
00763         if (has_mask)
00764         {
00765             vigra::resizeImageLinearInterpolation(srcImageRange(*(entry->mask)),
00766                                           destImageRange(out_alpha));
00767         }*/
00768         
00769         // much faster. It shouldn't be so bad after it
00770         vigra::resizeImageNoInterpolation(srcImageRange(*img),
00771                                           destImageRange(out_img));
00772         if (has_mask)
00773         {
00774             vigra::resizeImageNoInterpolation(srcImageRange(*(mask)),
00775                                               destImageRange(*out_alpha));
00776         }
00777         // now perform photometric correction
00778         if (photometric_correct)
00779         {
00780             DEBUG_INFO("Performing photometric correction");
00781             // setup photometric transform for this image type
00782             // this corrects for response curve, white balance, exposure and
00783             // radial vignetting
00784             HuginBase::Photometric::InvResponseTransform <unsigned char, double>
00785                                     invResponse(src_img);
00786            // Assume LDR for now.
00787            // if (m_destImg.outputMode == PanoramaOptions::OUTPUT_LDR) {
00788                // select exposure and response curve for LDR output
00789                std::vector<double> outLut;
00790                // @TODO better handling of output EMoR parameters
00791                // Hugin's stitcher is currently using the EMoR parameters of the first image
00792                // as so called output EMoR parameter, so enforce this also for the fast
00793                // preview window
00794                // vigra_ext::EMoR::createEMoRLUT(dest_img.outputEMoRParams, outLut);
00795                vigra_ext::EMoR::createEMoRLUT(m_viewState->GetSrcImage(0)->getEMoRParams(), outLut);
00796                vigra_ext::enforceMonotonicity(outLut);
00797                invResponse.setOutput(1.0/pow(2.0,dest_img.outputExposureValue),
00798                                      outLut, 255.0);
00799             /*} else {
00800                // HDR output. not sure how that would be handled by the opengl
00801                // preview, though. It might be possible to apply a logarithmic
00802                // lookup table here, and average the overlapping pixels
00803                // in the OpenGL renderer?
00804                // TODO
00805                invResponse.setHDROutput();
00806             }*/
00807             // now perform the corrections
00808             double scale_x = (double) src_img.getSize().width() / (double) wo,
00809                    scale_y = (double) src_img.getSize().height() / (double) ho;
00810             for (int x = 0; x < wo; x++)
00811             {
00812                 for (int y = 0; y < ho; y++)
00813                 {
00814                     double sx = (double) x * scale_x,
00815                            sy = (double) y * scale_y;
00816                     out_img[y][x] = invResponse(out_img[y][x],
00817                                                 hugin_utils::FDiff2D(sx, sy));
00818                 }
00819             }
00820         }
00821     }
00822     
00823     //  make all of the smaller ones until we are done.
00824     // this will use a box filter.
00825     // dependent on OpenGL 1.3. Might need an alternative for 1.2.
00826     // TODO use texture compresion?
00827     DEBUG_INFO("Defining mipmap levels " <<  min << " to " << max
00828           << " of texture " << num << ", starting with a size of "
00829           << wo << " by " << ho << ".");
00830     GLint error;
00831     if (has_mask)
00832     {
00833         // combine the alpha bitmap with the red green and blue one.
00834         unsigned char *image = new unsigned char[ho * wo * 4];
00835         unsigned char *pix_start = image;
00836         for (int h = 0; h < ho; h++)
00837         {
00838             for (int w = 0; w < wo; w++)
00839             {
00840                 pix_start[0] = out_img[h][w].red();
00841                 pix_start[1] = out_img[h][w].green();
00842                 pix_start[2] = out_img[h][w].blue();
00843                 pix_start[3] = (*out_alpha)[h][w];
00844                 pix_start += 4;
00845             }
00846         }
00847         // We don't need to worry about levels with the ATI bug work around,
00848         // and Windows doesn't like it as gluBuild2DMipmapLevels is in OpenGL
00849         // version 1.3 and above only (Microsoft's SDK only uses 1.1)
00850         error = gluBuild2DMipmaps/*Levels*/(GL_TEXTURE_2D, GL_RGBA8, wo, ho,
00851                                GL_RGBA, GL_UNSIGNED_BYTE, /*min, min, max,*/
00852                                image);
00853         delete [] image;
00854         delete out_alpha;
00855     } else {
00856         // we don't need to rearange the data in memory if there is no mask.
00857         error = gluBuild2DMipmaps/*Levels*/(GL_TEXTURE_2D, GL_RGB8, wo, ho,
00858                                GL_RGB, GL_UNSIGNED_BYTE, /*min, min, max,*/
00859                                (unsigned char *) out_img.data());
00860     }
00861     if (error)
00862     {
00863         DEBUG_ERROR("GLU Error when building mipmap levels: "
00864                   << gluErrorString(error) << ".");
00865     }
00866     error = glGetError();
00867     if (error != GL_NO_ERROR)
00868     {
00869         DEBUG_ERROR("GL Error when bulding mipmap levels: "
00870                   << gluErrorString(error) << ".");
00871     }
00872     SetParameters();
00873     DEBUG_INFO("Finsihed loading texture.");
00874 
00875 
00876 }
00877 
00878 void TextureManager::TextureInfo::DefineMaskTexture(const HuginBase::SrcPanoImage &srcImg)
00879 {
00880     has_active_masks=srcImg.hasActiveMasks();
00881     HuginBase::MaskPolygonVector masks=srcImg.getActiveMasks();
00882     if(has_active_masks)
00883     {
00884         unsigned int maskSize=(width>height) ? width : height;
00885         if(maskSize>64)
00886             maskSize/=2;
00887         BindMaskTexture();
00888         for(unsigned int i=0;i<masks.size();i++)
00889             masks[i].scale((double)maskSize/srcImg.getWidth(),(double)maskSize/srcImg.getHeight());
00890         vigra::UInt8Image mask(maskSize,maskSize,255);
00891         //we don't draw mask if the size is smaller than 4 pixel
00892         if(maskSize>4)
00893             vigra_ext::applyMask(vigra::destImageRange(mask), masks);
00894 #ifdef __APPLE__
00895         // see comment to PreviewLayoutLinesTool::PreviewLayoutLinesTool
00896         // on MacOS a single alpha channel seems not to work, so this workaround
00897         unsigned char *image = new unsigned char[maskSize * maskSize * 2];
00898         unsigned char *pix_start = image;
00899         for (int h = 0; h < maskSize; h++)
00900         {
00901             for (int w = 0; w < maskSize; w++)
00902             {
00903                 pix_start[0] = 255;
00904                 pix_start[1] = mask[h][w];
00905                 pix_start += 2;
00906             }
00907         }
00908         gluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, maskSize, maskSize,
00909             GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, image);
00910         delete [] image;
00911 #else
00912         gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, maskSize,maskSize,GL_ALPHA, GL_UNSIGNED_BYTE,(unsigned char *) mask.data());
00913 #endif
00914     };
00915 };
00916 
00917 void TextureManager::TextureInfo::UpdateMask(const HuginBase::SrcPanoImage &srcImg)
00918 {
00919     if(m_viewState->GetSupportMultiTexture())
00920     {
00921         //delete old mask
00922         glDeleteTextures(1, (GLuint*) &numMask);
00923         //new create new mask
00924         glGenTextures(1, (GLuint*) &numMask);
00925         SetParameters();
00926         DefineMaskTexture(srcImg);
00927     };
00928 };
00929 
00930 void TextureManager::TextureInfo::SetMaxLevel(int level)
00931 {
00932     // we want to tell openGL the highest defined mip level of our texture.
00933     BindImageTexture();
00934     // FIXME the ati graphics driver on Ubuntu is known to crash due to this
00935     // practice. ati users should disable direct renderering if using the
00936     // #if 0'ed code above.
00937     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level);
00938     if(m_viewState->GetSupportMultiTexture())
00939     {
00940         // now for the mask texture
00941         BindMaskTexture();
00942         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level);
00943     }
00944     // we don't set min_lod so we can 'DefineLevels' using the old value.
00945     GLenum error = glGetError();
00946     if (error != GL_NO_ERROR)
00947     {
00948         DEBUG_ERROR("Error when setting the base mipmap level: "
00949                   << gluErrorString(error) << ".");
00950     }
00951 }
00952 
00953 TextureManager::TextureKey::TextureKey(HuginBase::SrcPanoImage *source,
00954                                        bool *photometric_correct_ptr)
00955 {
00956     SetOptions(source);
00957     photometric_correct = photometric_correct_ptr;
00958 }
00959 
00960 // This is only used by clean textures
00961 const bool TextureManager::TextureKey::operator==(const TextureKey comp) const
00962 {
00963     return !(*this < comp || comp < *this);
00964 }
00965 
00966 const bool TextureManager::TextureKey::operator<(const TextureKey comp) const
00967 {
00968     // compare two keys for ordering.
00969     // first try the filename.
00970     if (filename < comp.filename) return true;
00971     if (filename > comp.filename) return false;
00972     // Are there different masks?
00973     if (masks < comp.masks) return true;
00974     if (masks > comp.masks) return false;
00975     // if we are not using photometric correction, the textures are equivalent.
00976     if (!(*photometric_correct)) return false;
00977     // now try the photometric properties
00978     if (exposure < comp.exposure) return true;
00979     if (exposure > comp.exposure) return false;
00980     if (white_balance_red < comp.white_balance_red) return true;
00981     if (white_balance_red > comp.white_balance_red) return false;
00982     if (white_balance_blue < comp.white_balance_blue) return true;
00983     if (white_balance_blue > comp.white_balance_blue) return false;
00984     if (EMoR_params < comp.EMoR_params) return true;
00985     if (EMoR_params > comp.EMoR_params) return false;
00986     if (radial_vig_corr_coeff < comp.radial_vig_corr_coeff) return true;
00987     if (radial_vig_corr_coeff > comp.radial_vig_corr_coeff) return false;
00988     if (vig_corr_mode < comp.vig_corr_mode) return true;
00989     if (vig_corr_mode > comp.vig_corr_mode) return false;
00990     if (response_type < comp.response_type) return true;
00991     if (response_type > comp.response_type) return false;
00992     if (gamma < comp.gamma) return true;
00993     if (gamma > comp.gamma) return false;
00994     if (radial_distortion_red < comp.radial_distortion_red) return true;
00995     if (radial_distortion_red > comp.radial_distortion_red) return false;
00996     if (radial_distortion_blue < comp.radial_distortion_blue) return true;
00997     if (radial_distortion_blue > comp.radial_distortion_blue) return false;
00998     // If we've reached here it should be exactly the same:
00999     return false;
01000 }
01001 
01002 void TextureManager::TextureKey::SetOptions(HuginBase::SrcPanoImage *source)
01003 {
01004     filename = source->getFilename();
01005     // Record the masks. Images with different masks require different
01006     // textures since the mask is stored with them.
01007     std::stringstream mask_ss;
01008     source->printMaskLines(mask_ss, 0);
01009     masks = mask_ss.str();
01010     
01011     exposure = source->getExposure();
01012     white_balance_red = source->getWhiteBalanceRed();
01013     white_balance_blue = source->getWhiteBalanceBlue();
01014     EMoR_params = source->getEMoRParams();
01015     radial_vig_corr_coeff = source->getRadialVigCorrCoeff();
01016     vig_corr_mode = source->getVigCorrMode();
01017     response_type = source->getResponseType();
01018     gamma = source->getGamma();
01019     radial_distortion_red = source->getRadialDistortionRed();
01020     radial_distortion_blue = source->getRadialDistortionBlue();
01021 }
01022 

Generated on 1 Nov 2014 for Hugintrunk by  doxygen 1.4.7