Hugin trunk 0.1
Loading...
Searching...
No Matches
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
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
81
83{
84 // free up the textures
85 textures.clear();
86}
87
88void 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;
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.
104 }
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);
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 {
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);
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);
156 }
159 glColor3f(1.0, 1.0, 1.0);
160 }
161 } else {
162 // we've already corrected all the photometrics, just draw once normally
164 if (it->second->GetUseAlpha() || it->second->GetHasActiveMasks())
165 {
167 }
168 }
169}
170
171unsigned int TextureManager::GetTextureName(unsigned int image_number)
172{
173 // bind the texture that represents the given image number.
174 TexturesMap::iterator it;
177 it = textures.find(key);
178 DEBUG_ASSERT(it != textures.end());
179 return it->second->GetNumber();
180}
181
182void TextureManager::BindTexture(unsigned int image_number)
183{
184 // bind the texture that represents the given image number.
185 TexturesMap::iterator it;
188 it = textures.find(key);
189 DEBUG_ASSERT(it != textures.end());
190 it->second->Bind();
191}
192
209
211{
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 {
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
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;
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
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();
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.
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),
317 // we would like a mipmap with this size:
320 // Ideally this mipmap would bring us up to this many 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;
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)))
333 {
334 // scale down
335 mip_level ++;
336 }
337 // don't allow any mipmaps smaller than the 1 by 1 pixel one.
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.
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,
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()),
381 // we would like a texture this size:
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,
394 // check this is hardware supported.
395 {
396 unsigned int biggest = GetMaxTextureSizePower();
399 }
400
401 // check if this is over budget.
403 // while the texture is over budget, shrink it
404 while ( (texels_used + (1 << (tex_width_p + tex_height_p)))
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
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
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
443 *dest_img,
445 texinfo->DefineMaskTexture(*view_state->GetSrcImage(image_index));
446 }
447 else
448 {
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;
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
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);
588 has_active_masks=false;
589 has_mask=false;
591}
592
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();
623 // we don't want the edges to repeat the other side of the texture
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;
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
639#endif
640 {
641 has_anisotropic = true;
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 {
654 anisotropy);
655 }
656 if(m_viewState->GetSupportMultiTexture())
657 {
658 BindMaskTexture();
661 // we don't want the edges to repeat the other side of the texture
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)
691 else
694 };
695}
696
698{
699 if(m_viewState->GetSupportMultiTexture())
700 {
703 }
704 else
706};
708{
709 if(m_viewState->GetSupportMultiTexture())
710 {
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,
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(),
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.
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
814 Exiv2::PreviewImage previewImage = previews->getPreviewImage(lists[previewIndex]);
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();
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
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 };
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(wxConfigBase::Get()->Read("/ImageCache/Mapping", HUGIN_IMGCACHE_MAPPING_FLOAT));
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
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 {
929 {
931 inputICC = NULL;
932 };
933 };
934 // if there is no icc profile in file fall back to sRGB
935 if (inputICC == NULL)
936 {
938 };
939 // now build transform
941 huginApp::Get()->GetMonitorProfile(), TYPE_RGB_8,
943 };
944 // now perform photometric correction
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
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 {
958 }
959 HuginBase::Photometric::InvResponseTransform < unsigned char, double >
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],
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 {
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)
1094 vigra_ext::applyMask(vigra::destImageRange(mask), masks);
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 }
1111 delete [] image;
1112#else
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.
1139 if(m_viewState->GetSupportMultiTexture())
1140 {
1141 // now for the mask texture
1142 BindMaskTexture();
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
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
declaration of classes to work with mask
void redrawPreview()
Display an updated version of the preview images.
@ RESPONSE_LINEAR
linear response
@ RESPONSE_EMOR
empirical model of response
Panorama image options.
Model for a panorama.
Definition Panorama.h:153
const PanoramaOptions & getOptions() const
returns the options for this panorama
Definition Panorama.h:481
std::size_t getNrOfImages() const
number of images.
Definition Panorama.h:205
All variables of a source image.
TextureInfo(ViewState *new_view_state)
void DefineMaskTexture(const HuginBase::SrcPanoImage &srcImg)
void UpdateMask(const HuginBase::SrcPanoImage &srcImg)
void DefineLevels(int min, int max, bool photometric_correct, const HuginBase::PanoramaOptions &dest_img, const HuginBase::SrcPanoImage &state)
const bool operator==(const TextureKey &comp) const
void SetOptions(const HuginBase::SrcPanoImage *source)
const bool operator<(const TextureKey &comp) const
virtual ~TextureManager()
void BindTexture(unsigned int image_number)
HuginBase::Panorama * m_pano
TexturesMap textures
void DrawImage(unsigned int image_number, unsigned int display_list)
void LoadingImageFinished(int min, int max, bool texture_photometric_correct, const HuginBase::PanoramaOptions &dest_img, const HuginBase::SrcPanoImage &state)
void SetPhotometricCorrect(bool state)
ViewState * view_state
unsigned int GetTextureName(unsigned int image_number)
unsigned int GetMaxTotalTexels()
void DisableTexture(bool maskOnly=false)
TextureManager(HuginBase::Panorama *pano, ViewState *view)
unsigned int GetMaxTextureSizePower()
bool RequireRecalculateMasks(unsigned int image_nr)
bool GetSupportMultiTexture() const
Definition ViewState.h:106
HuginBase::PanoramaOptions * GetOptions()
HuginBase::SrcPanoImage * GetSrcImage(unsigned int image_nr)
TextureManager * GetTextureManager()
Definition ViewState.h:104
bool RequireRecalculatePhotometric()
static MainFrame * getMainFrame()
Definition huginApp.cpp:662
static huginApp * Get()
hack.. kind of a pseudo singleton...
Definition huginApp.cpp:651
#define HUGIN_IMGCACHE_MAPPING_FLOAT
implementation of huginApp Class
#define DEBUG_ERROR(msg)
Definition utils.h:76
#define DEBUG_ASSERT(cond)
Definition utils.h:80
#define DEBUG_DEBUG(msg)
Definition utils.h:68
#define DEBUG_INFO(msg)
Definition utils.h:69
std::vector< MaskPolygon > MaskPolygonVector
Definition Mask.h:150
void createEMoRLUT(const std::vector< float > &params, VECTOR &lut)
Definition emor.h:44
void applyMask(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > img, HuginBase::MaskPolygonVector masks)
Definition Mask.h:163
void enforceMonotonicity(LUT &lut)
enforce monotonicity of an array (mostly used for lookup tables)
Definition lut.h:87
include file for the hugin project
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41