Hugin trunk 0.1
Loading...
Searching...
No Matches
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"
37#include "hugin/MaskImageCtrl.h"
40#include <vigra/inspectimage.hxx>
41
43const int polygonPointSize=3;
45const int maxSelectionDistance = 10;
46
47// our image control
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;
61 m_showActiveMasks = false;
62 m_maskMode = true;
63 m_oldScrollPosX = -1;
64 m_oldScrollPosY = -1;
65 m_middleMouseScroll = false;
67 m_resizeTimer.SetOwner(this);
68 // bind event handler
93
94 return true;
95}
96
101
103{
105 if(m_maskMode)
106 {
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 };
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
206
215
217{
219 {
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();
267 fill_set(m_selectedPoints,0,m_imageMask[m_activeMask].getMaskPolygon().size()-1);
268};
269
270// returns where the user clicked
272{
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()));
280 {
281 return CLICK_CIRCLE;
282 };
283 };
284 if(m_cropRect.intersects(testRect))
285 {
287 {
288 return CLICK_LEFT;
289 };
291 {
292 return CLICK_RIGHT;
293 };
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{
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:
322 {
323 double newHalfWidth=m_cropRect.width()/2.0-delta.x;
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;
338 {
339 double newHalfWidth=m_cropRect.width()/2.0+delta.x;
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:
354 {
355 double newHalfHeight=m_cropRect.height()/2.0-delta.y;
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;
370 {
371 double newHalfHeight=m_cropRect.height()/2.0+delta.y;
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;
385 {
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;
407 };
408 if(newTop>newBottom)
409 {
410 int temp=newTop;
413 };
414 m_cropRect.setUpperLeft(vigra::Point2D(newLeft, newTop));
415 m_cropRect.setLowerRight(vigra::Point2D(newRight, newBottom));
416 };
417};
418
420{
421 if(m_previewOnly)
422 return;
424 {
425 // handle scrolling and break out
427 viewStart = viewStart - (mouse.GetPosition() - m_scrollPos);
429 m_scrollPos = mouse.GetPosition();
430 return;
431 };
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;
442 break;
444 case REGION_SELECTING:
445 case POINTS_DELETING:
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)
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)
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 {
470 }
471 else
472 {
474 };
475 break;
476 case CLICK_LEFT:
477 case CLICK_RIGHT:
478 switch (m_imgRotation)
479 {
480 case ROT90:
481 case ROT270:
483 break;
484 default:
486 };
487 break;
488 case CLICK_TOP:
489 case CLICK_BOTTOM:
490 switch (m_imgRotation)
491 {
492 case ROT90:
493 case ROT270:
495 break;
496 default:
498 };
499 break;
500 case CLICK_CIRCLE:
502 break;
503 default:
505 };
506 break;
507 case CROP_MOVING:
508 case CROP_LEFT_MOVING:
510 case CROP_TOP_MOVING:
515 Refresh(false);
517 break;
518 case NO_IMAGE:
519 case NO_MASK:
520 case NO_SELECTION:
521 case POINTS_SELECTED:
523 // in all other cases do nothing
524 break;
525 };
526 if(doUpdate)
527 Refresh(false);
528}
529
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 {
545 //starting polygon creating
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
560 if(index<UINT_MAX)
561 {
562 m_selectedPoints.clear();
564 m_selectedPoints.insert(index);
566 };
567 }
568 else
569 {
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
588 if(index<UINT_MAX)
589 {
590 m_selectedPoints.clear();
592 m_selectedPoints.insert(index);
594 };
595 }
596 else
597 {
600 {
601 //selected point clicked, starting moving
603 }
604 else
605 {
606 //unselected point clicked
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
665{
667 return;
668 DEBUG_DEBUG("LEFT MOUSE UP");
670 CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
671 &mpos.x, & mpos.y);
673 bool doUpdate=false;
674 switch(m_maskEditState)
675 {
677 doUpdate=true;
681 break;
683 //next point of polygen selected
684 doUpdate=true;
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;
709 if(HasCapture())
710 ReleaseMouse();
711 m_overlay.Reset();
712 doUpdate = true;
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();
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)
765 break;
766 case CROP_MOVING:
767 case CROP_LEFT_MOVING:
769 case CROP_TOP_MOVING:
772 if(HasCapture())
773 ReleaseMouse();
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
789{
791 return;
792 switch(m_maskEditState)
793 {
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;
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
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 {
852 {
855 Refresh();
856 };
857 };
858 };
859};
860
862{
864 return;
866 CalcUnscrolledPosition(mouse.GetPosition().x, mouse.GetPosition().y,
867 &mpos.x, & mpos.y);
869 if(HasCapture())
870 ReleaseMouse();
871 switch(m_maskEditState)
872 {
874 {
877 m_editingMask=mask;
878 m_selectedPoints.clear();
879 MainFrame::Get()->SetStatusText(wxEmptyString,0);
880 break;
881 };
883 {
884 //close newly generated polygon
887 if(m_editingMask.getMaskPolygon().size()>2)
888 {
889 m_imageMask.push_back(m_editingMask);
890 m_activeMask=m_imageMask.size()-1;
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 {
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++)
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++)
927 };
928 for(HuginBase::UIntSet::const_iterator it=temp.begin();it!=temp.end();++it)
929 if(!set_contains(points,*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
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:
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
999{
1000 if (m_previewOnly)
1001 {
1002 return;
1003 };
1004 if (HasCapture())
1005 {
1006 ReleaseMouse();
1007 };
1008 m_middleMouseScroll = false;
1009}
1010
1012{
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);
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
1056{
1057 const int key=e.GetKeyCode();
1058 bool processed=false;
1059 if((key==WXK_DELETE) || (key==WXK_NUMPAD_DELETE))
1060 {
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;
1080 }
1081 else
1082 wxBell();
1083 };
1084 }
1085 else
1086 {
1088 {
1089 wxCommandEvent dummy;
1090 processed=true;
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
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}
1171
1173{
1174 if(HasCapture())
1175 ReleaseMouse();
1177 {
1178 wxBell();
1181 m_editingMask=mask;
1182 m_selectedPoints.clear();
1183 Refresh();
1184 };
1185};
1186
1194
1196{
1197 return wxSize(m_imageSize.GetWidth(),m_imageSize.GetHeight());
1198};
1199
1201{
1202 unsigned int nrOfPoints=poly.getMaskPolygon().size();
1203 if (nrOfPoints<2)
1204 return;
1206 for(unsigned int j=0;j<nrOfPoints;j++)
1207 {
1208 polygonPoints[j]=transform(applyRot(poly.getMaskPolygon()[j]));
1209 };
1210 if(isSelected)
1212 else
1213 switch(poly.getMaskType())
1214 {
1219 break;
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 {
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 };
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);
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
1281{
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
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;
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 {
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 {
1334 };
1336 if(m_masksToDraw[i].isInverted())
1337 {
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
1385{
1386 // start timer once
1387 if (!m_imageFilename.empty())
1388 {
1389 m_resizeTimer.StartOnce(100);
1390 }
1391 e.Skip();
1392}
1393
1395{
1396 // rescale m_bitmap if needed.
1397 if (!m_imageFilename.empty() && m_fitToWindow)
1398 {
1399 setScale(0);
1400 }
1401 e.Skip();
1402};
1403
1410
1412{
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 {
1424 }
1425 else
1426 {
1428 };
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()));
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)
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 }
1486 HuginBase::Color::CorrectImage(img, iccProfile, huginApp::Get()->GetMonitorProfile());
1487 };
1488 m_bitmap=wxBitmap(img);
1489
1490 //create disabled m_bitmap for drawing active masks
1491 img = img.ConvertToDisabled(192);
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);
1517 overlayDC.Clear();
1518#endif
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))
1533 i++;
1534 };
1537};
1538
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;
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
1563void MaskImageCtrl::setScale(double factor)
1564{
1565 if (factor == 0)
1566 {
1567 m_fitToWindow = true;
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();
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
1613
1614IMPLEMENT_DYNAMIC_CLASS(MaskImageCtrl, wxScrolledWindow)
1615
1617 : wxXmlResourceHandler()
1618{
1620};
1621
1623{
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
1642IMPLEMENT_DYNAMIC_CLASS(MaskImageCtrlXmlHandler, wxXmlResourceHandler)
const int maxSelectionDistance
maximal distance for selection of one point
const int polygonPointSize
half size of markers
base class, which stores one mask polygon
Definition Mask.h:53
void movePointBy(const unsigned int index, const hugin_utils::FDiff2D diff)
relativ moves the point at position index by diff
Definition Mask.cpp:142
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 removePoint(const unsigned int index)
removes point at the position index from the polygon
Definition Mask.cpp:124
VectorPolygon getMaskPolygon() const
returns vector with coordinates of the polygon
Definition Mask.h:81
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 addPoint(const hugin_utils::FDiff2D p)
adds point at the end to the polygon
Definition Mask.cpp:109
bool isInside(const hugin_utils::FDiff2D p) const
checks if given point is inside of the stored polygon
Definition Mask.cpp:40
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
hugin_utils::FDiff2D getCenter() const
return the center point of the polygon
Definition Mask.cpp:645
void insertPoint(const unsigned int index, const hugin_utils::FDiff2D p)
insert point at the position index into the polygon
Definition Mask.cpp:115
static MainFrame * Get()
hack.. kind of a pseudo singleton...
mask editor panel.
void UpdateCropFromImage()
updates the displayed crop in the text boxes (for dragging)
void UpdateMask()
called when mask where changed in MaskImageCtrl
void AddMask()
called when new mask added in MaskImageCtrl
void OnMaskDelete(wxCommandEvent &e)
called when user wants to delete active mask
void SelectMask(unsigned int newMaskNr)
selects the mask with index newMaskNr in the listbox
void UpdateCrop(bool updateFromImgCtrl=false)
updated the crop in the Panorama object with the current values from GUI
xrc handler for mask editor
virtual wxObject * DoCreateResource()
virtual bool CanHandle(wxXmlNode *node)
mask editor
unsigned int m_activeMask
void setActiveMask(unsigned int newMask, bool doUpdate=true)
mark mask with image as beeing editing
hugin_utils::FDiff2D m_cropCenter
MaskEditorState m_maskEditState
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
void UpdateCrop(hugin_utils::FDiff2D delta)
bool SelectPointsInsideMouseRect(HuginBase::UIntSet &points, const bool considerSelectedOnly)
int scale(int x) const
scale of width/height
std::string m_imageFilename
void OnPaint(wxPaintEvent &e)
drawing routine
T applyRotInv(const T &p) const
void setScale(double factor)
set the scaling factor for mask editing display.
wxPoint m_scrollPos
void setDrawingActiveMasks(bool newDrawActiveMasks)
set if active masks should be drawn
void setNewMasks(HuginBase::MaskPolygonVector newMasks, HuginBase::MaskPolygonVector masksToDraw)
updates masks for currently selected image
HuginBase::MaskPolygonVector m_imageMask
MaskEditorPanel * m_editPanel
void startNewPolygon()
starts creating a new polygon
wxColor m_colour_polygon_positive
ImageCache::EntryPtr m_img
vigra::Rect2D m_cropRect
ImageRotation
image rotation.
wxPoint m_currentPos
void DrawSelectionRectangle()
void OnLeftMouseDblClick(wxMouseEvent &mouse)
event handler for left double click
void FindPolygon(hugin_utils::FDiff2D p)
void Init(MaskEditorPanel *parent)
void DrawCrop(wxDC &dc)
double calcAutoScaleFactor(wxSize size)
calculate new scale factor for this image
void DrawPolygon(wxDC &dc, HuginBase::MaskPolygon poly, bool isSelected, bool drawMarker)
void OnKeyUp(wxKeyEvent &e)
event handler for keyboard
ClickPos GetClickPos(vigra::Point2D pos)
void OnRightMouseUp(wxMouseEvent &mouse)
event handler when right mouse button is released
wxBitmap m_bitmap
void OnKillFocus(wxFocusEvent &e)
event handler, when editor lost focus, mainly cancels creating new polygon
double getScaleFactor() const
get scale factor (calculates factor when fit to window is active)
HuginBase::MaskPolygonVector m_masksToDraw
void OnMouseMove(wxMouseEvent &mouse)
event handler when mouse is moving
void OnSizeFinished(wxTimerEvent &e)
handler called at end of all resize
void OnMiddleMouseUp(wxMouseEvent &mouse)
event handler for middle mouse button, end scrolling
void OnLeftMouseDown(wxMouseEvent &mouse)
event handler when left mouse button is pressed
int transform(int x) const
convert image coordinate to screen coordinates, considers additional added border
wxOverlay m_overlay
wxColour m_color_selection
wxColor m_colour_point_selected
wxPoint m_dragStartPos
void OnLeftMouseUp(wxMouseEvent &mouse)
event handler when right mouse button is released
ImageRotation m_imgRotation
void rescaleImage()
rescale the image
wxSize DoGetBestSize() const override
returns size of currently scaled image
HuginBase::MaskPolygon m_editingMask
wxColor m_colour_point_unselected
void SetMaskMode(bool newMaskMode)
sets the control to mask (newMaskMode=true) or crop (newMaskMode=false) mode
void OnChar(wxKeyEvent &e)
event handler for scrolling with keyboard
void OnMouseWheel(wxMouseEvent &mouse)
event handler for mouse wheel
wxBitmap m_disabledBitmap
void selectAllMarkers()
select all points of active mask
wxTimer m_resizeTimer
void setCrop(HuginBase::SrcPanoImage::CropMode newCropMode, vigra::Rect2D newCropRect, bool isCentered, hugin_utils::FDiff2D center, bool isCircleCrop)
updates the crop mode and crop rect
T applyRot(const T &p) const
void OnSize(wxSizeEvent &e)
handler called when size of control was changed
void OnCaptureLost(wxMouseCaptureLostEvent &e)
event handler, when mouse capture is lost, e.g.
HuginBase::UIntSet m_selectedPoints
void OnScroll(wxScrollWinEvent &e)
event handler for remember scroll position
void OnRightMouseDown(wxMouseEvent &mouse)
event handler when right mouse button is pressed
int invtransform(int x) const
translate screen coordinates to image coordinates, considers additional added border
bool Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxTAB_TRAVERSAL, const wxString &name="panel")
wxColor m_colour_polygon_negative
HuginBase::SrcPanoImage::CropMode m_cropMode
void OnMiddleMouseDown(wxMouseEvent &mouse)
event handler for middle mouse button, start scrolling
bool HasMonitorProfile() const
return true if we found a suitable monitor profile and could loading it
Definition huginApp.h:120
static huginApp * Get()
hack.. kind of a pseudo singleton...
Definition huginApp.cpp:651
implementation of huginApp Class
#define HUGIN_CONV_FILENAME
Definition platform.h:40
#define DEBUG_DEBUG(msg)
Definition utils.h:68
#define DEBUG_TRACE(msg)
Definition utils.h:67
#define PI
Header file for Khan's deghosting algorithm Copyright (C) 2009 Lukáš Jirkovský l.jirkovsky@gmail....
Definition khan.h:43
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
std::vector< MaskPolygon > MaskPolygonVector
Definition Mask.h:150
const int maskOffset
polygon can exceed the image maximal maskOffset pixels in each direction bigger polygons will be clip...
Definition Mask.h:44
std::set< unsigned int > UIntSet
std::vector< hugin_utils::FDiff2D > VectorPolygon
vector, which stores coordinates of one polygon
Definition Mask.h:39
int roundi(T x)
Definition hugin_math.h:73
include file for the hugin project
include file for the hugin project
bool set_contains(const _Container &c, const typename _Container::key_type &key)
Definition stl_utils.h:74
void fill_set(_Container &c, typename _Container::key_type begin, typename _Container::key_type end)
Definition stl_utils.h:81
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41
wxImage imageCacheEntry2wxImage(ImageCache::EntryPtr e)