CPImageCtrl.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00027 // standard wx include
00028 #include <config.h>
00029 #include "panoinc_WX.h"
00030 
00031 // standard hugin include
00032 #include "panoinc.h"
00033 #include "base_wx/platform.h"
00034 
00035 #include <vigra/inspectimage.hxx>
00036 #include <vigra/transformimage.hxx>
00037 #include <vigra/basicimageview.hxx>
00038 #ifdef HAVE_CXX11
00039 #include <functional>  // std::bind 
00040 #else
00041 #include <boost/bind.hpp>
00042 #endif
00043 
00044 #include "hugin/config_defaults.h"
00045 #include "hugin/CPImageCtrl.h"
00046 #include "base_wx/wxImageCache.h"
00047 #include "hugin/CPEditorPanel.h"
00048 #include "hugin/MainFrame.h"
00049 #include "hugin/huginApp.h"
00050 #include "base_wx/wxcms.h"
00051 
00052 #include "vigra_ext/ImageTransforms.h"
00053 
00054 using namespace std;
00055 using namespace hugin_utils;
00056 
00057 #if !wxCHECK_VERSION(3,0,0)
00058 #define wxPENSTYLE_SOLID wxSOLID
00059 #define wxPENSTYLE_DOT wxDOT
00060 #define wxBRUSHSTYLE_TRANSPARENT wxTRANSPARENT
00061 #define wxBRUSHSTYLE_SOLID wxSOLID
00062 #endif
00063 
00064 // definition of the control point event
00065 
00066 IMPLEMENT_DYNAMIC_CLASS( CPEvent, wxEvent )
00067 #if _WINDOWS && defined Hugin_shared
00068 DEFINE_LOCAL_EVENT_TYPE( EVT_CPEVENT )
00069 #else
00070 DEFINE_EVENT_TYPE( EVT_CPEVENT )
00071 #endif
00072 
00073 CPEvent::CPEvent( )
00074 {
00075     SetEventType( EVT_CPEVENT );
00076     SetEventObject( (wxWindow *) NULL );
00077     mode = NONE;
00078 }
00079 
00080 CPEvent::CPEvent(wxWindow* win, FDiff2D & p)
00081 {
00082     SetEventType( EVT_CPEVENT );
00083     SetEventObject( win );
00084     mode = NEW_POINT_CHANGED;
00085     point = p;
00086 }
00087 
00088 CPEvent::CPEvent(wxWindow *win, unsigned int cpNr)
00089 {
00090     SetEventType( EVT_CPEVENT );
00091     SetEventObject( win );
00092     mode = POINT_SELECTED;
00093     pointNr = cpNr;
00094 }
00095 
00096 CPEvent::CPEvent(wxWindow* win, unsigned int cpNr, const FDiff2D & p)
00097 {
00098     SetEventType( EVT_CPEVENT );
00099     SetEventObject( win );
00100     mode = POINT_CHANGED;
00101     pointNr = cpNr;
00102     point = p;
00103 }
00104 
00105 CPEvent::CPEvent(wxWindow* win, const hugin_utils::FDiff2D & p1, const hugin_utils::FDiff2D & p2)
00106 {
00107     SetEventType(EVT_CPEVENT);
00108     SetEventObject(win);
00109     mode=DELETE_REGION_SELECTED;
00110     region=wxRect(roundi(min(p1.x,p2.x)),roundi(min(p1.y,p2.y)),abs(roundi(p2.x-p1.x)),abs(roundi(p2.y-p1.y)));
00111 };
00112 
00113 CPEvent::CPEvent(wxWindow* win, CPEventMode evt_mode, const FDiff2D & p)
00114 {
00115     SetEventType(EVT_CPEVENT);
00116     SetEventObject(win);
00117     mode = evt_mode;
00118     point = p;
00119 }
00120 
00121 CPEvent::CPEvent(wxWindow* win, CPEventMode evt_mode, const HuginBase::ControlPoint cp)
00122 {
00123     SetEventType(EVT_CPEVENT);
00124     SetEventObject(win);
00125     mode=evt_mode;
00126     m_cp=cp;
00127 };
00128 
00129 CPEvent::CPEvent(wxWindow* win, CPEventMode evt_mode, size_t cpNr, const HuginBase::ControlPoint cp)
00130 {
00131     SetEventType(EVT_CPEVENT);
00132     SetEventObject(win);
00133     mode=evt_mode;
00134     pointNr=cpNr;
00135     m_cp=cp;
00136 };
00137 
00138 wxEvent * CPEvent::Clone() const
00139 {
00140     return new CPEvent(*this);
00141 }
00142 
00143 DisplayedControlPoint::DisplayedControlPoint(const HuginBase::ControlPoint& cp, CPImageCtrl* control, bool mirrored)
00144 {
00145     m_cp=cp;
00146     m_control=control;
00147     m_mirrored=mirrored;
00148     m_line=(m_cp.mode!=HuginBase::ControlPoint::X_Y) && (m_cp.image1Nr==m_cp.image2Nr);
00149 };
00150 
00151 void DisplayedControlPoint::SetColour(wxColour pointColour, wxColour textColour)
00152 {
00153     m_pointColour=pointColour;
00154     m_textColour=textColour;
00155 };
00156 
00157 void DisplayedControlPoint::SetLabel(wxString newLabel)
00158 {
00159     m_label=newLabel;
00160 };
00161 
00162 void DisplayedControlPoint::SetControl(CPImageCtrl* control)
00163 {
00164     m_control=control;
00165 };
00166 
00167 void DrawCross(wxDC& dc, wxPoint p, int l)
00168 {
00169     dc.DrawLine(p + wxPoint(-l, 0),
00170                 p + wxPoint(-1, 0));
00171     dc.DrawLine(p + wxPoint(2, 0),
00172                 p + wxPoint(l+1, 0));
00173     dc.DrawLine(p + wxPoint(0, -l),
00174                 p + wxPoint(0, -1));
00175     dc.DrawLine(p + wxPoint(0, 2),
00176                 p + wxPoint(0, l+1));
00177 };
00178 
00179 void DisplayedControlPoint::Draw(wxDC& dc, bool selected, bool newPoint)
00180 {
00181     if(m_control==NULL)
00182     {
00183         return;
00184     };
00185     // select color
00186     wxColour bgColor = m_pointColour;
00187     wxColour textColor = m_textColour;
00188     bool drawMag = false;
00189     if (selected)
00190     {
00191         bgColor = wxTheColourDatabase->Find(wxT("RED"));
00192         textColor = wxTheColourDatabase->Find(wxT("WHITE"));
00193         drawMag = !m_control->GetMouseInWindow() || m_control->GetForceMagnifier();
00194     }
00195     if (newPoint)
00196     {
00197         bgColor = wxTheColourDatabase->Find(wxT("YELLOW"));
00198         textColor = wxTheColourDatabase->Find(wxT("BLACK"));
00199         drawMag = true;
00200     }
00201 
00202     dc.SetPen(wxPen(wxT("WHITE"), 1, wxPENSTYLE_SOLID));
00203     dc.SetBrush(wxBrush(wxT("BLACK"),wxBRUSHSTYLE_TRANSPARENT));
00204 
00205     FDiff2D pointInput=m_mirrored ? hugin_utils::FDiff2D(m_cp.x2, m_cp.y2) : hugin_utils::FDiff2D(m_cp.x1, m_cp.y1);
00206     FDiff2D point = m_control->applyRot(pointInput);
00207     wxPoint p = m_control->roundP(m_control->scale(point));
00208     FDiff2D pointInput2;
00209     wxPoint p2;
00210     if(m_line)
00211     {
00212         pointInput2=m_mirrored ? hugin_utils::FDiff2D(m_cp.x1, m_cp.y1) : hugin_utils::FDiff2D(m_cp.x2, m_cp.y2);
00213         FDiff2D point2 = m_control->applyRot(pointInput2);
00214         p2 = m_control->roundP(m_control->scale(point2));
00215     };
00216     int l = 6;
00217     // draw cursor line, choose white or black
00218     vigra::Rect2D box;
00219     if(m_line)
00220     {
00221         box.setUpperLeft(vigra::Point2D(roundi(std::min(m_cp.x1, m_cp.x2))-l, roundi(std::min(m_cp.y1, m_cp.y2))-l));
00222         box.setSize(roundi(abs(m_cp.x1-m_cp.x2)+2.0*l), roundi(abs(m_cp.y1-m_cp.y2)+2.0*l)); 
00223     }
00224     else
00225     {
00226         box.setUpperLeft(vigra::Point2D(roundi(pointInput.x-l), roundi(pointInput.y-l)));
00227         box.setSize(2*l, 2*l);
00228     };                
00229     // only use part inside.
00230     box &= vigra::Rect2D(m_control->GetImg()->size());
00231     if(box.width()<=0 || box.height()<=0)
00232     {
00233         return;
00234     };
00235     // calculate mean "luminance value"
00236     vigra::FindAverage<vigra::UInt8> average;   // init functor
00237     vigra::RGBToGrayAccessor<vigra::RGBValue<vigra::UInt8> > lumac;
00238     vigra::inspectImage(m_control->GetImg()->upperLeft()+ box.upperLeft(),
00239                         m_control->GetImg()->upperLeft()+ box.lowerRight(),
00240                         lumac, average);
00241     if (average() < 150)
00242     {
00243         dc.SetPen(wxPen(wxT("WHITE"), 1, wxPENSTYLE_SOLID));
00244     }
00245     else
00246     {
00247         dc.SetPen(wxPen(wxT("BLACK"), 1, wxPENSTYLE_SOLID));
00248     }
00249 
00250     if(m_line)
00251     {
00252         DrawLine(dc);
00253         DrawCross(dc, p, l);
00254         DrawCross(dc, p2, l);
00255     }
00256     else
00257     {
00258         if (m_cp.mode != HuginBase::ControlPoint::X_Y)
00259         {
00260             DrawLineSegment(dc);
00261         };
00262         DrawCross(dc, p, l);
00263     };
00264     // calculate distance to the image boundaries,
00265     // decide where to put the label and magnifier
00266     m_labelPos=DrawTextMag(dc, p, pointInput, drawMag, textColor, bgColor);
00267     if(m_line)
00268     {
00269         m_labelPos2=DrawTextMag(dc, p2, pointInput2, drawMag, textColor, bgColor);
00270     };
00271 }
00272 
00273 wxRect DisplayedControlPoint::DrawTextMag(wxDC& dc, wxPoint p, hugin_utils::FDiff2D pointInput, bool drawMag, wxColour textColour, wxColour bgColour)
00274 {
00275     wxRect labelPos;
00276     int l = 6;
00277     wxSize clientSize = m_control->GetClientSize();
00278     int vx0, vy0;
00279     m_control->GetViewStart(&vx0, &vy0);
00280     wxFont font(8, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT);
00281     dc.SetFont(font);
00282     wxPoint pClient(p.x - vx0, p.y - vy0);
00283     // space in upper left, upper right, lower left, lower right
00284     int maxDistUR = std::min(clientSize.x - pClient.x, pClient.y);
00285     int maxDistLL = std::min(pClient.x, clientSize.y - pClient.y);
00286     int maxDistLR = std::min(clientSize.x - pClient.x, clientSize.y - pClient.y);
00287 
00288     // text and magnifier offset
00289     int toff = l-1;
00290     // default to lower right
00291     wxPoint tul = p + wxPoint(toff,toff);
00292 
00293     // calculate text position and extend
00294     // width of border around text label
00295     int tB = 2;
00296     wxCoord tw, th;
00297     dc.GetTextExtent(m_label, &tw, &th);
00298 
00299     if (drawMag)
00300     {
00301         wxBitmap magBitmap = m_control->generateMagBitmap(pointInput, p);
00302         // TODO: select position depending on visible part of canvas
00303         wxPoint ulMag = tul;
00304         // choose placement of the magnifier
00305         int w = toff  + magBitmap.GetWidth()+3;
00306         int db = 5;
00307         if ( maxDistLR > w + db  )
00308         {
00309             ulMag = p + wxPoint(toff,toff);
00310         }
00311         else
00312         {
00313             if (maxDistLL > w + db)
00314             {
00315                 ulMag = p + wxPoint(-w, toff);
00316             }
00317             else
00318             {
00319                 if (maxDistUR > w + db)
00320                 {
00321                     ulMag = p + wxPoint(toff, -w);
00322                 }
00323                 else
00324                 {
00325                     ulMag = p + wxPoint(-w, -w);
00326                 }
00327             }
00328         };
00329 
00330         dc.DrawBitmap(magBitmap, ulMag);
00331         dc.SetPen(wxPen(wxT("BLACK"), 1, wxPENSTYLE_SOLID));
00332         dc.SetBrush(wxBrush(wxT("WHITE"),wxBRUSHSTYLE_TRANSPARENT));
00333 
00334         // draw Bevel
00335         int bw = magBitmap.GetWidth();
00336         int bh = magBitmap.GetHeight();
00337         dc.DrawLine(ulMag.x-1, ulMag.y+bh, 
00338                     ulMag.x+bw+1, ulMag.y+bh);
00339         dc.DrawLine(ulMag.x+bw, ulMag.y+bh, 
00340                     ulMag.x+bw, ulMag.y-2);
00341         dc.SetPen(wxPen(wxT("WHITE"), 1, wxPENSTYLE_SOLID));
00342         dc.DrawLine(ulMag.x-1, ulMag.y-1, 
00343                     ulMag.x+bw+1, ulMag.y-1);
00344         dc.DrawLine(ulMag.x-1, ulMag.y+bh, 
00345                     ulMag.x-1, ulMag.y-2);
00346     }
00347     // choose placement of text.
00348     int db = 5;
00349     int w = toff+tw+2*tB;
00350     if ( maxDistLR > w + db && (!drawMag) )
00351     {
00352         tul = p + wxPoint(toff,toff);
00353     }
00354     else
00355     {
00356         if (maxDistLL > w + db)
00357         {
00358             tul = p + wxPoint(-w, toff);
00359         }
00360         else
00361         {
00362             if (maxDistUR > w + db)
00363             {
00364                 tul = p + wxPoint(toff, -(toff) - (th+2*tB));
00365             }
00366             else
00367             {
00368                 tul = p + wxPoint(-w, -(toff) - (th+2*tB));
00369             };
00370         };
00371     };
00372 
00373     // draw background
00374     dc.SetPen(wxPen(textColour, 1, wxPENSTYLE_SOLID));
00375     dc.SetBrush(wxBrush(bgColour, wxBRUSHSTYLE_SOLID));
00376     dc.DrawRectangle(tul.x, tul.y, tw+2*tB+1, th+2*tB);
00377     labelPos.SetLeft(tul.x);
00378     labelPos.SetTop(tul.y);
00379     labelPos.SetWidth(tw+2*tB+1);
00380     labelPos.SetHeight(th+2*tB);
00381     // draw number
00382     dc.SetTextForeground(textColour);
00383     dc.DrawText(m_label, tul + wxPoint(tB,tB));
00384     return labelPos;
00385 };
00386 
00387 void DisplayedControlPoint::DrawLine(wxDC& dc)
00388 {
00389     if(m_control==NULL)
00390     {
00391         return;
00392     };
00393     FDiff2D p1, p2, dp;
00394     // transform end points to pano space
00395     if(!m_control->getFirstInvTrans()->transformImgCoord(p1, hugin_utils::FDiff2D(m_cp.x1, m_cp.y1)))
00396     {
00397         return;
00398     };
00399     if(!m_control->getFirstInvTrans()->transformImgCoord(p2, hugin_utils::FDiff2D(m_cp.x2, m_cp.y2)))
00400     {
00401         return;
00402     };
00403     dp=p2-p1;
00404     int len=roundi(sqrt((m_cp.x1-m_cp.x2)*(m_cp.x1-m_cp.x2)+(m_cp.y1-m_cp.y2)*(m_cp.y1-m_cp.y2)))+1;
00405     if(len<5)
00406     {
00407         //very short line, draw straight line
00408         dc.DrawLine(m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x1, m_cp.y1)))),
00409             m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x2, m_cp.y2)))));
00410     }
00411     else
00412     {
00413         //longer line, draw correct line, taking output projection into account
00414         wxPoint* points=new wxPoint[len+1];
00415         for(size_t i=0; i<len+1; i++)
00416         {
00417             FDiff2D p=p1+dp*((double)i/len);
00418             // transform line coordinates back to image space
00419             if(!m_control->getFirstTrans()->transformImgCoord(p2, p))
00420             {
00421                 delete []points;
00422                 //fall through, draw direct line, not exact, but better than no line
00423                 dc.DrawLine(m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x1, m_cp.y1)))),
00424                     m_control->roundP(m_control->scale(m_control->applyRot(hugin_utils::FDiff2D(m_cp.x2, m_cp.y2)))));
00425                 return;
00426             };
00427             points[i]=m_control->roundP(m_control->scale(m_control->applyRot(p2)));
00428         };
00429         dc.DrawLines(len+1, points);
00430         delete []points;
00431     };
00432 };
00433 
00434 void DisplayedControlPoint::DrawLineSegment(wxDC& dc)
00435 {
00436     if(m_control==NULL)
00437     {
00438         return;
00439     };
00440     // calculate line equation
00441     FDiff2D p1_image=m_mirrored ? hugin_utils::FDiff2D(m_cp.x2, m_cp.y2) : hugin_utils::FDiff2D(m_cp.x1, m_cp.y1);
00442     FDiff2D p2_image=m_mirrored ? hugin_utils::FDiff2D(m_cp.x1, m_cp.y1) : hugin_utils::FDiff2D(m_cp.x2, m_cp.y2);
00443     FDiff2D p1, p2, dp;
00444     if(!m_control->getFirstInvTrans()->transformImgCoord(p1, p1_image))
00445     {
00446         return;
00447     };
00448     if(!m_control->getSecondInvTrans()->transformImgCoord(p2, p2_image))
00449     {
00450         return;
00451     };
00452     dp=p2-p1;
00453     // now find the parameter to draw an appropriate long line segment
00454     double f=1.0;
00455     int image_width=m_control->GetRealImageSize().GetWidth();
00456     int image_height=m_control->GetRealImageSize().GetHeight();
00457     int image_dimension=std::min(image_width, image_height);
00458     int line_length=-1;
00459     while(f>1e-4)
00460     {
00461         p2=p1+dp*f;
00462         if(m_control->getFirstTrans()->transformImgCoord(p2_image, p2))
00463         {
00464             double length=sqrt(p1_image.squareDistance(p2_image));
00465             if(length > 0.05f * image_dimension && length < 0.75f * image_dimension)
00466             {
00467                 line_length=hugin_utils::roundi(length);
00468                 break;
00469             };
00470         };
00471         f*=0.9;
00472     };
00473     // found no suitable length, don't draw line
00474     if(line_length<1)
00475     {
00476         return;
00477     };
00478     // now calc line positions
00479     wxPoint* points=new wxPoint[2*line_length+1];
00480     for(size_t i=0; i<2*line_length+1; i++)
00481     {
00482         FDiff2D p=p1+dp*f*(((double)i-line_length)/(2.0f*line_length));
00483         if(!m_control->getFirstTrans()->transformImgCoord(p2, p))
00484         {
00485             delete []points;
00486             return;
00487         };
00488         points[i]=m_control->roundP(m_control->scale(m_control->applyRot(p2)));
00489     };
00490     //and finally draw line segment
00491     dc.SetClippingRegion(wxPoint(0,0), m_control->GetBitmapSize());
00492     dc.DrawLines(2*line_length+1, points);
00493     dc.DestroyClippingRegion();
00494     delete []points;
00495 };
00496 
00497 const bool DisplayedControlPoint::isOccupiedLabel(const wxPoint mousePos) const
00498 {
00499     if(m_line)
00500     {
00501         return m_labelPos.Contains(mousePos) || m_labelPos2.Contains(mousePos);
00502     }
00503     else
00504     {
00505         return m_labelPos.Contains(mousePos);
00506     };
00507 };
00508 
00509 const bool DisplayedControlPoint::isOccupiedPos(const hugin_utils::FDiff2D &p) const
00510 {
00511     double d=m_control->invScale(3.0);
00512     if(m_line)
00513     {
00514         return (p.x < m_cp.x1 + d && p.x > m_cp.x1 - d && p.y < m_cp.y1 + d && p.y > m_cp.y1 - d) ||
00515                (p.x < m_cp.x2 + d && p.x > m_cp.x2 - d && p.y < m_cp.y2 + d && p.y > m_cp.y2 - d);
00516     }
00517     else
00518     {
00519         if(m_mirrored)
00520         {
00521             return (p.x < m_cp.x2 + d && p.x > m_cp.x2 - d && p.y < m_cp.y2 + d && p.y > m_cp.y2 - d);
00522         }
00523         else
00524         {
00525             return (p.x < m_cp.x1 + d && p.x > m_cp.x1 - d && p.y < m_cp.y1 + d && p.y > m_cp.y1 - d);
00526         };
00527     };
00528 };
00529 
00530 void DisplayedControlPoint::CheckSelection(const wxPoint mousePos, const hugin_utils::FDiff2D& p)
00531 {
00532     if(!m_line)
00533     {
00534         return;
00535     };
00536     double d=m_control->invScale(3.0);
00537     m_mirrored=m_labelPos2.Contains(mousePos) || 
00538         (p.x < m_cp.x2 + d && p.x > m_cp.x2 - d && p.y < m_cp.y2 + d && p.y > m_cp.y2 - d);
00539 };
00540 
00541 void DisplayedControlPoint::UpdateControlPointX(double x)
00542 {
00543     if(m_mirrored)
00544     {
00545         m_cp.x2=x;
00546     }
00547     else
00548     {
00549         m_cp.x1=x;
00550     };
00551 };
00552 
00553 void DisplayedControlPoint::UpdateControlPointY(double y)
00554 {
00555     if(m_mirrored)
00556     {
00557         m_cp.y2=y;
00558     }
00559     else
00560     {
00561         m_cp.y1=y;
00562     };
00563 };
00564 
00565 void DisplayedControlPoint::UpdateControlPoint(hugin_utils::FDiff2D newPoint)
00566 {
00567     if(m_mirrored)
00568     {
00569         m_cp.x2=newPoint.x;
00570         m_cp.y2=newPoint.y;
00571     }
00572     else
00573     {
00574         m_cp.x1=newPoint.x;
00575         m_cp.y1=newPoint.y;
00576     };
00577 };
00578 
00579 void DisplayedControlPoint::ShiftControlPoint(hugin_utils::FDiff2D shift)
00580 {
00581     if(m_mirrored)
00582     {
00583         m_cp.x2+=shift.x;
00584         m_cp.y2+=shift.y;
00585     }
00586     else
00587     {
00588         m_cp.x1+=shift.x;
00589         m_cp.y1+=shift.y;
00590     };
00591 };
00592 
00593 void DisplayedControlPoint::StartLineControlPoint(hugin_utils::FDiff2D newPoint)
00594 {
00595     //start a new line control point
00596     m_line=true;
00597     m_mirrored=true;
00598     m_label=_("new");
00599     m_cp.image1Nr=UINT_MAX;
00600     m_cp.x1=newPoint.x;
00601     m_cp.y1=newPoint.y;
00602     m_cp.image2Nr=m_cp.image1Nr;
00603     m_cp.x2=m_cp.x1;
00604     m_cp.y2=m_cp.y1;
00605     m_cp.mode = HuginBase::ControlPoint::X;
00606 };
00607 
00608 hugin_utils::FDiff2D DisplayedControlPoint::GetPos() const
00609 {
00610     return m_mirrored ? FDiff2D(m_cp.x2, m_cp.y2) : FDiff2D(m_cp.x1, m_cp.y1);
00611 };
00612 
00613 bool DisplayedControlPoint::operator==(const DisplayedControlPoint other)
00614 {
00615     return m_cp==other.GetControlPoint() && m_mirrored == other.IsMirrored() && m_label == other.GetLabel();
00616 };
00617 
00618 // our image control
00619 BEGIN_EVENT_TABLE(CPImageCtrl, wxScrolledWindow)
00620     EVT_SIZE(CPImageCtrl::OnSize)
00621     EVT_CHAR(CPImageCtrl::OnKey)
00622 //    EVT_KEY_UP(CPImageCtrl::OnKeyUp)
00623     EVT_KEY_DOWN(CPImageCtrl::OnKeyDown)
00624     EVT_LEAVE_WINDOW(CPImageCtrl::OnMouseLeave)
00625     EVT_ENTER_WINDOW(CPImageCtrl::OnMouseEnter)
00626     EVT_MOTION(CPImageCtrl::mouseMoveEvent)
00627     EVT_LEFT_DOWN(CPImageCtrl::mousePressLMBEvent)
00628     EVT_LEFT_UP(CPImageCtrl::mouseReleaseLMBEvent)
00629     EVT_RIGHT_DOWN(CPImageCtrl::mousePressRMBEvent)
00630     EVT_RIGHT_UP(CPImageCtrl::mouseReleaseRMBEvent)
00631     EVT_MIDDLE_DOWN(CPImageCtrl::mousePressMMBEvent)
00632     EVT_MIDDLE_UP(CPImageCtrl::mouseReleaseMMBEvent)
00633     EVT_TIMER(-1, CPImageCtrl::OnTimer)
00634 END_EVENT_TABLE()
00635 
00636 bool CPImageCtrl::Create(wxWindow * parent, wxWindowID id,
00637                          const wxPoint& pos,
00638                          const wxSize& size,
00639                          long style,
00640                          const wxString& name)
00641 {
00642     wxScrolledWindow::Create(parent, id, pos, size, style, name);
00643     selectedPointNr = 0;
00644     editState = NO_IMAGE;
00645     scaleFactor = 1;
00646     fitToWindow = false;
00647     m_showSearchArea = false;
00648     m_searchRectWidth = 0;
00649     m_showTemplateArea = false;
00650     m_templateRectWidth = 0;
00651     m_editPanel = 0;
00652     m_imgRotation = ROT0;
00653     m_sameImage = false;
00654 
00655     wxString filename;
00656 
00657 #if defined(__WXMSW__) 
00658     wxString cursorPath = huginApp::Get()->GetXRCPath() + wxT("/data/cursor_cp_pick.cur");
00659     m_CPSelectCursor = new wxCursor(cursorPath, wxBITMAP_TYPE_CUR);
00660 #else
00661     m_CPSelectCursor = new wxCursor(wxCURSOR_CROSS);
00662 #endif
00663     SetCursor(*m_CPSelectCursor);
00664 
00665     // TODO: define custom, light background colors.
00666     pointColors.push_back(wxTheColourDatabase->Find(wxT("BLUE")));
00667     textColours.push_back(wxTheColourDatabase->Find(wxT("WHITE")));
00668 
00669     pointColors.push_back(wxTheColourDatabase->Find(wxT("GREEN")));
00670     textColours.push_back(wxTheColourDatabase->Find(wxT("WHITE")));
00671 
00672     pointColors.push_back(wxTheColourDatabase->Find(wxT("CYAN")));
00673     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00674     pointColors.push_back(wxTheColourDatabase->Find(wxT("GOLD")));
00675     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00676 
00677     pointColors.push_back(wxTheColourDatabase->Find(wxT("NAVY")));
00678     textColours.push_back(wxTheColourDatabase->Find(wxT("WHITE")));
00679 
00680     pointColors.push_back(wxTheColourDatabase->Find(wxT("DARK TURQUOISE")));
00681     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00682 
00683     pointColors.push_back(wxTheColourDatabase->Find(wxT("SALMON")));
00684     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00685 
00686     pointColors.push_back(wxTheColourDatabase->Find(wxT("MAROON")));
00687     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00688 
00689     pointColors.push_back(wxTheColourDatabase->Find(wxT("KHAKI")));
00690     textColours.push_back(wxTheColourDatabase->Find(wxT("BLACK")));
00691 
00692     m_searchRectWidth = 120;
00693     m_mouseInWindow = false;
00694     m_forceMagnifier = false;
00695     m_timer.SetOwner(this);
00696 
00697     return true;
00698 }
00699 
00700 void CPImageCtrl::Init(CPEditorPanel * parent)
00701 {
00702     m_editPanel = parent;
00703     m_sameImage = false;
00704 }
00705 
00706 CPImageCtrl::~CPImageCtrl()
00707 {
00708     DEBUG_TRACE("dtor");
00709     this->SetCursor(wxNullCursor);
00710     delete m_CPSelectCursor;
00711     DEBUG_TRACE("dtor end");
00712 }
00713 
00714 void CPImageCtrl::OnDraw(wxDC & dc)
00715 {
00716     wxSize vSize = GetClientSize();
00717     // draw image (FIXME, redraw only visible regions.)
00718     if (editState != NO_IMAGE && m_img.get()) {
00719                 //clear the blank rectangle to the left of the image
00720         if (bitmap.GetWidth() < vSize.GetWidth()) {
00721             dc.SetPen(wxPen(GetBackgroundColour(), 1, wxPENSTYLE_SOLID));
00722             dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
00723             dc.DrawRectangle(bitmap.GetWidth(), 0,
00724                              vSize.GetWidth() - bitmap.GetWidth(),vSize.GetHeight());
00725         }
00726                 //clear the blank rectangle below the image
00727         if (bitmap.GetHeight() < vSize.GetHeight()) {
00728             dc.SetPen(wxPen(GetBackgroundColour(), 1, wxPENSTYLE_SOLID));
00729             dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
00730                         dc.DrawRectangle(0, bitmap.GetHeight(),
00731                              vSize.GetWidth(), vSize.GetHeight() - bitmap.GetHeight());
00732         }
00733         dc.DrawBitmap(bitmap,0,0);
00734         } else {
00735                 // clear the rectangle and exit
00736         dc.SetPen(wxPen(GetBackgroundColour(), 1, wxPENSTYLE_SOLID));
00737         dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
00738         dc.Clear();
00739                 return;
00740         }
00741 
00742     // draw known points.
00743     for(size_t i=0; i<m_points.size(); i++)
00744     {
00745         if (!(editState == KNOWN_POINT_SELECTED && i==selectedPointNr))
00746         {
00747             m_points[i].Draw(dc, false);
00748         };
00749     }
00750 
00751     switch(editState) {
00752     case NEW_POINT_SELECTED:
00753         // Boundary check
00754         if ((newPoint.x < 0) || (newPoint.y < 0)) {
00755             // Tried to create a point outside of the canvas.  Ignore it.
00756             break;
00757         } 
00758         {
00759             DisplayedControlPoint dsp(HuginBase::ControlPoint(0, newPoint.x, newPoint.y, 0, 0, 0), this, false);
00760             dsp.SetLabel(_("new"));
00761             dsp.Draw(dc, false, true);
00762         }
00763         if (m_showTemplateArea) {
00764             dc.SetLogicalFunction(wxINVERT);
00765             dc.SetPen(wxPen(wxT("RED"), 1, wxPENSTYLE_SOLID));
00766             dc.SetBrush(wxBrush(wxT("WHITE"), wxBRUSHSTYLE_TRANSPARENT));
00767             wxPoint upperLeft = applyRot(roundP(newPoint));
00768             upperLeft = scale(upperLeft);
00769 
00770             int width = scale(m_templateRectWidth);
00771 
00772             dc.DrawRectangle(upperLeft.x-width, upperLeft.y-width, 2*width, 2*width);
00773             dc.SetLogicalFunction(wxCOPY);
00774         }
00775 
00776         break;
00777     case NEW_LINE_CREATING:
00778         m_selectedPoint.Draw(dc, false, true);
00779         break;
00780     case KNOWN_POINT_SELECTED:
00781         m_points[selectedPointNr].Draw(dc, true);
00782         break;
00783     case NO_SELECTION:
00784     case NO_IMAGE:
00785         break;
00786     }
00787 
00788     if (m_showSearchArea && m_mousePos.x != -1){
00789         dc.SetLogicalFunction(wxINVERT);
00790         dc.SetPen(wxPen(wxT("WHITE"), 1, wxPENSTYLE_SOLID));
00791         dc.SetBrush(wxBrush(wxT("WHITE"), wxBRUSHSTYLE_TRANSPARENT));
00792 
00793         FDiff2D upperLeft = applyRot(m_mousePos);
00794         upperLeft = scale(upperLeft);
00795         int width = scale(m_searchRectWidth);
00796         DEBUG_DEBUG("drawing rect " << upperLeft << " with width " << 2*width << " orig: " << m_searchRectWidth*2  << " scale factor: " << getScaleFactor());
00797 
00798         dc.DrawRectangle(roundi(upperLeft.x - width), roundi(upperLeft.y-width), 2*width, 2*width);
00799         dc.SetLogicalFunction(wxCOPY);
00800     }
00801 }
00802 
00803 class ScalingTransform
00804 {
00805 public:
00806     explicit ScalingTransform(double scale)
00807     : m_scale(scale) {};
00808 
00809     bool transformImgCoord(double & sx, double & sy, double x, double y)
00810     {
00811         sx = m_scale*x;
00812         sy = m_scale*y;
00813         return true;
00814     }
00815 
00816     double m_scale;
00817 };
00818 
00819 wxBitmap CPImageCtrl::generateMagBitmap(FDiff2D point, wxPoint canvasPos) const
00820 {
00821     typedef vigra::RGBValue<vigra::UInt8> VT;
00822     DEBUG_TRACE("")
00823 
00824     // draw magnified image (TODO: warp!)
00825     double magScale = 3.0;
00826     wxConfigBase::Get()->Read(wxT("/CPEditorPanel/MagnifierScale"), &magScale);
00827     // width (and height) of magnifier region (output), should be odd
00828     int magWidth = wxConfigBase::Get()->Read(wxT("/CPEditorPanel/MagnifierWidth"),61l);
00829     int hw = magWidth/2;
00830     magWidth = hw*2+1;
00831 
00832     // setup simple scaling transformation function.
00833     ScalingTransform transform(1.0/magScale);
00834     ScalingTransform invTransform(magScale);
00835     wxImage img(magWidth, magWidth);
00836     vigra::BasicImageView<VT> magImg((VT*)img.GetData(), magWidth,magWidth);
00837     vigra::BImage maskImg(magWidth, magWidth);
00838     vigra_ext::PassThroughFunctor<vigra::UInt8> ptf;
00839 
00840 
00841     // middle pixel
00842     double mx, my;
00843     invTransform.transformImgCoord(mx, my, point.x, point.y);
00844 
00845     // apply the transform
00846     AppBase::DummyProgressDisplay progDisp;
00847     vigra_ext::transformImageIntern(vigra::srcImageRange(*(m_img->image8)),
00848                          vigra::destImageRange(magImg),
00849                          vigra::destImage(maskImg),
00850                          transform,
00851                          ptf,
00852                          vigra::Diff2D(hugin_utils::roundi(mx - hw),
00853                                        hugin_utils::roundi(my - hw)),
00854                          vigra_ext::interp_cubic(),
00855                          false,
00856                          &progDisp,
00857                          false);
00858 
00859     // TODO: contrast enhancement
00860     vigra::FindMinMax<vigra::UInt8> minmax;
00861     vigra::inspectImage(vigra::srcImageRange(magImg), minmax);
00862 
00863     // transform to range 0...255
00864     vigra::transformImage(vigra::srcImageRange(magImg), vigra::destImage(magImg),
00865                           vigra::linearRangeMapping(
00866                             VT(minmax.min), VT(minmax.max),               // src range
00867                             VT(0, 0, 0), VT(255, 255, 255)) // dest range
00868                           );
00869 //    vigra::transformImage(srcImageRange(magImg), destImage(magImg),
00870 //       vigra::BrightnessContrastFunctor<float>(brightness, contrast, minmax.min, minmax.max));
00871 
00872     // draw cursor
00873     for(int x=0; x < magWidth; x++) {
00874         VT p =magImg(x,hw+1);
00875         vigra::UInt8 v = 0.3/255*p.red() + 0.6/255*p.green() + 0.1/255*p.blue() < 0.5 ? 255 : 0;
00876         p[0] = v;
00877         p[1] = v;
00878         p[2] = v;
00879         magImg(x,hw+1) = p;
00880         p = magImg(hw+1, x);
00881         v = 0.3/255*p.red() + 0.6/255*p.green() + 0.1/255*p.blue() < 0.5 ? 255 : 0;
00882         p[0] = v;
00883         p[1] = v;
00884         p[2] = v;
00885         magImg(hw+1, x) = p;
00886     }
00887 
00888     // rotate image according to current display
00889     switch(m_imgRotation) {
00890         case ROT90:
00891             img = img.Rotate90(true);
00892             break;
00893         case ROT180:
00894             // this is slower than it needs to be...
00895             img = img.Rotate90(true);
00896             img = img.Rotate90(true);
00897             break;
00898         case ROT270:
00899             img = img.Rotate90(false);
00900             break;
00901         default:
00902             break;
00903     }
00904     return wxBitmap (img);
00905 }
00906 
00907 wxSize CPImageCtrl::DoGetBestSize() const
00908 {
00909     return wxSize(imageSize.GetWidth(),imageSize.GetHeight());
00910 }
00911 
00912 
00913 void CPImageCtrl::setImage(const std::string & file, ImageRotation imgRot)
00914 {
00915     DEBUG_TRACE("setting Image " << file);
00916     imageFilename = file;
00917     m_sameImage=false;
00918     wxString fn(imageFilename.c_str(),HUGIN_CONV_FILENAME);
00919     if (wxFileName::FileExists(fn)) {
00920         m_imgRotation = imgRot;
00921         m_img = ImageCache::getInstance().getImageIfAvailable(imageFilename);
00922         editState = NO_SELECTION;
00923         if (m_img.get()) {
00924             rescaleImage();
00925         } else {
00926             // load the image in the background.
00927             m_imgRequest = ImageCache::getInstance().requestAsyncImage(imageFilename);
00928             m_imgRequest->ready.push_back(
00929 #ifdef HAVE_CXX11
00930                 std::bind(&CPImageCtrl::OnImageLoaded, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)
00931 #else
00932                 boost::bind(&CPImageCtrl::OnImageLoaded, this, _1, _2, _3)
00933 #endif
00934                 );
00935             // With m_img.get() 0, everything will act as normal except drawing.
00936         }
00937     } else {
00938         editState = NO_IMAGE;
00939         bitmap = wxBitmap();
00940         // delete the image (release shared_ptr)
00941         // create an empty image.
00942         m_img = ImageCache::EntryPtr(new ImageCache::Entry);
00943     }
00944 }
00945 
00946 void CPImageCtrl::setTransforms(HuginBase::PTools::Transform* firstTrans, HuginBase::PTools::Transform* firstInvTrans, HuginBase::PTools::Transform* secondInvTrans)
00947 {
00948     m_firstTrans=firstTrans;
00949     m_firstInvTrans=firstInvTrans;
00950     m_secondInvTrans=secondInvTrans;
00951 };
00952 
00953 void CPImageCtrl::setSameImage(bool sameImage)
00954 {
00955     m_sameImage=sameImage;
00956 };
00957 
00958 void CPImageCtrl::OnImageLoaded(ImageCache::EntryPtr entry, std::string filename, bool load_small)
00959 {
00960     // check we are still displaying this image
00961     if (imageFilename == filename)
00962     {
00963         m_img = entry;
00964         rescaleImage();
00965     }
00966 }
00967 
00968 void CPImageCtrl::rescaleImage()
00969 {
00970     if (editState == NO_IMAGE || !m_img.get()) {
00971         return;
00972     }
00973     wxImage img = imageCacheEntry2wxImage(m_img);
00974     if (img.GetWidth() == 0) {
00975         return;
00976     }
00977     imageSize = wxSize(img.GetWidth(), img.GetHeight());
00978     m_realSize = imageSize;
00979     if (fitToWindow) {
00980         scaleFactor = calcAutoScaleFactor(imageSize);
00981     }
00982     DEBUG_DEBUG("src image size "
00983                 << imageSize.GetHeight() << "x" << imageSize.GetWidth());
00984     if (getScaleFactor() == 1.0) {
00985         //the icc correction would work on the original cached image file
00986         //therefore we need to create a copy to work on it
00987         img = img.Copy();
00988     }
00989     else
00990     {
00991         // rescale image
00992         imageSize.SetWidth(scale(imageSize.GetWidth()));
00993         imageSize.SetHeight(scale(imageSize.GetHeight()));
00994         DEBUG_DEBUG("rescaling to " << imageSize.GetWidth() << "x"
00995             << imageSize.GetHeight());
00996         img = img.Scale(imageSize.GetWidth(), imageSize.GetHeight());
00997     };
00998     // need to rotate full image. warning. this can be very memory intensive
00999     if (m_imgRotation != ROT0)
01000     {
01001         switch (m_imgRotation)
01002         {
01003             case ROT90:
01004                 img = img.Rotate90(true);
01005                 break;
01006             case ROT180:
01007                 // this is slower than it needs to be...
01008                 img = img.Rotate90(true);
01009                 img = img.Rotate90(true);
01010                 break;
01011             case ROT270:
01012                 img = img.Rotate90(false);
01013                 break;
01014             default:
01015                 break;
01016         };
01017     };
01018     // do color correction only if input image has icc profile or if we found a monitor profile
01019     if (!m_img->iccProfile->empty() || huginApp::Get()->HasMonitorProfile())
01020     {
01021         HuginBase::Color::CorrectImage(img, *(m_img->iccProfile), huginApp::Get()->GetMonitorProfile());
01022     };
01023     bitmap = wxBitmap(img);
01024 
01025     if (m_imgRotation == ROT90 || m_imgRotation == ROT270) {
01026         SetVirtualSize(imageSize.GetHeight(), imageSize.GetWidth());
01027     } else {
01028         SetVirtualSize(imageSize.GetWidth(), imageSize.GetHeight());
01029     }
01030     SetScrollRate(1,1);
01031     Refresh(FALSE);
01032 }
01033 
01034 void CPImageCtrl::setCtrlPoint(const HuginBase::ControlPoint& cp, const bool mirrored)
01035 {
01036     DisplayedControlPoint dcp(cp, this, mirrored);
01037     dcp.SetColour(pointColors[m_points.size() % pointColors.size()], textColours[m_points.size() % textColours.size()]);
01038     dcp.SetLabel(wxString::Format(wxT("%lu"), (unsigned long int)m_points.size()));
01039     m_points.push_back(dcp);
01040 }
01041 
01042 void CPImageCtrl::clearCtrlPointList()
01043 {
01044     m_points.clear();
01045     if(editState == KNOWN_POINT_SELECTED)
01046     {
01047         editState = NO_SELECTION;
01048     };
01049     selectedPointNr = UINT_MAX;
01050 };
01051 
01052 void CPImageCtrl::clearNewPoint()
01053 {
01054     DEBUG_TRACE("clearNewPoint");
01055     if (editState != NO_IMAGE) {
01056         editState = NO_SELECTION;
01057     }
01058 }
01059 
01060 
01061 void CPImageCtrl::selectPoint(unsigned int nr)
01062 {
01063     DEBUG_TRACE("nr: " << nr);
01064     if (nr < m_points.size()) {
01065         selectedPointNr = nr;
01066         editState = KNOWN_POINT_SELECTED;
01067         showPosition(m_points[nr].GetPos());
01068         update();
01069     } else {
01070         DEBUG_DEBUG("trying to select invalid point nr: " << nr << ". Nr of points: " << m_points.size());
01071     }
01072 }
01073 
01074 void CPImageCtrl::deselect()
01075 {
01076     DEBUG_TRACE("deselecting points");
01077     if (editState == KNOWN_POINT_SELECTED) {
01078         editState = NO_SELECTION;
01079     }
01080     // update view
01081     update();
01082 }
01083 
01084 void CPImageCtrl::showPosition(FDiff2D point, bool warpPointer)
01085 {
01086     DEBUG_DEBUG("x: " << point.x  << " y: " << point.y);
01087     // transform and scale the co-ordinate to the screen.
01088     point = applyRot(point);
01089     point = scale(point);
01090     int x = roundi(point.x);
01091     int y = roundi(point.y);
01092 
01093     wxSize sz = GetClientSize();
01094     int scrollx = x - sz.GetWidth()/2;
01095     int scrolly = y - sz.GetHeight()/2;
01096     Scroll(scrollx, scrolly);
01097     if (warpPointer) {
01098         int sx,sy;
01099         GetViewStart(&sx, &sy);
01100         DEBUG_DEBUG("relative coordinages: " << x-sx << "," << y-sy);
01101         WarpPointer(x-sx,y-sy);
01102     }
01103 }
01104 
01105 CPImageCtrl::EditorState CPImageCtrl::isOccupied(wxPoint mousePos, const FDiff2D &p, unsigned int & pointNr) const
01106 {
01107     // check if mouse is hovering over a label
01108     if(m_points.size()>0)
01109     {
01110         for(int i=m_points.size()-1; i>=0; i--)
01111         {
01112             if(m_points[i].isOccupiedLabel(mousePos))
01113             {
01114                 pointNr = i;
01115                 return KNOWN_POINT_SELECTED;
01116             }
01117         };
01118         // check if mouse is over a known point
01119         for(vector<DisplayedControlPoint>::const_iterator it=m_points.begin(); it!=m_points.end(); ++it)
01120         {
01121             if(it->isOccupiedPos(p))
01122             {
01123                 pointNr = it - m_points.begin();
01124                 return KNOWN_POINT_SELECTED;
01125             }
01126         };
01127     };
01128 
01129     return NEW_POINT_SELECTED;
01130 }
01131 
01132 void CPImageCtrl::DrawSelectionRectangle(hugin_utils::FDiff2D pos1,hugin_utils::FDiff2D pos2)
01133 {
01134     wxClientDC dc(this);
01135     PrepareDC(dc);
01136     dc.SetLogicalFunction(wxINVERT);
01137     dc.SetPen(wxPen(*wxWHITE,1, wxPENSTYLE_DOT));
01138     dc.SetBrush(*wxTRANSPARENT_BRUSH);
01139     wxPoint p1=roundP(scale(applyRot(pos1)));
01140     wxPoint p2=roundP(scale(applyRot(pos2)));
01141     dc.DrawRectangle(p1.x,p1.y,p2.x-p1.x,p2.y-p1.y);
01142 };
01143 
01144 void CPImageCtrl::mouseMoveEvent(wxMouseEvent& mouse)
01145 {
01146     if (!m_img.get()) return; // ignore events if no image loaded.
01147     wxPoint unScrolledMousePos;
01148     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01149                            &unScrolledMousePos.x, & unScrolledMousePos.y);
01150     FDiff2D mpos(unScrolledMousePos.x, unScrolledMousePos.y);
01151     bool doUpdate = false;
01152     mpos = applyRotInv(invScale(mpos));
01153     wxPoint mpos_ = applyRotInv(invScale(unScrolledMousePos));
01154     // if mouseclick is out of image, ignore
01155     if ((mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) && editState!=SELECT_DELETE_REGION)
01156     {
01157         return;
01158     }
01159 
01160 //    DEBUG_DEBUG(" pos:" << mpos.x << ", " << mpos.y);
01161     // only if the shift key is not pressed.
01162     if (mouse.LeftIsDown() && ! mouse.ShiftDown()) {
01163         switch(editState) {
01164         case NO_SELECTION:
01165             DEBUG_DEBUG("mouse down movement without selection, in NO_SELECTION state!");
01166             break;
01167         case KNOWN_POINT_SELECTED:
01168             if (mpos.x >= 0 && mpos.x <= m_realSize.GetWidth()){
01169                 m_points[selectedPointNr].UpdateControlPointX(mpos.x);
01170             } else if (mpos.x < 0) {
01171                 m_points[selectedPointNr].UpdateControlPointX(0);
01172             } else if (mpos.x > m_realSize.GetWidth()) {
01173                 m_points[selectedPointNr].UpdateControlPointX(m_realSize.GetWidth());
01174             }
01175 
01176             if (mpos.y >= 0 && mpos.y <= m_realSize.GetHeight()){
01177                 m_points[selectedPointNr].UpdateControlPointY(mpos.y);
01178             } else if (mpos.y < 0) {
01179                 m_points[selectedPointNr].UpdateControlPointY(0);
01180             } else if (mpos.y > m_realSize.GetHeight()) {
01181                 m_points[selectedPointNr].UpdateControlPointY(m_realSize.GetHeight());
01182             }
01183             // emit a notify event here.
01184             //
01185             //emit(pointMoved(selectedPointNr, points[selectedPointNr]));
01186             // do more intelligent updating here?
01187             doUpdate = true;
01188             break;
01189             // not possible.
01190         case NEW_POINT_SELECTED:
01191             DEBUG_DEBUG("WARNING: mouse move in new point state")
01192             newPoint = mpos;
01193             doUpdate = true;
01194             break;
01195         case NEW_LINE_CREATING:
01196             m_selectedPoint.UpdateControlPoint(mpos);
01197             doUpdate = true;
01198             break;
01199         case NO_IMAGE:
01200             break;
01201         }
01202     }
01203 
01204     if ((mouse.MiddleIsDown() || mouse.ShiftDown() || mouse.m_controlDown ) && editState!=SELECT_DELETE_REGION) {
01205         // scrolling with the mouse
01206         if (m_mouseScrollPos !=mouse.GetPosition()) {
01207             wxPoint delta_ = mouse.GetPosition() - m_mouseScrollPos;
01208             double speed = (double)GetVirtualSize().GetHeight() / GetClientSize().GetHeight();
01209 //          int speed = wxConfigBase::Get()->Read(wxT("/CPEditorPanel/scrollSpeed"),5);
01210             wxPoint delta;
01211             delta.x = roundi(delta_.x * speed);
01212             delta.y =  roundi(delta_.y * speed);
01213             // scrolling is done later
01214             if (mouse.ShiftDown()) {
01215                 // emit scroll event, so that other window can be scrolled
01216                 // as well.
01217                 CPEvent e(this, CPEvent::SCROLLED, FDiff2D(delta.x, delta.y));
01218                 emit(e);
01219             } else {
01220                 // scroll only our window
01221                 ScrollDelta(delta);
01222             }
01223             m_mouseScrollPos = mouse.GetPosition();
01224         }
01225     }
01226 
01227     if(mouse.RightIsDown() && editState==SELECT_DELETE_REGION)
01228     {
01229         //update selection rectangle
01230         DrawSelectionRectangle(rectStartPos,m_mousePos);
01231         DrawSelectionRectangle(rectStartPos,mpos);
01232     }
01233 //    DEBUG_DEBUG("ImageDisplay: mouse move, state: " << editState);
01234 
01235     // draw a rectangle
01236     if (m_showSearchArea) {
01237         doUpdate = true;
01238     }
01239 
01240     unsigned int selPointNr;
01241     if (isOccupied(unScrolledMousePos, mpos, selPointNr) == KNOWN_POINT_SELECTED &&
01242         (! (editState == KNOWN_POINT_SELECTED && selectedPointNr == selPointNr) ) ) {
01243         SetCursor(wxCursor(wxCURSOR_ARROW));
01244     } else {
01245         SetCursor(*m_CPSelectCursor);
01246     }
01247 
01248     m_mousePos = mpos;
01249     // repaint
01250     if (doUpdate) {
01251         update();
01252     }
01253 }
01254 
01255 
01256 void CPImageCtrl::mousePressLMBEvent(wxMouseEvent& mouse)
01257 {
01258     DEBUG_DEBUG("LEFT MOUSE DOWN");
01259     if (!m_img.get()) return; // ignore events if no image loaded.
01260     //ignore left mouse button if selecting region with right mouse button
01261     if(editState==SELECT_DELETE_REGION) 
01262         return;
01263     wxPoint unScrolledMousePos;
01264     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01265                            &unScrolledMousePos.x, & unScrolledMousePos.y);
01266     FDiff2D mpos(unScrolledMousePos.x, unScrolledMousePos.y);
01267     mpos = applyRotInv(invScale(mpos));
01268     wxPoint mpos_ = applyRotInv(invScale(unScrolledMousePos));
01269     DEBUG_DEBUG("mousePressEvent, pos:" << mpos.x
01270                 << ", " << mpos.y);
01271     // if mouseclick is out of image, ignore
01272     if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) {
01273         return;
01274     }
01275     unsigned int selPointNr = 0;
01276     EditorState clickState = isOccupied(unScrolledMousePos, mpos, selPointNr);
01277     if (mouse.LeftDown() && editState != NO_IMAGE
01278         && mpos.x < m_realSize.x && mpos.y < m_realSize.y)
01279     {
01280         // we can always select a new point
01281         if (clickState == KNOWN_POINT_SELECTED) {
01282             DEBUG_DEBUG("click on point: " << selPointNr);
01283             selectedPointNr = selPointNr;
01284             m_points[selectedPointNr].CheckSelection(unScrolledMousePos, mpos);
01285             m_selectedPoint = m_points[selectedPointNr];
01286             editState = clickState;
01287             CPEvent e( this, selectedPointNr);
01288             m_forceMagnifier = true;
01289             emit(e);
01290         } else if (clickState == NEW_POINT_SELECTED) {
01291             DEBUG_DEBUG("click on new space, select new point");
01292             if(m_sameImage && mouse.AltDown())
01293             {
01294                 editState = NEW_LINE_CREATING;
01295                 m_selectedPoint.StartLineControlPoint(mpos);
01296                 m_selectedPoint.SetControl(this);
01297             }
01298             else
01299             {
01300                 editState = NEW_POINT_SELECTED;
01301             };
01302             newPoint = mpos;
01303         } else {
01304             DEBUG_ERROR("invalid state " << clickState << " on mouse down");
01305         }
01306 //        DEBUG_DEBUG("ImageDisplay: mouse down, state change: " << oldstate
01307 //                    << " -> " << editState);
01308     }
01309     m_mousePos = mpos;
01310 }
01311 
01312 void CPImageCtrl::OnTimer(wxTimerEvent & e)
01313 {
01314     if (!m_img.get()) return; // ignore events if no image loaded.
01315     m_forceMagnifier = false;
01316     update();
01317 }
01318 
01319 void CPImageCtrl::mouseReleaseLMBEvent(wxMouseEvent& mouse)
01320 {
01321     DEBUG_DEBUG("LEFT MOUSE UP");
01322     if (!m_img.get()) return; // ignore events if no image loaded.
01323     //ignore left mouse button if selecting region with right mouse button
01324     if(editState==SELECT_DELETE_REGION) 
01325         return;
01326 
01327     m_timer.Start(2000, true);
01328 
01329     wxPoint mpos_;
01330     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01331                            &mpos_.x, & mpos_.y);
01332     FDiff2D mpos(mpos_.x, mpos_.y);
01333     mpos = applyRotInv(invScale(mpos));
01334     DEBUG_DEBUG("mouseReleaseEvent, pos:" << mpos.x
01335                 << ", " << mpos.y);
01336     // if mouseclick is out of image, ignore
01337     if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) {
01338         return;
01339     }
01340 //    EditorState oldState = editState;
01341     if (mouse.LeftUp()) {
01342         switch(editState) {
01343         case NO_SELECTION:
01344             DEBUG_DEBUG("mouse release without selection");
01345             break;
01346         case KNOWN_POINT_SELECTED:
01347         {
01348             DEBUG_DEBUG("mouse release with known point " << selectedPointNr);
01349             if (! (m_selectedPoint == m_points[selectedPointNr]) ) {
01350                 CPEvent e( this, CPEvent::POINT_CHANGED, selectedPointNr, m_points[selectedPointNr].GetControlPoint());
01351                 emit(e);
01352             }
01353             break;
01354         }
01355         case NEW_POINT_SELECTED:
01356         {
01357             DEBUG_DEBUG("new Point changed (event fire): x:" << mpos.x << " y:" << mpos.y);
01358             // fire the wxWin event
01359             CPEvent e( this, newPoint);
01360             emit(e);
01361             break;
01362         }
01363         case NEW_LINE_CREATING:
01364         {
01365             //notify parent
01366             CPEvent e(this, CPEvent::NEW_LINE_ADDED, m_selectedPoint.GetControlPoint());
01367             emit(e);
01368             break;
01369         }
01370         case NO_IMAGE:
01371             break;
01372 
01373         }
01374 //        DEBUG_DEBUG("ImageDisplay: mouse release, state change: " << oldState
01375 //                    << " -> " << editState);
01376     }
01377 
01378 }
01379 
01380 
01381 void CPImageCtrl::mouseReleaseMMBEvent(wxMouseEvent& mouse)
01382 {
01383     DEBUG_DEBUG("middle mouse button released, leaving scroll mode")
01384 //    SetCursor(wxCursor(wxCURSOR_BULLSEYE));
01385 }
01386 
01387 
01388 void CPImageCtrl::mousePressMMBEvent(wxMouseEvent& mouse)
01389 {
01390     DEBUG_DEBUG("middle mouse button pressed, entering scroll mode")
01391     if (!m_img.get()) return; // ignore events if no image loaded.
01392     m_mouseScrollPos = mouse.GetPosition();
01393 //    SetCursor(wxCursor(wxCURSOR_HAND));
01394 }
01395 
01396 void CPImageCtrl::mousePressRMBEvent(wxMouseEvent& mouse)
01397 {
01398     //ignore event if no image loaded
01399     if(!m_img.get()) 
01400         return;
01401     wxPoint mpos_;
01402     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y, &mpos_.x, & mpos_.y);
01403     FDiff2D mpos(mpos_.x, mpos_.y);
01404     mpos = applyRotInv(invScale(mpos));
01405     // if mouseclick is out of image, ignore
01406     if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight())
01407     {
01408         return;
01409     }
01410     if(mouse.CmdDown() && (editState==NO_SELECTION || editState==KNOWN_POINT_SELECTED || editState==NEW_POINT_SELECTED))
01411     {
01412         rectStartPos=mpos;
01413         editState=SELECT_DELETE_REGION;
01414         DrawSelectionRectangle(mpos,mpos);
01415     };
01416 };
01417 
01418 void CPImageCtrl::mouseReleaseRMBEvent(wxMouseEvent& mouse)
01419 {
01420     if (!m_img.get()) return; // ignore events if no image loaded.
01421     wxPoint mpos_;
01422     CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
01423                            &mpos_.x, & mpos_.y);
01424     FDiff2D mpos(mpos_.x, mpos_.y);
01425     mpos = applyRotInv(invScale(mpos));
01426     DEBUG_DEBUG("mouseReleaseEvent, pos:" << mpos.x
01427                 << ", " << mpos.y);
01428 
01429     if (mouse.RightUp())
01430     {
01431         if(editState==SELECT_DELETE_REGION)
01432         {
01433             DrawSelectionRectangle(rectStartPos,mpos);
01434             editState=NO_SELECTION;
01435             CPEvent e(this,rectStartPos,mpos);
01436             emit(e);
01437         }
01438         else
01439         {
01440             // if mouseclick is out of image, ignore
01441             if (mpos.x >= m_realSize.GetWidth() || mpos.y >= m_realSize.GetHeight()) {
01442                 return;
01443             }
01444             // set right up event
01445             DEBUG_DEBUG("Emitting right click (rmb release)");
01446             CPEvent e(this, CPEvent::RIGHT_CLICK, mpos);
01447             emit(e);
01448         }
01449     }
01450 }
01451 
01452 void CPImageCtrl::update()
01453 {
01454     DEBUG_TRACE("edit state:" << editState);
01455     wxClientDC dc(this);
01456     PrepareDC(dc);
01457     OnDraw(dc);
01458 }
01459 
01460 bool CPImageCtrl::emit(CPEvent & ev)
01461 {
01462     if ( ProcessEvent( ev ) == FALSE ) {
01463         wxLogWarning( _("Could not process event!") );
01464         return false;
01465     } else {
01466         return true;
01467     }
01468 }
01469 
01470 void CPImageCtrl::setScale(double factor)
01471 {
01472     if (factor == 0) {
01473         fitToWindow = true;
01474         factor = calcAutoScaleFactor(imageSize);
01475     } else {
01476         fitToWindow = false;
01477     }
01478     DEBUG_DEBUG("new scale factor:" << factor);
01479     // update if factor changed
01480     if (factor != scaleFactor) {
01481         scaleFactor = factor;
01482         // keep existing scale focussed.
01483         rescaleImage();
01484     }
01485 }
01486 
01487 double CPImageCtrl::calcAutoScaleFactor(wxSize size)
01488 {
01489     // TODO correctly autoscale rotated iamges
01490     int w = size.GetWidth();
01491     int h = size.GetHeight();
01492     if (m_imgRotation ==  ROT90 || m_imgRotation == ROT270) {
01493         int t = w;
01494         w = h;
01495         h = t;
01496     }
01497 
01498 //    wxSize csize = GetClientSize();
01499     wxSize csize = GetSize();
01500     DEBUG_DEBUG("csize: " << csize.GetWidth() << "x" << csize.GetHeight() << "image: " << w << "x" << h);
01501     double s1 = (double)csize.GetWidth()/w;
01502     double s2 = (double)csize.GetHeight()/h;
01503     DEBUG_DEBUG("s1: " << s1 << "  s2:" << s2);
01504     return s1 < s2 ? s1 : s2;
01505 }
01506 
01507 double CPImageCtrl::getScaleFactor() const
01508 {
01509     return scaleFactor;
01510 }
01511 
01512 void CPImageCtrl::OnSize(wxSizeEvent &e)
01513 {
01514     DEBUG_TRACE("size: " << e.GetSize().GetWidth() << "x" << e.GetSize().GetHeight());
01515     // rescale bitmap if needed.
01516     if (imageFilename != "") {
01517         if (fitToWindow) {
01518             setScale(0);
01519         }
01520     }
01521 }
01522 
01523 void CPImageCtrl::OnKey(wxKeyEvent & e)
01524 {
01525     if (!m_img.get()) return; // ignore events if no image loaded.
01526     DEBUG_TRACE(" OnKey, key:" << e.m_keyCode);
01527     wxPoint delta(0,0);
01528     // check for cursor keys, if control is not pressed
01529     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_LEFT ) delta.x = -1;
01530     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_RIGHT ) delta.x = 1;
01531     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_UP ) delta.y = -1;
01532     if ((!e.CmdDown()) && e.GetKeyCode() == WXK_DOWN ) delta.y = 1;
01533     if ( (delta.x != 0 || delta.y != 0 ) && (e.ShiftDown() || e.CmdDown())) {
01534         // move to the left
01535         double speed = (double) GetClientSize().GetWidth()/10;
01536         delta.x = (int) (delta.x * speed);
01537         delta.y = (int) (delta.y * speed);
01538         if (e.ShiftDown()) {
01539             // emit scroll event, so that other window can be scrolled
01540             // as well.
01541             CPEvent e(this, CPEvent::SCROLLED, FDiff2D(delta.x, delta.y));
01542             emit(e);
01543         } else if (e.CmdDown()) {
01544             ScrollDelta(delta);
01545         }
01546     } else if (delta.x != 0 || delta.y != 0 ) {
01547 
01548         FDiff2D shift(delta.x/3.0, delta.y/3.0);
01549         // rotate shift according to current display
01550         double t;
01551         switch (m_imgRotation) {
01552             case ROT90:
01553                 t = shift.x;
01554                 shift.x = shift.y;
01555                 shift.y = -t;
01556                 break;
01557             case ROT180:
01558                 shift.x = -shift.x;
01559                 shift.y = -shift.y;
01560                 break;
01561             case ROT270:
01562                 t = shift.x;
01563                 shift.x = -shift.y;
01564                 shift.y = t;
01565             default:
01566                 break;
01567         }
01568         // move control point by half a pixel, if a point is selected
01569         if (editState == KNOWN_POINT_SELECTED ) {
01570             DisplayedControlPoint updatedCp=m_points[selectedPointNr];
01571             updatedCp.ShiftControlPoint(shift);
01572             CPEvent e( this, CPEvent::POINT_CHANGED, selectedPointNr, updatedCp.GetControlPoint());
01573             emit(e);
01574             m_forceMagnifier = true;
01575             m_timer.Stop();
01576             m_timer.Start(2000, true);
01577         } else if (editState == NEW_POINT_SELECTED) {
01578             newPoint = newPoint + shift;
01579             // update display.
01580             update();
01581         }
01582 
01583     } else if (e.m_keyCode == 'a') {
01584         DEBUG_DEBUG("adding point with a key, faking right click");
01585         // faking right mouse button with "a"
01586         // set right up event
01587         CPEvent ev(this, CPEvent::RIGHT_CLICK, FDiff2D(0,0));
01588         emit(ev);
01589     } else {
01590         // forward some keys...
01591         bool forward = false;
01592         switch (e.GetKeyCode())
01593         {
01594             case 'g':
01595             case '0':
01596             case '1':
01597             case '2':
01598             case 'f':
01599             case WXK_RIGHT:
01600             case WXK_LEFT:
01601             case WXK_UP:
01602             case WXK_DOWN:
01603             case WXK_DELETE:
01604                 forward = true;
01605                 break;
01606             default:
01607                 break;
01608         }
01609 
01610         if (forward) {
01611             // dangelo: I don't understand why some keys are forwarded and others are not..
01612             // Delete is forwared under wxGTK, and g not..
01613             // wxWidgets 2.6.1 using gtk 2 doesn't set the event object
01614             // properly.. do it here by hand
01615             e.SetEventObject(this);
01616             DEBUG_DEBUG("forwarding key " << e.GetKeyCode()
01617                         << " origin: id:" << e.GetId() << " obj: "
01618                         << e.GetEventObject());
01619             // forward all keys to our parent
01620             //GetParent()->GetEventHandler()->ProcessEvent(e);
01621             m_editPanel->GetEventHandler()->ProcessEvent(e);
01622         } else {
01623             e.Skip();
01624         }
01625     }
01626 }
01627 
01628 void CPImageCtrl::OnKeyDown(wxKeyEvent & e)
01629 {
01630     DEBUG_TRACE("key:" << e.m_keyCode);
01631     if (!m_img.get()) return; // ignore events if no image loaded.
01632     if (e.m_keyCode == WXK_SHIFT || e.m_keyCode == WXK_CONTROL) {
01633         DEBUG_DEBUG("shift or control down, reseting scoll position");
01634         m_mouseScrollPos = e.GetPosition();
01635     }
01636     e.Skip();
01637 }
01638 
01639 void CPImageCtrl::OnMouseLeave(wxMouseEvent & e)
01640 {
01641     DEBUG_TRACE("MOUSE LEAVE");
01642     m_mousePos = FDiff2D(-1,-1);
01643     m_mouseInWindow = false;
01644     update();
01645 }
01646 
01647 void CPImageCtrl::OnMouseEnter(wxMouseEvent & e)
01648 {
01649     DEBUG_TRACE("MOUSE Enter, setting focus");
01650     m_mouseInWindow = true;
01651     SetFocus();
01652     update();
01653 }
01654 
01655 FDiff2D CPImageCtrl::getNewPoint()
01656 {
01657     // only possible if a new point is actually selected
01658     // DEBUG_ASSERT(editState == NEW_POINT_SELECTED);
01659     return newPoint;
01660 }
01661 
01662 void CPImageCtrl::setNewPoint(const FDiff2D & p)
01663 {
01664     DEBUG_DEBUG("setting new point " << p.x << "," << p.y);
01665     // should we need to check for some precondition?
01666     newPoint = p;
01667     editState = NEW_POINT_SELECTED;
01668 
01669     // show new point.
01670     showPosition(p);
01671 
01672     // we do not send an event, since CPEditorPanel
01673     // caused the change.. so it doesn't need to filter
01674     // out its own change messages.
01675 }
01676 
01677 void CPImageCtrl::showSearchArea(bool show)
01678 {
01679     m_showSearchArea = show;
01680     if (show)
01681     {
01682         int templSearchAreaPercent = wxConfigBase::Get()->Read(wxT("/Finetune/SearchAreaPercent"), HUGIN_FT_SEARCH_AREA_PERCENT);
01683         m_searchRectWidth = (m_realSize.GetWidth() * templSearchAreaPercent) / 200;
01684         DEBUG_DEBUG("Setting new search area: w in %:" << templSearchAreaPercent << " bitmap width: " << bitmap.GetWidth() << "  resulting size: " << m_searchRectWidth);
01685         m_mousePos = FDiff2D(-1,-1);
01686     }
01687 }
01688 
01689 void CPImageCtrl::showTemplateArea(bool show)
01690 {
01691     m_showTemplateArea = show;
01692     if (show)
01693     {
01694         m_templateRectWidth = wxConfigBase::Get()->Read(wxT("/Finetune/TemplateSize"),HUGIN_FT_TEMPLATE_SIZE) / 2;
01695     }
01696 }
01697 
01698 wxPoint CPImageCtrl::MaxScrollDelta(wxPoint delta)
01699 {
01700     int x,y;
01701     GetViewStart( &x, &y );
01702 
01703     wxSize winSize = GetClientSize();
01704     wxSize imgSize;
01705     imgSize.x = bitmap.GetWidth();
01706     imgSize.y = bitmap.GetHeight();
01707     // check for top and left border
01708     if (x + delta.x < 0) {
01709         delta.x = -x;
01710     }
01711     if (y + delta.y < 0) {
01712         delta.y = -y;
01713     }
01714     // check for right and bottom border
01715     int right = x + delta.x + winSize.x ;
01716     if (right > imgSize.x) {
01717         delta.x = imgSize.x - right;
01718         if (delta.x < 0) {
01719             delta.x = 0;
01720         }
01721     }
01722     int bottom = y + delta.y + winSize.y ;
01723     if (bottom > imgSize.y) {
01724         delta.y = imgSize.y - bottom;
01725         if (delta.y < 0) {
01726             delta.y = 0;
01727         }
01728     }
01729     return delta;
01730 }
01731 
01732 void CPImageCtrl::ScrollDelta(const wxPoint & delta)
01733 {
01734     // TODO: adjust
01735     if (delta.x == 0 && delta.y == 0) {
01736         return;
01737     }
01738     int x,y;
01739     GetViewStart( &x, &y );
01740     x = x + delta.x;
01741     y = y + delta.y;
01742     if (x<0) x = 0;
01743     if (y<0) y = 0;
01744     Scroll( x, y);
01745 }
01746 
01747 const wxSize CPImageCtrl::GetBitmapSize() const
01748 {
01749 #if wxCHECK_VERSION(2,9,0)
01750     return bitmap.GetSize();
01751 #else
01752     return wxSize(bitmap.GetWidth(), bitmap.GetHeight());
01753 #endif
01754 };
01755 
01756 IMPLEMENT_DYNAMIC_CLASS(CPImageCtrl, wxScrolledWindow)
01757 
01758 CPImageCtrlXmlHandler::CPImageCtrlXmlHandler()
01759                 : wxXmlResourceHandler()
01760 {
01761     AddWindowStyles();
01762 }
01763 
01764 wxObject *CPImageCtrlXmlHandler::DoCreateResource()
01765 {
01766     XRC_MAKE_INSTANCE(cp, CPImageCtrl)
01767 
01768     cp->Create(m_parentAsWindow,
01769                    GetID(),
01770                    GetPosition(), GetSize(),
01771                    GetStyle(wxT("style")),
01772                    GetName());
01773 
01774     SetupWindow( cp);
01775 
01776     return cp;
01777 }
01778 
01779 bool CPImageCtrlXmlHandler::CanHandle(wxXmlNode *node)
01780 {
01781     return IsOfClass(node, wxT("CPImageCtrl"));
01782 }
01783 
01784 IMPLEMENT_DYNAMIC_CLASS(CPImageCtrlXmlHandler, wxXmlResourceHandler)

Generated on 29 Jul 2015 for Hugintrunk by  doxygen 1.4.7