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     CreateTexture();
00570 }
00571 
00572 TextureManager::TextureInfo::TextureInfo(ViewState *new_view_state, unsigned int width_p_in,
00573                                          unsigned int height_p_in)
00574 {
00575     m_viewState=new_view_state;
00576     has_active_masks=false;
00577     width_p = width_p_in;
00578     height_p = height_p_in;
00579     width = 1 << width_p;
00580     height = 1 << height_p;
00581     CreateTexture();
00582 }
00583 
00584 void TextureManager::TextureInfo::CreateTexture()
00585 {
00586     // Get an number for an OpenGL texture
00587     glGenTextures(1, (GLuint*) &num);
00588     DEBUG_DEBUG("textures num created " << num);
00589     glGenTextures(1, (GLuint*) &numMask);
00590     // we want to generate all levels of detail, they are all undefined.
00591     min_lod = 1000;
00592     SetParameters();
00593 }
00594 
00595 void TextureManager::TextureInfo::SetParameters()
00596 {
00597     BindImageTexture();
00598     glEnable(GL_TEXTURE_2D);
00599     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
00600                     GL_LINEAR_MIPMAP_LINEAR);
00601     // we don't want the edges to repeat the other side of the texture
00602     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00603     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00604     // use anistropic filtering if supported. This is good because we are
00605     // sretching and distorting the textures rather a lot in places and still
00606     // want good image quality.
00607     static bool checked_anisotropic = false;
00608     static bool has_anisotropic;
00609     static float anisotropy;
00610     if (!checked_anisotropic)
00611     {
00612         // check if it is supported
00613         if (GLEW_EXT_texture_filter_anisotropic)
00614         {
00615             has_anisotropic = true;
00616             glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropy);
00617             DEBUG_INFO("Using anisotropic filtering at maximum value "
00618                       << anisotropy);
00619         } else {
00620             has_anisotropic = false;
00621             DEBUG_INFO("Anisotropic filtering is not available.");
00622         }
00623         checked_anisotropic = true;
00624     }
00625     if (has_anisotropic)
00626     {
00627         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
00628                         anisotropy);
00629     }
00630     if(m_viewState->GetSupportMultiTexture())
00631     {
00632         BindMaskTexture();
00633         glEnable(GL_TEXTURE_2D);
00634         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
00635         // we don't want the edges to repeat the other side of the texture
00636         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
00637         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
00638         if(has_anisotropic)
00639             glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,anisotropy);
00640     };
00641     GLenum error = glGetError();
00642     if (error != GL_NO_ERROR)
00643     {
00644         DEBUG_ERROR("GL Error when setting texture parameters: "
00645                     << gluErrorString(error) << ".");
00646     }
00647 }
00648 
00649 TextureManager::TextureInfo::~TextureInfo()
00650 {
00651     // free up the graphics system's memory for this texture
00652     DEBUG_DEBUG("textures num deleting " <<  num);
00653     glDeleteTextures(1, (GLuint*) &num);
00654     glDeleteTextures(1, (GLuint*) &numMask);
00655 }
00656 
00657 void TextureManager::TextureInfo::Bind()
00658 {
00659     BindImageTexture();
00660     BindMaskTexture();
00661     if(m_viewState->GetSupportMultiTexture())
00662     {
00663         if(has_active_masks)
00664             glEnable(GL_TEXTURE_2D);
00665         else
00666             glDisable(GL_TEXTURE_2D);
00667         glActiveTexture(GL_TEXTURE0);
00668     };
00669 }
00670 
00671 void TextureManager::TextureInfo::BindImageTexture()
00672 {
00673     if(m_viewState->GetSupportMultiTexture())
00674     {
00675         glActiveTexture(GL_TEXTURE0);
00676         glBindTexture(GL_TEXTURE_2D, num);
00677     }
00678     else
00679         glBindTexture(GL_TEXTURE_2D, num);
00680 };
00681 void TextureManager::TextureInfo::BindMaskTexture()
00682 {
00683     if(m_viewState->GetSupportMultiTexture())
00684     {
00685         glActiveTexture(GL_TEXTURE1);
00686         glEnable(GL_TEXTURE_2D);
00687         glBindTexture(GL_TEXTURE_2D, numMask);
00688     }
00689 };
00690 
00691 // Note min and max refer to the mipmap levels, not the sizes of them. min has
00692 // the biggest size.
00693 void TextureManager::TextureInfo::DefineLevels(int min,
00694                                                int max,
00695                                                bool photometric_correct,
00696                                      const HuginBase::PanoramaOptions &dest_img,
00697                                          const HuginBase::SrcPanoImage &src_img)
00698 {
00699     // This might take a while, so show a busy cursor.
00700     //FIXME: busy cursor creates weird problem with calling checkupdate function again and messing up the textures
00701 //    wxBusyCursor busy_cursor;
00702     // activate the texture so we can change it.
00703     BindImageTexture();
00704     // find the highest allowable mip level
00705     int max_mip_level = (width_p > height_p) ? width_p : height_p;
00706     if (max > max_mip_level) max = max_mip_level;
00707     
00708     // add more detail textures. We need to get the biggest one first.
00709     // find the original image to scale down.
00710     // TODO cache full texture to disk after scaling?
00711     // TODO use small image if don't need bigger?
00712     // It is also possible to use HDR textures, but I can't see the point using
00713     // them as the only difference on an LDR display would be spending extra 
00714     // time reading the texture and converting the numbers. (float and uint16)
00715     // remove some cache items if we are using lots of memory:
00716     ImageCache::getInstance().softFlush();
00717     DEBUG_INFO("Loading image");
00718     const std::string img_name = src_img.getFilename();
00719     ImageCache::EntryPtr entry = ImageCache::getInstance().getImageIfAvailable(img_name);
00720     if (!entry.get())
00721     {
00722         // Image isn't loaded yet. Request it for later.
00723         m_imageRequest = ImageCache::getInstance().requestAsyncImage(img_name);
00724         // call this function with the same parameters after the image loads
00725         // it would be easier to call DefineLevels directly
00726         // but this fails if the TextureInfo object is destroyed during loading of the image
00727         // this can happen if a new project is opened during the loading cycling
00728         // so we go about LoadingImageFinished to check if the texture is still needed
00729         m_imageRequest->ready.push_back(
00730             std::bind(&TextureManager::LoadingImageFinished, m_viewState->GetTextureManager(),
00731                       min, max, photometric_correct, dest_img, src_img)
00732         );
00733         // After that, redraw the preview.
00734         m_imageRequest->ready.push_back(
00735             std::bind(&GLPreviewFrame::redrawPreview,
00736                       huginApp::getMainFrame()->getGLPreview())
00737         );
00738         
00739         // make a temporary placeholder image.
00740         GLubyte* placeholder_image;
00741         size_t placeholderWidth = 64;
00742         size_t placeholderHeight = 64;
00743         Exiv2::Image::AutoPtr image;
00744         bool hasPreview = false;
00745         try
00746         {
00747             image = Exiv2::ImageFactory::open(img_name.c_str());
00748             hasPreview = true;
00749         }
00750         catch (...)
00751         {
00752             std::cerr << __FILE__ << " " << __LINE__ << " Error opening file" << std::endl;
00753         }
00754         if (hasPreview)
00755         {
00756             image->readMetadata();
00757             // read all thumbnails
00758             Exiv2::PreviewManager previews(*image);
00759             Exiv2::PreviewPropertiesList lists = previews.getPreviewProperties();
00760             if (lists.empty())
00761             {
00762                 // no preview found
00763                 hasPreview = false;
00764             }
00765             else
00766             {
00767                 // select a preview with matching size
00768                 int previewIndex = 0;
00769                 while (previewIndex < lists.size() - 1 && lists[previewIndex].width_ < 200 && lists[previewIndex].height_ < 200)
00770                 {
00771                     ++previewIndex;
00772                 };
00773                 // load preview image to wxImage
00774                 wxImage rawImage;
00775                 Exiv2::PreviewImage previewImage = previews.getPreviewImage(lists[previewIndex]);
00776                 wxMemoryInputStream stream(previewImage.pData(), previewImage.size());
00777                 rawImage.LoadFile(stream, wxString(previewImage.mimeType().c_str(), wxConvLocal), -1);
00778                 placeholderWidth = rawImage.GetWidth();
00779                 placeholderHeight = rawImage.GetHeight();
00780                 placeholder_image = new GLubyte[placeholderWidth * placeholderHeight * 4];
00781                 size_t index = 0;
00782                 for (size_t y = 0; y < placeholderHeight; ++y)
00783                 {
00784                     for (size_t x = 0; x < placeholderWidth; ++x)
00785                     {
00786                         placeholder_image[index++] = rawImage.GetRed(x, y);
00787                         placeholder_image[index++] = rawImage.GetGreen(x, y);
00788                         placeholder_image[index++] = rawImage.GetBlue(x, y);
00789                         placeholder_image[index++] = 63;
00790                     };
00791                 };
00792             };
00793         };
00794         if (!hasPreview)
00795         {
00796             // no preview, create checker board
00797             placeholder_image = new GLubyte[placeholderWidth * placeholderHeight * 4];
00798             size_t index = 0;
00799             for (int i = 0; i < placeholderHeight; i++)
00800             {
00801                 for (int j = 0; j < placeholderWidth; j++)
00802                 {
00803                     // checkboard pattern
00804                     GLubyte c = ((i / 8 + j / 8) % 2) ? 63 : 191;
00805                     placeholder_image[index++] = c;
00806                     placeholder_image[index++] = c;
00807                     placeholder_image[index++] = c;
00808                     // alpha is low, so the placeholder is mostly transparent.
00809                     placeholder_image[index++] = 63;
00810                 }
00811             }
00812         };
00813         gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA8, placeholderWidth, placeholderHeight,
00814                                GL_RGBA, GL_UNSIGNED_BYTE,
00815                                placeholder_image);
00816         SetParameters();
00817         delete[] placeholder_image;
00818         return;
00819     }
00820     // forget the request if we made one before.
00821     m_imageRequest = ImageCache::RequestPtr();
00822     DEBUG_INFO("Converting to 8 bits");
00823     std::shared_ptr<vigra::BRGBImage> img = entry->get8BitImage();
00824     std::shared_ptr<vigra::BImage> mask = entry->mask;
00825     // first make the biggest mip level.
00826     int wo = 1 << (width_p - min), ho = 1 << (height_p - min);
00827     if (wo < 1) wo = 1; if (ho < 1) ho = 1;
00828     // use Vigra to resize image
00829     DEBUG_INFO("Scaling image");
00830     vigra::BRGBImage out_img(wo, ho);
00831     // also read in the mask. OpenGL requires that the mask is in the same array
00832     // as the colour data, but the ImageCache doesn't work in this way.
00833     has_mask = mask->width()  && mask->height();
00834     vigra::UInt8Image *out_alpha;
00835     if (has_mask) out_alpha = new vigra::UInt8Image(wo, ho);
00836     if (wo < 2 || ho < 2)
00837     {
00838         // too small for vigra to scale
00839         // we still need to define some mipmap levels though, so use only (0, 0)
00840         for (int h = 0; h < ho; h++)
00841         {
00842             for (int w = 0; w < wo; w++)
00843             {
00844                 out_img[h][w] = (*img)[0][0];
00845                 if (has_mask) (*out_alpha)[h][w] = (*mask)[0][0];
00846             }
00847         }
00848     } else {
00849         // I think this takes to long, although it should be prettier.
00850         /*vigra::resizeImageLinearInterpolation(srcImageRange(*img),
00851                                                destImageRange(out_img));
00852         if (has_mask)
00853         {
00854             vigra::resizeImageLinearInterpolation(srcImageRange(*(entry->mask)),
00855                                           destImageRange(out_alpha));
00856         }*/
00857         
00858         // much faster. It shouldn't be so bad after it
00859         vigra::resizeImageNoInterpolation(srcImageRange(*img),
00860                                           destImageRange(out_img));
00861         if (has_mask)
00862         {
00863             vigra::resizeImageNoInterpolation(srcImageRange(*(mask)),
00864                                               destImageRange(*out_alpha));
00865         }
00866         // prepare color management
00867         cmsHPROFILE inputICC = NULL;
00868         if (!entry->iccProfile->empty())
00869         {
00870             inputICC = cmsOpenProfileFromMem(entry->iccProfile->data(), entry->iccProfile->size());
00871         };
00872         cmsHTRANSFORM transform = NULL;
00873         // do color correction only if input image has icc profile or if we found a monitor profile
00874         if (inputICC != NULL || huginApp::Get()->HasMonitorProfile())
00875         {
00876             // check input profile
00877             if (inputICC != NULL)
00878             {
00879                 if (cmsGetColorSpace(inputICC) != cmsSigRgbData)
00880                 {
00881                     cmsCloseProfile(inputICC);
00882                     inputICC = NULL;
00883                 };
00884             };
00885             // if there is no icc profile in file fall back to sRGB
00886             if (inputICC == NULL)
00887             {
00888                 inputICC = cmsCreate_sRGBProfile();
00889             };
00890             // now build transform
00891             transform = cmsCreateTransform(inputICC, TYPE_RGB_8,
00892                 huginApp::Get()->GetMonitorProfile(), TYPE_RGB_8,
00893                 INTENT_PERCEPTUAL, cmsFLAGS_BLACKPOINTCOMPENSATION);
00894         };
00895         // now perform photometric correction
00896         if (photometric_correct)
00897         {
00898             DEBUG_INFO("Performing photometric correction");
00899             // setup photometric transform for this image type
00900             // this corrects for response curve, white balance, exposure and
00901             // radial vignetting
00902             HuginBase::Photometric::InvResponseTransform < unsigned char, double >
00903                 invResponse(src_img);
00904             // Assume LDR for now.
00905             // if (m_destImg.outputMode == PanoramaOptions::OUTPUT_LDR) {
00906             // select exposure and response curve for LDR output
00907             std::vector<double> outLut;
00908             // @TODO better handling of output EMoR parameters
00909             // Hugin's stitcher is currently using the EMoR parameters of the first image
00910             // as so called output EMoR parameter, so enforce this also for the fast
00911             // preview window
00912             // vigra_ext::EMoR::createEMoRLUT(dest_img.outputEMoRParams, outLut);
00913             vigra_ext::EMoR::createEMoRLUT(m_viewState->GetSrcImage(0)->getEMoRParams(), outLut);
00914             vigra_ext::enforceMonotonicity(outLut);
00915             invResponse.setOutput(1.0 / pow(2.0, dest_img.outputExposureValue),
00916                 outLut, 255.0);
00917             /*} else {
00918                // HDR output. not sure how that would be handled by the opengl
00919                // preview, though. It might be possible to apply a logarithmic
00920                // lookup table here, and average the overlapping pixels
00921                // in the OpenGL renderer?
00922                // TODO
00923                invResponse.setHDROutput();
00924                }*/
00925             // now perform the corrections
00926             double scale_x = (double)src_img.getSize().width() / (double)wo,
00927                 scale_y = (double)src_img.getSize().height() / (double)ho;
00928 #pragma omp parallel for
00929             for (int y = 0; y < ho; y++)
00930             {
00931                 for (int x = 0; x < wo; x++)
00932                 {
00933                     double sx = (double)x * scale_x,
00934                         sy = (double)y * scale_y;
00935                     out_img[y][x] = invResponse(out_img[y][x],
00936                         hugin_utils::FDiff2D(sx, sy));
00937                 }
00938                 // now take color profiles in file and of monitor into account
00939                 if (transform != NULL)
00940                 {
00941                     cmsDoTransform(transform, out_img[y], out_img[y], out_img.width());
00942                 };
00943             }
00944         }
00945         else
00946         {
00947             // no photometric correction
00948             if (transform != NULL)
00949             {
00950 #pragma omp parallel for
00951                 for (int y = 0; y < ho; y++)
00952                 {
00953                     cmsDoTransform(transform, out_img[y], out_img[y], out_img.width());
00954                 };
00955             };
00956         };
00957         if (transform != NULL)
00958         {
00959             cmsDeleteTransform(transform);
00960         };
00961         if (inputICC != NULL)
00962         {
00963             cmsCloseProfile(inputICC);
00964         };
00965     }
00966     
00967     //  make all of the smaller ones until we are done.
00968     // this will use a box filter.
00969     // dependent on OpenGL 1.3. Might need an alternative for 1.2.
00970     // TODO use texture compresion?
00971     DEBUG_INFO("Defining mipmap levels " <<  min << " to " << max
00972           << " of texture " << num << ", starting with a size of "
00973           << wo << " by " << ho << ".");
00974     GLint error;
00975     if (has_mask)
00976     {
00977         // combine the alpha bitmap with the red green and blue one.
00978         unsigned char *image = new unsigned char[ho * wo * 4];
00979         unsigned char *pix_start = image;
00980         for (int h = 0; h < ho; h++)
00981         {
00982             for (int w = 0; w < wo; w++)
00983             {
00984                 pix_start[0] = out_img[h][w].red();
00985                 pix_start[1] = out_img[h][w].green();
00986                 pix_start[2] = out_img[h][w].blue();
00987                 pix_start[3] = (*out_alpha)[h][w];
00988                 pix_start += 4;
00989             }
00990         }
00991         // We don't need to worry about levels with the ATI bug work around,
00992         // and Windows doesn't like it as gluBuild2DMipmapLevels is in OpenGL
00993         // version 1.3 and above only (Microsoft's SDK only uses 1.1)
00994         error = gluBuild2DMipmaps/*Levels*/(GL_TEXTURE_2D, GL_RGBA8, wo, ho,
00995                                GL_RGBA, GL_UNSIGNED_BYTE, /*min, min, max,*/
00996                                image);
00997         delete [] image;
00998         delete out_alpha;
00999     } else {
01000         // we don't need to rearange the data in memory if there is no mask.
01001         error = gluBuild2DMipmaps/*Levels*/(GL_TEXTURE_2D, GL_RGB8, wo, ho,
01002                                GL_RGB, GL_UNSIGNED_BYTE, /*min, min, max,*/
01003                                (unsigned char *) out_img.data());
01004     }
01005     if (error)
01006     {
01007         DEBUG_ERROR("GLU Error when building mipmap levels: "
01008                   << gluErrorString(error) << ".");
01009     }
01010     error = glGetError();
01011     if (error != GL_NO_ERROR)
01012     {
01013         DEBUG_ERROR("GL Error when bulding mipmap levels: "
01014                   << gluErrorString(error) << ".");
01015     }
01016     SetParameters();
01017     DEBUG_INFO("Finsihed loading texture.");
01018 
01019 
01020 }
01021 
01022 void TextureManager::TextureInfo::DefineMaskTexture(const HuginBase::SrcPanoImage &srcImg)
01023 {
01024     has_active_masks=srcImg.hasActiveMasks();
01025     HuginBase::MaskPolygonVector masks=srcImg.getActiveMasks();
01026     if(has_active_masks)
01027     {
01028         unsigned int maskSize=(width>height) ? width : height;
01029         if(maskSize>64)
01030             maskSize/=2;
01031         BindMaskTexture();
01032         for(unsigned int i=0;i<masks.size();i++)
01033             masks[i].scale((double)maskSize/srcImg.getWidth(),(double)maskSize/srcImg.getHeight());
01034         vigra::UInt8Image mask(maskSize,maskSize,255);
01035         //we don't draw mask if the size is smaller than 4 pixel
01036         if(maskSize>4)
01037             vigra_ext::applyMask(vigra::destImageRange(mask), masks);
01038 #ifdef __APPLE__
01039         // see comment to PreviewLayoutLinesTool::PreviewLayoutLinesTool
01040         // on MacOS a single alpha channel seems not to work, so this workaround
01041         unsigned char *image = new unsigned char[maskSize * maskSize * 2];
01042         unsigned char *pix_start = image;
01043         for (int h = 0; h < maskSize; h++)
01044         {
01045             for (int w = 0; w < maskSize; w++)
01046             {
01047                 pix_start[0] = 255;
01048                 pix_start[1] = mask[h][w];
01049                 pix_start += 2;
01050             }
01051         }
01052         gluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, maskSize, maskSize,
01053             GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, image);
01054         delete [] image;
01055 #else
01056         gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, maskSize,maskSize,GL_ALPHA, GL_UNSIGNED_BYTE,(unsigned char *) mask.data());
01057 #endif
01058     };
01059 };
01060 
01061 void TextureManager::TextureInfo::UpdateMask(const HuginBase::SrcPanoImage &srcImg)
01062 {
01063     if(m_viewState->GetSupportMultiTexture())
01064     {
01065         //delete old mask
01066         glDeleteTextures(1, (GLuint*) &numMask);
01067         //new create new mask
01068         glGenTextures(1, (GLuint*) &numMask);
01069         SetParameters();
01070         DefineMaskTexture(srcImg);
01071     };
01072 };
01073 
01074 void TextureManager::TextureInfo::SetMaxLevel(int level)
01075 {
01076     // we want to tell openGL the highest defined mip level of our texture.
01077     BindImageTexture();
01078     // FIXME the ati graphics driver on Ubuntu is known to crash due to this
01079     // practice. ati users should disable direct renderering if using the
01080     // #if 0'ed code above.
01081     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level);
01082     if(m_viewState->GetSupportMultiTexture())
01083     {
01084         // now for the mask texture
01085         BindMaskTexture();
01086         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level);
01087     }
01088     // we don't set min_lod so we can 'DefineLevels' using the old value.
01089     GLenum error = glGetError();
01090     if (error != GL_NO_ERROR)
01091     {
01092         DEBUG_ERROR("Error when setting the base mipmap level: "
01093                   << gluErrorString(error) << ".");
01094     }
01095 }
01096 
01097 TextureManager::TextureKey::TextureKey(const HuginBase::SrcPanoImage *source,
01098                                        bool *photometric_correct_ptr)
01099 {
01100     SetOptions(source);
01101     photometric_correct = photometric_correct_ptr;
01102 }
01103 
01104 // This is only used by clean textures
01105 const bool TextureManager::TextureKey::operator==(const TextureKey& comp) const
01106 {
01107     return !(*this < comp || comp < *this);
01108 }
01109 
01110 const bool TextureManager::TextureKey::operator<(const TextureKey& comp) const
01111 {
01112     // compare two keys for ordering.
01113     // first try the filename.
01114     if (filename < comp.filename) return true;
01115     if (filename > comp.filename) return false;
01116     // Are there different masks?
01117     if (masks < comp.masks) return true;
01118     if (masks > comp.masks) return false;
01119     // if we are not using photometric correction, the textures are equivalent.
01120     if (!(*photometric_correct)) return false;
01121     // now try the photometric properties
01122     if (exposure < comp.exposure) return true;
01123     if (exposure > comp.exposure) return false;
01124     if (white_balance_red < comp.white_balance_red) return true;
01125     if (white_balance_red > comp.white_balance_red) return false;
01126     if (white_balance_blue < comp.white_balance_blue) return true;
01127     if (white_balance_blue > comp.white_balance_blue) return false;
01128     if (EMoR_params < comp.EMoR_params) return true;
01129     if (EMoR_params > comp.EMoR_params) return false;
01130     if (radial_vig_corr_coeff < comp.radial_vig_corr_coeff) return true;
01131     if (radial_vig_corr_coeff > comp.radial_vig_corr_coeff) return false;
01132     if (vig_corr_mode < comp.vig_corr_mode) return true;
01133     if (vig_corr_mode > comp.vig_corr_mode) return false;
01134     if (response_type < comp.response_type) return true;
01135     if (response_type > comp.response_type) return false;
01136     if (gamma < comp.gamma) return true;
01137     if (gamma > comp.gamma) return false;
01138     if (radial_distortion_red < comp.radial_distortion_red) return true;
01139     if (radial_distortion_red > comp.radial_distortion_red) return false;
01140     if (radial_distortion_blue < comp.radial_distortion_blue) return true;
01141     if (radial_distortion_blue > comp.radial_distortion_blue) return false;
01142     // If we've reached here it should be exactly the same:
01143     return false;
01144 }
01145 
01146 void TextureManager::TextureKey::SetOptions(const HuginBase::SrcPanoImage *source)
01147 {
01148     filename = source->getFilename();
01149     // Record the masks. Images with different masks require different
01150     // textures since the mask is stored with them.
01151     std::stringstream mask_ss;
01152     source->printMaskLines(mask_ss, 0);
01153     masks = mask_ss.str();
01154     
01155     exposure = source->getExposure();
01156     white_balance_red = source->getWhiteBalanceRed();
01157     white_balance_blue = source->getWhiteBalanceBlue();
01158     EMoR_params = source->getEMoRParams();
01159     radial_vig_corr_coeff = source->getRadialVigCorrCoeff();
01160     vig_corr_mode = source->getVigCorrMode();
01161     response_type = source->getResponseType();
01162     gamma = source->getGamma();
01163     radial_distortion_red = source->getRadialDistortionRed();
01164     radial_distortion_blue = source->getRadialDistortionBlue();
01165 }
01166 

Generated on 29 Apr 2016 for Hugintrunk by  doxygen 1.4.7