Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TextureManager.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
23 #ifdef _WIN32
24 #include "wx/msw/wrapwin.h"
25 #endif
26 #include <math.h>
27 #include <iostream>
28 
29 #include "hugin_config.h"
30 
31 #ifdef __APPLE__
32 #include "panoinc.h"
33 #endif
34 
35 
36 #include "ViewState.h"
37 #include "TextureManager.h"
38 #include "huginApp.h"
39 #include "GLPreviewFrame.h"
40 
41 #include "vigra/stdimage.hxx"
42 #include "vigra/resizeimage.hxx"
43 #include <functional> // std::bind
44 #include "base_wx/wxImageCache.h"
46 #include "panodata/Mask.h"
47 #include <lcms2.h>
48 
49 // The OpenGL Extension wrangler libray will find extensions and the latest
50 // supported OpenGL version on all platforms.
51 #if defined HAVE_EPOXY && HAVE_EPOXY
52 #include <epoxy/gl.h>
53 #else
54 #include <GL/glew.h>
55 #endif
56 #include <wx/platform.h>
57 #include <wx/display.h>
58 
59 #ifdef __WXMAC__
60 #include <OpenGL/gl.h>
61 #include <OpenGL/glu.h>
62 #else
63 #include <GL/gl.h>
64 #include <GL/glu.h>
65 #endif
66 
67 // for loading preview images
68 #include "wx/mstream.h"
69 #ifdef _WIN32
70 // workaround for a conflict between exiv2 and wxWidgets/CMake built
71 #define HAVE_PID_T 1
72 #endif
73 #include <exiv2/exiv2.hpp>
74 
76 {
77  m_pano = pano;
78  photometric_correct = false;
79  view_state = view_state_in;
80 }
81 
83 {
84  // free up the textures
85  textures.clear();
86 }
87 
88 void TextureManager::DrawImage(unsigned int image_number,
89  unsigned int display_list)
90 {
91  // bind the texture that represents the given image number.
92  TexturesMap::iterator it;
93  HuginBase::SrcPanoImage *img_p = view_state->GetSrcImage(image_number);
94  TextureKey key(img_p, &photometric_correct);
95  it = textures.find(key);
96  DEBUG_ASSERT(it != textures.end());
97  it->second->Bind();
98  glColor4f(1.0,1.0,1.0,1.0);
99  if (it->second->GetUseAlpha() || it->second->GetHasActiveMasks())
100  {
101  // use an alpha blend if there is a alpha channel or a mask for this image.
102  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
103  glEnable(GL_BLEND);
104  }
105  if (!photometric_correct)
106  {
107  // When using real time photometric correction, we multiply the colour
108  // components to get the white balance and exposure correct.
109  HuginBase::SrcPanoImage *img = view_state->GetSrcImage(image_number);
110  // we adjust the intensity by using a darker colour
111  float es = static_cast<float>(viewer_exposure / img->getExposure());
112  float scale[4] = {static_cast<float>(es / img->getWhiteBalanceRed()),
113  es,
114  static_cast<float>(es /img->getWhiteBalanceBlue()),
115  1.0};
116  glColor3fv(scale);
117  glCallList(display_list);
118  // Since the intensity was clamped to 0.0 - 1.0, we might overdraw a
119  // few times to make it brighter.
120  // FIXME If the image has areas masked out, these will also be
121  // brightened. It might be better to do using the texture, but this
122  // way we can only add the texture to the frame buffer, (we can't double
123  // the intensity multiple times) and there is a cost in processing the
124  // texture. It also won't work properly on partially transparent places.
125  if (scale[0] > 1.0 || scale[1] > 1.0 || scale[2] > 1.0)
126  {
128  glEnable(GL_BLEND);
129  glBlendFunc(GL_DST_COLOR, GL_ONE);
130  glColor4f(1.0, 1.0, 1.0, 1.0);
131  // double the brightness for colour components until it is almost
132  // right, however limit it incase it is really bright.
133  bool r = (scale[0] > 2.0);
134  bool g = (scale[1] > 2.0);
135  bool b = (scale[2] > 2.0);
136  unsigned short int count = 0;
137  while ((r || g || b) && count < 9)
138  {
139  glColor4f(r ? 1.0 : 0.0, g ? 1.0 : 0.0, b ? 1.0 : 0.0, 1.0);
140  glCallList(display_list);
141  if (r) scale[0] /= 2.0;
142  if (g) scale[1] /= 2.0;
143  if (b) scale[2] /= 2.0;
144  r = (scale[0] > 2.0);
145  g = (scale[1] > 2.0);
146  b = (scale[2] > 2.0);
147  count++;
148  }
149  // now add on anything remaining.
150  if (scale[0] > 1.0 || scale[1] > 1.0 || scale[2] > 1.0)
151  {
152  // clamped to 0.0-1.0, so it won't get darker.
153  scale[0] -= 1.0; scale[1] -= 1.0; scale[2] -= 1.0;
154  glColor3fv(scale);
155  glCallList(display_list);
156  }
157  glEnable(GL_TEXTURE_2D);
158  glDisable(GL_BLEND);
159  glColor3f(1.0, 1.0, 1.0);
160  }
161  } else {
162  // we've already corrected all the photometrics, just draw once normally
163  glCallList(display_list);
164  if (it->second->GetUseAlpha() || it->second->GetHasActiveMasks())
165  {
166  glDisable(GL_BLEND);
167  }
168  }
169 }
170 
171 unsigned int TextureManager::GetTextureName(unsigned int image_number)
172 {
173  // bind the texture that represents the given image number.
174  TexturesMap::iterator it;
175  HuginBase::SrcPanoImage *img_p = view_state->GetSrcImage(image_number);
176  TextureKey key(img_p, &photometric_correct);
177  it = textures.find(key);
178  DEBUG_ASSERT(it != textures.end());
179  return it->second->GetNumber();
180 }
181 
182 void TextureManager::BindTexture(unsigned int image_number)
183 {
184  // bind the texture that represents the given image number.
185  TexturesMap::iterator it;
186  HuginBase::SrcPanoImage *img_p = view_state->GetSrcImage(image_number);
187  TextureKey key(img_p, &photometric_correct);
188  it = textures.find(key);
189  DEBUG_ASSERT(it != textures.end());
190  it->second->Bind();
191 }
192 
194 {
196  {
197  glActiveTexture(GL_TEXTURE1);
198  glDisable(GL_TEXTURE_2D);
199  glActiveTexture(GL_TEXTURE0);
200  if(!maskOnly)
201  glDisable(GL_TEXTURE_2D);
202  }
203  else
204  {
205  if(!maskOnly)
206  glDisable(GL_TEXTURE_2D);
207  };
208 };
209 
211 {
212  if (!photometric_correct)
213  {
214  // find the exposure factor to scale by.
215  viewer_exposure = 1.0 / pow(2.0,
217  };
218 };
219 
221 {
222 }
223 
225 {
226  // The images or their lenses have changed.
227  // Find what size we should have the textures.
228  // Note that one image changing does affect the rest, if an image suddenly
229  // takes up more space, the others should take up less.
230  unsigned int num_images = m_pano->getNrOfImages();
231  if (num_images == 0)
232  {
233  textures.clear();
234  return;
235  }
236  // if we are doing photometric correction, and someone changed the output
237  // exposure, all of our images are at the wrong exposure.
239  {
240  textures.clear();
241  }
243  // Recalculuate the ideal image density if required
244  // TODO tidy up once it works.
245  DEBUG_INFO("Updating texture sizes.");
246  // find the total of fields of view of the images, in degrees squared
247  // we assume each image has the same density across all it's pixels
248  double total_fov = 0.0;
249  for (unsigned int image_index = 0; image_index < num_images; image_index++)
250  {
251  HuginBase::SrcPanoImage *src = view_state->GetSrcImage(image_index);
252  double aspect = double(src->getSize().height())
253  / double(src->getSize().width());
254  total_fov += src->getHFOV() * aspect;
255  };
256  // now find the ideal density
257  texel_density = double(GetMaxTotalTexels()) / total_fov;
258 
259  // now recalculate the best image sizes
260  // The actual texture size is the biggest one possible withouth scaling the
261  // image up in any direction. We only specify mipmap levels we can fit in
262  // a given amount of texture memory, while respecting the image's FOV.
263  int texels_used = 0;
264  double ideal_texels_used = 0.0;
265  for (unsigned int image_index = 0; image_index < num_images; image_index++)
266  {
267  // find this texture
268  // if it has not been created before, it will be created now.
269  TexturesMap::iterator it;
270  HuginBase::SrcPanoImage *img_p = view_state->GetSrcImage(image_index);
271  TextureKey key(img_p, &photometric_correct);
272  it = textures.find(key);
273  /* This section would allow us to reuse textures generated when we want
274  * to change the size. It is not used as it causes segmentation faults
275  * under Ubuntu 8.04's "ati" graphics driver.
276  */
277  #if 0
278  TextureInfo *texinfo;
279  if (it == textures.end())
280  {
281  // We haven't seen this image before.
282  // Find a size for it and make its texture.
283  // store the power that 2 is raised to, not the actual size
284  unsigned int max_tex_width_p = int(log2(img_p->getSize().width())),
285  max_tex_height_p = int(log2(img_p->getSize().height()));
286  // check this is hardware supported.
287  {
288  unsigned int biggest = GetMaxTextureSizePower();
289  if (biggest < max_tex_width_p) max_tex_width_p = biggest;
290  if (biggest < max_tex_height_p) max_tex_height_p = biggest;
291  }
292  std::cout << "Texture size for image " << image_index << " is "
293  << (1 << max_tex_width_p) << " by "
294  << (1 << max_tex_height_p) << "\n";
295  // create a new texinfo and store the texture details.
296  std::cout << "About to create new TextureInfo for "
297  << img_p->getFilename()
298  << ".\n";
299  std::pair<std::map<TextureKey, TextureInfo>::iterator, bool> ins;
300  ins = textures.insert(std::pair<TextureKey, TextureInfo>
302  // the key is used to identify the image with (or without)
303  // photometric correction parameters.
304  TextureInfo(max_tex_width_p, max_tex_height_p)
305  ));
306  texinfo = &((ins.first)->second);
307  }
308  else
309  {
310  texinfo = &(it->second);
311  }
312 
313  // find the highest mipmap we want to use.
314  double hfov = img_p->getHFOV(),
315  aspect = double (texinfo->height) / double (texinfo->width),
316  ideal_texels = texel_density * hfov * aspect,
317  // we would like a mipmap with this size:
318  ideal_tex_width = sqrt(ideal_texels / aspect),
319  ideal_tex_height = aspect * ideal_tex_width;
320  // Ideally this mipmap would bring us up to this many texels
321  ideal_texels_used += ideal_texels;
322  std::cout << "Ideal mip size: " << ideal_tex_width << " by "
323  << ideal_tex_height << "\n";
324  // Find the smallest mipmap level that is at least this size.
325  int max_mip_level = (texinfo->width_p > texinfo->height_p)
326  ? texinfo->width_p : texinfo->height_p;
327  int mip_level = max_mip_level - ceil((ideal_tex_width > ideal_tex_height)
328  ? log2(ideal_tex_width) : log2(ideal_tex_height));
329  // move to the next mipmap level if we are over budget.
330  if ((texels_used + (1 << (texinfo->width_p + texinfo->height_p
331  - mip_level * 2)))
332  > ideal_texels_used)
333  {
334  // scale down
335  mip_level ++;
336  }
337  // don't allow any mipmaps smaller than the 1 by 1 pixel one.
338  if (mip_level > max_mip_level) mip_level = max_mip_level;
339  // don't allow any mipmaps with a negative level of detail (scales up)
340  if (mip_level < 0) mip_level = 0;
341  // find the size of this level
342  int mip_width_p = texinfo->width_p - mip_level,
343  mip_height_p = texinfo->height_p - mip_level;
344  // check if we have scaled down to a single line, and make sure we
345  // limit the line's width to 1 pixel.
346  if (mip_width_p < 0) mip_width_p = 0;
347  if (mip_height_p < 0) mip_height_p = 0;
348 
349  // now count these texels as used- we are ignoring the smaller mip
350  // levels, they add 1/3 on to the size.
351  texels_used += 1 << (mip_width_p + mip_height_p);
352  std::cout << "biggest mipmap of image " << image_index << " is "
353  << (1 << mip_width_p) << " by " << (1 << mip_height_p)
354  << " (level " << mip_level <<").\n";
355  std::cout << "Ideal texels used " << int(ideal_texels_used)
356  << ", actually used " << texels_used << ".\n\n";
357  if (texinfo->min_lod != mip_level)
358  {
359  // maximum level required changed.
360  if (texinfo->min_lod > mip_level)
361  {
362  // generate more levels
363  texinfo->DefineLevels(mip_level,
364  (texinfo->min_lod > max_mip_level) ?
365  max_mip_level : texinfo->min_lod - 1,
366  photometric_correct, dest_img,
367  view_state->GetSrcImage(image_index));
368  }
369  texinfo->SetMaxLevel(mip_level);
370  texinfo->min_lod = mip_level;
371  }
372  }
373  #endif
374  /* Instead of the above section, replace the whole texture when appropriate:
375  */
376  // Find a size for it
377  double hfov = img_p->getHFOV(),
378  aspect = double (img_p->getSize().height())
379  / double (img_p->getSize().width()),
380  ideal_texels = texel_density * hfov * aspect,
381  // we would like a texture this size:
382  ideal_tex_width = sqrt(ideal_texels / aspect),
383  ideal_tex_height = aspect * ideal_tex_width;
384  // shrink if bigger than the original, avoids scaling up excessively.
385  if (ideal_tex_width > img_p->getSize().width())
386  ideal_tex_width = img_p->getSize().width();
387  if (ideal_tex_height > img_p->getSize().height())
388  ideal_tex_height = img_p->getSize().height();
389  // we will need to round up/down to a power of two
390  // round up first, then shrink if over budget.
391  // store the power that 2 is raised to, not the actual size
392  unsigned int tex_width_p = int(log2(ideal_tex_width)) + 1,
393  tex_height_p = int(log2(ideal_tex_height)) + 1;
394  // check this is hardware supported.
395  {
396  unsigned int biggest = GetMaxTextureSizePower();
397  if (biggest < tex_width_p) tex_width_p = biggest;
398  if (biggest < tex_height_p) tex_height_p = biggest;
399  }
400 
401  // check if this is over budget.
402  ideal_texels_used += ideal_texels;
403  // while the texture is over budget, shrink it
404  while ( (texels_used + (1 << (tex_width_p + tex_height_p)))
405  > ideal_texels_used)
406  {
407  // smaller aspect means the texture is wider.
408  if ((double) (1 << tex_height_p) / (double) (1 << tex_width_p)
409  < aspect)
410  {
411  tex_width_p--;
412  } else {
413  tex_height_p--;
414  }
415  }
416  // we have a nice size
417  texels_used += 1 << (tex_width_p + tex_height_p);
418  if ( it == textures.end()
419  || (it->second)->width_p != tex_width_p
420  || (it->second)->height_p != tex_height_p)
421  {
422  // Either: 1. We haven't seen this image before
423  // or: 2. Our texture for this is image is the wrong size
424  // ...therefore we make a new one the right size:
425  //
426  // remove duplicate key if exists
427  TextureKey checkKey (img_p, &photometric_correct);
428  textures.erase(checkKey);
429 
430  std::pair<TexturesMap::iterator, bool> ins;
431  ins = textures.insert(std::pair<TextureKey, std::shared_ptr<TextureInfo> >
433  // the key is used to identify the image with (or without)
434  // photometric correction parameters.
435  std::make_shared<TextureInfo>(view_state, tex_width_p, tex_height_p)
436  ));
437  // create and upload the texture image
438  TextureInfo* texinfo = (ins.first)->second.get();
439  texinfo->DefineLevels(0, // minimum mip level
440  // maximum mip level
441  tex_width_p > tex_height_p ? tex_width_p : tex_height_p,
443  *dest_img,
444  *view_state->GetSrcImage(image_index));
445  texinfo->DefineMaskTexture(*view_state->GetSrcImage(image_index));
446  }
447  else
448  {
449  if(view_state->RequireRecalculateMasks(image_index))
450  {
451  //mask for this image has changed, also update only mask
452  it->second->UpdateMask(*view_state->GetSrcImage(image_index));
453  };
454  }
455  }
456  // We should remove any images' texture when it is no longer in the panorama
457  // with the ati bug work around, we might make unneassry textures whenever
458  //if (photometric_correct || view_state->ImagesRemoved())
459  {
460  CleanTextures();
461  }
462 // std::map<TextureKey, TextureInfo>::iterator it;
463 // for (it = textures.begin() ; it != textures.end() ; it++) {
464 // DEBUG_DEBUG("textures num " << it->second.GetNumber());
465 // }
466 }
467 
469 {
470  // change the photometric correction state.
471  if (state != photometric_correct)
472  {
473  photometric_correct = state;
474  // We will need to recalculate all the images.
475  /* TODO It may be possible to keep textures that have some identity
476  * photometric transformation.
477  * Be warned that when turning off photometric correction, two images
478  * with the same filename will suddenly have the same key, which will
479  * break the textures map, hence clearing now */
480  textures.clear();
481  }
482 }
483 
485 {
486  static int displaySize = 0;
487  if (displaySize == 0)
488  {
489  // check the resolution of all displays and use the biggest one
490  for (size_t i = 0; i < wxDisplay::GetCount(); ++i)
491  {
492  const wxRect currentDisplaySize = wxDisplay(i).GetGeometry();
493  displaySize = std::max(displaySize, currentDisplaySize.GetWidth() * currentDisplaySize.GetHeight());
494  };
495  };
496  // set to something like 4 times the size of the screen.
497  // The value is guestimated as good for a view where each point is
498  // covered by 4 images.
499  return 4 * displaySize;
500  // Note: since we use mipmaps, the amount of actual maximum of pixels stored
501  // will be 4/3 of this value. It should use a maximum of 8MB of video memory
502  // for 8 bits per channel rgb images, 12MB if we include a mask.
503  // Video memory is also used for two copies of the screen and any auxilary
504  // buffers, and the meshes, so we should do fine with ~24MB of video memory.
505 }
506 
508 {
509  // get the maximum texture size supported by the hardware
510  // note the value can be too small, it is for a square texture with borders.
511  // we don't use borders, and the textures aren't always square.
512  static unsigned int max_size_p = 0;
513  if (max_size_p) return max_size_p; // don't ask openGL again.
514 
515  GLint max_size;
516  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
517  if (glGetError())
518  {
519  DEBUG_ERROR("Cannot find maximum texture size!");
520  // opengl docs say 64 pixels square is the minimum size guranteed to be supported.
521  return 6;
522  }
523  max_size_p = int(log2(max_size));
524  DEBUG_INFO("Max texture size supported is " << max_size <<
525  " (2^" << max_size_p << ")");
526  return max_size_p;
527 }
528 
530 {
531  // clean up all the textures from removed images.
532  // TODO can this be more efficient?
533  unsigned int num_images = m_pano->getNrOfImages();
534  bool retry = true;
535  TexturesMap::iterator tex;
536  while (retry)
537  {
538  retry = false;
539  for (tex = textures.begin(); tex != textures.end(); ++tex)
540  {
541  bool found = false;
542 
543  // try and find an image with this key
544  for (unsigned int img = 0; img < num_images; img++)
545  {
547  if (ik == tex->first)
548  {
549  found = true;
550  break;
551  }
552  }
553  // remove it if it was not found
554  if (!found)
555  {
556  DEBUG_INFO("Removing old texture for " << tex->first.filename << ".");
557  retry = true;
558  textures.erase(tex);
559  break;
560  }
561  }
562  }
563 }
564 
565 // helper class for the image cache
566 // this procedure is called when a image was sucessfull loaded
567 // we check if we still need the image and if so prepare the texture with DefineLevels
569  bool texture_photometric_correct,
570  const HuginBase::PanoramaOptions &dest_img,
571  const HuginBase::SrcPanoImage &state)
572 {
573  TexturesMap::iterator it = textures.find(TextureKey(&state, &texture_photometric_correct));
574  // check if image is still there
575  if (it != textures.end())
576  {
577  // new shared pointer to keep class alive
578  std::shared_ptr<TextureInfo> tex(it->second);
579  tex->DefineLevels(min, max, texture_photometric_correct, dest_img, state);
580  };
581 };
582 
584 {
585  // we shouldn't be using this. It exists only to make std::map happy.
586  DEBUG_ASSERT(0);
587  m_viewState=new_view_state;
588  has_active_masks=false;
589  has_mask=false;
590  CreateTexture();
591 }
592 
593 TextureManager::TextureInfo::TextureInfo(ViewState *new_view_state, unsigned int width_p_in,
594  unsigned int height_p_in)
595 {
596  m_viewState=new_view_state;
597  has_active_masks=false;
598  has_mask=false;
599  width_p = width_p_in;
600  height_p = height_p_in;
601  width = 1 << width_p;
602  height = 1 << height_p;
603  CreateTexture();
604 }
605 
607 {
608  // Get an number for an OpenGL texture
609  glGenTextures(1, (GLuint*) &num);
610  DEBUG_DEBUG("textures num created " << num);
611  glGenTextures(1, (GLuint*) &numMask);
612  // we want to generate all levels of detail, they are all undefined.
613  min_lod = 1000;
614  SetParameters();
615 }
616 
618 {
619  BindImageTexture();
620  glEnable(GL_TEXTURE_2D);
621  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
622  GL_LINEAR_MIPMAP_LINEAR);
623  // we don't want the edges to repeat the other side of the texture
624  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
625  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
626  // use anistropic filtering if supported. This is good because we are
627  // sretching and distorting the textures rather a lot in places and still
628  // want good image quality.
629  static bool checked_anisotropic = false;
630  static bool has_anisotropic;
631  static float anisotropy;
632  if (!checked_anisotropic)
633  {
634  // check if it is supported
635 #if defined HAVE_EPOXY && HAVE_EPOXY
636  if (epoxy_has_gl_extension("GL_EXT_texture_filter_anisotropic"))
637 #else
638  if (GLEW_EXT_texture_filter_anisotropic)
639 #endif
640  {
641  has_anisotropic = true;
642  glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &anisotropy);
643  DEBUG_INFO("Using anisotropic filtering at maximum value "
644  << anisotropy);
645  } else {
646  has_anisotropic = false;
647  DEBUG_INFO("Anisotropic filtering is not available.");
648  }
649  checked_anisotropic = true;
650  }
651  if (has_anisotropic)
652  {
653  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
654  anisotropy);
655  }
656  if(m_viewState->GetSupportMultiTexture())
657  {
658  BindMaskTexture();
659  glEnable(GL_TEXTURE_2D);
660  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
661  // we don't want the edges to repeat the other side of the texture
662  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
663  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
664  if(has_anisotropic)
665  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,anisotropy);
666  };
667  GLenum error = glGetError();
668  if (error != GL_NO_ERROR)
669  {
670  DEBUG_ERROR("GL Error when setting texture parameters: "
671  << gluErrorString(error) << ".");
672  }
673 }
674 
676 {
677  // free up the graphics system's memory for this texture
678  DEBUG_DEBUG("textures num deleting " << num);
679  glDeleteTextures(1, (GLuint*) &num);
680  glDeleteTextures(1, (GLuint*) &numMask);
681 }
682 
684 {
685  BindImageTexture();
686  BindMaskTexture();
687  if(m_viewState->GetSupportMultiTexture())
688  {
689  if(has_active_masks)
690  glEnable(GL_TEXTURE_2D);
691  else
692  glDisable(GL_TEXTURE_2D);
693  glActiveTexture(GL_TEXTURE0);
694  };
695 }
696 
698 {
699  if(m_viewState->GetSupportMultiTexture())
700  {
701  glActiveTexture(GL_TEXTURE0);
702  glBindTexture(GL_TEXTURE_2D, num);
703  }
704  else
705  glBindTexture(GL_TEXTURE_2D, num);
706 };
708 {
709  if(m_viewState->GetSupportMultiTexture())
710  {
711  glActiveTexture(GL_TEXTURE1);
712  glEnable(GL_TEXTURE_2D);
713  glBindTexture(GL_TEXTURE_2D, numMask);
714  }
715 };
716 
717 // Note min and max refer to the mipmap levels, not the sizes of them. min has
718 // the biggest size.
720  int max,
721  bool photometric_correct,
722  const HuginBase::PanoramaOptions &dest_img,
723  const HuginBase::SrcPanoImage &src_img)
724 {
725  // This might take a while, so show a busy cursor.
726  //FIXME: busy cursor creates weird problem with calling checkupdate function again and messing up the textures
727 // wxBusyCursor busy_cursor;
728  // activate the texture so we can change it.
729  BindImageTexture();
730  // find the highest allowable mip level
731  int max_mip_level = (width_p > height_p) ? width_p : height_p;
732  if (max > max_mip_level) max = max_mip_level;
733 
734  // add more detail textures. We need to get the biggest one first.
735  // find the original image to scale down.
736  // TODO cache full texture to disk after scaling?
737  // TODO use small image if don't need bigger?
738  // It is also possible to use HDR textures, but I can't see the point using
739  // them as the only difference on an LDR display would be spending extra
740  // time reading the texture and converting the numbers. (float and uint16)
741  // remove some cache items if we are using lots of memory:
742  ImageCache::getInstance().softFlush();
743  DEBUG_INFO("Loading image");
744  const std::string img_name = src_img.getFilename();
745  ImageCache::EntryPtr entry = ImageCache::getInstance().getImageIfAvailable(img_name);
746  if (!entry.get())
747  {
748  // Image isn't loaded yet. Request it for later.
749  m_imageRequest = ImageCache::getInstance().requestAsyncImage(img_name);
750  // call this function with the same parameters after the image loads
751  // it would be easier to call DefineLevels directly
752  // but this fails if the TextureInfo object is destroyed during loading of the image
753  // this can happen if a new project is opened during the loading cycling
754  // so we go about LoadingImageFinished to check if the texture is still needed
755  m_imageRequest->ready.push_back(
756  std::bind(&TextureManager::LoadingImageFinished, m_viewState->GetTextureManager(),
757  min, max, photometric_correct, dest_img, src_img)
758  );
759  // After that, redraw the preview.
760  m_imageRequest->ready.push_back(
762  huginApp::getMainFrame()->getGLPreview())
763  );
764 
765  // make a temporary placeholder image.
766  GLubyte* placeholder_image;
767  size_t placeholderWidth = 64;
768  size_t placeholderHeight = 64;
769 #if defined EXIV2_VERSION && EXIV2_TEST_VERSION(0,27,99)
770  Exiv2::Image::UniquePtr image;
771 #else
772  Exiv2::Image::AutoPtr image;
773 #endif
774  bool hasPreview = false;
775  try
776  {
777  image = Exiv2::ImageFactory::open(img_name.c_str());
778  hasPreview = true;
779  }
780  catch (...)
781  {
782  std::cerr << __FILE__ << " " << __LINE__ << " Error opening file" << std::endl;
783  }
784  if (hasPreview)
785  {
786  std::shared_ptr<Exiv2::PreviewManager> previews;
787  Exiv2::PreviewPropertiesList lists;
788  try
789  {
790  // read all thumbnails
791  image->readMetadata();
792  previews = std::make_shared<Exiv2::PreviewManager>(*image);
793  lists = previews->getPreviewProperties();
794  }
795  catch (const Exiv2::Error&)
796  {
797  hasPreview = false;
798  };
799  if (lists.empty())
800  {
801  // no preview found
802  hasPreview = false;
803  }
804  else
805  {
806  // select a preview with matching size
807  int previewIndex = 0;
808  while (previewIndex < lists.size() - 1 && lists[previewIndex].width_ < 200 && lists[previewIndex].height_ < 200)
809  {
810  ++previewIndex;
811  };
812  // load preview image to wxImage
813  wxImage rawImage;
814  Exiv2::PreviewImage previewImage = previews->getPreviewImage(lists[previewIndex]);
815  wxMemoryInputStream stream(previewImage.pData(), previewImage.size());
816  // disable logging for loading preview images
817  wxLog::EnableLogging(false);
818  if (rawImage.LoadFile(stream, wxString(previewImage.mimeType().c_str(), wxConvLocal), -1))
819  {
820  placeholderWidth = rawImage.GetWidth();
821  placeholderHeight = rawImage.GetHeight();
822  placeholder_image = new GLubyte[placeholderWidth * placeholderHeight * 4];
823  size_t index = 0;
824  for (size_t y = 0; y < placeholderHeight; ++y)
825  {
826  for (size_t x = 0; x < placeholderWidth; ++x)
827  {
828  placeholder_image[index++] = rawImage.GetRed(x, y);
829  placeholder_image[index++] = rawImage.GetGreen(x, y);
830  placeholder_image[index++] = rawImage.GetBlue(x, y);
831  placeholder_image[index++] = 63;
832  };
833  };
834  }
835  else
836  {
837  hasPreview = false;
838  };
839  // re-enable logging
840  wxLog::EnableLogging(true);
841  };
842  };
843  if (!hasPreview)
844  {
845  // no preview, create checker board
846  placeholder_image = new GLubyte[placeholderWidth * placeholderHeight * 4];
847  size_t index = 0;
848  for (int i = 0; i < placeholderHeight; i++)
849  {
850  for (int j = 0; j < placeholderWidth; j++)
851  {
852  // checkboard pattern
853  GLubyte c = ((i / 8 + j / 8) % 2) ? 63 : 191;
854  placeholder_image[index++] = c;
855  placeholder_image[index++] = c;
856  placeholder_image[index++] = c;
857  // alpha is low, so the placeholder is mostly transparent.
858  placeholder_image[index++] = 63;
859  }
860  }
861  };
862  gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA8, placeholderWidth, placeholderHeight,
863  GL_RGBA, GL_UNSIGNED_BYTE,
864  placeholder_image);
865  SetParameters();
866  delete[] placeholder_image;
867  return;
868  }
869  // forget the request if we made one before.
870  m_imageRequest = ImageCache::RequestPtr();
871  DEBUG_INFO("Converting to 8 bits");
872  std::shared_ptr<vigra::BRGBImage> img = entry->get8BitImage();
873  std::shared_ptr<vigra::BImage> mask = entry->mask;
874  // first make the biggest mip level.
875  int wo = 1 << (width_p - min), ho = 1 << (height_p - min);
876  if (wo < 1) wo = 1; if (ho < 1) ho = 1;
877  // use Vigra to resize image
878  DEBUG_INFO("Scaling image");
879  vigra::BRGBImage out_img(wo, ho);
880  // also read in the mask. OpenGL requires that the mask is in the same array
881  // as the colour data, but the ImageCache doesn't work in this way.
882  has_mask = mask->width() && mask->height();
883  vigra::UInt8Image *out_alpha;
884  if (has_mask) out_alpha = new vigra::UInt8Image(wo, ho);
885  if (wo < 2 || ho < 2)
886  {
887  // too small for vigra to scale
888  // we still need to define some mipmap levels though, so use only (0, 0)
889  for (int h = 0; h < ho; h++)
890  {
891  for (int w = 0; w < wo; w++)
892  {
893  out_img[h][w] = (*img)[0][0];
894  if (has_mask) (*out_alpha)[h][w] = (*mask)[0][0];
895  }
896  }
897  } else {
898  // I think this takes to long, although it should be prettier.
899  /*vigra::resizeImageLinearInterpolation(srcImageRange(*img),
900  destImageRange(out_img));
901  if (has_mask)
902  {
903  vigra::resizeImageLinearInterpolation(srcImageRange(*(entry->mask)),
904  destImageRange(out_alpha));
905  }*/
906 
907  // much faster. It shouldn't be so bad after it
908  vigra::resizeImageNoInterpolation(srcImageRange(*img),
909  destImageRange(out_img));
910  if (has_mask)
911  {
912  vigra::resizeImageNoInterpolation(srcImageRange(*(mask)),
913  destImageRange(*out_alpha));
914  }
915  // prepare color management
916  cmsHPROFILE inputICC = NULL;
917  if (!entry->iccProfile->empty())
918  {
919  inputICC = cmsOpenProfileFromMem(entry->iccProfile->data(), entry->iccProfile->size());
920  };
921  cmsHTRANSFORM transform = NULL;
922  // do color correction only if input image has icc profile or if we found a monitor profile
923  if (inputICC != NULL || huginApp::Get()->HasMonitorProfile())
924  {
925  // check input profile
926  if (inputICC != NULL)
927  {
928  if (cmsGetColorSpace(inputICC) != cmsSigRgbData)
929  {
930  cmsCloseProfile(inputICC);
931  inputICC = NULL;
932  };
933  };
934  // if there is no icc profile in file fall back to sRGB
935  if (inputICC == NULL)
936  {
937  inputICC = cmsCreate_sRGBProfile();
938  };
939  // now build transform
940  transform = cmsCreateTransform(inputICC, TYPE_RGB_8,
941  huginApp::Get()->GetMonitorProfile(), TYPE_RGB_8,
942  INTENT_PERCEPTUAL, cmsFLAGS_BLACKPOINTCOMPENSATION);
943  };
944  // now perform photometric correction
945  if (photometric_correct)
946  {
947  DEBUG_INFO("Performing photometric correction");
948  // setup photometric transform for this image type
949  // this corrects for response curve, white balance, exposure and
950  // radial vignetting
951  HuginBase::SrcPanoImage tempSrcImg(src_img);
952  // for linear float image reset response type
953  // because ImageCache returns already modified image information
954  // depending on settings in the preferences
955  if (entry->imageFloat->size().area() > 0 && tempSrcImg.getResponseType() == HuginBase::SrcPanoImage::RESPONSE_LINEAR)
956  {
957  tempSrcImg.setResponseType(HuginBase::SrcPanoImage::RESPONSE_EMOR);
958  }
960  invResponse(tempSrcImg);
961  // Assume LDR for now.
962  // if (m_destImg.outputMode == PanoramaOptions::OUTPUT_LDR) {
963  // select exposure and response curve for LDR output
964  std::vector<double> outLut;
965  // @TODO better handling of output EMoR parameters
966  // Hugin's stitcher is currently using the EMoR parameters of the first image
967  // as so called output EMoR parameter, so enforce this also for the fast
968  // preview window
969  // vigra_ext::EMoR::createEMoRLUT(dest_img.outputEMoRParams, outLut);
970  vigra_ext::EMoR::createEMoRLUT(m_viewState->GetSrcImage(0)->getEMoRParams(), outLut);
972  invResponse.setOutput(1.0 / pow(2.0, dest_img.outputExposureValue),
973  outLut, 255.0, dest_img.outputRangeCompression);
974  /*} else {
975  // HDR output. not sure how that would be handled by the opengl
976  // preview, though. It might be possible to apply a logarithmic
977  // lookup table here, and average the overlapping pixels
978  // in the OpenGL renderer?
979  // TODO
980  invResponse.setHDROutput();
981  }*/
982  // now perform the corrections
983  double scale_x = (double)src_img.getSize().width() / (double)wo,
984  scale_y = (double)src_img.getSize().height() / (double)ho;
985 #pragma omp parallel for
986  for (int y = 0; y < ho; y++)
987  {
988  for (int x = 0; x < wo; x++)
989  {
990  double sx = (double)x * scale_x,
991  sy = (double)y * scale_y;
992  out_img[y][x] = invResponse(out_img[y][x],
993  hugin_utils::FDiff2D(sx, sy));
994  }
995  // now take color profiles in file and of monitor into account
996  if (transform != NULL)
997  {
998  cmsDoTransform(transform, out_img[y], out_img[y], out_img.width());
999  };
1000  }
1001  }
1002  else
1003  {
1004  // no photometric correction
1005  if (transform != NULL)
1006  {
1007 #pragma omp parallel for
1008  for (int y = 0; y < ho; y++)
1009  {
1010  cmsDoTransform(transform, out_img[y], out_img[y], out_img.width());
1011  };
1012  };
1013  };
1014  if (transform != NULL)
1015  {
1016  cmsDeleteTransform(transform);
1017  };
1018  if (inputICC != NULL)
1019  {
1020  cmsCloseProfile(inputICC);
1021  };
1022  }
1023 
1024  // make all of the smaller ones until we are done.
1025  // this will use a box filter.
1026  // dependent on OpenGL 1.3. Might need an alternative for 1.2.
1027  // TODO use texture compresion?
1028  DEBUG_INFO("Defining mipmap levels " << min << " to " << max
1029  << " of texture " << num << ", starting with a size of "
1030  << wo << " by " << ho << ".");
1031  GLint error;
1032  if (has_mask)
1033  {
1034  // combine the alpha bitmap with the red green and blue one.
1035  GLubyte *image = new GLubyte[ho * wo * 4];
1036  GLubyte *pix_start = image;
1037  for (int h = 0; h < ho; h++)
1038  {
1039  for (int w = 0; w < wo; w++)
1040  {
1041  pix_start[0] = out_img[h][w].red();
1042  pix_start[1] = out_img[h][w].green();
1043  pix_start[2] = out_img[h][w].blue();
1044  pix_start[3] = (*out_alpha)[h][w];
1045  pix_start += 4;
1046  }
1047  }
1048  // We don't need to worry about levels with the ATI bug work around,
1049  // and Windows doesn't like it as gluBuild2DMipmapLevels is in OpenGL
1050  // version 1.3 and above only (Microsoft's SDK only uses 1.1)
1051  error = gluBuild2DMipmaps/*Levels*/(GL_TEXTURE_2D, GL_RGBA8, wo, ho,
1052  GL_RGBA, GL_UNSIGNED_BYTE, /*min, min, max,*/
1053  image);
1054  delete [] image;
1055  delete out_alpha;
1056  } else {
1057  // we don't need to rearange the data in memory if there is no mask.
1058  error = gluBuild2DMipmaps/*Levels*/(GL_TEXTURE_2D, GL_RGB8, wo, ho,
1059  GL_RGB, GL_UNSIGNED_BYTE, /*min, min, max,*/
1060  (unsigned char *) out_img.data());
1061  }
1062  if (error)
1063  {
1064  DEBUG_ERROR("GLU Error when building mipmap levels: "
1065  << gluErrorString(error) << ".");
1066  }
1067  error = glGetError();
1068  if (error != GL_NO_ERROR)
1069  {
1070  DEBUG_ERROR("GL Error when building mipmap levels: "
1071  << gluErrorString(error) << ".");
1072  }
1073  SetParameters();
1074  DEBUG_INFO("Finsihed loading texture.");
1075 
1076 
1077 }
1078 
1080 {
1081  has_active_masks=srcImg.hasActiveMasks();
1082  HuginBase::MaskPolygonVector masks=srcImg.getActiveMasks();
1083  if(has_active_masks)
1084  {
1085  unsigned int maskSize=(width>height) ? width : height;
1086  if(maskSize>64)
1087  maskSize/=2;
1088  BindMaskTexture();
1089  for(unsigned int i=0;i<masks.size();i++)
1090  masks[i].scale((double)maskSize/srcImg.getWidth(),(double)maskSize/srcImg.getHeight());
1091  vigra::UInt8Image mask(maskSize,maskSize,255);
1092  //we don't draw mask if the size is smaller than 4 pixel
1093  if(maskSize>4)
1095 #ifdef __APPLE__
1096  // see comment to PreviewLayoutLinesTool::PreviewLayoutLinesTool
1097  // on MacOS a single alpha channel seems not to work, so this workaround
1098  GLubyte *image = new GLubyte[maskSize * maskSize * 2];
1099  GLubyte *pix_start = image;
1100  for (int h = 0; h < maskSize; h++)
1101  {
1102  for (int w = 0; w < maskSize; w++)
1103  {
1104  pix_start[0] = 255;
1105  pix_start[1] = mask[h][w];
1106  pix_start += 2;
1107  }
1108  }
1109  gluBuild2DMipmaps(GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, maskSize, maskSize,
1110  GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, image);
1111  delete [] image;
1112 #else
1113  gluBuild2DMipmaps(GL_TEXTURE_2D, GL_ALPHA, maskSize,maskSize,GL_ALPHA, GL_UNSIGNED_BYTE,(unsigned char *) mask.data());
1114 #endif
1115  };
1116 };
1117 
1119 {
1120  if(m_viewState->GetSupportMultiTexture())
1121  {
1122  //delete old mask
1123  glDeleteTextures(1, (GLuint*) &numMask);
1124  //new create new mask
1125  glGenTextures(1, (GLuint*) &numMask);
1126  SetParameters();
1127  DefineMaskTexture(srcImg);
1128  };
1129 };
1130 
1132 {
1133  // we want to tell openGL the highest defined mip level of our texture.
1134  BindImageTexture();
1135  // FIXME the ati graphics driver on Ubuntu is known to crash due to this
1136  // practice. ati users should disable direct renderering if using the
1137  // #if 0'ed code above.
1138  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level);
1139  if(m_viewState->GetSupportMultiTexture())
1140  {
1141  // now for the mask texture
1142  BindMaskTexture();
1143  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, level);
1144  }
1145  // we don't set min_lod so we can 'DefineLevels' using the old value.
1146  GLenum error = glGetError();
1147  if (error != GL_NO_ERROR)
1148  {
1149  DEBUG_ERROR("Error when setting the base mipmap level: "
1150  << gluErrorString(error) << ".");
1151  }
1152 }
1153 
1155  bool *photometric_correct_ptr)
1156 {
1157  SetOptions(source);
1158  photometric_correct = photometric_correct_ptr;
1159 }
1160 
1161 // This is only used by clean textures
1163 {
1164  return !(*this < comp || comp < *this);
1165 }
1166 
1168 {
1169  // compare two keys for ordering.
1170  // first try the filename.
1171  if (filename < comp.filename) return true;
1172  if (filename > comp.filename) return false;
1173  // Are there different masks?
1174  if (masks < comp.masks) return true;
1175  if (masks > comp.masks) return false;
1176  // if we are not using photometric correction, the textures are equivalent.
1177  if (!(*photometric_correct)) return false;
1178  // now try the photometric properties
1179  if (exposure < comp.exposure) return true;
1180  if (exposure > comp.exposure) return false;
1181  if (white_balance_red < comp.white_balance_red) return true;
1182  if (white_balance_red > comp.white_balance_red) return false;
1183  if (white_balance_blue < comp.white_balance_blue) return true;
1184  if (white_balance_blue > comp.white_balance_blue) return false;
1185  if (EMoR_params < comp.EMoR_params) return true;
1186  if (EMoR_params > comp.EMoR_params) return false;
1187  if (radial_vig_corr_coeff < comp.radial_vig_corr_coeff) return true;
1188  if (radial_vig_corr_coeff > comp.radial_vig_corr_coeff) return false;
1189  if (vig_corr_mode < comp.vig_corr_mode) return true;
1190  if (vig_corr_mode > comp.vig_corr_mode) return false;
1191  if (response_type < comp.response_type) return true;
1192  if (response_type > comp.response_type) return false;
1193  if (gamma < comp.gamma) return true;
1194  if (gamma > comp.gamma) return false;
1195  if (radial_distortion_red < comp.radial_distortion_red) return true;
1196  if (radial_distortion_red > comp.radial_distortion_red) return false;
1197  if (radial_distortion_blue < comp.radial_distortion_blue) return true;
1198  if (radial_distortion_blue > comp.radial_distortion_blue) return false;
1199  // If we've reached here it should be exactly the same:
1200  return false;
1201 }
1202 
1204 {
1205  filename = source->getFilename();
1206  // Record the masks. Images with different masks require different
1207  // textures since the mask is stored with them.
1208  std::stringstream mask_ss;
1209  source->printMaskLines(mask_ss, 0);
1210  masks = mask_ss.str();
1211 
1212  exposure = source->getExposure();
1213  white_balance_red = source->getWhiteBalanceRed();
1214  white_balance_blue = source->getWhiteBalanceBlue();
1215  EMoR_params = source->getEMoRParams();
1216  radial_vig_corr_coeff = source->getRadialVigCorrCoeff();
1217  vig_corr_mode = source->getVigCorrMode();
1218  response_type = source->getResponseType();
1219  gamma = source->getGamma();
1220  radial_distortion_red = source->getRadialDistortionRed();
1221  radial_distortion_blue = source->getRadialDistortionBlue();
1222 }
1223 
#define DEBUG_INFO(msg)
Definition: utils.h:69
implementation of huginApp Class
void applyMask(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > img, HuginBase::MaskPolygonVector masks)
Definition: Mask.h:160
unsigned int GetTextureName(unsigned int image_number)
void DefineMaskTexture(const HuginBase::SrcPanoImage &srcImg)
std::vector< double > radial_distortion_blue
unsigned int GetMaxTotalTexels()
void SetOptions(const HuginBase::SrcPanoImage *source)
radiometric transformation, includes exposure, vignetting and white balance
bool hasActiveMasks() const
returns true, if image has active masks
void DisableTexture(bool maskOnly=false)
void enforceMonotonicity(LUT &lut)
enforce monotonicity of an array (mostly used for lookup tables)
Definition: lut.h:87
void printMaskLines(std::ostream &o, unsigned int newImgNr) const
writes all mask lines to stream, using given image number
#define DEBUG_ASSERT(cond)
Definition: utils.h:80
include file for the hugin project
std::vector< double > radial_distortion_red
declaration of classes to work with mask
int getHeight() const
Get the height of the image in pixels.
Definition: SrcPanoImage.h:276
void GetMonitorProfile(wxString &profileName, cmsHPROFILE &profile)
Definition: wxcms.cpp:195
static huginApp * Get()
hack.. kind of a pseudo singleton...
Definition: huginApp.cpp:641
HuginBase::SrcPanoImage::ResponseType response_type
HuginBase::PanoramaOptions * GetOptions()
Definition: ViewState.cpp:273
void SetPhotometricCorrect(bool state)
Model for a panorama.
Definition: Panorama.h:152
empirical model of response
Definition: SrcPanoImage.h:100
void DefineLevels(int min, int max, bool photometric_correct, const HuginBase::PanoramaOptions &dest_img, const HuginBase::SrcPanoImage &state)
std::size_t getNrOfImages() const
number of images.
Definition: Panorama.h:205
float pow(float a, double b)
Definition: utils.h:181
int getWidth() const
Get the width of the image in pixels.
Definition: SrcPanoImage.h:266
void redrawPreview()
Display an updated version of the preview images.
void UpdateMask(const HuginBase::SrcPanoImage &srcImg)
static MainFrame * getMainFrame()
Definition: huginApp.cpp:652
TextureManager * GetTextureManager()
Definition: ViewState.h:104
bool HasMonitorProfile() const
return true if we found a suitable monitor profile and could loading it
Definition: huginApp.h:152
IMPEX double h[25][1024]
Definition: emor.cpp:169
std::vector< float > EMoR_params
std::vector< double > radial_vig_corr_coeff
TextureInfo(ViewState *new_view_state)
std::vector< MaskPolygon > MaskPolygonVector
Definition: Mask.h:147
#define DEBUG_ERROR(msg)
Definition: utils.h:76
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
HuginBase::Panorama * m_pano
void createEMoRLUT(const std::vector< float > &params, VECTOR &lut)
Definition: emor.h:44
const bool operator==(const TextureKey &comp) const
double getExposure() const
void BindTexture(unsigned int image_number)
void DrawImage(unsigned int image_number, unsigned int display_list)
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition: Panorama.h:481
ViewState * view_state
TextureManager(HuginBase::Panorama *pano, ViewState *view)
bool RequireRecalculateMasks(unsigned int image_nr)
Definition: ViewState.cpp:304
bool RequireRecalculatePhotometric()
Definition: ViewState.cpp:294
static T max(T x, T y)
Definition: svm.cpp:65
#define DEBUG_DEBUG(msg)
Definition: utils.h:68
unsigned int GetMaxTextureSizePower()
virtual ~TextureManager()
vigra::triple< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImageRange(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:312
const bool operator<(const TextureKey &comp) const
HuginBase::SrcPanoImage * GetSrcImage(unsigned int image_nr)
Definition: ViewState.cpp:283
All variables of a source image.
Definition: SrcPanoImage.h:194
Panorama image options.
static T min(T x, T y)
Definition: svm.cpp:62
bool GetSupportMultiTexture() const
Definition: ViewState.h:106
TexturesMap textures
void LoadingImageFinished(int min, int max, bool texture_photometric_correct, const HuginBase::PanoramaOptions &dest_img, const HuginBase::SrcPanoImage &state)
void setOutput(double destExposure, const LUTD &destLut, double scale, double rangeCompression=0.0)
output lut