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