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