From: Pablo d'A. <da...@us...> - 2006-08-09 20:55:43
|
Update of /cvsroot/hugin/hugin/src/hugin In directory sc8-pr-cvs5.sourceforge.net:/tmp/cvs-serv16491/hugin Modified Files: CPImageCtrl.cpp CPZoomDisplayPanel.cpp ImageCache.cpp ImageOrientationPanel.cpp ImagesList.cpp ImagesPanel.cpp LensPanel.cpp MainFrame.cpp PreferencesDialog.cpp VigCorrDialog.cpp Log Message: added logarithmic and gamma mapping for display of HDR images in the GUI Index: PreferencesDialog.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/PreferencesDialog.cpp,v retrieving revision 1.49 retrieving revision 1.50 diff -u -d -r1.49 -r1.50 --- PreferencesDialog.cpp 22 Jul 2006 13:28:18 -0000 1.49 +++ PreferencesDialog.cpp 9 Aug 2006 20:55:39 -0000 1.50 @@ -592,6 +592,10 @@ DEBUG_WARN("Unknown language configured"); } + // hdr display settings + MY_CHOICE_VAL("prefs_misc_hdr_mapping", cfg->Read(wxT("/ImageCache/Mapping"), HUGIN_IMGCACHE_MAPPING)); + MY_CHOICE_VAL("prefs_misc_hdr_range", cfg->Read(wxT("/ImageCache/Range"), HUGIN_IMGCACHE_RANGE)); + // cursor setting // mem = cfg->Read(wxT("/CPImageCtrl/CursorType"), HUGIN_CP_CURSOR); @@ -690,6 +694,9 @@ cfg->Write(wxT("/Nona/NumberOfThreads"), cpucount); // locale cfg->Write(wxT("language"), HUGIN_LANGUAGE); + // hdr + cfg->Write(wxT("/ImageCache/Mapping"), HUGIN_IMGCACHE_MAPPING); + cfg->Write(wxT("/ImageCache/Range"), HUGIN_IMGCACHE_RANGE); // druid cfg->Write(wxT("/PreviewFrame/showDruid"), HUGIN_PREVIEW_SHOW_DRUID); // use preview images as active images @@ -766,6 +773,10 @@ cfg->Write(wxT("language"), templ); DEBUG_INFO("Language Selection ID: " << templ); + // hdr display + cfg->Write(wxT("/ImageCache/Mapping"),MY_G_CHOICE_VAL("prefs_misc_hdr_mapping")); + cfg->Write(wxT("/ImageCache/Range"),MY_G_CHOICE_VAL("prefs_misc_hdr_range")); + // cursor // cfg->Write(wxT("/CPImageCtrl/CursorType"), MY_G_SPIN_VAL("prefs_cp_CursorType")); // tempdir Index: MainFrame.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/MainFrame.cpp,v retrieving revision 1.176 retrieving revision 1.177 diff -u -d -r1.176 -r1.177 --- MainFrame.cpp 6 Aug 2006 14:51:10 -0000 1.176 +++ MainFrame.cpp 9 Aug 2006 20:55:39 -0000 1.177 @@ -100,6 +100,7 @@ file.GetExt().CmpNoCase(wxT("gif")) == 0 || file.GetExt().CmpNoCase(wxT("pnm")) == 0 || file.GetExt().CmpNoCase(wxT("sun")) == 0 || + file.GetExt().CmpNoCase(wxT("hdr")) == 0 || file.GetExt().CmpNoCase(wxT("viff")) == 0 ) { filesv.push_back((const char *)filenames[i].mb_str()); @@ -613,7 +614,7 @@ // get the global config object wxConfigBase* config = wxConfigBase::Get(); - wxString wildcard (_("All Image files|*.jpg;*.JPG;*.tif;*.TIF;*.tiff;*.TIFF;*.png;*.PNG;*.bmp;*.BMP;*.gif;*.GIF;*.pnm;*.PNM;*.sun;*.viff|JPEG files (*.jpg)|*.jpg;*.JPG|All files (*)|*")); + wxString wildcard (_("All Image files|*.jpg;*.JPG;*.tif;*.TIF;*.tiff;*.TIFF;*.png;*.PNG;*.bmp;*.BMP;*.gif;*.GIF;*.pnm;*.PNM;*.sun;*.viff;*.hdr|JPEG files (*.jpg)|*.jpg;*.JPG|All files (*)|*")); wxFileDialog dlg(this,_("Add images"), config->Read(wxT("actualPath"),wxT("")), wxT(""), wildcard, wxOPEN|wxMULTIPLE , wxDefaultPosition); Index: ImageOrientationPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/ImageOrientationPanel.cpp,v retrieving revision 1.14 retrieving revision 1.15 diff -u -d -r1.14 -r1.15 --- ImageOrientationPanel.cpp 10 Jun 2005 13:27:40 -0000 1.14 +++ ImageOrientationPanel.cpp 9 Aug 2006 20:55:39 -0000 1.15 @@ -272,7 +272,7 @@ return; } const string & fname = pano.getImage(m_refImgNr).getFilename(); - wxImage * img = ImageCache::getInstance().getImage(fname); + wxImage * img = ImageCache::getInstance().getImage(fname)->image; m_imageSize = wxSize(img->GetWidth(), img->GetHeight()); Index: VigCorrDialog.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/VigCorrDialog.cpp,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- VigCorrDialog.cpp 28 Apr 2006 06:59:10 -0000 1.8 +++ VigCorrDialog.cpp 9 Aug 2006 20:55:39 -0000 1.9 @@ -164,7 +164,7 @@ void VigCorrDialog::OnFlatfieldSelect(wxCommandEvent & e) { - wxString wildcard (_("All Image files|*.jpg;*.JPG;*.tif;*.TIF;*.tiff;*.TIFF;*.png;*.PNG;*.bmp;*.BMP;*.gif;*.GIF;*.pnm;*.PNM;*.sun;*.viff|JPEG files (*.jpg)|*.jpg;*.JPG|All files (*)|*")); + wxString wildcard (_("All Image files|*.jpg;*.JPG;*.tif;*.TIF;*.tiff;*.TIFF;*.png;*.PNG;*.bmp;*.BMP;*.gif;*.GIF;*.pnm;*.PNM;*.sun;*.viff;*.hdr|JPEG files (*.jpg)|*.jpg;*.JPG|All files (*)|*")); wxFileDialog dlg(this,_("Select flatfield image"), wxConfigBase::Get()->Read(wxT("flatfieldPath"),wxT("")), wxT(""), wildcard, @@ -272,12 +272,25 @@ PT::ImageOptions iopt = img.getOptions(); // get hfov double v = const_map_get(m_pano.getImageVariables(m_imgNr),"v").getValue(); - wxImage * src = ImageCache::getInstance().getSmallImage(img.getFilename().c_str()); + ImageCache::Entry * cacheEntry = ImageCache::getInstance().getSmallImage(img.getFilename().c_str()); + if (! cacheEntry) { + throw std::runtime_error("could not retrieve small source image for vignetting optimisation"); + } + wxImage * src = cacheEntry->image; if (!src->Ok()) { throw std::runtime_error("could not retrieve small source image for vignetting optimisation"); } + // check if image is linear! + if (!cacheEntry->linear) { + int ret = wxMessageBox(_("Vignetting correction currently does not work with nonlinear mapped 16 bit and HDR images.\nPlease enable linear mapping\nContinue anyway?"), _("Warning") , wxYES_NO); + if (ret == wxNO) { + return; + } + } double scale = calcOptimalPanoScale(m_pano.getSrcImage(m_imgNr), opts); + // take small scale images into account + scale = scale / m_pano.getSrcImage(m_imgNr).getSize().x * src->GetWidth(); opts.setWidth(roundi(opts.getWidth()*scale)); @@ -309,10 +322,11 @@ if (m_pano.getImage(i).getLensNr() == lensNr) { SrcPanoImage src = m_pano.getSrcImage(i); - wxImage * srcImgWX = ImageCache::getInstance().getSmallImage(src.getFilename().c_str()); - if (!srcImgWX->Ok()) { + ImageCache::Entry * cacheEntry = ImageCache::getInstance().getSmallImage(src.getFilename().c_str()); + if (!cacheEntry) { throw std::runtime_error("could not retrieve small source image for vignetting optimisation"); } + wxImage * srcImgWX = cacheEntry->image; // image view BasicImageView<RGBValue<unsigned char> > srcImgInCache((RGBValue<unsigned char> *)srcImgWX->GetData(), srcImgWX->GetWidth(), @@ -321,7 +335,8 @@ src.resize(srcImgInCache.size()); BImage * grayImg = new BImage(srcImgInCache.size()); // change to grayscale - vigra::copyImage(vigra::srcImageRange(srcImgInCache, vigra::RGBToGrayAccessor<RGBValue<unsigned char> >()), + // just use green channel + vigra::copyImage(vigra::srcImageRange(srcImgInCache, vigra::GreenAccessor<RGBValue<unsigned char> >()), vigra::destImage(*grayImg)); BImage *maskImg = new BImage(srcImgInCache.size().x,srcImgInCache.size().y , 255); Index: ImageCache.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/ImageCache.cpp,v retrieving revision 1.45 retrieving revision 1.46 diff -u -d -r1.45 -r1.46 --- ImageCache.cpp 19 Apr 2006 20:35:49 -0000 1.45 +++ ImageCache.cpp 9 Aug 2006 20:55:39 -0000 1.46 @@ -35,67 +35,56 @@ #include <vigra/basicimageview.hxx> #include <vigra/rgbvalue.hxx> #include <vigra/impex.hxx> +#include <vigra_ext/utils.h> #include <vigra_ext/impexalpha.hxx> #include <vigra_ext/Pyramid.h> #include <vigra_ext/ImageTransforms.h> - #include <PT/Stitcher.h> +#include <vigra/functorexpression.hxx> #include "hugin/ImageCache.h" +#include "hugin/config_defaults.h" using namespace std; using namespace vigra; using namespace vigra_ext; using namespace utils; using namespace PT; +using namespace vigra::functor; - -template <class T1, class T2> - struct GetAlphaScaleFactor; +template <class T1> +struct GetRange; // define the scale factors to map from the alpha channel type (T2) // to valid alpha channels in image type (T1) // T1: image type // T2: alpha type // S: scale factor (given as type of T1). -#define VIGRA_EXT_GETALPHASCALE(T1,T2, S) \ +#define VIGRA_EXT_GETRANGE(T1, MI,MA) \ template<> \ -struct GetAlphaScaleFactor<T1, T2> \ +struct GetRange<T1> \ { \ - static vigra::NumericTraits<T1>::RealPromote get() \ + static T1 min() \ { \ - return S; \ + return MI; \ +} \ + static T1 max() \ +{ \ + return MA; \ } \ }; -// conversion from/to unsigned char -VIGRA_EXT_GETALPHASCALE(unsigned char, unsigned char, 1); -VIGRA_EXT_GETALPHASCALE(short, unsigned char, 128.498); -VIGRA_EXT_GETALPHASCALE(unsigned short, unsigned char, 257); -VIGRA_EXT_GETALPHASCALE(int, unsigned char, 8421504.49803922); -VIGRA_EXT_GETALPHASCALE(unsigned int, unsigned char, 16843009); -VIGRA_EXT_GETALPHASCALE(float, unsigned char, 1.0/255); -VIGRA_EXT_GETALPHASCALE(double, unsigned char, 1.0/255); - -// conversion from/to unsigned short -VIGRA_EXT_GETALPHASCALE(unsigned char, unsigned short, 0.00389105058365759); -VIGRA_EXT_GETALPHASCALE(short, unsigned short, 0.499992370489052); -VIGRA_EXT_GETALPHASCALE(unsigned short, unsigned short, 1); -VIGRA_EXT_GETALPHASCALE(int, unsigned short, 32768.4999923705); -VIGRA_EXT_GETALPHASCALE(unsigned int, unsigned short, 65537); -VIGRA_EXT_GETALPHASCALE(float, unsigned short, 1.0/65535); -VIGRA_EXT_GETALPHASCALE(double, unsigned short, 1.0/65535); -// conversion from/to unsigned int -VIGRA_EXT_GETALPHASCALE(unsigned char, unsigned int, 5.93718141455603e-08); -VIGRA_EXT_GETALPHASCALE(short, unsigned int, 7.62916170238265e-06); -VIGRA_EXT_GETALPHASCALE(unsigned short, unsigned int, 1.52585562354090e-05); -VIGRA_EXT_GETALPHASCALE(int, unsigned int, 0.499999999883585); -VIGRA_EXT_GETALPHASCALE(unsigned int, unsigned int, 1); -VIGRA_EXT_GETALPHASCALE(float, unsigned int, 1.0/4294967295.0); -VIGRA_EXT_GETALPHASCALE(double, unsigned int, 1.0/4294967295.0); +// conversion from/to unsigned char +VIGRA_EXT_GETRANGE(UInt8, 0, 255); +VIGRA_EXT_GETRANGE(Int16, 0, 32767); +VIGRA_EXT_GETRANGE(UInt16, 0, 65535); +VIGRA_EXT_GETRANGE(Int32, 0, 2147483647); +VIGRA_EXT_GETRANGE(UInt32, 0, 4294967295); +VIGRA_EXT_GETRANGE(float, 0, 1.0f); +VIGRA_EXT_GETRANGE(double, 0, 1.0); -#undef VIGRA_EXT_GETALPHASCALE +#undef VIGRA_EXT_GETRANGE ImageCache * ImageCache::instance = 0; @@ -115,7 +104,7 @@ void ImageCache::removeImage(const std::string & filename) { - map<string, wxImage*>::iterator it = images.find(filename); + map<string, Entry*>::iterator it = images.find(filename); if (it != images.end()) { delete it->second; images.erase(it); @@ -146,7 +135,7 @@ void ImageCache::flush() { - for (map<string, ImagePtr>::iterator it = images.begin(); + for (map<string, Entry*>::iterator it = images.begin(); it != images.end(); ++it) { @@ -171,9 +160,9 @@ // calculate used memory long imgMem = 0; - std::map<std::string, ImagePtr>::iterator imgIt; + std::map<std::string, Entry*>::iterator imgIt; for(imgIt=images.begin(); imgIt != images.end(); imgIt++) { - imgMem += imgIt->second->GetWidth() * imgIt->second->GetHeight() * 3; + imgMem += imgIt->second->image->GetWidth() * imgIt->second->image->GetHeight() * 3; } long pyrMem = 0; @@ -202,12 +191,12 @@ deleted = true; } else if (images.size() > 0) { // only remove full size images. - for (map<string, ImagePtr>::iterator it = images.begin(); + for (map<string, Entry*>::iterator it = images.begin(); it != images.end(); ++it) { if (it->first.substr(it->first.size()-6) != "_small") { - purgedMem += it->second->GetWidth() * it->second->GetHeight() * 3; + purgedMem += it->second->image->GetWidth() * it->second->image->GetHeight() * 3; delete it->second; images.erase(it); deleted = true; @@ -236,10 +225,223 @@ return *instance; } + +template <class TIn, class TOut=UInt8> +struct ApplyGammaFunctor +{ + float minv; + float maxv; + float gamma; + float scale; + + ApplyGammaFunctor(TIn min_, TIn max_, float gamma_) + { + minv = min_; + maxv = max_; + gamma = gamma_; + scale = float(maxv) - minv; + } + + TOut operator()(TIn v) const + { + typedef vigra::NumericTraits<TOut> DestTraits; + return DestTraits::fromRealPromote(pow((float(v)-minv)/scale, gamma)*255); + } + + RGBValue<TOut> operator()(const RGBValue<TIn> & v) const + { + typedef vigra::NumericTraits< RGBValue<TOut> > DestTraits; + typedef vigra::NumericTraits< RGBValue<TIn> > SrcTraits; + return DestTraits::fromRealPromote(pow((SrcTraits::toRealPromote(v)+(-minv))/scale, gamma)*255); + } +}; + +// gamma correction with lookup table +template <> +struct ApplyGammaFunctor<UInt16, UInt8> +{ + UInt8 lut[65536]; + + ApplyGammaFunctor(UInt16 min, UInt16 max, float gamma) + { + float scale = float(max) - min; + for (int i=0; i<65536; i++) { + lut[i] = roundi(pow((float(i)-min)/scale, gamma)*255); + } + } + + UInt8 operator()(UInt16 v) const + { + return lut[v]; + } + + RGBValue<UInt8> operator()(const RGBValue<UInt16> & v) const + { + return RGBValue<UInt8>(lut[v[0]], lut[v[1]], lut[v[2]]); + } +}; + +/* +struct ApplyGammaFunctor +{ + float minv; + float maxv; + float gamma; + float scale; + + ApplyGammaFunctor(float min_, float max_, float gamma_) + { + minv = min_; + maxv = max_; + gamma = gamma_; + scale = maxv - minv; + } + + template <class T> + unsigned char operator()(T v) const + { + typedef vigra::NumericTraits<UInt8> DestTraits; + return DestTraits::fromRealPromote(pow((float(v)-minv)/scale, gamma)*255); + } + + template <class T, unsigned int R, unsigned int G, unsigned int B> + RGBValue<UInt8,0,1,2> operator()(const RGBValue<T,R,G,B> & v) const + { + typedef vigra::NumericTraits< RGBValue<UInt8,0,1,2> > DestTraits; + typedef vigra::NumericTraits< RGBValue<T,R,G,B> > SrcTraits; + return DestTraits::fromRealPromote(pow((SrcTraits::toRealPromote(v)+(-minv))/scale, gamma)*255); +// return DestTraits::fromRealPromote((log10(SrcTraits::toRealPromote(v)) + (-minv))/scale); + } +}; +*/ + +struct ApplyLogFunctor +{ + float minv; + float maxv; + float scale; + + ApplyLogFunctor(float min_, float max_) + { + // protect against zeros in image data + if (min_ == 0.0f) { + min_ = 1e-5; + } + minv = log10(min_); + maxv = log10(max_); + scale = (maxv - minv)/255; + DEBUG_DEBUG("gray range: " << min_ << " " << max_ << " log range: " << minv << " " << maxv << " scale:" << scale ); + } + + template <class T> + unsigned char operator()(T v) const + { + typedef vigra::NumericTraits<UInt8> DestTraits; + return DestTraits::fromRealPromote((log10(float(v))-minv)/scale); + } + + template <class T, unsigned int R, unsigned int G, unsigned int B> + RGBValue<UInt8,0,1,2> operator()(const RGBValue<T,R,G,B> & v) const + { + typedef vigra::NumericTraits< RGBValue<UInt8,0,1,2> > DestTraits; + typedef vigra::NumericTraits< RGBValue<T,R,G,B> > SrcTraits; + return DestTraits::fromRealPromote((log10(SrcTraits::toRealPromote(v)) + (-minv))/scale); + } +}; + + +/// add a scalar to all components, might break other stuff, therefore define just here +template <class V1, class V2> +inline +vigra::RGBValue<V1> +operator+(const vigra::RGBValue<V1> l, V2 const & r) +{ + return vigra::RGBValue<V1>(l.red() + r, l.green() + r, l.blue() + r); +} + +/// subtract a scalar to all components, might break other stuff, therefore define just here +template <class V1, class V2> +inline +vigra::RGBValue<V1> +operator-(const vigra::RGBValue<V1> l, V2 const & r) +{ + return vigra::RGBValue<V1>(l.red() - r, l.green() - r, l.blue() - r); +} + +template <class DestValueType> +struct LinearTransform +{ + public: + /* the functors argument type (actually, since + <tt>operator()</tt> is a template, much more types are possible) + */ + typedef DestValueType argument_type; + + /* the functors result type + */ + typedef DestValueType result_type; + + /* init scale and offset + */ + LinearTransform(float scale, float offset) + : scale_(scale), offset_(offset) + {} + template <class SrcValueType> + result_type operator()(SrcValueType const & s) const + { + return NumericTraits<result_type>::fromRealPromote(scale_ * (NumericTraits<SrcValueType>::toRealPromote(s) + offset_)); + } + private: + + float scale_; + float offset_; +}; + +template <class SrcIterator, class SrcAccessor, class DestIterator, class DestAccessor, class T> +void applyMapping(vigra::triple<SrcIterator, SrcIterator, SrcAccessor> img, + vigra::pair<DestIterator, DestAccessor> dest, T min, T max ) +{ + int mapping = wxConfigBase::Get()->Read(wxT("/ImageCache/Mapping"), HUGIN_IMGCACHE_MAPPING); + + switch (mapping) + { + case 0: + { + // linear + float offset_ = -float(min); + float scale_ = 255/float(max)-float(min); + vigra::transformImage(img, dest, + LinearTransform<typename DestAccessor::value_type>( scale_, offset_) + ); + break; + } + case 1: + { + // log + ApplyLogFunctor logfunc(min, max); + transformImage(img, dest, + logfunc); + break; + } + case 2: + { + // gamma + ApplyGammaFunctor<T> logfunc(min, max, 1/2.2f); + transformImage(img, dest, + logfunc); + break; + } + default: + vigra_fail("Unknown image mapping mode"); + } +} + + template <class SrcPixelType, class DestIterator, class DestAccessor> void importAndConvertImage(const ImageImportInfo & info, - vigra::pair<DestIterator, DestAccessor> dest) + vigra::pair<DestIterator, DestAccessor> dest, + wxString & type) { typedef typename DestAccessor::value_type DestPixelType; typedef typename DestPixelType::value_type DestComponentType; @@ -249,27 +451,35 @@ BasicImage<SrcPixelType> tmp(info.width(), info.height()); vigra::importImage(info, destImage(tmp)); - // convert to destination type - typedef typename vigra::NumericTraits<SrcComponentType>::RealPromote ScaleType; - - ScaleType s = GetAlphaScaleFactor<SrcComponentType, DestComponentType>::get(); -// GetAlphaScaleFactor<float, unsigned char>::get(); - - // get the scaling factor - ScaleType scale = vigra::NumericTraits<DestComponentType>::one()/s; + SrcComponentType min,max; - std::cerr << " import scale factor: " << scale << std::endl; + int range = wxConfigBase::Get()->Read(wxT("/ImageCache/Range"), HUGIN_IMGCACHE_RANGE); + if (range == 0) { + double t; + wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/min"), &t, double(GetRange<SrcComponentType>::min())); + min = SrcComponentType(t); + wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/max"), &t, double(GetRange<SrcComponentType>::max())); + max = SrcComponentType(t); + } else if (range == 1) { + vigra::RGBToGrayAccessor<RGBValue<SrcPixelType> > ga; + vigra::FindMinMax<SrcComponentType> minmax; // init functor + vigra::inspectImage(srcImageRange(tmp, ga), + minmax); + min = minmax.min; + max = minmax.max; + } else{ + vigra_fail("Unknown image import range mode"); + } - // copy image to output - transformImage(srcImageRange(tmp), dest, - linearIntensityTransform<DestPixelType>(scale)); + applyMapping(srcImageRange(tmp), dest, min, max); } template <class SrcPixelType, class DestIterator, class DestAccessor> void importAndConvertGrayImage(const ImageImportInfo & info, - vigra::pair<DestIterator, DestAccessor> dest) + vigra::pair<DestIterator, DestAccessor> dest, + wxString type) { typedef typename DestAccessor::value_type DestPixelType; @@ -280,133 +490,110 @@ BasicImage<SrcPixelType> tmp(info.width(), info.height()); vigra::importImage(info, destImage(tmp)); - // convert to destination type - typedef typename vigra::NumericTraits<SrcComponentType>::RealPromote ScaleType; - - ScaleType s = GetAlphaScaleFactor<SrcComponentType, DestComponentType>::get(); -// GetAlphaScaleFactor<float, unsigned char>::get(); - - // get the scaling factor - ScaleType scale = vigra::NumericTraits<DestComponentType>::one()/s; - - std::cerr << " import scale factor: " << scale << std::endl; - - // copy image to output - transformImage(srcImageRange(tmp), - make_pair(dest.first, - RedAccessor<DestPixelType>()), - linearIntensityTransform<DestComponentType>(scale)); + SrcComponentType min,max; - copyImage(dest.first, dest.first + tmp.size(), - RedAccessor<DestPixelType>(), - dest.first, GreenAccessor<DestPixelType>()); + int range = wxConfigBase::Get()->Read(wxT("/ImageCache/Range"), HUGIN_IMGCACHE_RANGE); + if (range == 0) { + double t; + wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/min"), &t, double(GetRange<SrcComponentType>::min())); + min = SrcComponentType(t); + wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/max"), &t, double(GetRange<SrcComponentType>::max())); + max = SrcComponentType(t); + } else if (range == 1) { + vigra::FindMinMax<SrcComponentType> minmax; // init functor + vigra::inspectImage(srcImageRange(tmp), + minmax); + min = minmax.min; + max = minmax.max; + } else{ + vigra_fail("Unknown image import range mode"); + } - copyImage(dest.first, dest.first + tmp.size(), - RedAccessor<DestPixelType>(), - dest.first, BlueAccessor<DestPixelType>()); + applyMapping(srcImageRange(tmp), dest, min, max); } template <class SrcPixelType, class DestIterator, class DestAccessor> void importAndConvertGrayAlphaImage(const ImageImportInfo & info, - vigra::pair<DestIterator, DestAccessor> dest) + vigra::pair<DestIterator, DestAccessor> dest, + wxString type) { typedef typename DestAccessor::value_type DestPixelType; typedef typename DestPixelType::value_type DestComponentType; - typedef typename SrcPixelType::value_type SrcComponentType; + typedef SrcPixelType SrcComponentType; // load image into temporary buffer. BasicImage<SrcPixelType> tmp(info.width(), info.height()); - vigra::importImage(info, destImage(tmp)); - - // convert to destination type - typedef typename vigra::NumericTraits<SrcComponentType>::RealPromote ScaleType; - - ScaleType s = GetAlphaScaleFactor<SrcComponentType, DestComponentType>::get(); -// GetAlphaScaleFactor<float, unsigned char>::get(); - - // get the scaling factor - ScaleType scale = vigra::NumericTraits<DestComponentType>::one()/s; - - std::cerr << " import scale factor: " << scale << std::endl; + BImage mask(info.width(), info.height()); + vigra::importImageAlpha(info, destImage(tmp), destImage(mask)); - // copy image to output - transformImageIf(tmp.upperLeft(), tmp.lowerRight(), - VectorComponentAccessor<SrcPixelType>(0), - tmp.upperLeft(), - VectorComponentAccessor<SrcPixelType>(1), - dest.first, RedAccessor<DestPixelType>(), - linearIntensityTransform<DestComponentType>(scale)); + SrcComponentType min,max; - copyImage(dest.first, dest.first + tmp.size(), - RedAccessor<DestPixelType>() , - dest.first, GreenAccessor<DestPixelType>()); + int range = wxConfigBase::Get()->Read(wxT("/ImageCache/Range"), HUGIN_IMGCACHE_RANGE); + if (range == 0) { + double t; + wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/min"), &t, double(GetRange<SrcComponentType>::min())); + min = SrcComponentType(t); + wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/max"), &t, double(GetRange<SrcComponentType>::max())); + max = SrcComponentType(t); + } else if (range == 1) { + vigra::FindMinMax<SrcComponentType> minmax; // init functor + vigra::inspectImage(srcImageRange(tmp), + minmax); + min = minmax.min; + max = minmax.max; + } else{ + vigra_fail("Unknown image import range mode"); + } - copyImage(dest.first, dest.first + tmp.size(), - RedAccessor<DestPixelType>(), - dest.first, BlueAccessor<DestPixelType>()); + applyMapping(srcImageRange(tmp), dest, min, max); } template <class SrcPixelType, class DestIterator, class DestAccessor> void importAndConvertAlphaImage(const ImageImportInfo & info, - vigra::pair<DestIterator, DestAccessor> dest) + vigra::pair<DestIterator, DestAccessor> dest, + wxString type) { typedef typename DestAccessor::value_type DestPixelType; typedef typename DestPixelType::value_type DestComponentType; - typedef typename SrcPixelType::value_type SrcComponentType; + typedef SrcPixelType SrcComponentType; // load image into temporary buffer. - BasicImage<SrcPixelType> tmp(info.width(), info.height()); - vigra::importImage(info, destImage(tmp)); - - tmp.upperLeft(); - - // convert to destination type - typedef typename vigra::NumericTraits<SrcComponentType>::RealPromote ScaleType; - - ScaleType s = GetAlphaScaleFactor<SrcComponentType, DestComponentType>::get(); -// GetAlphaScaleFactor<float, unsigned char>::get(); - - // get the scaling factor - ScaleType scale = vigra::NumericTraits<DestComponentType>::one()/s; - - std::cerr << " import scale factor: " << scale << std::endl; - - // copy image to output - vigra::transformImageIf(tmp.upperLeft(), - tmp.lowerRight(), - VectorComponentAccessor<SrcPixelType>(0), - - tmp.upperLeft(), - VectorComponentAccessor<SrcPixelType>(3), - - dest.first, RedAccessor<DestPixelType>(), - linearIntensityTransform<DestComponentType>(scale)); + BasicImage<RGBValue<SrcPixelType> > tmp(info.width(), info.height()); + BImage mask(info.width(), info.height()); + vigra::importImageAlpha(info, destImage(tmp), destImage(mask)); + + SrcComponentType min,max; - transformImageIf(tmp.upperLeft(), tmp.lowerRight(), - VectorComponentAccessor<SrcPixelType>(1), - tmp.upperLeft(), - VectorComponentAccessor<SrcPixelType>(3), - dest.first, - GreenAccessor<DestPixelType>(), - linearIntensityTransform<DestComponentType>(scale)); + int range = wxConfigBase::Get()->Read(wxT("/ImageCache/Range"), HUGIN_IMGCACHE_RANGE); + if (range == 0) { + double t; + wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/min"), &t, double(GetRange<SrcComponentType>::min())); + min = SrcComponentType(t); + wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/max"), &t, double(GetRange<SrcComponentType>::max())); + max = SrcComponentType(t); + } else if (range == 1) { + vigra::RGBToGrayAccessor<RGBValue<SrcPixelType> > ga; + vigra::FindMinMax<SrcComponentType> minmax; // init functor + vigra::inspectImageIf(srcImageRange(tmp, ga), + maskImage(mask), + minmax); + min = minmax.min; + max = minmax.max; + } else{ + vigra_fail("Unknown image import range mode"); + } - transformImageIf(tmp.upperLeft(), tmp.lowerRight(), - VectorComponentAccessor<SrcPixelType>(2), - tmp.upperLeft(), - VectorComponentAccessor<SrcPixelType>(3), - dest.first, - BlueAccessor<DestPixelType>(), - linearIntensityTransform<DestComponentType>(scale)); + applyMapping(srcImageRange(tmp), dest, min, max); } -ImagePtr ImageCache::getImage(const std::string & filename) +ImageCache::Entry* ImageCache::getImage(const std::string & filename) { // softFlush(); - std::map<std::string, wxImage *>::iterator it; + std::map<std::string, Entry *>::iterator it; it = images.find(filename); if (it != images.end()) { return it->second; @@ -414,9 +601,12 @@ if (m_progress) { m_progress->pushTask(ProgressTask((const char *)wxString::Format(_("Loading image %s"),wxString(utils::stripPath(filename).c_str(), *wxConvCurrent).c_str()).mb_str(), "", 0)); } + wxBusyCursor wait; #if 1 - // load images with VIGRA impex, and scale to 8 bit + // load images with VIGRA impex, and convert to 8 bit, if required. wxImage * image; + std::string pixelTypeStr; + bool linear=true; try { ImageImportInfo info(filename.c_str()); @@ -429,24 +619,31 @@ int bands = info.numBands(); int extraBands = info.numExtraBands(); const char * pixelType = info.getPixelType(); + pixelTypeStr = pixelType; + DEBUG_DEBUG(filename << ": bands: " << bands << " extra bands: " << extraBands << " type: " << pixelType); + wxString pixelTypeWX(pixelType, *wxConvCurrent); if ( bands == 1) { // load and convert image to 8 bit, if needed if (strcmp(pixelType, "UINT8") == 0 ) { - importAndConvertGrayImage<unsigned char>(info, destImage(imgview)); + vigra::importImage(info, destImage(imgview, VectorComponentAccessor<RGBValue<UInt8> >(0))); + copyImage(srcImageRange(imgview, VectorComponentAccessor<RGBValue<UInt8> >(0)), + destImage(imgview, VectorComponentAccessor<RGBValue<UInt8> >(1))); + copyImage(srcImageRange(imgview, VectorComponentAccessor<RGBValue<UInt8> >(0)), + destImage(imgview, VectorComponentAccessor<RGBValue<UInt8> >(2))); } else if (strcmp(pixelType, "INT16") == 0 ) { - importAndConvertGrayImage<short> (info, destImage(imgview)); + importAndConvertGrayImage<short> (info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "UINT16") == 0 ) { - importAndConvertGrayImage<unsigned short >(info, destImage(imgview)); + importAndConvertGrayImage<unsigned short >(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "UINT32") == 0 ) { - importAndConvertGrayImage<unsigned int>(info, destImage(imgview)); + importAndConvertGrayImage<unsigned int>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "INT32") == 0 ) { - importAndConvertGrayImage<int>(info, destImage(imgview)); + importAndConvertGrayImage<int>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "FLOAT") == 0 ) { - importAndConvertGrayImage<float>(info, destImage(imgview)); + importAndConvertGrayImage<float>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "DOUBLE") == 0 ) { - importAndConvertGrayImage<double>(info, destImage(imgview)); + importAndConvertGrayImage<double>(info, destImage(imgview), pixelTypeWX); } else { DEBUG_FATAL("Unsupported pixel type: " << pixelType); } @@ -456,66 +653,73 @@ if (strcmp(pixelType, "UINT8") == 0 ) { vigra::importImage(info, destImage(imgview)); } else if (strcmp(pixelType, "INT16") == 0 ) { - importAndConvertImage<RGBValue<short> > (info, destImage(imgview)); + importAndConvertImage<RGBValue<short> > (info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "UINT16") == 0 ) { - importAndConvertImage<RGBValue<unsigned short> >(info, destImage(imgview)); + importAndConvertImage<RGBValue<unsigned short> >(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "UINT32") == 0 ) { - importAndConvertImage<RGBValue<unsigned int> >(info, destImage(imgview)); + importAndConvertImage<RGBValue<unsigned int> >(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "INT32") == 0 ) { - importAndConvertImage<RGBValue<int> >(info, destImage(imgview)); + importAndConvertImage<RGBValue<int> >(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "FLOAT") == 0 ) { - importAndConvertImage<RGBValue<float> >(info, destImage(imgview)); + importAndConvertImage<RGBValue<float> >(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "DOUBLE") == 0 ) { - importAndConvertImage<RGBValue<double> >(info, destImage(imgview)); + importAndConvertImage<RGBValue<double> >(info, destImage(imgview), pixelTypeWX); } else { DEBUG_FATAL("Unsupported pixel type: " << pixelType); } } else if ( bands == 4 && extraBands == 1) { // load and convert image to 8 bit, if needed if (strcmp(pixelType, "UINT8") == 0 ) { - importAndConvertAlphaImage<TinyVector<unsigned char, 4> > (info, destImage(imgview)); + vigra::BImage mask(imgview.size()); + vigra::importImageAlpha(info, destImage(imgview), destImage(mask)); } else if (strcmp(pixelType, "INT16") == 0 ) { - importAndConvertAlphaImage<TinyVector<short, 4> > (info, destImage(imgview)); + importAndConvertAlphaImage<short> (info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "UINT16") == 0 ) { - importAndConvertAlphaImage<TinyVector<unsigned short, 4> >(info, destImage(imgview)); + importAndConvertAlphaImage<unsigned short>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "UINT32") == 0 ) { - importAndConvertAlphaImage<TinyVector<unsigned int, 4> >(info, destImage(imgview)); + importAndConvertAlphaImage<unsigned int>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "INT32") == 0 ) { - importAndConvertAlphaImage<TinyVector<int, 4> >(info, destImage(imgview)); + importAndConvertAlphaImage<int>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "FLOAT") == 0 ) { - importAndConvertAlphaImage<TinyVector<float, 4> >(info, destImage(imgview)); + importAndConvertAlphaImage<float>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "DOUBLE") == 0 ) { - importAndConvertAlphaImage<TinyVector<double, 4> >(info, destImage(imgview)); + importAndConvertAlphaImage<double>(info, destImage(imgview), pixelTypeWX); } else { DEBUG_FATAL("Unsupported pixel type: " << pixelType); } } else if ( bands == 2 && extraBands == 1) { // load and convert image to 8 bit, if needed if (strcmp(pixelType, "UINT8") == 0 ) { - importAndConvertGrayAlphaImage<TinyVector<unsigned char, 2> > (info, destImage(imgview)); + vigra::BImage mask(imgview.size()); + vigra::importImageAlpha(info, destImage(imgview, + VectorComponentAccessor<RGBValue<UInt8> >(0)), + destImage(mask)); + copyImage(srcImageRange(imgview, VectorComponentAccessor<RGBValue<UInt8> >(0)), + destImage(imgview, VectorComponentAccessor<RGBValue<UInt8> >(1))); + copyImage(srcImageRange(imgview, VectorComponentAccessor<RGBValue<UInt8> >(0)), + destImage(imgview, VectorComponentAccessor<RGBValue<UInt8> >(2))); } else if (strcmp(pixelType, "INT16") == 0 ) { - importAndConvertGrayAlphaImage<TinyVector<short, 2> > (info, destImage(imgview)); + importAndConvertGrayAlphaImage<short> (info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "UINT16") == 0 ) { - importAndConvertGrayAlphaImage<TinyVector<unsigned short, 2> >(info, destImage(imgview)); + importAndConvertGrayAlphaImage<unsigned short>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "UINT32") == 0 ) { - importAndConvertGrayAlphaImage<TinyVector<unsigned int, 2> >(info, destImage(imgview)); + importAndConvertGrayAlphaImage<unsigned int>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "INT32") == 0 ) { - importAndConvertGrayAlphaImage<TinyVector<int, 2> >(info, destImage(imgview)); + importAndConvertGrayAlphaImage<int>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "FLOAT") == 0 ) { - importAndConvertGrayAlphaImage<TinyVector<float, 2> >(info, destImage(imgview)); + importAndConvertGrayAlphaImage<float>(info, destImage(imgview), pixelTypeWX); } else if (strcmp(pixelType, "DOUBLE") == 0 ) { - importAndConvertGrayAlphaImage<TinyVector<double, 2> >(info, destImage(imgview)); + importAndConvertGrayAlphaImage<double>(info, destImage(imgview), pixelTypeWX); } else { DEBUG_FATAL("Unsupported pixel type: " << pixelType); } - } else { DEBUG_ERROR("unsupported depth, only images with 1 or 3 channel images are supported."); } - } catch (vigra::PreconditionViolation & e) { + } catch (std::exception & e) { // could not load image.. wxLogError(wxString::Format(_("Error during image reading: %s"), wxString(e.what(),*wxConvCurrent).c_str())); - return new wxImage; + return 0; } #else @@ -529,26 +733,34 @@ if (m_progress) { m_progress->popTask(); } - images[filename] = image; - return image; + int mapping = wxConfigBase::Get()->Read(wxT("/ImageCache/Mapping"), HUGIN_IMGCACHE_MAPPING); + int range = wxConfigBase::Get()->Read(wxT("/ImageCache/Range"), HUGIN_IMGCACHE_RANGE); + + linear = (mapping == 0 && range == 0); + + Entry * e = new Entry(image, pixelTypeStr, linear); + images[filename] = e; + return e; } } -ImagePtr ImageCache::getSmallImage(const std::string & filename) +ImageCache::Entry * ImageCache::getSmallImage(const std::string & filename) { // softFlush(); - std::map<std::string, wxImage *>::iterator it; + std::map<std::string, Entry*>::iterator it; // "_small" is only used internally string name = filename + string("_small"); it = images.find(name); if (it != images.end()) { return it->second; } else { + wxBusyCursor wait; if (m_progress) { m_progress->pushTask(ProgressTask((const char *)wxString::Format(_("Scaling image %s"),wxString(utils::stripPath(filename).c_str(), *wxConvCurrent).c_str()).mb_str(), "", 0)); } DEBUG_DEBUG("creating small image " << name ); - ImagePtr image = getImage(filename); + Entry * entry = getImage(filename); + wxImage * image = entry->image; if (image->Ok()) { wxImage small_image; const int w = 512; @@ -556,17 +768,18 @@ small_image = image->Scale(w, (int) (w/ratio)); wxImage * tmp = new wxImage( &small_image ); - images[name] = tmp; + Entry * e = new Entry(tmp, entry->origType, entry->linear); + images[name] = e; DEBUG_INFO ( "created small image: " << name); if (m_progress) { m_progress->popTask(); } - return tmp; + return e; } else { if (m_progress) { m_progress->popTask(); } - return image; + return 0; } } } @@ -597,7 +810,7 @@ // we need to create this resolution step if (key.level == 0) { // special case, create first gray image - wxImage * srcImg = getImage(filename); + wxImage * srcImg = getImage(filename)->image; img = new vigra::BImage(srcImg->GetWidth(), srcImg->GetHeight()); DEBUG_DEBUG("creating level 0 pyramid image for "<< filename); if (m_progress) { @@ -781,7 +994,11 @@ const PanoImage & img = pano.getImage(imgNr); const PT::ImageOptions & iopts = img.getOptions(); - wxImage * src = ImageCache::getInstance().getSmallImage(img.getFilename().c_str()); + ImageCache::Entry * e = ImageCache::getInstance().getSmallImage(img.getFilename().c_str()); + if (!e) { + throw std::runtime_error("could not retrieve small source image for preview generation"); + } + wxImage * src = e->image; if (!src->Ok()) { throw std::runtime_error("could not retrieve small source image for preview generation"); } @@ -803,10 +1020,15 @@ BImage srcMask; if (iopts.m_vigCorrMode & ImageOptions::VIGCORR_FLATFIELD) { - wxImage * flatsrc = ImageCache::getInstance().getSmallImage(iopts.m_flatfield.c_str()); - if (!flatsrc->Ok()) { - throw std::runtime_error("could not retrieve flatfield image for preview generation"); - } + ImageCache::Entry * e = ImageCache::getInstance().getSmallImage(iopts.m_flatfield.c_str()); + if (!e) { + throw std::runtime_error("could not retrieve flatfield image for preview generation"); + } + + wxImage * flatsrc = e->image; + if (!flatsrc->Ok()) { + throw std::runtime_error("could not retrieve flatfield image for preview generation"); + } // image view BRGBImageView flatImgRGB((RGBValue<unsigned char> *)flatsrc->GetData(), Index: ImagesPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/ImagesPanel.cpp,v retrieving revision 1.96 retrieving revision 1.97 diff -u -d -r1.96 -r1.97 --- ImagesPanel.cpp 6 Aug 2006 14:51:10 -0000 1.96 +++ ImagesPanel.cpp 9 Aug 2006 20:55:39 -0000 1.97 @@ -515,8 +515,12 @@ if (m_showImgNr < 0 || m_showImgNr >= pano.getNrOfImages()) { return; } - const wxImage * img = ImageCache::getInstance().getSmallImage( - pano.getImage(m_showImgNr).getFilename()); + ImageCache::Entry * cacheEntry = ImageCache::getInstance().getSmallImage( + pano.getImage(m_showImgNr).getFilename()); + if (! cacheEntry) { + return; + } + const wxImage * img = cacheEntry->image; double iRatio = (double)img->GetWidth() / img->GetHeight(); Index: CPImageCtrl.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/CPImageCtrl.cpp,v retrieving revision 1.53 retrieving revision 1.54 diff -u -d -r1.53 -r1.54 --- CPImageCtrl.cpp 23 Jul 2006 11:16:08 -0000 1.53 +++ CPImageCtrl.cpp 9 Aug 2006 20:55:39 -0000 1.54 @@ -367,7 +367,11 @@ return; } // rescale image - wxImage * img = ImageCache::getInstance().getImage(imageFilename); + ImageCache::Entry * e = ImageCache::getInstance().getImage(imageFilename); + if (!e) { + return; + } + wxImage * img = e->image; imageSize = wxSize(img->GetWidth(), img->GetHeight()); m_realSize = imageSize; if (fitToWindow) { @@ -956,7 +960,7 @@ if (show) { int templSearchAreaPercent = wxConfigBase::Get()->Read(wxT("/Finetune/SearchAreaPercent"), HUGIN_FT_SEARCH_AREA_PERCENT); - wxImage * img = ImageCache::getInstance().getImage(imageFilename); + wxImage * img = ImageCache::getInstance().getImage(imageFilename)->image; m_searchRectWidth = (img->GetWidth() * templSearchAreaPercent) / 200; DEBUG_DEBUG("Setting new search area: w in %:" << templSearchAreaPercent << " bitmap width: " << bitmap.GetWidth() << " resulting size: " << m_searchRectWidth); Index: LensPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/LensPanel.cpp,v retrieving revision 1.95 retrieving revision 1.96 diff -u -d -r1.95 -r1.96 --- LensPanel.cpp 6 Aug 2006 14:51:10 -0000 1.95 +++ LensPanel.cpp 9 Aug 2006 20:55:39 -0000 1.96 @@ -921,7 +921,7 @@ const PanoImage & img = pano.getImage(imgNr); // show an image preview wxImage * wximg = ImageCache::getInstance().getImage( - img.getFilename()); + img.getFilename())->image; bool circular_crop = pano.getLens(img.getLensNr()).getProjection() == PT::Lens::CIRCULAR_FISHEYE; ImageOptions opts = img.getOptions(); Index: CPZoomDisplayPanel.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/CPZoomDisplayPanel.cpp,v retrieving revision 1.9 retrieving revision 1.10 diff -u -d -r1.9 -r1.10 --- CPZoomDisplayPanel.cpp 6 Jan 2006 16:02:34 -0000 1.9 +++ CPZoomDisplayPanel.cpp 9 Aug 2006 20:55:39 -0000 1.10 @@ -241,11 +241,12 @@ const PanoImage & img = m_pano.getImage(m_imgNr); string imageFilename = img.getFilename(); // get source image from image cache - wxImage * src = ImageCache::getInstance().getImage(imageFilename); - if (!src || !src->Ok()) { + ImageCache::Entry * e = ImageCache::getInstance().getImage(imageFilename); + if (!e) { DEBUG_ERROR("Could not load image: " << imageFilename); throw std::runtime_error("could not retrieve source image for ctrl point zoom view"); } + wxImage * src = e->image; Diff2D srcSize(src->GetWidth(), src->GetHeight()); DEBUG_DEBUG("src size: " << srcSize); Index: ImagesList.cpp =================================================================== RCS file: /cvsroot/hugin/hugin/src/hugin/ImagesList.cpp,v retrieving revision 1.36 retrieving revision 1.37 diff -u -d -r1.36 -r1.37 --- ImagesList.cpp 4 May 2006 06:25:40 -0000 1.36 +++ ImagesList.cpp 9 Aug 2006 20:55:39 -0000 1.37 @@ -167,8 +167,12 @@ void ImagesList::createIcon(wxBitmap & bitmap, unsigned int imgNr, unsigned int size) { - wxImage * s_img = ImageCache::getInstance().getSmallImage( - pano.getImage(imgNr).getFilename()); + ImageCache::Entry * cacheEntry = ImageCache::getInstance().getSmallImage( + pano.getImage(imgNr).getFilename()); + if (! cacheEntry) { + return; + } + wxImage * s_img = cacheEntry->image; if (!s_img->Ok()) { return; } |