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  m_overlay.Reset();
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  m_overlay.Reset();
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  m_overlay.Reset();
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  UpdateCrop(currentPos);
765  SetCursor(wxNullCursor);
766  m_editPanel->UpdateCrop(true);
767  doUpdate = true;
768  break;
769  default:
770  if(HasCapture())
771  ReleaseMouse();
772  };
773  if(doUpdate)
774  update();
775 }
776 
777 void MaskImageCtrl::OnLeftMouseDblClick(wxMouseEvent &mouse)
778 {
780  return;
781  switch(m_maskEditState)
782  {
783  case NEW_POLYGON_STARTED:
784  {
787  m_editingMask=mask;
788  m_selectedPoints.clear();
789  MainFrame::Get()->SetStatusText(wxT(""),0);
790  break;
791  };
793  {
794  //close newly generated polygon
796  //delete last point otherwise it would be added twice, because we added it
797  //already in release left mouse button
799  if(m_editingMask.getMaskPolygon().size()>2)
800  {
801  m_imageMask.push_back(m_editingMask);
802  m_activeMask=m_imageMask.size()-1;
803  m_editPanel->AddMask();
804  }
805  else
806  {
808  m_editingMask=mask;
809  m_selectedPoints.clear();
810  update();
811  };
812  MainFrame::Get()->SetStatusText(wxT(""),0);
813  break;
814  };
815  default:
816  // the other case do nothing here
817  break;
818  };
819 };
820 
821 void MaskImageCtrl::OnRightMouseDown(wxMouseEvent& mouse)
822 {
824  return;
825  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
829  if(!HasCapture())
830  CaptureMouse();
831  SetFocus();
833  {
834  if(mouse.CmdDown())
835  {
837  }
838  else
839  {
840  if (m_editingMask.isInside(currentPos))
841  {
844  update();
845  };
846  };
847  };
848 };
849 
850 void MaskImageCtrl::OnRightMouseUp(wxMouseEvent& mouse)
851 {
853  return;
854  wxPoint mpos;
855  CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
856  &mpos.x, & mpos.y);
858  if(HasCapture())
859  ReleaseMouse();
860  switch(m_maskEditState)
861  {
862  case NEW_POLYGON_STARTED:
863  {
866  m_editingMask=mask;
867  m_selectedPoints.clear();
868  MainFrame::Get()->SetStatusText(wxT(""),0);
869  break;
870  };
872  {
873  //close newly generated polygon
875  m_editingMask.movePointTo(m_editingMask.getMaskPolygon().size()-1,currentPos);
876  if(m_editingMask.getMaskPolygon().size()>2)
877  {
878  m_imageMask.push_back(m_editingMask);
879  m_activeMask=m_imageMask.size()-1;
880  m_editPanel->AddMask();
881  }
882  else
883  {
885  m_editingMask=mask;
886  m_selectedPoints.clear();
887  update();
888  };
889  MainFrame::Get()->SetStatusText(wxT(""),0);
890  break;
891  };
892  case POINTS_DELETING:
893  {
894  HuginBase::UIntSet points;
895  m_currentPos=mpos;
896  if(SelectPointsInsideMouseRect(points,false))
897  {
898  if(m_editingMask.getMaskPolygon().size()-points.size()>2)
899  {
900  // clear all selected points
901  for(HuginBase::UIntSet::const_reverse_iterator it=points.rbegin();it!=points.rend();++it)
903  // now update set of selected points
904  if(!m_selectedPoints.empty())
905  {
906  std::vector<unsigned int> mappedSelectedPoints(m_imageMask[m_activeMask].getMaskPolygon().size());
907  for(unsigned int i=0;i<mappedSelectedPoints.size();i++)
908  mappedSelectedPoints[i]=i;
910  m_selectedPoints.clear();
911  for(HuginBase::UIntSet::const_iterator it=points.begin();it!=points.end();++it)
912  {
913  if((*it)<mappedSelectedPoints.size()-1)
914  for(unsigned int i=(*it)+1;i<mappedSelectedPoints.size();i++)
915  mappedSelectedPoints[i]--;
916  };
917  for(HuginBase::UIntSet::const_iterator it=temp.begin();it!=temp.end();++it)
918  if(!set_contains(points,*it))
919  m_selectedPoints.insert(mappedSelectedPoints[*it]);
920  };
921  //now update the saved mask
924  }
925  else
926  wxBell();
927  };
928  if(m_selectedPoints.empty())
930  else
932  break;
933  };
934  case POINTS_MOVING:
935  {
938  {
939  for(HuginBase::UIntSet::const_iterator it=m_selectedPoints.begin();it!=m_selectedPoints.end();++it)
940  m_imageMask[m_activeMask].movePointBy(*it,delta);
943  }
944  else
945  {
948  };
949  break;
950  };
951  default:
952  break;
953  };
954 };
955 
956 void MaskImageCtrl::OnMiddleMouseDown(wxMouseEvent& mouse)
957 {
959  {
960  return;
961  };
962  switch (m_maskEditState)
963  {
964  case NO_MASK:
965  case NO_SELECTION:
966  case POINTS_SELECTED:
967  case POINTS_ADDING:
968  case NEW_POLYGON_STARTED:
970  case CROP_SHOWING:
971  if (!HasCapture())
972  CaptureMouse();
973  // store the scroll rate, the scroll rate set in rescaleImage is optimized for srolling
974  // with the cursor keys, but this is too high for mouse scrolling
975  // so change the scroll rate here and restore later in OnMiddleMouseUp
976  m_middleMouseScroll = true;
977  m_scrollPos = mouse.GetPosition();
978  break;
979  default:
980  // in other case ignore the middle mouse button
981  break;
982  };
983 }
984 
985 void MaskImageCtrl::OnMiddleMouseUp(wxMouseEvent& mouse)
986 {
987  if (m_previewOnly)
988  {
989  return;
990  };
991  if (HasCapture())
992  {
993  ReleaseMouse();
994  };
995  m_middleMouseScroll = false;
996 }
997 
998 void MaskImageCtrl::OnKeyUp(wxKeyEvent &e)
999 {
1000  const int key=e.GetKeyCode();
1001  bool processed=false;
1002  if((key==WXK_DELETE) || (key==WXK_NUMPAD_DELETE))
1003  {
1004  if(m_activeMask<UINT_MAX)
1005  {
1007  {
1008  if ((!m_selectedPoints.empty()) && (m_editingMask.getMaskPolygon().size() - m_selectedPoints.size() > 2))
1009  {
1010  for (HuginBase::UIntSet::const_reverse_iterator it = m_selectedPoints.rbegin(); it != m_selectedPoints.rend(); ++it)
1013  processed = true;
1015  }
1016  else
1017  {
1018  if (m_editingMask.getMaskPolygon().size() == m_selectedPoints.size())
1019  {
1020  wxCommandEvent dummy;
1021  processed = true;
1022  m_editPanel->OnMaskDelete(dummy);
1023  }
1024  else
1025  wxBell();
1026  };
1027  }
1028  else
1029  {
1031  {
1032  wxCommandEvent dummy;
1033  processed=true;
1034  m_editPanel->OnMaskDelete(dummy);
1035  };
1036  };
1037  };
1038  }
1039  else
1040  {
1041  // handle key 0|1|2 only when editor state allows it
1044  {
1045  if (key == '1')
1046  {
1047  wxCommandEvent zoomEvent(wxEVT_CHOICE, XRCID("mask_editor_choice_zoom"));
1048  zoomEvent.SetInt(0);
1049  zoomEvent.SetString("update_selection");
1050  GetParent()->GetEventHandler()->AddPendingEvent(zoomEvent);
1051  }
1052  else
1053  {
1054  if (key == '2')
1055  {
1056  wxCommandEvent zoomEvent(wxEVT_CHOICE, XRCID("mask_editor_choice_zoom"));
1057  zoomEvent.SetInt(2);
1058  zoomEvent.SetString("update_selection");
1059  GetParent()->GetEventHandler()->AddPendingEvent(zoomEvent);
1060  }
1061  else
1062  {
1063  if (key == '0')
1064  {
1065  wxCommandEvent zoomEvent(wxEVT_CHOICE, XRCID("mask_editor_choice_zoom"));
1066  zoomEvent.SetInt(1);
1067  zoomEvent.SetString("update_selection");
1068  GetParent()->GetEventHandler()->AddPendingEvent(zoomEvent);
1069  };
1070  };
1071  };
1072  };
1073  };
1074  if(!processed)
1075  e.Skip();
1076 };
1077 
1078 void MaskImageCtrl::OnChar(wxKeyEvent& e)
1079 {
1080  wxPoint offset = GetViewStart();
1081  const int speed = std::max(m_bitmap.GetWidth(), m_bitmap.GetHeight()) / 75;
1082  switch (e.GetKeyCode())
1083  {
1084  // the default scrolling behaviour of the cursor key is too fine
1085  // using SetScrollRate with higher values conflicts with the middle mouse button
1086  // scrollling
1087  // so instead handle the cursor scrolling here with a higher step rate
1088  case WXK_LEFT:
1089  offset.x -= speed;
1090  Scroll(offset);
1091  break;
1092  case WXK_RIGHT:
1093  offset.x += speed;
1094  Scroll(offset);
1095  break;
1096  case WXK_UP:
1097  offset.y -= speed;
1098  Scroll(offset);
1099  break;
1100  case WXK_DOWN:
1101  offset.y += speed;
1102  Scroll(offset);
1103  break;
1104  default:
1105  //process event further
1106  e.Skip();
1107  };
1108 }
1109 void MaskImageCtrl::OnCaptureLost(wxMouseCaptureLostEvent &e)
1110 {
1111  wxFocusEvent dummy;
1112  OnKillFocus(dummy);
1113 };
1114 
1115 void MaskImageCtrl::OnKillFocus(wxFocusEvent &e)
1116 {
1117  if(HasCapture())
1118  ReleaseMouse();
1120  {
1121  wxBell();
1124  m_editingMask=mask;
1125  m_selectedPoints.clear();
1126  update();
1127  };
1128 };
1129 
1131 {
1133  HuginBase::MaskPolygon newMask;
1134  m_editingMask=newMask;
1135  m_selectedPoints.clear();
1136 };
1137 
1139 {
1140  return wxSize(m_imageSize.GetWidth(),m_imageSize.GetHeight());
1141 };
1142 
1144 {
1145  wxClientDC dc(this);
1146  PrepareDC(dc);
1147  OnDraw(dc);
1148 };
1149 
1150 void MaskImageCtrl::DrawPolygon(wxDC &dc, HuginBase::MaskPolygon poly, bool isSelected, bool drawMarker)
1151 {
1152  unsigned int nrOfPoints=poly.getMaskPolygon().size();
1153  if (nrOfPoints<2)
1154  return;
1155  wxPoint *polygonPoints=new wxPoint[nrOfPoints];
1156  for(unsigned int j=0;j<nrOfPoints;j++)
1157  {
1158  polygonPoints[j]=transform(applyRot(poly.getMaskPolygon()[j]));
1159  };
1160  if(isSelected)
1161  dc.SetPen(wxPen(m_colour_point_unselected, 1, wxPENSTYLE_SOLID));
1162  else
1163  switch(poly.getMaskType())
1164  {
1168  dc.SetPen(wxPen(m_colour_polygon_negative, 1, wxPENSTYLE_SOLID));
1169  break;
1172  dc.SetPen(wxPen(m_colour_polygon_positive, 1, wxPENSTYLE_SOLID));
1173  break;
1174  };
1175  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1176  if(nrOfPoints>2)
1177  dc.DrawPolygon(nrOfPoints,polygonPoints);
1178  else
1179  dc.DrawLine(polygonPoints[0],polygonPoints[1]);
1180  if(drawMarker)
1181  {
1182  wxPen penSelected(m_colour_point_selected);
1183  wxPen penUnselected(m_colour_point_unselected);
1184  wxBrush brushSelected(m_colour_point_selected);
1185  wxBrush brushUnselected(m_colour_point_unselected);
1186  for(unsigned int j=0;j<nrOfPoints;j++)
1187  {
1189  {
1190  dc.SetPen(penSelected);
1191  dc.SetBrush(brushSelected);
1192  }
1193  else
1194  {
1195  dc.SetPen(penUnselected);
1196  dc.SetBrush(brushUnselected);
1197  };
1198  dc.DrawRectangle(polygonPoints[j].x-polygonPointSize,polygonPoints[j].y-polygonPointSize,
1200  };
1201  };
1202  delete []polygonPoints;
1203 };
1204 
1206 {
1207  wxClientDC dc(this);
1208  PrepareDC(dc);
1210  DrawCrop(dc);
1211 };
1212 
1214 {
1215  // draw crop rectangle/circle
1216  if(!m_maskMode)
1217  {
1218  // draw all areas without fillings
1219  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1220  dc.SetPen(wxPen(m_color_selection, 2, wxPENSTYLE_SOLID));
1221  wxPoint middle=transform(applyRot((m_cropRect.lowerRight()+m_cropRect.upperLeft())/2));
1222 
1223  int c = 8; // size of midpoint cross
1224  dc.DrawLine( middle.x + c, middle.y + c, middle.x - c, middle.y - c);
1225  dc.DrawLine( middle.x - c, middle.y + c, middle.x + c, middle.y - c);
1226  dc.DrawRectangle(wxRect(transform(applyRot(hugin_utils::FDiff2D(m_cropRect.left(), m_cropRect.top()))),
1228 
1229  // draw crop circle as well, if requested.
1231  {
1232  double radius=std::min<int>(m_cropRect.width(),m_cropRect.height())/2.0;
1233  dc.DrawCircle(middle.x, middle.y, scale(radius));
1234  }
1235  };
1236 };
1237 
1238 void MaskImageCtrl::DrawImageBitmap(wxDC& dc, int offset)
1239 {
1240  //draw border around image to allow drawing mask over boudaries of image
1241  //don't draw as one complete rectangle to prevent flickering
1242  dc.SetPen(wxPen(GetBackgroundColour(), 1, wxPENSTYLE_SOLID));
1243  dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
1244  dc.DrawRectangle(0, 0, offset, m_bitmap.GetHeight() + 2 * offset);
1245  dc.DrawRectangle(0, 0, m_bitmap.GetWidth() + 2 * offset, offset);
1246  dc.DrawRectangle(m_bitmap.GetWidth() + offset, 0, m_bitmap.GetWidth() + 2 * offset, m_bitmap.GetHeight() + 2 * offset);
1247  dc.DrawRectangle(0, m_bitmap.GetHeight() + offset, m_bitmap.GetWidth() + 2 * offset, m_bitmap.GetHeight() + 2 * offset);
1248  dc.DrawBitmap(m_bitmap, offset, offset);
1249 }
1250 
1251 void MaskImageCtrl::OnDraw(wxDC & dc)
1252 {
1253  if(m_maskEditState!=NO_IMAGE && m_bitmap.IsOk())
1254  {
1255  const int offset=scale(HuginBase::maskOffset);
1256  DrawImageBitmap(dc, offset);
1257  if(m_fitToWindow)
1258  {
1259  //draw border when image is fit to window, otherwise the border (without image) is not updated
1260  wxSize clientSize=GetClientSize();
1261  if(m_bitmap.GetWidth()+2*offset<clientSize.GetWidth())
1262  {
1263  dc.DrawRectangle(m_bitmap.GetWidth()+2*offset,0,clientSize.GetWidth()-m_bitmap.GetWidth()+2*offset,clientSize.GetHeight());
1264  };
1265  if(m_bitmap.GetHeight()+2*offset<clientSize.GetHeight())
1266  {
1267  dc.DrawRectangle(0,m_bitmap.GetHeight()+2*offset,clientSize.GetWidth(),clientSize.GetHeight()-m_bitmap.GetHeight()+2*offset);
1268  };
1269  };
1271  {
1272  //whole image, we need it several times
1273  wxRegion wholeImage(transform(applyRot(hugin_utils::FDiff2D(0,0))),
1274  transform(applyRot(hugin_utils::FDiff2D(m_realSize.GetWidth(),m_realSize.GetHeight()))));
1275  wxRegion region;
1277  {
1278  region.Union(wholeImage);
1279  //now the crop
1280  switch(m_cropMode)
1281  {
1283  region.Subtract(wxRegion(transform(applyRot(m_cropRect.upperLeft())),
1284  transform(applyRot(m_cropRect.lowerRight()))));
1285  break;
1287  {
1288  unsigned int nrOfPoints = dc.GetSize().GetWidth() * 2;
1289  wxPoint* circlePoints = new wxPoint[nrOfPoints];
1290  vigra::Point2D middle = (m_cropRect.lowerRight() + m_cropRect.upperLeft()) / 2;
1291  double radius = std::min<int>(m_cropRect.width(), m_cropRect.height()) / 2;
1292  double interval = 2 * PI / nrOfPoints;
1293  for (unsigned int i = 0; i < nrOfPoints; i++)
1294  {
1295  circlePoints[i] = transform(applyRot(hugin_utils::FDiff2D(middle.x + radius*cos(i*interval), middle.y + radius*sin(i*interval))));
1296  };
1297  region.Subtract(wxRegion(nrOfPoints, circlePoints));
1298  delete[]circlePoints;
1299  }
1300  break;
1302  break;
1303  };
1304  };
1305  if(!m_masksToDraw.empty())
1306  {
1307  for(unsigned int i=0;i<m_masksToDraw.size();i++)
1308  {
1309  HuginBase::VectorPolygon poly=m_masksToDraw[i].getMaskPolygon();
1310  wxPoint *polygonPoints=new wxPoint[poly.size()];
1311  for(unsigned int j=0;j<poly.size();j++)
1312  {
1313  polygonPoints[j]=transform(applyRot(poly[j]));
1314  };
1315  wxRegion singleRegion(poly.size(),polygonPoints,wxWINDING_RULE);
1316  if(m_masksToDraw[i].isInverted())
1317  {
1318  wxRegion newRegion(wholeImage);
1319  newRegion.Subtract(singleRegion);
1320  region.Union(newRegion);
1321  }
1322  else
1323  {
1324  region.Union(singleRegion);
1325  };
1326  delete []polygonPoints;
1327  };
1328  };
1329 #ifndef __WXMAC__
1330  // on Windows and GTK we need to compensate to clipping region
1331  // by the scroll offset
1332  // this seems not to be necessary for wxMac
1333  int x;
1334  int y;
1335  GetViewStart(&x,&y);
1336  region.Offset(-x,-y);
1337 #endif
1338  dc.SetDeviceClippingRegion(region);
1339  dc.DrawBitmap(m_disabledBitmap,offset,offset);
1340  dc.DestroyClippingRegion();
1341  };
1342  DrawCrop(dc);
1343  if(m_maskMode && !m_imageMask.empty())
1344  {
1345  //now draw all polygons
1348  for(unsigned int i=0;i<maskList.size();i++)
1349  {
1350  if(i!=m_activeMask)
1351  DrawPolygon(dc,maskList[i],false,false);
1352  else
1353  if(drawSelected)
1354  DrawPolygon(dc,maskList[i],true,true);
1355  };
1356  };
1357  //and now the actual polygon
1359  DrawPolygon(dc,m_editingMask,true,true);
1360  }
1361  else
1362  {
1363  // clear the rectangle and exit
1364  dc.SetPen(wxPen(GetBackgroundColour(), 1, wxPENSTYLE_SOLID));
1365  dc.SetBrush(wxBrush(GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
1366  dc.Clear();
1367  return;
1368  };
1369 }
1370 
1371 void MaskImageCtrl::OnSize(wxSizeEvent &e)
1372 {
1373  DEBUG_TRACE("size: " << e.GetSize().GetWidth() << "x" << e.GetSize().GetHeight());
1374  // rescale m_bitmap if needed.
1375  if (m_imageFilename != "") {
1376  if (m_fitToWindow) {
1377  setScale(0);
1378  }
1379  }
1380 };
1381 
1382 void MaskImageCtrl::OnScroll(wxScrollWinEvent &e)
1383 {
1384  m_oldScrollPosX = GetScrollPos(wxHORIZONTAL);
1385  m_oldScrollPosY = GetScrollPos(wxVERTICAL);
1386  e.Skip();
1387 }
1388 
1390 {
1391  if (m_maskEditState == NO_IMAGE)
1392  {
1393  return;
1394  }
1395  //determine average colour and set selection colour corresponding
1396  vigra::FindAverage<vigra::RGBValue<vigra::UInt8> > average;
1397  vigra::inspectImage(vigra::srcImageRange(*(m_img->get8BitImage())), average);
1398  vigra::RGBValue<vigra::UInt8> RGBaverage=average.average();
1399  if(RGBaverage[0]<180 && RGBaverage[1]<180 && RGBaverage[2]<180)
1400  {
1401  m_color_selection=*wxWHITE;
1402  }
1403  else
1404  {
1405  m_color_selection=*wxBLACK;
1406  };
1407  wxImage img = imageCacheEntry2wxImage(m_img);
1408  if (img.GetWidth() == 0)
1409  {
1410  return;
1411  }
1412  m_imageSize = wxSize(img.GetWidth(), img.GetHeight());
1415  if (m_fitToWindow)
1417 
1418  //scaling image to screen size
1419  if (getScaleFactor()!=1.0)
1420  {
1421  m_imageSize.SetWidth(scale(m_imageSize.GetWidth()));
1422  m_imageSize.SetHeight(scale(m_imageSize.GetHeight()));
1423  wxImageResizeQuality resizeQuality = wxIMAGE_QUALITY_NORMAL;
1424  if (std::max(img.GetWidth(), img.GetHeight()) > (ULONG_MAX >> 16))
1425  {
1426  // wxIMAGE_QUALITY_NORMAL resizes the image with ResampleNearest
1427  // this algorithm works only if image dimensions are smaller then
1428  // ULONG_MAX >> 16 (actual size of unsigned long differ from system
1429  // to system)
1430  resizeQuality = wxIMAGE_QUALITY_BOX_AVERAGE;
1431  };
1432  img=img.Scale(scale(m_realSize.GetWidth()), scale(m_realSize.GetHeight()), resizeQuality);
1433  }
1434  else
1435  {
1436  //the conversion to disabled m_bitmap would work on the original cached image file
1437  //therefore we need to create a copy to work on it
1438  img=img.Copy();
1439  };
1440  //and now rotating
1441  switch(m_imgRotation)
1442  {
1443  case ROT90:
1444  img = img.Rotate90(true);
1445  break;
1446  case ROT180:
1447  img = img.Rotate180();
1448  break;
1449  case ROT270:
1450  img = img.Rotate90(false);
1451  break;
1452  default:
1453  break;
1454  }
1455  // do color correction only if input image has icc profile or if we found a monitor profile
1456  if (!m_img->iccProfile->empty() || huginApp::Get()->HasMonitorProfile())
1457  {
1459  };
1460  m_bitmap=wxBitmap(img);
1461 
1462  //create disabled m_bitmap for drawing active masks
1463  img = img.ConvertToDisabled(192);
1464  m_disabledBitmap=wxBitmap(img);
1465  if (m_imgRotation == ROT90 || m_imgRotation == ROT270)
1466  {
1467  SetVirtualSize(m_imageSize.GetHeight(), m_imageSize.GetWidth());
1468  }
1469  else
1470  {
1471  SetVirtualSize(m_imageSize.GetWidth(), m_imageSize.GetHeight());
1472  };
1473  SetScrollRate(1, 1);
1474  // reset overlay
1475  m_overlay.Reset();
1476  Refresh(true);
1477 };
1478 
1480 {
1481  wxClientDC dc(this);
1482  PrepareDC(dc);
1483  wxDCOverlay overlayDC(m_overlay, &dc);
1484  overlayDC.Clear();
1485  dc.SetPen(wxPen(m_color_selection, 2, wxPENSTYLE_LONG_DASH));
1486  dc.SetBrush(*wxTRANSPARENT_BRUSH);
1487  dc.DrawRectangle(m_dragStartPos.x,m_dragStartPos.y,
1489 };
1490 
1492 {
1493  unsigned int selectedPolygon=UINT_MAX;
1494  unsigned int i=0;
1495  while(selectedPolygon==UINT_MAX && i<m_imageMask.size())
1496  {
1497  if(m_imageMask[i].isInside(p))
1498  selectedPolygon=i;
1499  i++;
1500  };
1501  if(selectedPolygon<UINT_MAX)
1502  m_editPanel->SelectMask(selectedPolygon);
1503 };
1504 
1505 bool MaskImageCtrl::SelectPointsInsideMouseRect(HuginBase::UIntSet &points,const bool considerSelectedOnly)
1506 {
1507  bool found=false;
1510  double xmin = std::min(p1.x, p2.x) - maxSelectionDistance / getScaleFactor();
1511  double xmax = std::max(p1.x, p2.x) + maxSelectionDistance / getScaleFactor();
1512  double ymin = std::min(p1.y, p2.y) - maxSelectionDistance / getScaleFactor();
1513  double ymax = std::max(p1.y, p2.y) + maxSelectionDistance / getScaleFactor();
1515  for(unsigned int i=0;i<poly.size();i++)
1516  {
1517  bool activePoints=true;
1518  if(considerSelectedOnly)
1519  activePoints=set_contains(m_selectedPoints,i);
1520  if(activePoints && xmin<=poly[i].x && poly[i].x<=xmax && ymin<=poly[i].y && poly[i].y<=ymax)
1521  {
1522  points.insert(i);
1523  found=true;
1524  };
1525  };
1526  return found;
1527 };
1528 
1529 void MaskImageCtrl::setScale(double factor)
1530 {
1531  if (factor == 0)
1532  {
1533  m_fitToWindow = true;
1534  factor = calcAutoScaleFactor(m_imageSize);
1535  }
1536  else
1537  {
1538  m_fitToWindow = false;
1539  }
1540  DEBUG_DEBUG("new scale factor:" << factor);
1541  // update if factor changed
1542  if (factor != m_scaleFactor)
1543  {
1544  m_scaleFactor = factor;
1545  // keep existing scale focussed.
1546  rescaleImage();
1547  }
1548 };
1549 
1551 {
1552  int w = size.GetWidth();
1553  int h = size.GetHeight();
1554  if (m_imgRotation == ROT90 || m_imgRotation == ROT270)
1555  {
1556  int t = w;
1557  w = h;
1558  h = t;
1559  }
1560 
1561  wxSize csize = GetSize();
1562  DEBUG_DEBUG("csize: " << csize.GetWidth() << "x" << csize.GetHeight() << "image: " << w << "x" << h);
1563  double s1 = (double)csize.GetWidth()/w;
1564  double s2 = (double)csize.GetHeight()/h;
1565  DEBUG_DEBUG("s1: " << s1 << " s2:" << s2);
1566  return s1 < s2 ? s1 : s2;
1567 };
1568 
1570 {
1571  return m_scaleFactor;
1572 };
1573 
1574 void MaskImageCtrl::setDrawingActiveMasks(bool newDrawActiveMasks)
1575 {
1576  m_showActiveMasks=newDrawActiveMasks;
1577  update();
1578 };
1579 
1580 IMPLEMENT_DYNAMIC_CLASS(MaskImageCtrl, wxScrolledWindow)
1581 
1583  : wxXmlResourceHandler()
1584 {
1585  AddWindowStyles();
1586 };
1587 
1589 {
1590  XRC_MAKE_INSTANCE(cp, MaskImageCtrl)
1591 
1592  cp->Create(m_parentAsWindow,
1593  GetID(),
1594  GetPosition(), GetSize(),
1595  GetStyle(wxT("style")),
1596  GetName());
1597 
1598  SetupWindow(cp);
1599 
1600  return cp;
1601 };
1602 
1604 {
1605  return IsOfClass(node, wxT("MaskImageCtrl"));
1606 };
1607 
1608 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
void DrawImageBitmap(wxDC &dc, int offset)
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
virtual void OnDraw(wxDC &dc) wxOVERRIDE
drawing routine
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:147
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:641
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:2169
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:152
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
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