"Fossies" - the Fresh Open Source Software Archive

Member "pfstools-2.2.0/src/pfsview/pfsview_widget.cpp" (12 Aug 2021, 22865 Bytes) of package /linux/privat/pfstools-2.2.0.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "pfsview_widget.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.1.0_vs_2.2.0.

    1 /**
    2  * @brief 
    3  * 
    4  * This file is a part of PFSTOOLS package.
    5  * ---------------------------------------------------------------------- 
    6  * Copyright (C) 2003,2004 Rafal Mantiuk and Grzegorz Krawczyk
    7  * 
    8  *  This program is free software; you can redistribute it and/or modify
    9  *  it under the terms of the GNU General Public License as published by
   10  *  the Free Software Foundation; either version 2 of the License, or
   11  *  (at your option) any later version.
   12  *
   13  *  This program is distributed in the hope that it will be useful,
   14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16  *  GNU General Public License for more details.
   17  *
   18  *  You should have received a copy of the GNU General Public License
   19  *  along with this program; if not, write to the Free Software
   20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   21  * ---------------------------------------------------------------------- 
   22  *
   23  * @author Rafal Mantiuk, <mantiuk@mpi-sb.mpg.de>
   24  *
   25  * $Id: pfsview_widget.cpp,v 1.26 2014/10/13 09:27:38 rafm Exp $
   26  */
   27 
   28 #include <config.h>
   29 #include <cmath>
   30 
   31 #include <stdio.h>
   32 #include <math.h>
   33 #include <pfs.h>
   34 
   35 #include <qmessagebox.h>
   36 #include <qcolor.h>
   37 #include <qcursor.h>
   38 #include <qapplication.h>
   39 #include <QMouseEvent>
   40 #include <QEvent>
   41 #include <QApplication>
   42 #include <QDesktopWidget>
   43 #include <QList>
   44 
   45 #include <algorithm>
   46 
   47 #include "pfsview_widget.h"
   48 
   49 //#include "pfsglviewwidget.h"
   50 
   51 
   52 #include <assert.h>
   53 
   54 #define min(x,y) ( (x)<(y) ? (x) : (y) )
   55 #define max(x,y) ( (x)>(y) ? (x) : (y) )
   56 
   57 
   58 #define D65_LUM_R 0.212656f
   59 #define D65_LUM_G 0.715158f
   60 #define D65_LUM_B 0.072186f
   61 
   62 
   63 inline float clamp( float val, float min, float max )
   64 {
   65   if( val < min ) return min;
   66   if( val > max ) return max;
   67   return val;
   68 }
   69 
   70 inline int clamp( int val, int min, int max )
   71 {
   72   if( val < min ) return min;
   73   if( val > max ) return max;
   74   return val;
   75 }
   76 
   77 
   78 const char* COLOR_CHANNELS = "Color";
   79 
   80 PFSViewWidgetArea::PFSViewWidgetArea( QWidget *parent ) :
   81   QScrollArea( parent )
   82 {
   83   PFSViewWidget *pfsview = new PFSViewWidget( this );
   84   setWidget( pfsview );
   85   setWidgetResizable( true );
   86   
   87   setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred );  
   88 }
   89 
   90 QSize PFSViewWidgetArea::sizeHint() const
   91 {
   92   QSize sz = widget()->sizeHint();
   93 //  printf( "widget: %d %d\n", sz.width(), sz.height() );
   94   sz += QSize( frameWidth()*2, frameWidth()*2 );  
   95 //  printf( "area: %d %d\n", sz.width(), sz.height() );
   96   return sz;
   97 }
   98 
   99 
  100 PFSViewWidget::PFSViewWidget( QWidget *parent ) :
  101   QWidget( parent ), image( NULL ),
  102   minValue( 1.f ), maxValue( 100.f ), zoom( 1.f ),
  103   mappingMethod( MAP_GAMMA2_2 ), visibleChannel( COLOR_CHANNELS ),
  104   negativeTreatment( NEGATIVE_BLACK ), infNaNTreatment( INFNAN_MARK_AS_RED ),
  105   updateMappingRequested( true ), clippingMethod(CLIP_SIMPLE)
  106 {
  107 
  108   fit_to_content_mode = true;  
  109   setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
  110   
  111   
  112   setMouseTracking( true );
  113   setCursor( QCursor( Qt::CrossCursor ) );
  114 
  115 //name, Qt::WNoAutoErase
  116   
  117 //  setResizePolicy( Manual );
  118   
  119   pfsFrame = NULL;
  120   workArea[0] = workArea[1] = workArea[2] = NULL;
  121 
  122   selection += QRegion( 10, 10, 100, 100 );
  123 
  124   pointerValue.valid = false;    
  125   
  126 }
  127 
  128 PFSViewWidget::~PFSViewWidget()
  129 {
  130   delete pfsFrame;
  131   delete workArea[0];
  132   delete workArea[1];
  133   delete workArea[2];
  134 }
  135 
  136 // ======================= Set current frame  ==========================
  137 
  138 void PFSViewWidget::setFrame( pfs::Frame *frame )
  139 {  
  140   pfsFrame = frame;    
  141   if( frame == NULL ) return;
  142   
  143   // If selected channel not available
  144   if( ( visibleChannel == COLOR_CHANNELS && !hasColorChannels( frame ) ) ||
  145     ( visibleChannel != COLOR_CHANNELS && frame->getChannel( visibleChannel ) == NULL ) ) 
  146   {
  147     // Chose first available channel
  148     pfs::ChannelIterator *it = frame->getChannels();
  149     if( !it->hasNext() )      // TODO: failover
  150       throw new pfs::Exception( "No channels available!" );
  151     visibleChannel = it->getNext()->getName();
  152   } else if( visibleChannel != COLOR_CHANNELS ) {
  153     // Get a new pointer, as the old frame object
  154     // can be delete after returning from this method
  155     visibleChannel = frame->getChannel( visibleChannel )->getName(); 
  156   }
  157 
  158   if( fit_to_content_mode ) {    
  159     int desktopWidth, desktopHeight;
  160     desktopWidth = QApplication::desktop()->width();
  161     desktopHeight = QApplication::desktop()->height();
  162     if( frame->getWidth() > desktopWidth ||
  163       frame->getHeight() > desktopHeight ) {
  164       zoom = min( (float)desktopWidth / (float)frame->getWidth(),
  165         (float)desktopHeight / (float)frame->getHeight() );
  166     }  
  167   }
  168   updateZoom();
  169   
  170   setPointer();  
  171 }
  172 
  173 //void PFSViewWidget::drawContents( QPainter * p, int clipx, int clipy, int clipw, int cliph )
  174 
  175 void PFSViewWidget::paintEvent( QPaintEvent *event )
  176 {
  177   assert( pfsFrame != NULL );
  178 
  179   QRect clip = event->rect();
  180   int clipx = clip.x();
  181   int clipy = clip.y();
  182   int clipw = clip.width();
  183   int cliph = clip.height();
  184   
  185   
  186   if( updateMappingRequested )
  187     updateMapping();
  188   
  189   QPainter p( this );
  190 //  if( image != NULL ) p->drawImage( 0, 0, *image );
  191   if( image != NULL ) {
  192       p.drawImage( clipx, clipy, *image, clipx, clipy, clipw, cliph );
  193 
  194     //Erase area outside image
  195     if( clipx+clipw > image->width() )
  196       p.eraseRect( image->width(), 0, clipx+clipw - image->width(), image->height() );
  197     if( clipy+cliph > image->height() )
  198       p.eraseRect( 0, image->height(), image->width(), clipy+cliph - image->height() );
  199     if( clipx+clipw > image->width() && clipy+cliph > image->height() )
  200       p.eraseRect( image->width(), image->height(),
  201         clipx+clipw - image->width(), clipy+cliph - image->height() );
  202     
  203   }
  204   
  205   
  206 }
  207 
  208 // ======================= Update ===========================
  209 
  210 static void scaleCopyArray(const pfs::Array2D *from, pfs::Array2D *to)
  211 {
  212   assert( from->getRows() >= to->getRows() );
  213   assert( from->getCols() >= to->getCols() );
  214   
  215   float sx, sy;
  216   float dx = (float)from->getCols() / (float)to->getCols();
  217   float dy = (float)from->getRows() / (float)to->getRows();
  218 
  219   int x, y, i = 0;
  220   for( sy = 0, y = 0; y < to->getRows(); y++, sy += dy ) {
  221     for( sx = 0, x = 0; x < to->getCols(); x++, sx += dx ) {
  222       (*to)(i++) = (*from)((int)(sx), (int)(sy) );
  223     }
  224   }
  225   
  226 }
  227 
  228 static void transformImageToWorkArea( pfs::Array2D **workArea, float zoom, bool color,
  229   pfs::Array2D *X, pfs::Array2D *Y = NULL, pfs::Array2D *Z = NULL )
  230 {
  231   int origCols, origRows;
  232   origCols = X->getCols();
  233   origRows = X->getRows();
  234 
  235   int workCols, workRows;
  236   workCols = min( (int)((float)X->getCols() * zoom), X->getCols() );
  237   workRows = min( (int)((float)X->getRows() * zoom), X->getRows() );
  238 
  239   int requiredChannels = color ? 3 : 1;
  240   
  241   // Reallocate work area arrays to fit new size
  242   {
  243     for( int c = 0; c < requiredChannels; c++ ) {
  244       if( workArea[c] == NULL || workArea[c]->getCols() != workCols ||
  245         workArea[c]->getRows() != workRows ) {
  246         if( workArea[c] != NULL ) delete workArea[c];
  247         workArea[c] = new pfs::Array2DImpl( workCols, workRows );        
  248       }
  249     }
  250   }
  251 
  252   //Copy | rescale & tranform image to work area
  253   if( color )
  254   {
  255     if( workCols == origCols && workRows == origRows ) {
  256       copyArray( X, workArea[0] );
  257       copyArray( Y, workArea[1] );
  258       copyArray( Z, workArea[2] );
  259     } else {
  260       scaleCopyArray( X, workArea[0] );
  261       scaleCopyArray( Y, workArea[1] );
  262       scaleCopyArray( Z, workArea[2] );
  263     } 
  264     pfs::transformColorSpace( pfs::CS_XYZ, workArea[0], workArea[1], workArea[2],
  265       pfs::CS_RGB, workArea[0], workArea[1], workArea[2] );
  266   } else {
  267     if( workCols == origCols && workRows == origRows ) 
  268       copyArray( X, workArea[0] );
  269     else 
  270       scaleCopyArray( X, workArea[0] );
  271   }
  272   
  273 }
  274 
  275 #define LUTSIZE 256
  276 
  277 inline int binarySearchPixels( float x, const float *lut, const int lutSize )
  278 {
  279   int l = 0, r = lutSize;
  280   while( true ) {
  281     int m = (l+r)/2;
  282     if( m == l ) break;
  283     if( x < lut[m] )
  284       r = m;
  285     else
  286       l = m;
  287   }
  288   return l;
  289 }
  290 
  291 static float getInverseMapping( LumMappingMethod mappingMethod,
  292   float v, float minValue, float maxValue )
  293 {
  294   switch( mappingMethod ) {
  295   case MAP_GAMMA1_4:
  296     return powf( v, 1.4 )*(maxValue-minValue) + minValue;
  297   case MAP_GAMMA1_8:
  298     return powf( v, 1.8 )*(maxValue-minValue) + minValue;
  299   case MAP_GAMMA2_2:
  300     return powf( v, 2.2 )*(maxValue-minValue) + minValue;
  301   case MAP_GAMMA2_6:
  302     return powf( v, 2.6 )*(maxValue-minValue) + minValue;
  303   case MAP_LINEAR:
  304     return v*(maxValue-minValue) + minValue;
  305   case MAP_LOGARITHMIC:
  306     return powf( 10, v * (log10f(maxValue) - log10f(minValue)) + log10f( minValue ) );
  307   default:
  308     assert(0);
  309     return 0;
  310   }
  311 }
  312 
  313 /**
  314  * Copy pixels in work area to QImage using proper clipping and mapping
  315  * method. Handle also expansion if image in zoomed in.
  316  */
  317 static void mapFrameToImage( pfs::Array2D *R, pfs::Array2D *G, pfs::Array2D *B, 
  318   QImage *img,
  319   float minValue, float maxValue,
  320   RGBClippingMethod clippingMethod,
  321   LumMappingMethod mappingMethod,
  322   InfNaNTreatment infNaNTreatment,
  323   NegativeTreatment negativeTreatment )
  324 {
  325   assert( R != NULL );
  326   assert( img != NULL );
  327   int rows = R->getRows();
  328   int cols = R->getCols();
  329   bool expand = cols != img->width() || rows != img->height(); // Zoom in
  330 
  331   float imgDeltaRow, imgDeltaCol;
  332   if( expand ) {
  333     imgDeltaRow = (float)(img->height()) / (float)rows;
  334     imgDeltaCol = (float)(img->width()) / (float)cols;
  335   } else {
  336     imgDeltaRow = imgDeltaCol = 1;
  337   }
  338 
  339   bool color = (G != NULL);
  340   assert( !color || (color && B != NULL) );
  341 
  342   
  343   float lutPixFloor[257*2];
  344   QRgb lutPixel[257*2];
  345   int lutSize;
  346   if( !color && ( negativeTreatment == NEGATIVE_GREEN_SCALE ||
  347         negativeTreatment == NEGATIVE_ABSOLUTE ) ) { // Handle negative numbers
  348     lutSize = 257*2+1;
  349     for( int p = 256; p >= 0; p-- ) {
  350       float p_left = (float)p/255.f;
  351       lutPixFloor[256-p+1] = -getInverseMapping( mappingMethod, p_left, minValue, maxValue );
  352       if( p != 0 ) lutPixel[256-p+1] = negativeTreatment == NEGATIVE_GREEN_SCALE ?
  353                      QColor( 0, p-1, 0 ).rgb() : QColor( p-1, p-1, p-1 ).rgb();
  354     }
  355     for( int p = 0; p < 257; p++ ) {
  356       float p_left = (float)p/255.f;
  357       lutPixFloor[257+p+1] = getInverseMapping( mappingMethod, p_left, minValue, maxValue );
  358       if( p != 256 ) lutPixel[257+p+1] = QColor( p, p, p ).rgb();
  359     }
  360 
  361     if( clippingMethod == CLIP_COLORCODED ) {
  362       lutPixel[0] = negativeTreatment == NEGATIVE_GREEN_SCALE ?
  363         QColor( 0, 255, 255 ).rgb() : QColor( 255, 255, 0 ).rgb();
  364       lutPixel[257] = QColor( 0, 0, 255 ).rgb();
  365       lutPixel[257*2-1] = QColor( 255, 255, 0 ).rgb();
  366     } else {
  367       lutPixel[0] = lutPixel[1];
  368       lutPixel[257] = QColor( 0, 0, 0 ).rgb();
  369       lutPixel[257*2-1] = QColor( 255, 255, 255 ).rgb();
  370     }  
  371     
  372   } else {                      // clip negative numbers
  373     int neg_offset = ((!color && negativeTreatment == NEGATIVE_MARK_AS_RED) ? 1 : 0);
  374     lutSize = 257+1 + neg_offset;            // +1 - for lower bound
  375     for( int p = 1+neg_offset; p <= LUTSIZE+1; p++ ) {
  376       float p_left = ((float)p - 1.f - (float)neg_offset)/255.f; // Should be -1.5f, but we don't want negative nums
  377       lutPixFloor[p] = getInverseMapping( mappingMethod, p_left, minValue, maxValue );
  378       if( !color && p < LUTSIZE+1+neg_offset ) lutPixel[p] = QColor( p-1-neg_offset, p-1-neg_offset, p-1-neg_offset ).rgb();      
  379 //      printf( "p = %d\tl = %g\n", p, lookupTable[p] );
  380     }
  381     if( clippingMethod == CLIP_COLORCODED ) {
  382       lutPixel[neg_offset] = QColor( 0, 0, 255 ).rgb();
  383       lutPixel[LUTSIZE+1+neg_offset] = QColor( 255, 255, 0 ).rgb();
  384     } else {
  385       lutPixel[neg_offset] = QColor( 0, 0, 0 ).rgb();
  386       lutPixel[LUTSIZE+1+neg_offset] = QColor( 255, 255, 255 ).rgb();
  387     }
  388     if( negativeTreatment == NEGATIVE_MARK_AS_RED && !color) {
  389       lutPixFloor[1] = 0;
  390       lutPixel[0] = QColor( 255, 0, 0 ).rgb();
  391     }
  392   }
  393 
  394 //  bool once = true;
  395   
  396  // #pragma omp parallel for private (index)
  397   #pragma omp parallel for
  398   for( int r = 0; r < rows; r++ ) {
  399     float imgRow, imgCol;
  400     int index = r*cols;
  401     imgRow = (float)r * imgDeltaRow;
  402     QRgb* line = (QRgb*)img->scanLine( (int)imgRow );
  403     imgCol = 0;
  404     for( int c = 0; c < cols; c++, index++, imgCol += imgDeltaCol ) {
  405       QRgb pixel;
  406       if( color ) {
  407         // Color channels
  408         int pr, pg, pb;
  409         pr = binarySearchPixels( (*R)(index), lutPixFloor, lutSize );        
  410         pg = binarySearchPixels( (*G)(index), lutPixFloor, lutSize );        
  411         pb = binarySearchPixels( (*B)(index), lutPixFloor, lutSize );        
  412 
  413         // Clipping
  414         if( clippingMethod == CLIP_COLORCODED  ) {
  415           if( pr == 0 || pg == 0 || pb == 0 ) 
  416             pixel = lutPixel[0];
  417           else if( pr == lutSize-1 || pg == lutSize-1 || pb == lutSize-1 )
  418             pixel = lutPixel[LUTSIZE+1];
  419           else
  420             pixel = QColor( pr-1, pg-1, pb-1 ).rgb();
  421         } else if( clippingMethod == CLIP_KEEP_BRI_HUE ) {
  422           if( pr == lutSize-1 || pg == lutSize-1 || pb == lutSize-1 ||
  423             pr == 0 || pg == 0 || pb == 0 ) {
  424             float p[3];
  425             p[0] = (*R)(index); p[1] = (*G)(index); p[2] = (*B)(index);
  426             float gray = (p[0]+p[1]+p[2])/3.f;
  427 //            float gray = D65_LUM_R*p[0]+D65_LUM_G*p[1]+D65_LUM_B*p[2];
  428             float t;
  429 
  430             if( gray >= maxValue ) {
  431               pixel = QColor( 255, 255, 255 ).rgb();
  432             } else if( gray <= minValue ) {
  433               pixel = QColor( 0, 0, 0 ).rgb();
  434             } else {
  435               int i;
  436               for( i = 0; i < 3; i++ ) {
  437                 t = (maxValue - p[i])/(gray - p[i]);             
  438                 if( t >= 0 && t <= 1 ) {
  439                   break;
  440                 }              
  441                 t = (minValue - p[i])/(gray - p[i]);             
  442                 if( t >= 0 && t <= 1 ) {
  443                   break;
  444                 }              
  445               }
  446             
  447               if( i == 3 ) 
  448                 pixel = QColor( 255, 255, 255 ).rgb();
  449               else {
  450                 for( int i = 0; i < 3; i++ )
  451                   p[i] = gray*t + p[i]*(1-t);
  452                 
  453 //               if( once ) {
  454 //                 printf( "min = %g max = %g\n", minValue, maxValue );
  455 //                 printf( "r = %g g = %g b = %g; t = %g\n", p[0], p[1], p[2], t );
  456 //                 once = false;
  457 //               }              
  458               
  459                 pr = binarySearchPixels( p[0], lutPixFloor, lutSize );        
  460                 pg = binarySearchPixels( p[1], lutPixFloor, lutSize );        
  461                 pb = binarySearchPixels( p[2], lutPixFloor, lutSize );
  462                 pixel = QColor( clamp( pr-1, 0, 255 ),
  463                   clamp( pg-1, 0, 255 ),
  464                   clamp( pb-1, 0, 255 ) ).rgb();
  465               }
  466             }  
  467           } else {
  468             pixel = QColor( clamp( pr-1, 0, 255 ),
  469               clamp( pg-1, 0, 255 ),
  470               clamp( pb-1, 0, 255 ) ).rgb();
  471           }
  472         } else {
  473           pixel = QColor( clamp( pr-1, 0, 255 ),
  474             clamp( pg-1, 0, 255 ),
  475             clamp( pb-1, 0, 255 ) ).rgb();
  476         }
  477         if( infNaNTreatment == INFNAN_MARK_AS_RED ) {
  478           if( !std::isfinite( (*R)(index) ) || !std::isfinite( (*G)(index) ) || !std::isfinite( (*B)(index) ) )
  479           {   // x is NaN or Inf 
  480             pixel = QColor( 255, 0, 0 ).rgb();
  481           }
  482         }
  483         if( negativeTreatment == NEGATIVE_MARK_AS_RED ) {
  484           if( (*R)(index)<0 || (*G)(index)<0 || (*B)(index)<0 )
  485           {   // x is negative
  486             pixel = QColor( 255, 0, 0 ).rgb();
  487           }
  488         }
  489         
  490       } else {
  491         // Single channel
  492         int p = binarySearchPixels( (*R)(index), lutPixFloor, lutSize );        
  493         pixel = lutPixel[p];
  494         if( infNaNTreatment == INFNAN_MARK_AS_RED && (p == 0 || p == LUTSIZE+1)) 
  495           if( !std::isfinite( (*R)(index) ) ) {   // x is NaN or Inf 
  496             pixel = QColor( 255, 0, 0 ).rgb();
  497           }
  498         
  499       }
  500       
  501       line[(int)imgCol] = pixel;
  502 
  503       if( expand ) {
  504         for( int ec = (int)imgCol + 1; ec < (int)(imgCol+imgDeltaCol); ec++ ) {
  505           line[ec] = pixel;
  506         }
  507       }
  508           
  509     }
  510 
  511     if( expand ) {
  512       for( int er = (int)(imgRow + 1); er < (int)(imgRow + imgDeltaRow); er++ ) {
  513         QRgb* eLine = (QRgb*)img->scanLine( er );
  514         memcpy( eLine, line, sizeof( QRgb )*img->width() );
  515       }
  516       
  517     }
  518 
  519   }
  520   
  521 }
  522 
  523 void PFSViewWidget::postUpdateMapping()
  524 {
  525   updateMappingRequested = true;
  526 
  527   update( 0, 0, image->width(), image->height() );
  528   //updateContents( 0, 0, image->width(), image->height() );
  529 }
  530 
  531 
  532 void PFSViewWidget::updateMapping()
  533 { 
  534   assert( image != NULL );
  535 
  536   QApplication::setOverrideCursor( Qt::WaitCursor );
  537   
  538   if( visibleChannel == COLOR_CHANNELS ) {
  539     
  540     assert( workArea[0] != NULL && workArea[1] != NULL && workArea[2] != NULL );
  541     mapFrameToImage( workArea[0], workArea[1], workArea[2], image,
  542       minValue, maxValue, clippingMethod, mappingMethod,
  543       infNaNTreatment, negativeTreatment );
  544     
  545   } else {
  546     
  547     assert( workArea[0] != NULL );
  548     mapFrameToImage( workArea[0], NULL, NULL, image,
  549       minValue, maxValue, clippingMethod, mappingMethod,
  550       infNaNTreatment, negativeTreatment );
  551   }  
  552   updateMappingRequested = false;
  553 
  554   QApplication::restoreOverrideCursor();
  555 }
  556 
  557 void PFSViewWidget::updateZoom()
  558 {
  559   assert( pfsFrame != NULL );
  560 
  561   QApplication::setOverrideCursor( Qt::WaitCursor );
  562   
  563   if( visibleChannel == COLOR_CHANNELS ) {
  564     pfs::Channel *X, *Y, *Z;
  565     pfsFrame->getXYZChannels( X, Y, Z );
  566     transformImageToWorkArea( workArea, zoom, true, X, Y, Z );
  567   } else {
  568     pfs::Channel *X = pfsFrame->getChannel( visibleChannel );
  569     transformImageToWorkArea( workArea, zoom, false, X );    
  570   }  
  571 
  572   int zoomedWidth = max( workArea[0]->getCols(), (int)((float)(pfsFrame->getWidth())*zoom) );
  573   int zoomedHeight = max( workArea[0]->getRows(), (int)((float)(pfsFrame->getHeight())*zoom) );    
  574   
  575   if( image != NULL ) delete image;
  576   image = new QImage( zoomedWidth, zoomedHeight, QImage::Format_RGB32 );
  577   assert( image != NULL );
  578   
  579   postUpdateMapping();
  580 
  581   resize( zoomedWidth, zoomedHeight );
  582   
  583 //  resizeContents( zoomedWidth, zoomedHeight );
  584 //  updateContents( 0, 0, zoomedWidth, zoomedHeight );
  585   update( 0, 0, zoomedWidth, zoomedHeight );
  586 
  587   QApplication::restoreOverrideCursor();  
  588 }
  589 
  590 QSize PFSViewWidget::sizeHint() const
  591 {
  592   if( pfsFrame != NULL ) 
  593     return QSize( (int)((float)pfsFrame->getWidth()*zoom), (int)((float)pfsFrame->getHeight()*zoom)  );
  594 
  595 //    return QSize( (int)((float)pfsFrame->getWidth()*zoom) + 2 * frameWidth(), (int)((float)pfsFrame->getHeight()*zoom) + 2 * frameWidth() );
  596 
  597   //    return QSize( pfsFrame->getWidth() + 2 * frameWidth(), pfsFrame->getHeight() + 2 * frameWidth() );
  598   else return QWidget::sizeHint();
  599 }
  600 
  601 // ======================= Events ===========================
  602 
  603 void PFSViewWidget::setRGBClippingMethod( QAction *action )
  604 {
  605   clippingMethod = (RGBClippingMethod)action->data().toUInt();
  606   postUpdateMapping();
  607 }
  608 
  609 void PFSViewWidget::setInfNaNTreatment( QAction *action )
  610 {
  611   infNaNTreatment = (InfNaNTreatment)action->data().toUInt();
  612   postUpdateMapping();
  613 }
  614 
  615 void PFSViewWidget::setNegativeTreatment( QAction *action )
  616 {
  617   negativeTreatment = (NegativeTreatment)action->data().toUInt();
  618   postUpdateMapping();
  619 }
  620 
  621 void PFSViewWidget::setLumMappingMethod( int method )
  622 {
  623   mappingMethod = (LumMappingMethod)method;
  624   postUpdateMapping();
  625 }
  626 
  627 
  628 
  629 void PFSViewWidget::zoomIn()
  630 {
  631   if( zoom >= 10 ) return;
  632   zoom *= 1.25f;
  633   fit_to_content_mode = false;  
  634   updateZoom();
  635 }
  636 
  637 
  638 void PFSViewWidget::zoomOut()
  639 {
  640   if( zoom <= 0.05 ) return;
  641   zoom *= 0.8f;
  642   fit_to_content_mode = false;  
  643   updateZoom();
  644 }
  645 
  646 void PFSViewWidget::zoomOriginal()
  647 {
  648   zoom = 1;
  649   fit_to_content_mode = false;  
  650   updateZoom();  
  651 }
  652 
  653 void PFSViewWidget::setRangeWindow( float min, float max )
  654 {
  655   minValue = min;
  656   maxValue = max;
  657   postUpdateMapping();
  658 }
  659 
  660 
  661 // ===================== Mouse interaction =========================
  662 
  663 void PFSViewWidget::mouseMoveEvent( QMouseEvent *mouseEvent )
  664 {
  665   assert( pfsFrame != NULL );
  666 
  667   setPointer( (int)((float)mouseEvent->x() / zoom),
  668     (int)((float)mouseEvent->y() / zoom) );  
  669 }
  670 
  671 void PFSViewWidget::setPointer( int x, int y )
  672 {
  673   assert( pfsFrame != NULL );
  674 
  675   if( x >= 0 ) {    
  676     pointerValue.x = x;
  677     pointerValue.y = y;
  678   }
  679   
  680   if( pointerValue.x >= 0 && pointerValue.x < pfsFrame->getWidth() &&
  681     pointerValue.y >= 0 && pointerValue.y < pfsFrame->getHeight() ) {
  682 
  683     if( visibleChannel == COLOR_CHANNELS ) {
  684       
  685       pfs::Channel *X, *Y, *Z;
  686       pfsFrame->getXYZChannels( X, Y, Z );
  687     
  688       pointerValue.value[0] = (*X)( pointerValue.x, pointerValue.y );
  689       pointerValue.value[1] = (*Y)( pointerValue.x, pointerValue.y );
  690       pointerValue.value[2] = (*Z)( pointerValue.x, pointerValue.y );
  691 
  692       pointerValue.valuesUsed = 3;
  693       pointerValue.valid = true;
  694 
  695     } else {
  696 
  697       pfs::Channel *X;
  698       X = pfsFrame->getChannel( visibleChannel );
  699     
  700       pointerValue.value[0] = (*X)( pointerValue.x, pointerValue.y );
  701 
  702       pointerValue.valuesUsed = 1;
  703       pointerValue.valid = true;      
  704       
  705     }
  706     
  707     updatePointerValue();
  708     
  709   } else {
  710     pointerValue.valid = false;    
  711     updatePointerValue();
  712   }
  713   
  714   
  715 }
  716 
  717 void PFSViewWidget::leaveEvent ( QEvent * )
  718 {
  719     pointerValue.valid = false;    
  720     updatePointerValue();
  721 }
  722 
  723 const PointerValue &PFSViewWidget::getPointerValue()
  724 {
  725   return pointerValue;
  726 }
  727 
  728 
  729 // ===================== Data access =========================
  730 
  731 
  732 const pfs::Array2D *PFSViewWidget::getPrimaryChannel()
  733 {
  734   assert( pfsFrame != NULL );
  735   if( visibleChannel == COLOR_CHANNELS ) {
  736     pfs::Channel *X, *Y, *Z;
  737     pfsFrame->getXYZChannels( X, Y, Z );
  738     return Y;
  739   } else {
  740     return pfsFrame->getChannel( visibleChannel );
  741   }
  742   
  743 }
  744 
  745 const QList<const char*> PFSViewWidget::getChannels()
  746 {
  747   assert( pfsFrame != NULL );
  748   
  749   QList<const char*> chArray;
  750 
  751   pfs::ChannelIterator *it = pfsFrame->getChannels();
  752   it = pfsFrame->getChannels();
  753   while( it->hasNext() )
  754   {
  755     pfs::Channel *ch = it->getNext();
  756     chArray.push_back(ch->getName());
  757   }
  758 
  759   return chArray;
  760 }
  761 
  762 void PFSViewWidget::setVisibleChannel( const char *channelName )
  763 {
  764   visibleChannelName = channelName;
  765   
  766   visibleChannel = channelName != NULL ? (const char*)visibleChannelName : COLOR_CHANNELS;
  767   
  768   updateZoom();
  769   setPointer();
  770 }
  771 
  772 const char *PFSViewWidget::getVisibleChannel()
  773 {
  774   return visibleChannel;
  775 }
  776 
  777 bool PFSViewWidget::hasColorChannels( pfs::Frame *frame )
  778 {
  779   if( frame == NULL ) frame = pfsFrame;
  780   assert( frame != NULL );
  781   pfs::Channel *X, *Y, *Z;
  782   frame->getXYZChannels( X, Y, Z );
  783   return ( X != NULL );
  784 }
  785