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