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

Generated on 24 May 2016 for Hugintrunk by  doxygen 1.4.7