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 "base_wx/platform.h"
32 #include "base_wx/wxcms.h"
33 #include "hugin/MainFrame.h"
34 #include "hugin/huginApp.h"
35 #include "hugin/config_defaults.h"
36 #include "hugin/MaskImageCtrl.h"
37 #include "hugin/MaskEditorPanel.h"
38 #include "base_wx/wxImageCache.h"
39 #include <vigra/inspectimage.hxx>
40 
42 const int polygonPointSize=3;
44 const int maxSelectionDistance = 10;
45 
46 // our image control
47 
48 BEGIN_EVENT_TABLE(MaskImageCtrl, wxScrolledWindow)
49  EVT_SIZE(MaskImageCtrl::OnSize)
50  EVT_MOTION(MaskImageCtrl::OnMouseMove)
51  EVT_LEFT_DOWN(MaskImageCtrl::OnLeftMouseDown)
52  EVT_LEFT_UP(MaskImageCtrl::OnLeftMouseUp)
53  EVT_LEFT_DCLICK(MaskImageCtrl::OnLeftMouseDblClick)
54  EVT_RIGHT_DOWN(MaskImageCtrl::OnRightMouseDown)
55  EVT_RIGHT_UP(MaskImageCtrl::OnRightMouseUp)
56  EVT_MIDDLE_DOWN(MaskImageCtrl::OnMiddleMouseDown)
57  EVT_MIDDLE_UP(MaskImageCtrl::OnMiddleMouseUp)
58  EVT_KEY_UP(MaskImageCtrl::OnKeyUp)
59  EVT_CHAR(MaskImageCtrl::OnChar)
60  EVT_MOUSE_CAPTURE_LOST(MaskImageCtrl::OnCaptureLost)
61  EVT_KILL_FOCUS(MaskImageCtrl::OnKillFocus)
62  EVT_SCROLLWIN(MaskImageCtrl::OnScroll)
64 
65 bool MaskImageCtrl::Create(wxWindow * parent, wxWindowID id,
66  const wxPoint& pos,
67  const wxSize& size,
68  long style,
69  const wxString& name)
70 {
71  wxScrolledWindow::Create(parent, id, pos, size, style, name);
72  m_maskEditState = NO_IMAGE;
73  m_imgRotation = ROT0;
74  m_scaleFactor = 1;
75  m_fitToWindow = false;
76  m_previewOnly = false;
77  m_activeMask = UINT_MAX;
78  m_showActiveMasks = false;
79  m_maskMode = true;
80  m_oldScrollPosX = -1;
81  m_oldScrollPosY = -1;
82  m_middleMouseScroll = false;
83 
84  return true;
85 }
86 
88 {
89  m_editPanel = parent;
90 }
91 
92 void MaskImageCtrl::SetMaskMode(bool newMaskMode)
93 {
94  m_maskMode=newMaskMode;
95  if(m_maskMode)
96  {
97  SetCursor(wxNullCursor);
99  {
101  setActiveMask(UINT_MAX,false);
102  };
103  }
104  else
105  {
107  {
109  };
110  };
111  ClearOverlay();
112  update();
113 };
114 
116 {
117  DEBUG_TRACE("setting Image " << file);
118  if(!file.empty())
119  {
120  try
121  {
122  m_img = ImageCache::getInstance().getImage(file);
123  }
124  catch (...)
125  {
126  // loading of image failed, set all to empty values
127  m_imageFilename = "";
129  m_bitmap = wxBitmap();
130  // delete the image (release shared_ptr)
131  // create an empty image.
132  m_img = ImageCache::EntryPtr(new ImageCache::Entry);
134  m_imageMask = mask;
135  m_masksToDraw = mask;
137  setActiveMask(UINT_MAX, false);
138  SetVirtualSize(100, 100);
139  Refresh(true);
140  // now notify main frame to remove the image from project
141  wxCommandEvent e(EVT_LOADING_FAILED);
142  e.SetString(wxString(file.c_str(), HUGIN_CONV_FILENAME));
143  MainFrame::Get()->GetEventHandler()->AddPendingEvent(e);
144  return;
145  }
146  m_imageFilename = file;
147  if(m_maskMode)
148  {
150  }
151  else
152  {
154  };
155  m_imageMask=newMask;
156  m_masksToDraw=masksToDraw;
157  m_imgRotation=rot;
158  setActiveMask(UINT_MAX,false);
159  rescaleImage();
160  update();
162  }
163  else
164  {
166  m_bitmap = wxBitmap();
168  m_overlay.Reset();
169  // delete the image (release shared_ptr)
170  // create an empty image.
171  m_img = ImageCache::EntryPtr(new ImageCache::Entry);
173  m_imageMask=mask;
174  m_masksToDraw=mask;
176  setActiveMask(UINT_MAX,false);
177  SetVirtualSize(100,100);
178  Refresh(true);
179  }
180 }
181 
183 {
184  m_imageMask=newMasks;
185  m_masksToDraw=masksToDraw;
186  if (m_activeMask >= m_imageMask.size())
187  {
188  setActiveMask(UINT_MAX);
189  }
190  else
191  {
193  };
194  Refresh(false);
195 };
196 
197 void MaskImageCtrl::setCrop(HuginBase::SrcPanoImage::CropMode newCropMode, vigra::Rect2D newCropRect, bool isCentered, hugin_utils::FDiff2D center, bool isCircleCrop)
198 {
199  m_cropMode=newCropMode;
200  m_cropRect=newCropRect;
201  m_cropCentered=isCentered;
202  m_cropCenter=center;
203  m_cropCircle=isCircleCrop;
204 };
205 
206 void MaskImageCtrl::setActiveMask(unsigned int newMask, bool doUpdate)
207 {
208  if(m_activeMask!=newMask)
209  {
210  m_activeMask=newMask;
211  m_selectedPoints.clear();
212  };
213  if(newMask<UINT_MAX)
214  {
215  if(m_maskMode)
216  {
217  if(m_selectedPoints.empty())
218  {
220  }
221  else
222  {
224  };
225  }
226  else
227  {
228  m_selectedPoints.clear();
230  };
232  }
233  else
234  {
235  if(!m_imageFilename.empty())
236  {
237  if(m_maskMode)
238  {
240  }
241  else
242  {
244  };
245  };
247  m_editingMask=mask;
248  };
249  if(doUpdate)
250  Refresh(true);
251 };
252 
254 {
255  m_selectedPoints.clear();
256  if(m_activeMask<UINT_MAX)
257  fill_set(m_selectedPoints,0,m_imageMask[m_activeMask].getMaskPolygon().size()-1);
258 };
259 
260 // returns where the user clicked
262 {
263  vigra::Rect2D testRect(pos.x-maxSelectionDistance, pos.y-maxSelectionDistance, pos.x+maxSelectionDistance, pos.y+maxSelectionDistance);
265  {
266  double radius=std::min<int>(m_cropRect.width(), m_cropRect.height())/2.0;
267  vigra::Point2D pos_center((m_cropRect.left()+m_cropRect.right())/2, (m_cropRect.top()+m_cropRect.bottom())/2);
268  double dist=sqrt(double((pos-pos_center).squaredMagnitude()));
269  if(dist-maxSelectionDistance<radius && radius<dist+maxSelectionDistance)
270  {
271  return CLICK_CIRCLE;
272  };
273  };
274  if(m_cropRect.intersects(testRect))
275  {
276  if(abs(pos.x-m_cropRect.left())<maxSelectionDistance)
277  {
278  return CLICK_LEFT;
279  };
280  if(abs(pos.x-m_cropRect.right())<maxSelectionDistance)
281  {
282  return CLICK_RIGHT;
283  };
284  if(abs(pos.y-m_cropRect.top())<maxSelectionDistance)
285  {
286  return CLICK_TOP;
287  };
288  if(abs(pos.y-m_cropRect.bottom())<maxSelectionDistance)
289  {
290  return CLICK_BOTTOM;
291  };
292  };
293  if(m_cropRect.contains(pos))
294  {
295  return CLICK_INSIDE;
296  };
297  return CLICK_OUTSIDE;
298 };
299 
301 {
303  int newLeft, newRight, newTop, newBottom;
304  bool needsUpdate=false;
305  switch (m_maskEditState)
306  {
307  case CROP_MOVING:
308  m_cropRect.moveBy(delta.x,delta.y);
309  break;
310  case CROP_LEFT_MOVING:
311  if(m_cropCentered)
312  {
313  double newHalfWidth=m_cropRect.width()/2.0-delta.x;
314  newLeft=hugin_utils::roundi(m_cropCenter.x-newHalfWidth);
315  newRight=hugin_utils::roundi(m_cropCenter.x+newHalfWidth);
316  }
317  else
318  {
319  newLeft=m_cropRect.left()+delta.x;
320  newRight=m_cropRect.right();
321  };
322  newTop=m_cropRect.top();
323  newBottom=m_cropRect.bottom();
324  needsUpdate=true;
325  break;
326  case CROP_RIGHT_MOVING:
327  if(m_cropCentered)
328  {
329  double newHalfWidth=m_cropRect.width()/2.0+delta.x;
330  newLeft=hugin_utils::roundi(m_cropCenter.x-newHalfWidth);
331  newRight=hugin_utils::roundi(m_cropCenter.x+newHalfWidth);
332  }
333  else
334  {
335  newLeft=m_cropRect.left();
336  newRight=m_cropRect.right()+delta.x;
337  };
338  newTop=m_cropRect.top();
339  newBottom=m_cropRect.bottom();
340  needsUpdate=true;
341  break;
342  case CROP_TOP_MOVING:
343  if(m_cropCentered)
344  {
345  double newHalfHeight=m_cropRect.height()/2.0-delta.y;
346  newTop=hugin_utils::roundi(m_cropCenter.y-newHalfHeight);
347  newBottom=hugin_utils::roundi(m_cropCenter.y+newHalfHeight);
348  }
349  else
350  {
351  newTop=m_cropRect.top()+delta.y;
352  newBottom=m_cropRect.bottom();
353  };
354  newLeft=m_cropRect.left();
355  newRight=m_cropRect.right();
356  needsUpdate=true;
357  break;
358  case CROP_BOTTOM_MOVING:
359  if(m_cropCentered)
360  {
361  double newHalfHeight=m_cropRect.height()/2.0+delta.y;
362  newTop=hugin_utils::roundi(m_cropCenter.y-newHalfHeight);
363  newBottom=hugin_utils::roundi(m_cropCenter.y+newHalfHeight);
364  }
365  else
366  {
367  newTop=m_cropRect.top();
368  newBottom=m_cropRect.bottom()+delta.y;
369  };
370  newLeft=m_cropRect.left();
371  newRight=m_cropRect.right();
372  needsUpdate=true;
373  break;
374  case CROP_CIRCLE_SCALING:
375  {
376  double radius=sqrt(hugin_utils::sqr(pos.x-m_dragStartPos.x)+hugin_utils::sqr(pos.y-m_dragStartPos.y));
377  newLeft=m_dragStartPos.x-radius;
378  newRight=m_dragStartPos.x+radius;
379  newTop=m_dragStartPos.y-radius;
380  newBottom=m_dragStartPos.y+radius;
381  needsUpdate=true;
382  };
383  break;
384  default:
385  // in all other cases the crop is not changed
386  // this should not happen
387  break;
388  };
389  if(needsUpdate)
390  {
391  // switch left/right or top/bottom if necessary
392  if(newLeft>newRight)
393  {
394  int temp=newLeft;
395  newLeft=newRight;
396  newRight=temp;
397  };
398  if(newTop>newBottom)
399  {
400  int temp=newTop;
401  newTop=newBottom;
402  newBottom=temp;
403  };
404  m_cropRect.setUpperLeft(vigra::Point2D(newLeft, newTop));
405  m_cropRect.setLowerRight(vigra::Point2D(newRight, newBottom));
406  };
407 };
408 
409 void MaskImageCtrl::OnMouseMove(wxMouseEvent& mouse)
410 {
411  if(m_previewOnly)
412  return;
414  {
415  // handle scrolling and break out
416  wxPoint viewStart = GetViewStart();
417  viewStart = viewStart - (mouse.GetPosition() - m_scrollPos);
418  Scroll(viewStart);
419  m_scrollPos = mouse.GetPosition();
420  return;
421  };
422  wxPoint mpos;
423  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
424  &mpos.x, & mpos.y);
426  bool doUpdate = false;
427  switch(m_maskEditState)
428  {
430  doUpdate=true;
431  m_editingMask.movePointTo(m_editingMask.getMaskPolygon().size()-1,currentPos);
432  break;
433  case POLYGON_SELECTING:
434  case REGION_SELECTING:
435  case POINTS_DELETING:
436  m_currentPos=mpos;
438  break;
439  case POINTS_MOVING:
440  doUpdate=true;
442  {
444  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
445  m_editingMask.movePointBy(*it,delta);
446  };
447  break;
448  case POINTS_ADDING:
449  doUpdate=true;
450  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
451  m_editingMask.movePointTo(*it,currentPos);
452  break;
453  case CROP_SHOWING:
454  switch(GetClickPos(vigra::Point2D(currentPos.x, currentPos.y)))
455  {
456  case CLICK_INSIDE:
457  if(!m_cropCentered)
458  {
459  SetCursor(wxCURSOR_HAND);
460  }
461  else
462  {
463  SetCursor(wxNullCursor);
464  };
465  break;
466  case CLICK_LEFT:
467  case CLICK_RIGHT:
468  switch (m_imgRotation)
469  {
470  case ROT90:
471  case ROT270:
472  SetCursor(wxCURSOR_SIZENS);
473  break;
474  default:
475  SetCursor(wxCURSOR_SIZEWE);
476  };
477  break;
478  case CLICK_TOP:
479  case CLICK_BOTTOM:
480  switch (m_imgRotation)
481  {
482  case ROT90:
483  case ROT270:
484  SetCursor(wxCURSOR_SIZEWE);
485  break;
486  default:
487  SetCursor(wxCURSOR_SIZENS);
488  };
489  break;
490  case CLICK_CIRCLE:
491  SetCursor(wxCURSOR_SIZING);
492  break;
493  default:
494  SetCursor(wxNullCursor);
495  };
496  break;
497  case CROP_MOVING:
498  case CROP_LEFT_MOVING:
499  case CROP_RIGHT_MOVING:
500  case CROP_TOP_MOVING:
501  case CROP_BOTTOM_MOVING:
502  case CROP_CIRCLE_SCALING:
503  UpdateCrop(currentPos);
504  m_currentPos=mpos;
505  DrawCrop();
507  break;
508  case NO_IMAGE:
509  case NO_MASK:
510  case NO_SELECTION:
511  case POINTS_SELECTED:
512  case NEW_POLYGON_STARTED:
513  // in all other cases do nothing
514  break;
515  };
516  if(doUpdate)
517  update();
518 }
519 
520 void MaskImageCtrl::OnLeftMouseDown(wxMouseEvent& mouse)
521 {
522  if(m_previewOnly)
523  return;
524  DEBUG_DEBUG("LEFT MOUSE DOWN");
525  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
529  if(!HasCapture())
530  CaptureMouse();
531  SetFocus();
532  switch(m_maskEditState)
533  {
534  case NEW_POLYGON_STARTED:
535  //starting polygon creating
536  m_editingMask.addPoint(currentPos);
537  m_selectedPoints.insert(m_editingMask.getMaskPolygon().size()-1);
538  break;
539  case NO_MASK:
540  if(m_maskMode)
541  {
543  };
544  break;
545  case NO_SELECTION:
546  if(mouse.CmdDown())
547  {
548  // check if mouse clicks happens near one line of active polygon
549  unsigned int index = m_editingMask.FindPointNearPos(currentPos, 5 * maxSelectionDistance / getScaleFactor());
550  if(index<UINT_MAX)
551  {
552  m_selectedPoints.clear();
553  m_editingMask.insertPoint(index,currentPos);
554  m_selectedPoints.insert(index);
556  };
557  }
558  else
559  {
560  HuginBase::UIntSet points;
561  if(SelectPointsInsideMouseRect(points,false))
562  {
563  for(HuginBase::UIntSet::const_iterator it=points.begin();it!=points.end();++it)
564  m_selectedPoints.insert(*it);
566  }
567  else
568  {
570  }
571  };
572  break;
573  case POINTS_SELECTED:
574  if(mouse.CmdDown())
575  {
576  // check if mouse clicks happens near one line of active polygon
577  unsigned int index = m_editingMask.FindPointNearPos(currentPos, 5 * maxSelectionDistance / getScaleFactor());
578  if(index<UINT_MAX)
579  {
580  m_selectedPoints.clear();
581  m_editingMask.insertPoint(index,currentPos);
582  m_selectedPoints.insert(index);
584  };
585  }
586  else
587  {
588  HuginBase::UIntSet points;
589  if(SelectPointsInsideMouseRect(points,true))
590  {
591  //selected point clicked, starting moving
593  }
594  else
595  {
596  //unselected point clicked
597  if(SelectPointsInsideMouseRect(points,false))
598  {
599  //clicked near other point
600  if(!mouse.ShiftDown())
601  m_selectedPoints.clear();
602  for(HuginBase::UIntSet::const_iterator it=points.begin();it!=points.end();++it)
603  m_selectedPoints.insert(*it);
605  }
606  else
607  {
609  };
610  }
611  };
612  break;
613  case CROP_SHOWING:
614  switch(GetClickPos(vigra::Point2D(currentPos.x,currentPos.y)))
615  {
616  case CLICK_OUTSIDE:
617  // clicked outside, do nothing
618  break;
619  case CLICK_INSIDE:
620  if(!m_cropCentered)
621  {
623  };
624  break;
625  case CLICK_LEFT:
627  break;
628  case CLICK_RIGHT:
630  break;
631  case CLICK_TOP:
633  break;
634  case CLICK_BOTTOM:
636  break;
637  case CLICK_CIRCLE:
638  m_dragStartPos.x=(m_cropRect.left()+m_cropRect.right())/2;
639  m_dragStartPos.y=(m_cropRect.top()+m_cropRect.bottom())/2;
641  break;
642  };
644  {
646  };
647  break;
648  default:
649  // otherwise do nothing
650  break;
651  };
652 };
653 
654 void MaskImageCtrl::OnLeftMouseUp(wxMouseEvent& mouse)
655 {
657  return;
658  DEBUG_DEBUG("LEFT MOUSE UP");
659  wxPoint mpos;
660  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
661  &mpos.x, & mpos.y);
663  bool doUpdate=false;
664  switch(m_maskEditState)
665  {
666  case NEW_POLYGON_STARTED:
667  doUpdate=true;
668  m_editingMask.addPoint(currentPos);
669  m_selectedPoints.insert(m_editingMask.getMaskPolygon().size()-1);
671  break;
673  //next point of polygen selected
674  doUpdate=true;
675  m_editingMask.addPoint(currentPos);
676  m_selectedPoints.insert(m_editingMask.getMaskPolygon().size()-1);
677  break;
678  case POINTS_MOVING:
679  if(HasCapture())
680  ReleaseMouse();
681  {
684  {
685  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
686  m_imageMask[m_activeMask].movePointBy(*it,delta);
689  }
690  else
691  {
694  doUpdate=true;
695  };
696  };
697  break;
698  case POLYGON_SELECTING:
699  if(HasCapture())
700  ReleaseMouse();
701  ClearOverlay();
702  m_currentPos=mpos;
704  {
708  p=applyRotInv(p);
709  FindPolygon(p);
710  };
711  break;
712  case REGION_SELECTING:
713  {
714  if(HasCapture())
715  ReleaseMouse();
716  ClearOverlay();
717  m_currentPos=mpos;
718  bool selectedPoints=!m_selectedPoints.empty();
719  if(!mouse.ShiftDown())
720  m_selectedPoints.clear();
722  {
723  //new points selected
724  if(m_selectedPoints.empty())
726  else
728  }
729  else
730  {
731  //there were no points selected
732  if(!selectedPoints)
733  {
734  //if there where no points selected before, we searching for another polygon
738  p=applyRotInv(p);
739  FindPolygon(p);
740  };
742  };
743  doUpdate=true;
744  break;
745  };
746  case POINTS_ADDING:
747  if(HasCapture())
748  ReleaseMouse();
749  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
750  m_editingMask.movePointTo(*it,currentPos);
754  break;
755  case CROP_MOVING:
756  case CROP_LEFT_MOVING:
757  case CROP_RIGHT_MOVING:
758  case CROP_TOP_MOVING:
759  case CROP_BOTTOM_MOVING:
760  case CROP_CIRCLE_SCALING:
761  if(HasCapture())
762  ReleaseMouse();
763  ClearOverlay();
764  UpdateCrop(currentPos);
766  SetCursor(wxNullCursor);
767  m_editPanel->UpdateCrop(true);
768  doUpdate = true;
769  break;
770  default:
771  if(HasCapture())
772  ReleaseMouse();
773  };
774  if(doUpdate)
775  update();
776 }
777 
778 void MaskImageCtrl::OnLeftMouseDblClick(wxMouseEvent &mouse)
779 {
781  return;
782  switch(m_maskEditState)
783  {
784  case NEW_POLYGON_STARTED:
785  {
788  m_editingMask=mask;
789  m_selectedPoints.clear();
790  MainFrame::Get()->SetStatusText(wxT(""),0);
791  break;
792  };
794  {
795  //close newly generated polygon
797  //delete last point otherwise it would be added twice, because we added it
798  //already in release left mouse button
800  if(m_editingMask.getMaskPolygon().size()>2)
801  {
802  m_imageMask.push_back(m_editingMask);
803  m_activeMask=m_imageMask.size()-1;
804  m_editPanel->AddMask();
805  }
806  else
807  {
809  m_editingMask=mask;
810  m_selectedPoints.clear();
811  update();
812  };
813  MainFrame::Get()->SetStatusText(wxT(""),0);
814  break;
815  };
816  default:
817  // the other case do nothing here
818  break;
819  };
820 };
821 
822 void MaskImageCtrl::OnRightMouseDown(wxMouseEvent& mouse)
823 {
825  return;
826  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
830  if(!HasCapture())
831  CaptureMouse();
832  SetFocus();
834  {
835  if(mouse.CmdDown())
836  {
838  }
839  else
840  {
841  if (m_editingMask.isInside(currentPos))
842  {
845  update();
846  };
847  };
848  };
849 };
850 
851 void MaskImageCtrl::OnRightMouseUp(wxMouseEvent& mouse)
852 {
854  return;
855  wxPoint mpos;
856  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
857  &mpos.x, & mpos.y);
859  if(HasCapture())
860  ReleaseMouse();
861  switch(m_maskEditState)
862  {
863  case NEW_POLYGON_STARTED:
864  {
867  m_editingMask=mask;
868  m_selectedPoints.clear();
869  MainFrame::Get()->SetStatusText(wxT(""),0);
870  break;
871  };
873  {
874  //close newly generated polygon
876  m_editingMask.movePointTo(m_editingMask.getMaskPolygon().size()-1,currentPos);
877  if(m_editingMask.getMaskPolygon().size()>2)
878  {
879  m_imageMask.push_back(m_editingMask);
880  m_activeMask=m_imageMask.size()-1;
881  m_editPanel->AddMask();
882  }
883  else
884  {
886  m_editingMask=mask;
887  m_selectedPoints.clear();
888  update();
889  };
890  MainFrame::Get()->SetStatusText(wxT(""),0);
891  break;
892  };
893  case POINTS_DELETING:
894  {
895  HuginBase::UIntSet points;
896  m_currentPos=mpos;
897  if(SelectPointsInsideMouseRect(points,false))
898  {
899  if(m_editingMask.getMaskPolygon().size()-points.size()>2)
900  {
901  // clear all selected points
902  for(HuginBase::UIntSet::const_reverse_iterator it=points.rbegin();it!=points.rend();++it)
904  // now update set of selected points
905  if(!m_selectedPoints.empty())
906  {
907  std::vector<unsigned int> mappedSelectedPoints(m_imageMask[m_activeMask].getMaskPolygon().size());
908  for(unsigned int i=0;i<mappedSelectedPoints.size();i++)
909  mappedSelectedPoints[i]=i;
911  m_selectedPoints.clear();
912  for(HuginBase::UIntSet::const_iterator it=points.begin();it!=points.end();++it)
913  {
914  if((*it)<mappedSelectedPoints.size()-1)
915  for(unsigned int i=(*it)+1;i<mappedSelectedPoints.size();i++)
916  mappedSelectedPoints[i]--;
917  };
918  for(HuginBase::UIntSet::const_iterator it=temp.begin();it!=temp.end();++it)
919  if(!set_contains(points,*it))
920  m_selectedPoints.insert(mappedSelectedPoints[*it]);
921  };
922  //now update the saved mask
925  }
926  else
927  wxBell();
928  };
929  if(m_selectedPoints.empty())
931  else
933  break;
934  };
935  case POINTS_MOVING:
936  {
939  {
940  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
941  m_imageMask[m_activeMask].movePointBy(*it,delta);
944  }
945  else
946  {
949  };
950  break;
951  };
952  default:
953  break;
954  };
955 };
956 
957 void MaskImageCtrl::OnMiddleMouseDown(wxMouseEvent& mouse)
958 {
960  {
961  return;
962  };
963  switch (m_maskEditState)
964  {
965  case NO_MASK:
966  case NO_SELECTION:
967  case POINTS_SELECTED:
968  case POINTS_ADDING:
969  case NEW_POLYGON_STARTED:
971  case CROP_SHOWING:
972  if (!HasCapture())
973  CaptureMouse();
974  // store the scroll rate, the scroll rate set in rescaleImage is optimized for srolling
975  // with the cursor keys, but this is too high for mouse scrolling
976  // so change the scroll rate here and restore later in OnMiddleMouseUp
977  m_middleMouseScroll = true;
978  m_scrollPos = mouse.GetPosition();
979  break;
980  default:
981  // in other case ignore the middle mouse button
982  break;
983  };
984 }
985 
986 void MaskImageCtrl::OnMiddleMouseUp(wxMouseEvent& mouse)
987 {
988  if (m_previewOnly)
989  {
990  return;
991  };
992  if (HasCapture())
993  {
994  ReleaseMouse();
995  };
996  m_middleMouseScroll = false;
997 }
998 
999 void MaskImageCtrl::OnKeyUp(wxKeyEvent &e)
1000 {
1001  const int key=e.GetKeyCode();
1002  bool processed=false;
1003  if((key==WXK_DELETE) || (key==WXK_NUMPAD_DELETE))
1004  {
1005  if(m_activeMask<UINT_MAX)
1006  {
1008  {
1009  if ((!m_selectedPoints.empty()) && (m_editingMask.getMaskPolygon().size() - m_selectedPoints.size() > 2))
1010  {
1011  for (HuginBase::UIntSet::const_reverse_iterator it = m_selectedPoints.rbegin(); it != m_selectedPoints.rend(); ++it)
1014  processed = true;
1016  }
1017  else
1018  {
1019  if (m_editingMask.getMaskPolygon().size() == m_selectedPoints.size())
1020  {
1021  wxCommandEvent dummy;
1022  processed = true;
1023  m_editPanel->OnMaskDelete(dummy);
1024  }
1025  else
1026  wxBell();
1027  };
1028  }
1029  else
1030  {
1032  {
1033  wxCommandEvent dummy;
1034  processed=true;
1035  m_editPanel->OnMaskDelete(dummy);
1036  };
1037  };
1038  };
1039  }
1040  else
1041  {
1042  // handle key 0|1|2 only when editor state allows it
1045  {
1046  if (key == '1')
1047  {
1048  wxCommandEvent zoomEvent(wxEVT_CHOICE, XRCID("mask_editor_choice_zoom"));
1049  zoomEvent.SetInt(0);
1050  zoomEvent.SetString("update_selection");
1051  GetParent()->GetEventHandler()->AddPendingEvent(zoomEvent);
1052  }
1053  else
1054  {
1055  if (key == '2')
1056  {
1057  wxCommandEvent zoomEvent(wxEVT_CHOICE, XRCID("mask_editor_choice_zoom"));
1058  zoomEvent.SetInt(2);
1059  zoomEvent.SetString("update_selection");
1060  GetParent()->GetEventHandler()->AddPendingEvent(zoomEvent);
1061  }
1062  else
1063  {
1064  if (key == '0')
1065  {
1066  wxCommandEvent zoomEvent(wxEVT_CHOICE, XRCID("mask_editor_choice_zoom"));
1067  zoomEvent.SetInt(1);
1068  zoomEvent.SetString("update_selection");
1069  GetParent()->GetEventHandler()->AddPendingEvent(zoomEvent);
1070  };
1071  };
1072  };
1073  };
1074  };
1075  if(!processed)
1076  e.Skip();
1077 };
1078 
1079 void MaskImageCtrl::OnChar(wxKeyEvent& e)
1080 {
1081  wxPoint offset = GetViewStart();
1082  const int speed = std::max(m_bitmap.GetWidth(), m_bitmap.GetHeight()) / 75;
1083  switch (e.GetKeyCode())
1084  {
1085  // the default scrolling behaviour of the cursor key is too fine
1086  // using SetScrollRate with higher values conflicts with the middle mouse button
1087  // scrollling
1088  // so instead handle the cursor scrolling here with a higher step rate
1089  case WXK_LEFT:
1090  offset.x -= speed;
1091  Scroll(offset);
1092  break;
1093  case WXK_RIGHT:
1094  offset.x += speed;
1095  Scroll(offset);
1096  break;
1097  case WXK_UP:
1098  offset.y -= speed;
1099  Scroll(offset);
1100  break;
1101  case WXK_DOWN:
1102  offset.y += speed;
1103  Scroll(offset);
1104  break;
1105  default:
1106  //process event further
1107  e.Skip();
1108  };
1109 }
1110 void MaskImageCtrl::OnCaptureLost(wxMouseCaptureLostEvent &e)
1111 {
1112  wxFocusEvent dummy;
1113  OnKillFocus(dummy);
1114 };
1115 
1116 void MaskImageCtrl::OnKillFocus(wxFocusEvent &e)
1117 {
1118  if(HasCapture())
1119  ReleaseMouse();
1121  {
1122  wxBell();
1125  m_editingMask=mask;
1126  m_selectedPoints.clear();
1127  update();
1128  };
1129 };
1130 
1132 {
1134  HuginBase::MaskPolygon newMask;
1135  m_editingMask=newMask;
1136  m_selectedPoints.clear();
1137 };
1138 
1140 {
1141  return wxSize(m_imageSize.GetWidth(),m_imageSize.GetHeight());
1142 };
1143 
1145 {
1146  wxClientDC dc(this);
1147  PrepareDC(dc);
1148  OnDraw(dc);
1149 };
1150 
1151 void MaskImageCtrl::DrawPolygon(wxDC &dc, HuginBase::MaskPolygon poly, bool isSelected, bool drawMarker)
1152 {
1153  unsigned int nrOfPoints=poly.getMaskPolygon().size();
1154  if (nrOfPoints<2)
1155  return;
1156  wxPoint *polygonPoints=new wxPoint[nrOfPoints];
1157  for(unsigned int j=0;j<nrOfPoints;j++)
1158  {
1159  polygonPoints[j]=transform(applyRot(poly.getMaskPolygon()[j]));
1160  };
1161  if(isSelected)
1162  dc.SetPen(wxPen(m_colour_point_unselected, 1, wxPENSTYLE_SOLID));
1163  else
1164  switch(poly.getMaskType())
1165  {
1169  dc.SetPen(wxPen(m_colour_polygon_negative, 1, wxPENSTYLE_SOLID));
1170  break;
1173  dc.SetPen(wxPen(m_colour_polygon_positive, 1, wxPENSTYLE_SOLID));
1174  break;
1175  };
1176  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1177  if(nrOfPoints>2)
1178  dc.DrawPolygon(nrOfPoints,polygonPoints);
1179  else
1180  dc.DrawLine(polygonPoints[0],polygonPoints[1]);
1181  if(drawMarker)
1182  {
1183  wxPen penSelected(m_colour_point_selected);
1184  wxPen penUnselected(m_colour_point_unselected);
1185  wxBrush brushSelected(m_colour_point_selected);
1186  wxBrush brushUnselected(m_colour_point_unselected);
1187  for(unsigned int j=0;j<nrOfPoints;j++)
1188  {
1190  {
1191  dc.SetPen(penSelected);
1192  dc.SetBrush(brushSelected);
1193  }
1194  else
1195  {
1196  dc.SetPen(penUnselected);
1197  dc.SetBrush(brushUnselected);
1198  };
1199  dc.DrawRectangle(polygonPoints[j].x-polygonPointSize,polygonPoints[j].y-polygonPointSize,
1201  };
1202  };
1203  delete []polygonPoints;
1204 };
1205 
1207 {
1208  wxClientDC dc(this);
1209  PrepareDC(dc);
1210  DrawCrop(dc);
1211 };
1212 
1214 {
1215  // draw crop rectangle/circle
1216  if(!m_maskMode)
1217  {
1218  wxDCOverlay overlayDC(m_overlay, &dc);
1219  overlayDC.Clear();
1220  // draw all areas without fillings
1221  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1222  dc.SetPen(wxPen(m_color_selection, 2, wxPENSTYLE_SOLID));
1223  wxPoint middle=transform(applyRot((m_cropRect.lowerRight()+m_cropRect.upperLeft())/2));
1224 
1225  int c = 8; // size of midpoint cross
1226  dc.DrawLine( middle.x + c, middle.y + c, middle.x - c, middle.y - c);
1227  dc.DrawLine( middle.x - c, middle.y + c, middle.x + c, middle.y - c);
1228  dc.DrawRectangle(wxRect(transform(applyRot(hugin_utils::FDiff2D(m_cropRect.left(), m_cropRect.top()))),
1230 
1231  // draw crop circle as well, if requested.
1233  {
1234  double radius=std::min<int>(m_cropRect.width(),m_cropRect.height())/2.0;
1235  dc.DrawCircle(middle.x, middle.y, scale(radius));
1236  }
1237  };
1238 };
1239 
1240 void MaskImageCtrl::OnDraw(wxDC & dc)
1241 {
1242  if(m_maskEditState!=NO_IMAGE && m_bitmap.IsOk())
1243  {
1244  int offset=scale(HuginBase::maskOffset);
1245  //draw border around image to allow drawing mask over boudaries of image
1246  //don't draw as one complete rectangle to prevent flickering
1247  dc.SetPen(wxPen(GetBackgroundColour(), 1, wxPENSTYLE_SOLID));
1248  dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
1249  dc.DrawRectangle(0,0,offset,m_bitmap.GetHeight()+2*offset);
1250  dc.DrawRectangle(0,0,m_bitmap.GetWidth()+2*offset,offset);
1251  dc.DrawRectangle(m_bitmap.GetWidth()+offset,0,m_bitmap.GetWidth()+2*offset,m_bitmap.GetHeight()+2*offset);
1252  dc.DrawRectangle(0,m_bitmap.GetHeight()+offset,m_bitmap.GetWidth()+2*offset,m_bitmap.GetHeight()+2*offset);
1253  dc.DrawBitmap(m_bitmap,offset,offset);
1254  if(m_fitToWindow)
1255  {
1256  //draw border when image is fit to window, otherwise the border (without image) is not updated
1257  wxSize clientSize=GetClientSize();
1258  if(m_bitmap.GetWidth()+2*offset<clientSize.GetWidth())
1259  {
1260  dc.DrawRectangle(m_bitmap.GetWidth()+2*offset,0,clientSize.GetWidth()-m_bitmap.GetWidth()+2*offset,clientSize.GetHeight());
1261  };
1262  if(m_bitmap.GetHeight()+2*offset<clientSize.GetHeight())
1263  {
1264  dc.DrawRectangle(0,m_bitmap.GetHeight()+2*offset,clientSize.GetWidth(),clientSize.GetHeight()-m_bitmap.GetHeight()+2*offset);
1265  };
1266  };
1268  {
1269  //whole image, we need it several times
1270  wxRegion wholeImage(transform(applyRot(hugin_utils::FDiff2D(0,0))),
1271  transform(applyRot(hugin_utils::FDiff2D(m_realSize.GetWidth(),m_realSize.GetHeight()))));
1272  wxRegion region;
1274  {
1275  region.Union(wholeImage);
1276  //now the crop
1277  switch(m_cropMode)
1278  {
1280  region.Subtract(wxRegion(transform(applyRot(m_cropRect.upperLeft())),
1281  transform(applyRot(m_cropRect.lowerRight()))));
1282  break;
1284  {
1285  unsigned int nrOfPoints = dc.GetSize().GetWidth() * 2;
1286  wxPoint* circlePoints = new wxPoint[nrOfPoints];
1287  vigra::Point2D middle = (m_cropRect.lowerRight() + m_cropRect.upperLeft()) / 2;
1288  double radius = std::min<int>(m_cropRect.width(), m_cropRect.height()) / 2;
1289  double interval = 2 * PI / nrOfPoints;
1290  for (unsigned int i = 0; i < nrOfPoints; i++)
1291  {
1292  circlePoints[i] = transform(applyRot(hugin_utils::FDiff2D(middle.x + radius*cos(i*interval), middle.y + radius*sin(i*interval))));
1293  };
1294  region.Subtract(wxRegion(nrOfPoints, circlePoints));
1295  delete[]circlePoints;
1296  }
1297  break;
1299  break;
1300  };
1301  };
1302  if(!m_masksToDraw.empty())
1303  {
1304  for(unsigned int i=0;i<m_masksToDraw.size();i++)
1305  {
1306  HuginBase::VectorPolygon poly=m_masksToDraw[i].getMaskPolygon();
1307  wxPoint *polygonPoints=new wxPoint[poly.size()];
1308  for(unsigned int j=0;j<poly.size();j++)
1309  {
1310  polygonPoints[j]=transform(applyRot(poly[j]));
1311  };
1312  wxRegion singleRegion(poly.size(),polygonPoints,wxWINDING_RULE);
1313  if(m_masksToDraw[i].isInverted())
1314  {
1315  wxRegion newRegion(wholeImage);
1316  newRegion.Subtract(singleRegion);
1317  region.Union(newRegion);
1318  }
1319  else
1320  {
1321  region.Union(singleRegion);
1322  };
1323  delete []polygonPoints;
1324  };
1325  };
1326 #ifndef __WXMAC__
1327  // on Windows and GTK we need to compensate to clipping region
1328  // by the scroll offset
1329  // this seems not to be necessary for wxMac
1330  int x;
1331  int y;
1332  GetViewStart(&x,&y);
1333  region.Offset(-x,-y);
1334 #endif
1335  dc.SetDeviceClippingRegion(region);
1336  dc.DrawBitmap(m_disabledBitmap,offset,offset);
1337  dc.DestroyClippingRegion();
1338  };
1339  DrawCrop(dc);
1340  if(m_maskMode && !m_imageMask.empty())
1341  {
1342  //now draw all polygons
1345  for(unsigned int i=0;i<maskList.size();i++)
1346  {
1347  if(i!=m_activeMask)
1348  DrawPolygon(dc,maskList[i],false,false);
1349  else
1350  if(drawSelected)
1351  DrawPolygon(dc,maskList[i],true,true);
1352  };
1353  };
1354  //and now the actual polygon
1356  DrawPolygon(dc,m_editingMask,true,true);
1357  }
1358  else
1359  {
1360  // clear the rectangle and exit
1361  dc.SetPen(wxPen(GetBackgroundColour(), 1, wxPENSTYLE_SOLID));
1362  dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
1363  dc.Clear();
1364  return;
1365  };
1366 }
1367 
1369 {
1370  {
1371  wxClientDC dc(this);
1372  PrepareDC(dc);
1373  wxDCOverlay overlaydc(m_overlay, &dc);
1374  overlaydc.Clear();
1375  };
1376  // before reseting the wxOverlay we need to disconnet it from wxDCOverlay
1377  // so destruct wxDCOverlay before calling Reset()
1378  m_overlay.Reset();
1379 }
1380 
1381 void MaskImageCtrl::OnSize(wxSizeEvent &e)
1382 {
1383  DEBUG_TRACE("size: " << e.GetSize().GetWidth() << "x" << e.GetSize().GetHeight());
1384  // rescale m_bitmap if needed.
1385  if (m_imageFilename != "") {
1386  if (m_fitToWindow) {
1387  setScale(0);
1388  }
1389  }
1390 };
1391 
1392 void MaskImageCtrl::OnScroll(wxScrollWinEvent &e)
1393 {
1394  m_oldScrollPosX = GetScrollPos(wxHORIZONTAL);
1395  m_oldScrollPosY = GetScrollPos(wxVERTICAL);
1396  e.Skip();
1397 }
1398 
1400 {
1401  if (m_maskEditState == NO_IMAGE)
1402  {
1403  return;
1404  }
1405  //determine average colour and set selection colour corresponding
1406  vigra::FindAverage<vigra::RGBValue<vigra::UInt8> > average;
1407  vigra::inspectImage(vigra::srcImageRange(*(m_img->get8BitImage())), average);
1408  vigra::RGBValue<vigra::UInt8> RGBaverage=average.average();
1409  if(RGBaverage[0]<180 && RGBaverage[1]<180 && RGBaverage[2]<180)
1410  {
1411  m_color_selection=*wxWHITE;
1412  }
1413  else
1414  {
1415  m_color_selection=*wxBLACK;
1416  };
1417  wxImage img = imageCacheEntry2wxImage(m_img);
1418  if (img.GetWidth() == 0)
1419  {
1420  return;
1421  }
1422  m_imageSize = wxSize(img.GetWidth(), img.GetHeight());
1425  if (m_fitToWindow)
1427 
1428  //scaling image to screen size
1429  if (getScaleFactor()!=1.0)
1430  {
1431  m_imageSize.SetWidth(scale(m_imageSize.GetWidth()));
1432  m_imageSize.SetHeight(scale(m_imageSize.GetHeight()));
1433  wxImageResizeQuality resizeQuality = wxIMAGE_QUALITY_NORMAL;
1434  if (std::max(img.GetWidth(), img.GetHeight()) > (ULONG_MAX >> 16))
1435  {
1436  // wxIMAGE_QUALITY_NORMAL resizes the image with ResampleNearest
1437  // this algorithm works only if image dimensions are smaller then
1438  // ULONG_MAX >> 16 (actual size of unsigned long differ from system
1439  // to system)
1440  resizeQuality = wxIMAGE_QUALITY_BOX_AVERAGE;
1441  };
1442  img=img.Scale(scale(m_realSize.GetWidth()), scale(m_realSize.GetHeight()), resizeQuality);
1443  }
1444  else
1445  {
1446  //the conversion to disabled m_bitmap would work on the original cached image file
1447  //therefore we need to create a copy to work on it
1448  img=img.Copy();
1449  };
1450  //and now rotating
1451  switch(m_imgRotation)
1452  {
1453  case ROT90:
1454  img = img.Rotate90(true);
1455  break;
1456  case ROT180:
1457  img = img.Rotate180();
1458  break;
1459  case ROT270:
1460  img = img.Rotate90(false);
1461  break;
1462  default:
1463  break;
1464  }
1465  // do color correction only if input image has icc profile or if we found a monitor profile
1466  if (!m_img->iccProfile->empty() || huginApp::Get()->HasMonitorProfile())
1467  {
1469  };
1470  m_bitmap=wxBitmap(img);
1471 
1472  //create disabled m_bitmap for drawing active masks
1473  img = img.ConvertToDisabled(192);
1474  m_disabledBitmap=wxBitmap(img);
1475  if (m_imgRotation == ROT90 || m_imgRotation == ROT270)
1476  {
1477  SetVirtualSize(m_imageSize.GetHeight(), m_imageSize.GetWidth());
1478  }
1479  else
1480  {
1481  SetVirtualSize(m_imageSize.GetWidth(), m_imageSize.GetHeight());
1482  };
1483  SetScrollRate(1, 1);
1484  // reset overlay
1485  m_overlay.Reset();
1486  Refresh(true);
1487 };
1488 
1490 {
1491  wxClientDC dc(this);
1492  PrepareDC(dc);
1493  wxDCOverlay overlayDC(m_overlay, &dc);
1494  overlayDC.Clear();
1495  dc.SetPen(wxPen(m_color_selection, 2, wxPENSTYLE_LONG_DASH));
1496  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1497  dc.DrawRectangle(m_dragStartPos.x,m_dragStartPos.y,
1499 };
1500 
1502 {
1503  unsigned int selectedPolygon=UINT_MAX;
1504  unsigned int i=0;
1505  while(selectedPolygon==UINT_MAX && i<m_imageMask.size())
1506  {
1507  if(m_imageMask[i].isInside(p))
1508  selectedPolygon=i;
1509  i++;
1510  };
1511  if(selectedPolygon<UINT_MAX)
1512  m_editPanel->SelectMask(selectedPolygon);
1513 };
1514 
1515 bool MaskImageCtrl::SelectPointsInsideMouseRect(HuginBase::UIntSet &points,const bool considerSelectedOnly)
1516 {
1517  bool found=false;
1520  double xmin = std::min(p1.x, p2.x) - maxSelectionDistance / getScaleFactor();
1521  double xmax = std::max(p1.x, p2.x) + maxSelectionDistance / getScaleFactor();
1522  double ymin = std::min(p1.y, p2.y) - maxSelectionDistance / getScaleFactor();
1523  double ymax = std::max(p1.y, p2.y) + maxSelectionDistance / getScaleFactor();
1525  for(unsigned int i=0;i<poly.size();i++)
1526  {
1527  bool activePoints=true;
1528  if(considerSelectedOnly)
1529  activePoints=set_contains(m_selectedPoints,i);
1530  if(activePoints && xmin<=poly[i].x && poly[i].x<=xmax && ymin<=poly[i].y && poly[i].y<=ymax)
1531  {
1532  points.insert(i);
1533  found=true;
1534  };
1535  };
1536  return found;
1537 };
1538 
1539 void MaskImageCtrl::setScale(double factor)
1540 {
1541  if (factor == 0)
1542  {
1543  m_fitToWindow = true;
1544  factor = calcAutoScaleFactor(m_imageSize);
1545  }
1546  else
1547  {
1548  m_fitToWindow = false;
1549  }
1550  DEBUG_DEBUG("new scale factor:" << factor);
1551  // update if factor changed
1552  if (factor != m_scaleFactor)
1553  {
1554  m_scaleFactor = factor;
1555  // keep existing scale focussed.
1556  rescaleImage();
1557  }
1558 };
1559 
1561 {
1562  int w = size.GetWidth();
1563  int h = size.GetHeight();
1564  if (m_imgRotation == ROT90 || m_imgRotation == ROT270)
1565  {
1566  int t = w;
1567  w = h;
1568  h = t;
1569  }
1570 
1571  wxSize csize = GetSize();
1572  DEBUG_DEBUG("csize: " << csize.GetWidth() << "x" << csize.GetHeight() << "image: " << w << "x" << h);
1573  double s1 = (double)csize.GetWidth()/w;
1574  double s2 = (double)csize.GetHeight()/h;
1575  DEBUG_DEBUG("s1: " << s1 << " s2:" << s2);
1576  return s1 < s2 ? s1 : s2;
1577 };
1578 
1580 {
1581  return m_scaleFactor;
1582 };
1583 
1584 void MaskImageCtrl::setDrawingActiveMasks(bool newDrawActiveMasks)
1585 {
1586  m_showActiveMasks=newDrawActiveMasks;
1587  update();
1588 };
1589 
1590 IMPLEMENT_DYNAMIC_CLASS(MaskImageCtrl, wxScrolledWindow)
1591 
1593  : wxXmlResourceHandler()
1594 {
1595  AddWindowStyles();
1596 };
1597 
1599 {
1600  XRC_MAKE_INSTANCE(cp, MaskImageCtrl)
1601 
1602  cp->Create(m_parentAsWindow,
1603  GetID(),
1604  GetPosition(), GetSize(),
1605  GetStyle(wxT("style")),
1606  GetName());
1607 
1608  SetupWindow(cp);
1609 
1610  return cp;
1611 };
1612 
1614 {
1615  return IsOfClass(node, wxT("MaskImageCtrl"));
1616 };
1617 
1618 IMPLEMENT_DYNAMIC_CLASS(MaskImageCtrlXmlHandler, wxXmlResourceHandler)
bool m_showActiveMasks
ImageRotation
image rotation.
Definition: MaskImageCtrl.h:55
static const int NO_IMAGE
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)
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:125
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:144
#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:151
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:169
hugin_utils::FDiff2D m_cropCenter
END_EVENT_TABLE()
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
static huginApp * Get()
hack.. kind of a pseudo singleton...
Definition: huginApp.cpp:649
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:2181
void update()
initiate redraw
void OnRightMouseDown(wxMouseEvent &mouse)
event handler when right mouse button is pressed
wxImage imageCacheEntry2wxImage(ImageCache::EntryPtr e)
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:156
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:147
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
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:134
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:559
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
virtual void OnDraw(wxDC &dc)
drawing routine
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
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
HuginBase::MaskPolygon m_editingMask