Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MaskImageCtrl.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
13 /* This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public
15  * License as published by the Free Software Foundation; either
16  * version 2 of the License, or (at your option) any later version.
17  *
18  * This software is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  * General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public
24  * License along with this software. If not, see
25  * <http://www.gnu.org/licenses/>.
26  *
27  */
28 
29 #include "panoinc_WX.h"
30 #include "panoinc.h"
31 #include <wx/dcbuffer.h>
32 #include "base_wx/platform.h"
33 #include "base_wx/wxcms.h"
34 #include "hugin/MainFrame.h"
35 #include "hugin/huginApp.h"
36 #include "hugin/config_defaults.h"
37 #include "hugin/MaskImageCtrl.h"
38 #include "hugin/MaskEditorPanel.h"
39 #include "base_wx/wxImageCache.h"
40 #include <vigra/inspectimage.hxx>
41 
43 const int polygonPointSize=3;
45 const int maxSelectionDistance = 10;
46 
47 // our image control
48 bool MaskImageCtrl::Create(wxWindow * parent, wxWindowID id,
49  const wxPoint& pos,
50  const wxSize& size,
51  long style,
52  const wxString& name)
53 {
54  wxScrolledWindow::Create(parent, id, pos, size, style, name);
57  m_scaleFactor = 1;
58  m_fitToWindow = false;
59  m_previewOnly = false;
60  m_activeMask = UINT_MAX;
61  m_showActiveMasks = false;
62  m_maskMode = true;
63  m_oldScrollPosX = -1;
64  m_oldScrollPosY = -1;
65  m_middleMouseScroll = false;
66  SetBackgroundStyle(wxBG_STYLE_PAINT);
67  // bind event handler
68  Bind(wxEVT_SIZE, &MaskImageCtrl::OnSize, this);
69  Bind(wxEVT_MOTION, &MaskImageCtrl::OnMouseMove, this);
70  Bind(wxEVT_LEFT_DOWN, &MaskImageCtrl::OnLeftMouseDown, this);
71  Bind(wxEVT_LEFT_UP, &MaskImageCtrl::OnLeftMouseUp, this);
72  Bind(wxEVT_LEFT_DCLICK, &MaskImageCtrl::OnLeftMouseDblClick, this);
73  Bind(wxEVT_RIGHT_DOWN, &MaskImageCtrl::OnRightMouseDown, this);
74  Bind(wxEVT_RIGHT_UP, &MaskImageCtrl::OnRightMouseUp, this);
75  Bind(wxEVT_MIDDLE_DOWN, &MaskImageCtrl::OnMiddleMouseDown, this);
76  Bind(wxEVT_MIDDLE_UP, &MaskImageCtrl::OnMiddleMouseUp, this);
77  Bind(wxEVT_KEY_UP, &MaskImageCtrl::OnKeyUp, this);
78  Bind(wxEVT_CHAR, &MaskImageCtrl::OnChar, this);
79  Bind(wxEVT_MOUSE_CAPTURE_LOST, &MaskImageCtrl::OnCaptureLost, this);
80  Bind(wxEVT_KILL_FOCUS, &MaskImageCtrl::OnKillFocus, this);
81  Bind(wxEVT_SCROLLWIN_TOP, &MaskImageCtrl::OnScroll, this);
82  Bind(wxEVT_SCROLLWIN_BOTTOM, &MaskImageCtrl::OnScroll, this);
83  Bind(wxEVT_SCROLLWIN_LINEUP, &MaskImageCtrl::OnScroll, this);
84  Bind(wxEVT_SCROLLWIN_LINEDOWN, &MaskImageCtrl::OnScroll, this);
85  Bind(wxEVT_SCROLLWIN_PAGEUP, &MaskImageCtrl::OnScroll, this);
86  Bind(wxEVT_SCROLLWIN_PAGEDOWN, &MaskImageCtrl::OnScroll, this);
87  Bind(wxEVT_SCROLLWIN_THUMBTRACK, &MaskImageCtrl::OnScroll, this);
88  Bind(wxEVT_SCROLLWIN_THUMBRELEASE, &MaskImageCtrl::OnScroll, this);
89  Bind(wxEVT_MOUSEWHEEL, &MaskImageCtrl::OnMouseWheel, this);
90  Bind(wxEVT_PAINT, &MaskImageCtrl::OnPaint, this);
91 
92  return true;
93 }
94 
96 {
97  m_editPanel = parent;
98 }
99 
100 void MaskImageCtrl::SetMaskMode(bool newMaskMode)
101 {
102  m_maskMode=newMaskMode;
103  if(m_maskMode)
104  {
105  SetCursor(wxNullCursor);
107  {
109  setActiveMask(UINT_MAX,false);
110  };
111  }
112  else
113  {
115  {
117  };
118  };
119  m_overlay.Reset();
120  Refresh();
121 };
122 
124 {
125  DEBUG_TRACE("setting Image " << file);
126  if(!file.empty())
127  {
128  try
129  {
130  m_img = ImageCache::getInstance().getImage(file);
131  }
132  catch (...)
133  {
134  // loading of image failed, set all to empty values
135  m_imageFilename = "";
137  m_bitmap = wxBitmap();
138  // delete the image (release shared_ptr)
139  // create an empty image.
140  m_img = ImageCache::EntryPtr(new ImageCache::Entry);
142  m_imageMask = mask;
143  m_masksToDraw = mask;
145  setActiveMask(UINT_MAX, false);
146  SetVirtualSize(100, 100);
147  Refresh(true);
148  // now notify main frame to remove the image from project
149  wxCommandEvent e(EVT_LOADING_FAILED);
150  e.SetString(wxString(file.c_str(), HUGIN_CONV_FILENAME));
151  MainFrame::Get()->GetEventHandler()->AddPendingEvent(e);
152  return;
153  }
154  m_imageFilename = file;
155  if(m_maskMode)
156  {
158  }
159  else
160  {
162  };
163  m_imageMask=newMask;
164  m_masksToDraw=masksToDraw;
165  m_imgRotation=rot;
166  setActiveMask(UINT_MAX,false);
167  rescaleImage();
168  Refresh();
170  }
171  else
172  {
174  m_bitmap = wxBitmap();
176  m_overlay.Reset();
177  // delete the image (release shared_ptr)
178  // create an empty image.
179  m_img = ImageCache::EntryPtr(new ImageCache::Entry);
181  m_imageMask=mask;
182  m_masksToDraw=mask;
184  setActiveMask(UINT_MAX,false);
185  SetVirtualSize(100,100);
186  Refresh(true);
187  }
188 }
189 
191 {
192  m_imageMask=newMasks;
193  m_masksToDraw=masksToDraw;
194  if (m_activeMask >= m_imageMask.size())
195  {
196  setActiveMask(UINT_MAX);
197  }
198  else
199  {
201  };
202  Refresh(false);
203 };
204 
205 void MaskImageCtrl::setCrop(HuginBase::SrcPanoImage::CropMode newCropMode, vigra::Rect2D newCropRect, bool isCentered, hugin_utils::FDiff2D center, bool isCircleCrop)
206 {
207  m_cropMode=newCropMode;
208  m_cropRect=newCropRect;
209  m_cropCentered=isCentered;
210  m_cropCenter=center;
211  m_cropCircle=isCircleCrop;
212 };
213 
214 void MaskImageCtrl::setActiveMask(unsigned int newMask, bool doUpdate)
215 {
216  if(m_activeMask!=newMask)
217  {
218  m_activeMask=newMask;
219  m_selectedPoints.clear();
220  };
221  if(newMask<UINT_MAX)
222  {
223  if(m_maskMode)
224  {
225  if(m_selectedPoints.empty())
226  {
228  }
229  else
230  {
232  };
233  }
234  else
235  {
236  m_selectedPoints.clear();
238  };
240  }
241  else
242  {
243  if(!m_imageFilename.empty())
244  {
245  if(m_maskMode)
246  {
248  }
249  else
250  {
252  };
253  };
255  m_editingMask=mask;
256  };
257  if(doUpdate)
258  Refresh(true);
259 };
260 
262 {
263  m_selectedPoints.clear();
264  if(m_activeMask<UINT_MAX)
265  fill_set(m_selectedPoints,0,m_imageMask[m_activeMask].getMaskPolygon().size()-1);
266 };
267 
268 // returns where the user clicked
270 {
271  vigra::Rect2D testRect(pos.x-maxSelectionDistance, pos.y-maxSelectionDistance, pos.x+maxSelectionDistance, pos.y+maxSelectionDistance);
273  {
274  double radius=std::min<int>(m_cropRect.width(), m_cropRect.height())/2.0;
275  vigra::Point2D pos_center((m_cropRect.left()+m_cropRect.right())/2, (m_cropRect.top()+m_cropRect.bottom())/2);
276  double dist=sqrt(double((pos-pos_center).squaredMagnitude()));
277  if(dist-maxSelectionDistance<radius && radius<dist+maxSelectionDistance)
278  {
279  return CLICK_CIRCLE;
280  };
281  };
282  if(m_cropRect.intersects(testRect))
283  {
284  if(abs(pos.x-m_cropRect.left())<maxSelectionDistance)
285  {
286  return CLICK_LEFT;
287  };
288  if(abs(pos.x-m_cropRect.right())<maxSelectionDistance)
289  {
290  return CLICK_RIGHT;
291  };
292  if(abs(pos.y-m_cropRect.top())<maxSelectionDistance)
293  {
294  return CLICK_TOP;
295  };
296  if(abs(pos.y-m_cropRect.bottom())<maxSelectionDistance)
297  {
298  return CLICK_BOTTOM;
299  };
300  };
301  if(m_cropRect.contains(pos))
302  {
303  return CLICK_INSIDE;
304  };
305  return CLICK_OUTSIDE;
306 };
307 
309 {
311  int newLeft, newRight, newTop, newBottom;
312  bool needsUpdate=false;
313  switch (m_maskEditState)
314  {
315  case CROP_MOVING:
316  m_cropRect.moveBy(delta.x,delta.y);
317  break;
318  case CROP_LEFT_MOVING:
319  if(m_cropCentered)
320  {
321  double newHalfWidth=m_cropRect.width()/2.0-delta.x;
322  newLeft=hugin_utils::roundi(m_cropCenter.x-newHalfWidth);
323  newRight=hugin_utils::roundi(m_cropCenter.x+newHalfWidth);
324  }
325  else
326  {
327  newLeft=m_cropRect.left()+delta.x;
328  newRight=m_cropRect.right();
329  };
330  newTop=m_cropRect.top();
331  newBottom=m_cropRect.bottom();
332  needsUpdate=true;
333  break;
334  case CROP_RIGHT_MOVING:
335  if(m_cropCentered)
336  {
337  double newHalfWidth=m_cropRect.width()/2.0+delta.x;
338  newLeft=hugin_utils::roundi(m_cropCenter.x-newHalfWidth);
339  newRight=hugin_utils::roundi(m_cropCenter.x+newHalfWidth);
340  }
341  else
342  {
343  newLeft=m_cropRect.left();
344  newRight=m_cropRect.right()+delta.x;
345  };
346  newTop=m_cropRect.top();
347  newBottom=m_cropRect.bottom();
348  needsUpdate=true;
349  break;
350  case CROP_TOP_MOVING:
351  if(m_cropCentered)
352  {
353  double newHalfHeight=m_cropRect.height()/2.0-delta.y;
354  newTop=hugin_utils::roundi(m_cropCenter.y-newHalfHeight);
355  newBottom=hugin_utils::roundi(m_cropCenter.y+newHalfHeight);
356  }
357  else
358  {
359  newTop=m_cropRect.top()+delta.y;
360  newBottom=m_cropRect.bottom();
361  };
362  newLeft=m_cropRect.left();
363  newRight=m_cropRect.right();
364  needsUpdate=true;
365  break;
366  case CROP_BOTTOM_MOVING:
367  if(m_cropCentered)
368  {
369  double newHalfHeight=m_cropRect.height()/2.0+delta.y;
370  newTop=hugin_utils::roundi(m_cropCenter.y-newHalfHeight);
371  newBottom=hugin_utils::roundi(m_cropCenter.y+newHalfHeight);
372  }
373  else
374  {
375  newTop=m_cropRect.top();
376  newBottom=m_cropRect.bottom()+delta.y;
377  };
378  newLeft=m_cropRect.left();
379  newRight=m_cropRect.right();
380  needsUpdate=true;
381  break;
382  case CROP_CIRCLE_SCALING:
383  {
384  double radius=sqrt(hugin_utils::sqr(pos.x-m_dragStartPos.x)+hugin_utils::sqr(pos.y-m_dragStartPos.y));
385  newLeft=m_dragStartPos.x-radius;
386  newRight=m_dragStartPos.x+radius;
387  newTop=m_dragStartPos.y-radius;
388  newBottom=m_dragStartPos.y+radius;
389  needsUpdate=true;
390  };
391  break;
392  default:
393  // in all other cases the crop is not changed
394  // this should not happen
395  break;
396  };
397  if(needsUpdate)
398  {
399  // switch left/right or top/bottom if necessary
400  if(newLeft>newRight)
401  {
402  int temp=newLeft;
403  newLeft=newRight;
404  newRight=temp;
405  };
406  if(newTop>newBottom)
407  {
408  int temp=newTop;
409  newTop=newBottom;
410  newBottom=temp;
411  };
412  m_cropRect.setUpperLeft(vigra::Point2D(newLeft, newTop));
413  m_cropRect.setLowerRight(vigra::Point2D(newRight, newBottom));
414  };
415 };
416 
417 void MaskImageCtrl::OnMouseMove(wxMouseEvent& mouse)
418 {
419  if(m_previewOnly)
420  return;
422  {
423  // handle scrolling and break out
424  wxPoint viewStart = GetViewStart();
425  viewStart = viewStart - (mouse.GetPosition() - m_scrollPos);
426  Scroll(viewStart);
427  m_scrollPos = mouse.GetPosition();
428  return;
429  };
430  wxPoint mpos;
431  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
432  &mpos.x, & mpos.y);
434  bool doUpdate = false;
435  switch(m_maskEditState)
436  {
438  doUpdate=true;
439  m_editingMask.movePointTo(m_editingMask.getMaskPolygon().size()-1,currentPos);
440  break;
441  case POLYGON_SELECTING:
442  case REGION_SELECTING:
443  case POINTS_DELETING:
444  m_currentPos=mpos;
446  break;
447  case POINTS_MOVING:
448  doUpdate=true;
450  {
452  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
453  m_editingMask.movePointBy(*it,delta);
454  };
455  break;
456  case POINTS_ADDING:
457  doUpdate=true;
458  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
459  m_editingMask.movePointTo(*it,currentPos);
460  break;
461  case CROP_SHOWING:
462  switch(GetClickPos(vigra::Point2D(currentPos.x, currentPos.y)))
463  {
464  case CLICK_INSIDE:
465  if(!m_cropCentered)
466  {
467  SetCursor(wxCURSOR_HAND);
468  }
469  else
470  {
471  SetCursor(wxNullCursor);
472  };
473  break;
474  case CLICK_LEFT:
475  case CLICK_RIGHT:
476  switch (m_imgRotation)
477  {
478  case ROT90:
479  case ROT270:
480  SetCursor(wxCURSOR_SIZENS);
481  break;
482  default:
483  SetCursor(wxCURSOR_SIZEWE);
484  };
485  break;
486  case CLICK_TOP:
487  case CLICK_BOTTOM:
488  switch (m_imgRotation)
489  {
490  case ROT90:
491  case ROT270:
492  SetCursor(wxCURSOR_SIZEWE);
493  break;
494  default:
495  SetCursor(wxCURSOR_SIZENS);
496  };
497  break;
498  case CLICK_CIRCLE:
499  SetCursor(wxCURSOR_SIZING);
500  break;
501  default:
502  SetCursor(wxNullCursor);
503  };
504  break;
505  case CROP_MOVING:
506  case CROP_LEFT_MOVING:
507  case CROP_RIGHT_MOVING:
508  case CROP_TOP_MOVING:
509  case CROP_BOTTOM_MOVING:
510  case CROP_CIRCLE_SCALING:
511  UpdateCrop(currentPos);
512  m_currentPos=mpos;
513  Refresh(false);
515  break;
516  case NO_IMAGE:
517  case NO_MASK:
518  case NO_SELECTION:
519  case POINTS_SELECTED:
520  case NEW_POLYGON_STARTED:
521  // in all other cases do nothing
522  break;
523  };
524  if(doUpdate)
525  Refresh(false);
526 }
527 
528 void MaskImageCtrl::OnLeftMouseDown(wxMouseEvent& mouse)
529 {
530  if(m_previewOnly)
531  return;
532  DEBUG_DEBUG("LEFT MOUSE DOWN");
533  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
537  if(!HasCapture())
538  CaptureMouse();
539  SetFocus();
540  switch(m_maskEditState)
541  {
542  case NEW_POLYGON_STARTED:
543  //starting polygon creating
544  m_editingMask.addPoint(currentPos);
545  m_selectedPoints.insert(m_editingMask.getMaskPolygon().size()-1);
546  break;
547  case NO_MASK:
548  if(m_maskMode)
549  {
551  };
552  break;
553  case NO_SELECTION:
554  if(mouse.CmdDown())
555  {
556  // check if mouse clicks happens near one line of active polygon
557  unsigned int index = m_editingMask.FindPointNearPos(currentPos, 5 * maxSelectionDistance / getScaleFactor());
558  if(index<UINT_MAX)
559  {
560  m_selectedPoints.clear();
561  m_editingMask.insertPoint(index,currentPos);
562  m_selectedPoints.insert(index);
564  };
565  }
566  else
567  {
568  HuginBase::UIntSet points;
569  if(SelectPointsInsideMouseRect(points,false))
570  {
571  for(HuginBase::UIntSet::const_iterator it=points.begin();it!=points.end();++it)
572  m_selectedPoints.insert(*it);
574  }
575  else
576  {
578  }
579  };
580  break;
581  case POINTS_SELECTED:
582  if(mouse.CmdDown())
583  {
584  // check if mouse clicks happens near one line of active polygon
585  unsigned int index = m_editingMask.FindPointNearPos(currentPos, 5 * maxSelectionDistance / getScaleFactor());
586  if(index<UINT_MAX)
587  {
588  m_selectedPoints.clear();
589  m_editingMask.insertPoint(index,currentPos);
590  m_selectedPoints.insert(index);
592  };
593  }
594  else
595  {
596  HuginBase::UIntSet points;
597  if(SelectPointsInsideMouseRect(points,true))
598  {
599  //selected point clicked, starting moving
601  }
602  else
603  {
604  //unselected point clicked
605  if(SelectPointsInsideMouseRect(points,false))
606  {
607  //clicked near other point
608  if(!mouse.ShiftDown())
609  m_selectedPoints.clear();
610  for(HuginBase::UIntSet::const_iterator it=points.begin();it!=points.end();++it)
611  m_selectedPoints.insert(*it);
613  }
614  else
615  {
617  };
618  }
619  };
620  break;
621  case CROP_SHOWING:
622  switch(GetClickPos(vigra::Point2D(currentPos.x,currentPos.y)))
623  {
624  case CLICK_OUTSIDE:
625  // clicked outside, do nothing
626  break;
627  case CLICK_INSIDE:
628  if(!m_cropCentered)
629  {
631  };
632  break;
633  case CLICK_LEFT:
635  break;
636  case CLICK_RIGHT:
638  break;
639  case CLICK_TOP:
641  break;
642  case CLICK_BOTTOM:
644  break;
645  case CLICK_CIRCLE:
646  m_dragStartPos.x=(m_cropRect.left()+m_cropRect.right())/2;
647  m_dragStartPos.y=(m_cropRect.top()+m_cropRect.bottom())/2;
649  break;
650  };
652  {
654  };
655  break;
656  default:
657  // otherwise do nothing
658  break;
659  };
660 };
661 
662 void MaskImageCtrl::OnLeftMouseUp(wxMouseEvent& mouse)
663 {
665  return;
666  DEBUG_DEBUG("LEFT MOUSE UP");
667  wxPoint mpos;
668  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
669  &mpos.x, & mpos.y);
671  bool doUpdate=false;
672  switch(m_maskEditState)
673  {
674  case NEW_POLYGON_STARTED:
675  doUpdate=true;
676  m_editingMask.addPoint(currentPos);
677  m_selectedPoints.insert(m_editingMask.getMaskPolygon().size()-1);
679  break;
681  //next point of polygen selected
682  doUpdate=true;
683  m_editingMask.addPoint(currentPos);
684  m_selectedPoints.insert(m_editingMask.getMaskPolygon().size()-1);
685  break;
686  case POINTS_MOVING:
687  if(HasCapture())
688  ReleaseMouse();
689  {
692  {
693  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
694  m_imageMask[m_activeMask].movePointBy(*it,delta);
697  }
698  else
699  {
702  doUpdate=true;
703  };
704  };
705  break;
706  case POLYGON_SELECTING:
707  if(HasCapture())
708  ReleaseMouse();
709  m_overlay.Reset();
710  doUpdate = true;
711  m_currentPos=mpos;
713  {
717  p=applyRotInv(p);
718  FindPolygon(p);
719  };
720  break;
721  case REGION_SELECTING:
722  {
723  if(HasCapture())
724  ReleaseMouse();
725  m_overlay.Reset();
726  m_currentPos=mpos;
727  bool selectedPoints=!m_selectedPoints.empty();
728  if(!mouse.ShiftDown())
729  m_selectedPoints.clear();
731  {
732  //new points selected
733  if(m_selectedPoints.empty())
735  else
737  }
738  else
739  {
740  //there were no points selected
741  if(!selectedPoints)
742  {
743  //if there where no points selected before, we searching for another polygon
747  p=applyRotInv(p);
748  FindPolygon(p);
749  };
751  };
752  doUpdate=true;
753  break;
754  };
755  case POINTS_ADDING:
756  if(HasCapture())
757  ReleaseMouse();
758  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
759  m_editingMask.movePointTo(*it,currentPos);
763  break;
764  case CROP_MOVING:
765  case CROP_LEFT_MOVING:
766  case CROP_RIGHT_MOVING:
767  case CROP_TOP_MOVING:
768  case CROP_BOTTOM_MOVING:
769  case CROP_CIRCLE_SCALING:
770  if(HasCapture())
771  ReleaseMouse();
772  UpdateCrop(currentPos);
774  SetCursor(wxNullCursor);
775  m_editPanel->UpdateCrop(true);
776  doUpdate = true;
777  break;
778  default:
779  if(HasCapture())
780  ReleaseMouse();
781  };
782  if(doUpdate)
783  Refresh(false);
784 }
785 
786 void MaskImageCtrl::OnLeftMouseDblClick(wxMouseEvent &mouse)
787 {
789  return;
790  switch(m_maskEditState)
791  {
792  case NEW_POLYGON_STARTED:
793  {
796  m_editingMask=mask;
797  m_selectedPoints.clear();
798  MainFrame::Get()->SetStatusText(wxEmptyString,0);
799  break;
800  };
802  {
803  //close newly generated polygon
805  //delete last point otherwise it would be added twice, because we added it
806  //already in release left mouse button
808  if(m_editingMask.getMaskPolygon().size()>2)
809  {
810  m_imageMask.push_back(m_editingMask);
811  m_activeMask=m_imageMask.size()-1;
812  m_editPanel->AddMask();
813  }
814  else
815  {
817  m_editingMask=mask;
818  m_selectedPoints.clear();
819  Refresh();
820  };
821  MainFrame::Get()->SetStatusText(wxEmptyString,0);
822  break;
823  };
824  default:
825  // the other case do nothing here
826  break;
827  };
828 };
829 
830 void MaskImageCtrl::OnRightMouseDown(wxMouseEvent& mouse)
831 {
833  return;
834  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
838  if(!HasCapture())
839  CaptureMouse();
840  SetFocus();
842  {
843  if(mouse.CmdDown())
844  {
846  }
847  else
848  {
849  if (m_editingMask.isInside(currentPos))
850  {
853  Refresh();
854  };
855  };
856  };
857 };
858 
859 void MaskImageCtrl::OnRightMouseUp(wxMouseEvent& mouse)
860 {
862  return;
863  wxPoint mpos;
864  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
865  &mpos.x, & mpos.y);
867  if(HasCapture())
868  ReleaseMouse();
869  switch(m_maskEditState)
870  {
871  case NEW_POLYGON_STARTED:
872  {
875  m_editingMask=mask;
876  m_selectedPoints.clear();
877  MainFrame::Get()->SetStatusText(wxEmptyString,0);
878  break;
879  };
881  {
882  //close newly generated polygon
884  m_editingMask.movePointTo(m_editingMask.getMaskPolygon().size()-1,currentPos);
885  if(m_editingMask.getMaskPolygon().size()>2)
886  {
887  m_imageMask.push_back(m_editingMask);
888  m_activeMask=m_imageMask.size()-1;
889  m_editPanel->AddMask();
890  }
891  else
892  {
894  m_editingMask=mask;
895  m_selectedPoints.clear();
896  Refresh();
897  };
898  MainFrame::Get()->SetStatusText(wxEmptyString,0);
899  break;
900  };
901  case POINTS_DELETING:
902  {
903  HuginBase::UIntSet points;
904  m_currentPos=mpos;
905  if(SelectPointsInsideMouseRect(points,false))
906  {
907  if(m_editingMask.getMaskPolygon().size()-points.size()>2)
908  {
909  // clear all selected points
910  for(HuginBase::UIntSet::const_reverse_iterator it=points.rbegin();it!=points.rend();++it)
912  // now update set of selected points
913  if(!m_selectedPoints.empty())
914  {
915  std::vector<unsigned int> mappedSelectedPoints(m_imageMask[m_activeMask].getMaskPolygon().size());
916  for(unsigned int i=0;i<mappedSelectedPoints.size();i++)
917  mappedSelectedPoints[i]=i;
919  m_selectedPoints.clear();
920  for(HuginBase::UIntSet::const_iterator it=points.begin();it!=points.end();++it)
921  {
922  if((*it)<mappedSelectedPoints.size()-1)
923  for(unsigned int i=(*it)+1;i<mappedSelectedPoints.size();i++)
924  mappedSelectedPoints[i]--;
925  };
926  for(HuginBase::UIntSet::const_iterator it=temp.begin();it!=temp.end();++it)
927  if(!set_contains(points,*it))
928  m_selectedPoints.insert(mappedSelectedPoints[*it]);
929  };
930  //now update the saved mask
933  }
934  else
935  wxBell();
936  };
937  if(m_selectedPoints.empty())
939  else
941  m_overlay.Reset();
942  Refresh();
943  break;
944  };
945  case POINTS_MOVING:
946  {
949  {
950  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
951  m_imageMask[m_activeMask].movePointBy(*it,delta);
954  }
955  else
956  {
959  };
960  break;
961  };
962  default:
963  break;
964  };
965 };
966 
967 void MaskImageCtrl::OnMiddleMouseDown(wxMouseEvent& mouse)
968 {
970  {
971  return;
972  };
973  switch (m_maskEditState)
974  {
975  case NO_MASK:
976  case NO_SELECTION:
977  case POINTS_SELECTED:
978  case POINTS_ADDING:
979  case NEW_POLYGON_STARTED:
981  case CROP_SHOWING:
982  if (!HasCapture())
983  CaptureMouse();
984  // store the scroll rate, the scroll rate set in rescaleImage is optimized for srolling
985  // with the cursor keys, but this is too high for mouse scrolling
986  // so change the scroll rate here and restore later in OnMiddleMouseUp
987  m_middleMouseScroll = true;
988  m_scrollPos = mouse.GetPosition();
989  break;
990  default:
991  // in other case ignore the middle mouse button
992  break;
993  };
994 }
995 
996 void MaskImageCtrl::OnMiddleMouseUp(wxMouseEvent& mouse)
997 {
998  if (m_previewOnly)
999  {
1000  return;
1001  };
1002  if (HasCapture())
1003  {
1004  ReleaseMouse();
1005  };
1006  m_middleMouseScroll = false;
1007 }
1008 
1009 void MaskImageCtrl::OnMouseWheel(wxMouseEvent& mouse)
1010 {
1011  if (!m_previewOnly && wxGetKeyState(WXK_COMMAND))
1012  {
1014  {
1015  // process mouse wheel event only when there is an active polygon with/without active selection
1016  // check that mouse is inside the polygon
1017  wxPoint mpos;
1018  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y, &mpos.x, &mpos.y);
1019  hugin_utils::FDiff2D currentPos = applyRotInv(invtransform(mpos));
1020  if (m_editingMask.isInside(currentPos))
1021  {
1022  // scale polygon around the center point of the polygon
1023  m_editingMask.scale(mouse.GetWheelRotation() > 0 ? 1.05 : 0.95, m_editingMask.getCenter());
1025  // update in Panorama class, lets do it by editor panel
1027  return;
1028  };
1029  }
1030  else
1031  {
1033  {
1034  // scale crop rect
1035  m_cropRect.addBorder((mouse.GetWheelRotation() > 0 ? 0.05 : -0.05) * std::min(m_cropRect.width(), m_cropRect.height()));
1036  if (!m_cropCircle)
1037  {
1038  // clip to image size, only for rectangular crop
1039  // for circular crop let the crop area let go outside of image
1040  m_cropRect &= vigra::Rect2D(0, 0, m_realSize.GetWidth(), m_realSize.GetHeight());
1041  };
1042  // update in Panorama class, lets do it by editor panel
1043  m_editPanel->UpdateCrop(true);
1044  return;
1045  };
1046  };
1047  }
1048  // in all other case do normal event processing
1049  // to allow scrolling with mouse wheel
1050  mouse.Skip();
1051 }
1052 
1053 void MaskImageCtrl::OnKeyUp(wxKeyEvent &e)
1054 {
1055  const int key=e.GetKeyCode();
1056  bool processed=false;
1057  if((key==WXK_DELETE) || (key==WXK_NUMPAD_DELETE))
1058  {
1059  if(m_activeMask<UINT_MAX)
1060  {
1062  {
1063  if ((!m_selectedPoints.empty()) && (m_editingMask.getMaskPolygon().size() - m_selectedPoints.size() > 2))
1064  {
1065  for (HuginBase::UIntSet::const_reverse_iterator it = m_selectedPoints.rbegin(); it != m_selectedPoints.rend(); ++it)
1068  processed = true;
1070  }
1071  else
1072  {
1073  if (m_editingMask.getMaskPolygon().size() == m_selectedPoints.size())
1074  {
1075  wxCommandEvent dummy;
1076  processed = true;
1077  m_editPanel->OnMaskDelete(dummy);
1078  }
1079  else
1080  wxBell();
1081  };
1082  }
1083  else
1084  {
1086  {
1087  wxCommandEvent dummy;
1088  processed=true;
1089  m_editPanel->OnMaskDelete(dummy);
1090  };
1091  };
1092  };
1093  }
1094  else
1095  {
1096  // handle key 0|1|2 only when editor state allows it
1099  {
1100  if (key == '1')
1101  {
1102  wxCommandEvent zoomEvent(wxEVT_CHOICE, XRCID("mask_editor_choice_zoom"));
1103  zoomEvent.SetInt(0);
1104  zoomEvent.SetString("update_selection");
1105  GetParent()->GetEventHandler()->AddPendingEvent(zoomEvent);
1106  }
1107  else
1108  {
1109  if (key == '2')
1110  {
1111  wxCommandEvent zoomEvent(wxEVT_CHOICE, XRCID("mask_editor_choice_zoom"));
1112  zoomEvent.SetInt(2);
1113  zoomEvent.SetString("update_selection");
1114  GetParent()->GetEventHandler()->AddPendingEvent(zoomEvent);
1115  }
1116  else
1117  {
1118  if (key == '0')
1119  {
1120  wxCommandEvent zoomEvent(wxEVT_CHOICE, XRCID("mask_editor_choice_zoom"));
1121  zoomEvent.SetInt(1);
1122  zoomEvent.SetString("update_selection");
1123  GetParent()->GetEventHandler()->AddPendingEvent(zoomEvent);
1124  };
1125  };
1126  };
1127  };
1128  };
1129  if(!processed)
1130  e.Skip();
1131 };
1132 
1133 void MaskImageCtrl::OnChar(wxKeyEvent& e)
1134 {
1135  wxPoint offset = GetViewStart();
1136  const int speed = std::max(m_bitmap.GetWidth(), m_bitmap.GetHeight()) / 75;
1137  switch (e.GetKeyCode())
1138  {
1139  // the default scrolling behaviour of the cursor key is too fine
1140  // using SetScrollRate with higher values conflicts with the middle mouse button
1141  // scrollling
1142  // so instead handle the cursor scrolling here with a higher step rate
1143  case WXK_LEFT:
1144  offset.x -= speed;
1145  Scroll(offset);
1146  break;
1147  case WXK_RIGHT:
1148  offset.x += speed;
1149  Scroll(offset);
1150  break;
1151  case WXK_UP:
1152  offset.y -= speed;
1153  Scroll(offset);
1154  break;
1155  case WXK_DOWN:
1156  offset.y += speed;
1157  Scroll(offset);
1158  break;
1159  default:
1160  //process event further
1161  e.Skip();
1162  };
1163 }
1164 void MaskImageCtrl::OnCaptureLost(wxMouseCaptureLostEvent &e)
1165 {
1166  wxFocusEvent dummy;
1167  OnKillFocus(dummy);
1168 };
1169 
1170 void MaskImageCtrl::OnKillFocus(wxFocusEvent &e)
1171 {
1172  if(HasCapture())
1173  ReleaseMouse();
1175  {
1176  wxBell();
1179  m_editingMask=mask;
1180  m_selectedPoints.clear();
1181  Refresh();
1182  };
1183 };
1184 
1186 {
1188  HuginBase::MaskPolygon newMask;
1189  m_editingMask=newMask;
1190  m_selectedPoints.clear();
1191 };
1192 
1194 {
1195  return wxSize(m_imageSize.GetWidth(),m_imageSize.GetHeight());
1196 };
1197 
1198 void MaskImageCtrl::DrawPolygon(wxDC &dc, HuginBase::MaskPolygon poly, bool isSelected, bool drawMarker)
1199 {
1200  unsigned int nrOfPoints=poly.getMaskPolygon().size();
1201  if (nrOfPoints<2)
1202  return;
1203  wxPoint *polygonPoints=new wxPoint[nrOfPoints];
1204  for(unsigned int j=0;j<nrOfPoints;j++)
1205  {
1206  polygonPoints[j]=transform(applyRot(poly.getMaskPolygon()[j]));
1207  };
1208  if(isSelected)
1209  dc.SetPen(wxPen(m_colour_point_unselected, 1, wxPENSTYLE_SOLID));
1210  else
1211  switch(poly.getMaskType())
1212  {
1216  dc.SetPen(wxPen(m_colour_polygon_negative, 1, wxPENSTYLE_SOLID));
1217  break;
1220  dc.SetPen(wxPen(m_colour_polygon_positive, 1, wxPENSTYLE_SOLID));
1221  break;
1222  };
1223  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1224  if(nrOfPoints>2)
1225  dc.DrawPolygon(nrOfPoints,polygonPoints);
1226  else
1227  dc.DrawLine(polygonPoints[0],polygonPoints[1]);
1228  if(drawMarker)
1229  {
1230  wxPen penSelected(m_colour_point_selected);
1231  wxPen penUnselected(m_colour_point_unselected);
1232  wxBrush brushSelected(m_colour_point_selected);
1233  wxBrush brushUnselected(m_colour_point_unselected);
1234  for(unsigned int j=0;j<nrOfPoints;j++)
1235  {
1237  {
1238  dc.SetPen(penSelected);
1239  dc.SetBrush(brushSelected);
1240  }
1241  else
1242  {
1243  dc.SetPen(penUnselected);
1244  dc.SetBrush(brushUnselected);
1245  };
1246  dc.DrawRectangle(polygonPoints[j].x-polygonPointSize,polygonPoints[j].y-polygonPointSize,
1248  };
1249  };
1250  delete []polygonPoints;
1251 };
1252 
1254 {
1255  // draw crop rectangle/circle
1256  if(!m_maskMode)
1257  {
1258  // draw all areas without fillings
1259  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1260  dc.SetPen(wxPen(m_color_selection, 2, wxPENSTYLE_SOLID));
1261  wxPoint middle=transform(applyRot((m_cropRect.lowerRight()+m_cropRect.upperLeft())/2));
1262 
1263  int c = 8; // size of midpoint cross
1264  dc.DrawLine( middle.x + c, middle.y + c, middle.x - c, middle.y - c);
1265  dc.DrawLine( middle.x - c, middle.y + c, middle.x + c, middle.y - c);
1266  dc.DrawRectangle(wxRect(transform(applyRot(hugin_utils::FDiff2D(m_cropRect.left(), m_cropRect.top()))),
1268 
1269  // draw crop circle as well, if requested.
1271  {
1272  double radius=std::min<int>(m_cropRect.width(),m_cropRect.height())/2.0;
1273  dc.DrawCircle(middle.x, middle.y, scale(radius));
1274  }
1275  };
1276 };
1277 
1278 void MaskImageCtrl::OnPaint(wxPaintEvent& e)
1279 {
1280  wxAutoBufferedPaintDC dc(this);
1281  PrepareDC(dc);
1282  dc.SetBackground(GetBackgroundColour());
1283  dc.Clear();
1284  if(m_maskEditState!=NO_IMAGE && m_bitmap.IsOk())
1285  {
1286  const int offset=scale(HuginBase::maskOffset);
1287  dc.DrawBitmap(m_bitmap, offset, offset);
1289  {
1290  //whole image, we need it several times
1291  wxRegion wholeImage(transform(applyRot(hugin_utils::FDiff2D(0,0))),
1292  transform(applyRot(hugin_utils::FDiff2D(m_realSize.GetWidth(),m_realSize.GetHeight()))));
1293  wxRegion region;
1295  {
1296  region.Union(wholeImage);
1297  //now the crop
1298  switch(m_cropMode)
1299  {
1301  region.Subtract(wxRegion(transform(applyRot(m_cropRect.upperLeft())),
1302  transform(applyRot(m_cropRect.lowerRight()))));
1303  break;
1305  {
1306  unsigned int nrOfPoints = dc.GetSize().GetWidth() * 2;
1307  wxPoint* circlePoints = new wxPoint[nrOfPoints];
1308  vigra::Point2D middle = (m_cropRect.lowerRight() + m_cropRect.upperLeft()) / 2;
1309  double radius = std::min<int>(m_cropRect.width(), m_cropRect.height()) / 2;
1310  double interval = 2 * PI / nrOfPoints;
1311  for (unsigned int i = 0; i < nrOfPoints; i++)
1312  {
1313  circlePoints[i] = transform(applyRot(hugin_utils::FDiff2D(middle.x + radius*cos(i*interval), middle.y + radius*sin(i*interval))));
1314  };
1315  region.Subtract(wxRegion(nrOfPoints, circlePoints));
1316  delete[]circlePoints;
1317  }
1318  break;
1320  break;
1321  };
1322  };
1323  if(!m_masksToDraw.empty())
1324  {
1325  for(unsigned int i=0;i<m_masksToDraw.size();i++)
1326  {
1327  HuginBase::VectorPolygon poly=m_masksToDraw[i].getMaskPolygon();
1328  wxPoint *polygonPoints=new wxPoint[poly.size()];
1329  for(unsigned int j=0;j<poly.size();j++)
1330  {
1331  polygonPoints[j]=transform(applyRot(poly[j]));
1332  };
1333  wxRegion singleRegion(poly.size(),polygonPoints,wxWINDING_RULE);
1334  if(m_masksToDraw[i].isInverted())
1335  {
1336  wxRegion newRegion(wholeImage);
1337  newRegion.Subtract(singleRegion);
1338  region.Union(newRegion);
1339  }
1340  else
1341  {
1342  region.Union(singleRegion);
1343  };
1344  delete []polygonPoints;
1345  };
1346  };
1347 #ifndef __WXMAC__
1348  // on Windows and GTK we need to compensate to clipping region
1349  // by the scroll offset
1350  // this seems not to be necessary for wxMac
1351  int x;
1352  int y;
1353  GetViewStart(&x,&y);
1354  region.Offset(-x,-y);
1355 #endif
1356  dc.SetDeviceClippingRegion(region);
1357  dc.DrawBitmap(m_disabledBitmap,offset,offset);
1358  dc.DestroyClippingRegion();
1359  };
1360  DrawCrop(dc);
1361  if(m_maskMode && !m_imageMask.empty())
1362  {
1363  //now draw all polygons
1366  for(unsigned int i=0;i<maskList.size();i++)
1367  {
1368  if(i!=m_activeMask)
1369  DrawPolygon(dc,maskList[i],false,false);
1370  else
1371  if(drawSelected)
1372  DrawPolygon(dc,maskList[i],true,true);
1373  };
1374  };
1375  //and now the actual polygon
1377  DrawPolygon(dc,m_editingMask,true,true);
1378  };
1379 
1380 }
1381 
1382 void MaskImageCtrl::OnSize(wxSizeEvent &e)
1383 {
1384  DEBUG_TRACE("size: " << e.GetSize().GetWidth() << "x" << e.GetSize().GetHeight());
1385  // rescale m_bitmap if needed.
1386  if (m_imageFilename != "") {
1387  if (m_fitToWindow) {
1388  setScale(0);
1389  }
1390  }
1391 };
1392 
1393 void MaskImageCtrl::OnScroll(wxScrollWinEvent &e)
1394 {
1395  m_oldScrollPosX = GetScrollPos(wxHORIZONTAL);
1396  m_oldScrollPosY = GetScrollPos(wxVERTICAL);
1397  e.Skip();
1398 }
1399 
1401 {
1402  if (m_maskEditState == NO_IMAGE)
1403  {
1404  return;
1405  }
1406  //determine average colour and set selection colour corresponding
1407  vigra::FindAverage<vigra::RGBValue<vigra::UInt8> > average;
1408  vigra::inspectImage(vigra::srcImageRange(*(m_img->get8BitImage())), average);
1409  vigra::RGBValue<vigra::UInt8> RGBaverage=average.average();
1410  if(RGBaverage[0]<180 && RGBaverage[1]<180 && RGBaverage[2]<180)
1411  {
1412  m_color_selection=*wxWHITE;
1413  }
1414  else
1415  {
1416  m_color_selection=*wxBLACK;
1417  };
1418  wxImage img = imageCacheEntry2wxImage(m_img);
1419  if (img.GetWidth() == 0)
1420  {
1421  return;
1422  }
1423  m_imageSize = wxSize(img.GetWidth(), img.GetHeight());
1426  if (m_fitToWindow)
1428 
1429  //scaling image to screen size
1430  if (getScaleFactor()!=1.0)
1431  {
1432  m_imageSize.SetWidth(scale(m_imageSize.GetWidth()));
1433  m_imageSize.SetHeight(scale(m_imageSize.GetHeight()));
1434  wxImageResizeQuality resizeQuality = wxIMAGE_QUALITY_NORMAL;
1435  if (std::max(img.GetWidth(), img.GetHeight()) > (ULONG_MAX >> 16))
1436  {
1437  // wxIMAGE_QUALITY_NORMAL resizes the image with ResampleNearest
1438  // this algorithm works only if image dimensions are smaller then
1439  // ULONG_MAX >> 16 (actual size of unsigned long differ from system
1440  // to system)
1441  resizeQuality = wxIMAGE_QUALITY_BOX_AVERAGE;
1442  };
1443  img=img.Scale(scale(m_realSize.GetWidth()), scale(m_realSize.GetHeight()), resizeQuality);
1444  }
1445  else
1446  {
1447  //the conversion to disabled m_bitmap would work on the original cached image file
1448  //therefore we need to create a copy to work on it
1449  img=img.Copy();
1450  };
1451  //and now rotating
1452  switch(m_imgRotation)
1453  {
1454  case ROT90:
1455  img = img.Rotate90(true);
1456  break;
1457  case ROT180:
1458  img = img.Rotate180();
1459  break;
1460  case ROT270:
1461  img = img.Rotate90(false);
1462  break;
1463  default:
1464  break;
1465  }
1466  // do color correction only if input image has icc profile or if we found a monitor profile
1467  if (!m_img->iccProfile->empty() || huginApp::Get()->HasMonitorProfile())
1468  {
1470  };
1471  m_bitmap=wxBitmap(img);
1472 
1473  //create disabled m_bitmap for drawing active masks
1474  img = img.ConvertToDisabled(192);
1475  m_disabledBitmap=wxBitmap(img);
1476  if (m_imgRotation == ROT90 || m_imgRotation == ROT270)
1477  {
1478  SetVirtualSize(m_imageSize.GetHeight(), m_imageSize.GetWidth());
1479  }
1480  else
1481  {
1482  SetVirtualSize(m_imageSize.GetWidth(), m_imageSize.GetHeight());
1483  };
1484  SetScrollRate(1, 1);
1485  // reset overlay
1486  m_overlay.Reset();
1487  Refresh(true);
1488 };
1489 
1491 {
1492 #if wxCHECK_VERSION(3,3,0)
1493  wxOverlayDC dc(m_overlay, this);
1494  PrepareDC(dc);
1495  dc.Clear();
1496 #else
1497  wxClientDC dc(this);
1498  PrepareDC(dc);
1499  wxDCOverlay overlayDC(m_overlay, &dc);
1500  overlayDC.Clear();
1501 #endif
1502  dc.SetPen(wxPen(m_color_selection, 2, wxPENSTYLE_LONG_DASH));
1503  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1504  dc.DrawRectangle(m_dragStartPos.x,m_dragStartPos.y,
1506 };
1507 
1509 {
1510  unsigned int selectedPolygon=UINT_MAX;
1511  unsigned int i=0;
1512  while(selectedPolygon==UINT_MAX && i<m_imageMask.size())
1513  {
1514  if(m_imageMask[i].isInside(p))
1515  selectedPolygon=i;
1516  i++;
1517  };
1518  if(selectedPolygon<UINT_MAX)
1519  m_editPanel->SelectMask(selectedPolygon);
1520 };
1521 
1522 bool MaskImageCtrl::SelectPointsInsideMouseRect(HuginBase::UIntSet &points,const bool considerSelectedOnly)
1523 {
1524  bool found=false;
1527  double xmin = std::min(p1.x, p2.x) - maxSelectionDistance / getScaleFactor();
1528  double xmax = std::max(p1.x, p2.x) + maxSelectionDistance / getScaleFactor();
1529  double ymin = std::min(p1.y, p2.y) - maxSelectionDistance / getScaleFactor();
1530  double ymax = std::max(p1.y, p2.y) + maxSelectionDistance / getScaleFactor();
1532  for(unsigned int i=0;i<poly.size();i++)
1533  {
1534  bool activePoints=true;
1535  if(considerSelectedOnly)
1536  activePoints=set_contains(m_selectedPoints,i);
1537  if(activePoints && xmin<=poly[i].x && poly[i].x<=xmax && ymin<=poly[i].y && poly[i].y<=ymax)
1538  {
1539  points.insert(i);
1540  found=true;
1541  };
1542  };
1543  return found;
1544 };
1545 
1546 void MaskImageCtrl::setScale(double factor)
1547 {
1548  if (factor == 0)
1549  {
1550  m_fitToWindow = true;
1551  factor = calcAutoScaleFactor(m_imageSize);
1552  }
1553  else
1554  {
1555  m_fitToWindow = false;
1556  }
1557  DEBUG_DEBUG("new scale factor:" << factor);
1558  // update if factor changed
1559  if (factor != m_scaleFactor)
1560  {
1561  m_scaleFactor = factor;
1562  // keep existing scale focussed.
1563  rescaleImage();
1564  }
1565 };
1566 
1568 {
1569  int w = size.GetWidth();
1570  int h = size.GetHeight();
1571  if (m_imgRotation == ROT90 || m_imgRotation == ROT270)
1572  {
1573  int t = w;
1574  w = h;
1575  h = t;
1576  }
1577 
1578  wxSize csize = GetSize();
1579  DEBUG_DEBUG("csize: " << csize.GetWidth() << "x" << csize.GetHeight() << "image: " << w << "x" << h);
1580  double s1 = (double)csize.GetWidth()/w;
1581  double s2 = (double)csize.GetHeight()/h;
1582  DEBUG_DEBUG("s1: " << s1 << " s2:" << s2);
1583  return s1 < s2 ? s1 : s2;
1584 };
1585 
1587 {
1588  return m_scaleFactor;
1589 };
1590 
1591 void MaskImageCtrl::setDrawingActiveMasks(bool newDrawActiveMasks)
1592 {
1593  m_showActiveMasks=newDrawActiveMasks;
1594  Refresh();
1595 };
1596 
1597 IMPLEMENT_DYNAMIC_CLASS(MaskImageCtrl, wxScrolledWindow)
1598 
1600  : wxXmlResourceHandler()
1601 {
1602  AddWindowStyles();
1603 };
1604 
1606 {
1607  XRC_MAKE_INSTANCE(cp, MaskImageCtrl)
1608 
1609  cp->Create(m_parentAsWindow,
1610  GetID(),
1611  GetPosition(), GetSize(),
1612  GetStyle("style"),
1613  GetName());
1614 
1615  SetupWindow(cp);
1616 
1617  return cp;
1618 };
1619 
1621 {
1622  return IsOfClass(node, "MaskImageCtrl");
1623 };
1624 
1625 IMPLEMENT_DYNAMIC_CLASS(MaskImageCtrlXmlHandler, wxXmlResourceHandler)
bool m_showActiveMasks
ImageRotation
image rotation.
Definition: MaskImageCtrl.h:55
implementation of huginApp Class
T applyRotInv(const T &p) const
wxPoint m_currentPos
void UpdateCropFromImage()
updates the displayed crop in the text boxes (for dragging)
hugin_utils::FDiff2D getCenter() const
return the center point of the polygon
Definition: Mask.cpp:646
bool isInside(const hugin_utils::FDiff2D p) const
checks if given point is inside of the stored polygon
Definition: Mask.cpp:40
void removePoint(const unsigned int index)
removes point at the position index from the polygon
Definition: Mask.cpp:124
int roundi(T x)
Definition: hugin_math.h:73
wxColour m_color_selection
void OnKeyUp(wxKeyEvent &e)
event handler for keyboard
wxBitmap m_bitmap
wxOverlay m_overlay
HuginBase::SrcPanoImage::CropMode m_cropMode
const int maskOffset
polygon can exceed the image maximal maskOffset pixels in each direction bigger polygons will be clip...
Definition: Mask.h:44
void movePointBy(const unsigned int index, const hugin_utils::FDiff2D diff)
relativ moves the point at position index by diff
Definition: Mask.cpp:142
#define HUGIN_CONV_FILENAME
Definition: platform.h:40
void setActiveMask(unsigned int newMask, bool doUpdate=true)
mark mask with image as beeing editing
bool SelectPointsInsideMouseRect(HuginBase::UIntSet &points, const bool considerSelectedOnly)
#define DEBUG_TRACE(msg)
Definition: utils.h:67
void SelectMask(unsigned int newMaskNr)
selects the mask with index newMaskNr in the listbox
const cmsHPROFILE GetMonitorProfile() const
returns the monitor profile, if no monitor profile was found the sRGB profile is used instead ...
Definition: huginApp.h:115
MaskEditorPanel * m_editPanel
std::string m_imageFilename
virtual wxObject * DoCreateResource()
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition: stl_utils.h:74
T sqr(T t)
Definition: hugin_math.h:181
hugin_utils::FDiff2D m_cropCenter
include file for the hugin project
mask editor panel.
void rescaleImage()
rescale the image
void OnLeftMouseDblClick(wxMouseEvent &mouse)
event handler for left double click
wxColor m_colour_point_selected
#define PI
Header file for Khan&#39;s deghosting algorithm Copyright (C) 2009 Lukáš Jirkovský l...
Definition: khan.h:43
void OnPaint(wxPaintEvent &e)
drawing routine
static huginApp * Get()
hack.. kind of a pseudo singleton...
Definition: huginApp.cpp:651
HuginBase::MaskPolygonVector m_imageMask
wxPoint m_scrollPos
VectorPolygon getMaskPolygon() const
returns vector with coordinates of the polygon
Definition: Mask.h:81
mask editor
Definition: MaskImageCtrl.h:38
void OnMouseMove(wxMouseEvent &mouse)
event handler when mouse is moving
std::set< unsigned int > UIntSet
Definition: PanoramaData.h:51
void OnRightMouseUp(wxMouseEvent &mouse)
event handler when right mouse button is released
xrc handler for mask editor
void setImage(const std::string &filename, HuginBase::MaskPolygonVector newMask, HuginBase::MaskPolygonVector masksToDraw, ImageRotation rot)
set the current image and mask list, this loads also the image from cache
const int polygonPointSize
half size of markers
int invtransform(int x) const
translate screen coordinates to image coordinates, considers additional added border ...
void CorrectImage(wxImage &image, const vigra::ImageImportInfo::ICCProfile &iccProfile, const cmsHPROFILE &monitorProfile)
apply color correction to given image using input iccProfile and monitor profile
Definition: wxcms.cpp:218
static MainFrame * Get()
hack.. kind of a pseudo singleton...
Definition: MainFrame.cpp:2129
void OnRightMouseDown(wxMouseEvent &mouse)
event handler when right mouse button is pressed
wxImage imageCacheEntry2wxImage(ImageCache::EntryPtr e)
void OnMouseWheel(wxMouseEvent &mouse)
event handler for mouse wheel
IMPLEMENT_DYNAMIC_CLASS(wxTreeListHeaderWindow, wxWindow)
double calcAutoScaleFactor(wxSize size)
calculate new scale factor for this image
bool HasMonitorProfile() const
return true if we found a suitable monitor profile and could loading it
Definition: huginApp.h:120
void UpdateMask()
called when mask where changed in MaskImageCtrl
IMPEX double h[25][1024]
Definition: emor.cpp:169
void OnSize(wxSizeEvent &e)
handler called when size of control was changed
void UpdateCrop(bool updateFromImgCtrl=false)
updated the crop in the Panorama object with the current values from GUI
int scale(int x) const
scale of width/height
void OnCaptureLost(wxMouseCaptureLostEvent &e)
event handler, when mouse capture is lost, e.g.
double getScaleFactor() const
get scale factor (calculates factor when fit to window is active)
wxSize m_imageSize
std::vector< MaskPolygon > MaskPolygonVector
Definition: Mask.h:150
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
void OnKillFocus(wxFocusEvent &e)
event handler, when editor lost focus, mainly cancels creating new polygon
void DrawSelectionRectangle()
void selectAllMarkers()
select all points of active mask
void setNewMasks(HuginBase::MaskPolygonVector newMasks, HuginBase::MaskPolygonVector masksToDraw)
updates masks for currently selected image
bool Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxTAB_TRAVERSAL, const wxString &name="panel")
void OnMiddleMouseDown(wxMouseEvent &mouse)
event handler for middle mouse button, start scrolling
ImageRotation m_imgRotation
include file for the hugin project
void movePointTo(const unsigned int index, const hugin_utils::FDiff2D p)
moves the point at position index to the new absolute position p
Definition: Mask.cpp:133
void OnLeftMouseUp(wxMouseEvent &mouse)
event handler when right mouse button is released
void insertPoint(const unsigned int index, const hugin_utils::FDiff2D p)
insert point at the position index into the polygon
Definition: Mask.cpp:115
bool m_middleMouseScroll
void setScale(double factor)
set the scaling factor for mask editing display.
MaskEditorState m_maskEditState
double m_scaleFactor
std::vector< hugin_utils::FDiff2D > VectorPolygon
vector, which stores coordinates of one polygon
Definition: Mask.h:39
static T max(T x, T y)
Definition: svm.cpp:65
ClickPos GetClickPos(vigra::Point2D pos)
#define DEBUG_DEBUG(msg)
Definition: utils.h:68
void Init(MaskEditorPanel *parent)
void SetMaskMode(bool newMaskMode)
sets the control to mask (newMaskMode=true) or crop (newMaskMode=false) mode
const int maxSelectionDistance
maximal distance for selection of one point
unsigned int FindPointNearPos(const hugin_utils::FDiff2D p, const double tol) const
search a point which lies near the polygon line and return the index for inserting the new point ...
Definition: Mask.cpp:565
void DrawPolygon(wxDC &dc, HuginBase::MaskPolygon poly, bool isSelected, bool drawMarker)
void OnMaskDelete(wxCommandEvent &e)
called when user wants to delete active mask
HuginBase::UIntSet m_selectedPoints
ImageCache::EntryPtr m_img
MaskType getMaskType() const
returns mask type
Definition: Mask.h:75
wxColor m_colour_polygon_positive
void fill_set(_Container &c, typename _Container::key_type begin, typename _Container::key_type end)
Definition: stl_utils.h:81
void OnScroll(wxScrollWinEvent &e)
event handler for remember scroll position
T applyRot(const T &p) const
void startNewPolygon()
starts creating a new polygon
wxBitmap m_disabledBitmap
unsigned int m_activeMask
wxColor m_colour_polygon_negative
wxColor m_colour_point_unselected
void AddMask()
called when new mask added in MaskImageCtrl
wxSize DoGetBestSize() const
returns size of currently scaled image
void FindPolygon(hugin_utils::FDiff2D p)
void setCrop(HuginBase::SrcPanoImage::CropMode newCropMode, vigra::Rect2D newCropRect, bool isCentered, hugin_utils::FDiff2D center, bool isCircleCrop)
updates the crop mode and crop rect
int transform(int x) const
convert image coordinate to screen coordinates, considers additional added border ...
HuginBase::MaskPolygonVector m_masksToDraw
vigra::Rect2D m_cropRect
wxPoint m_dragStartPos
void UpdateCrop(hugin_utils::FDiff2D delta)
virtual bool CanHandle(wxXmlNode *node)
static T min(T x, T y)
Definition: svm.cpp:62
void OnMiddleMouseUp(wxMouseEvent &mouse)
event handler for middle mouse button, end scrolling
void OnLeftMouseDown(wxMouseEvent &mouse)
event handler when left mouse button is pressed
void addPoint(const hugin_utils::FDiff2D p)
adds point at the end to the polygon
Definition: Mask.cpp:109
void scale(const double factorx, const double factory)
scales all polygon coordinates by factorx for x position and factory for y position ...
Definition: Mask.cpp:151
base class, which stores one mask polygon
Definition: Mask.h:52
void OnChar(wxKeyEvent &e)
event handler for scrolling with keyboard
void setDrawingActiveMasks(bool newDrawActiveMasks)
set if active masks should be drawn
void DrawCrop(wxDC &dc)
HuginBase::MaskPolygon m_editingMask