Mercurial > hg > orthanc-stone
comparison Applications/Samples/SingleFrameEditorApplication.h @ 356:885f0a5eaa49 am-2
mouse tracker to set windowing
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 29 Oct 2018 17:21:40 +0100 |
parents | d2468dd75b3f |
children | ec4ad6c5eb99 |
comparison
equal
deleted
inserted
replaced
355:d2468dd75b3f | 356:885f0a5eaa49 |
---|---|
360 | 360 |
361 return extent; | 361 return extent; |
362 } | 362 } |
363 | 363 |
364 | 364 |
365 virtual void Render(Orthanc::ImageAccessor& buffer, | |
366 const ViewportGeometry& view, | |
367 ImageInterpolation interpolation) const = 0; | |
368 | |
369 | |
370 bool Contains(double x, | 365 bool Contains(double x, |
371 double y) const | 366 double y) const |
372 { | 367 { |
373 ApplyTransform(x, y, transformInverse_); | 368 ApplyTransform(x, y, transformInverse_); |
374 | 369 |
378 return (x >= cropX && x <= cropX + cropWidth && | 373 return (x >= cropX && x <= cropX + cropWidth && |
379 y >= cropY && y <= cropY + cropHeight); | 374 y >= cropY && y <= cropY + cropHeight); |
380 } | 375 } |
381 | 376 |
382 | 377 |
383 bool GetPixel(unsigned int& pixelX, | 378 bool GetPixel(unsigned int& imageX, |
384 unsigned int& pixelY, | 379 unsigned int& imageY, |
385 double sceneX, | 380 double sceneX, |
386 double sceneY) const | 381 double sceneY) const |
387 { | 382 { |
388 if (width_ == 0 || | 383 if (width_ == 0 || |
389 height_ == 0) | 384 height_ == 0) |
397 int x = static_cast<int>(std::floor(sceneX)); | 392 int x = static_cast<int>(std::floor(sceneX)); |
398 int y = static_cast<int>(std::floor(sceneY)); | 393 int y = static_cast<int>(std::floor(sceneY)); |
399 | 394 |
400 if (x < 0) | 395 if (x < 0) |
401 { | 396 { |
402 pixelX = 0; | 397 imageX = 0; |
403 } | 398 } |
404 else if (x >= static_cast<int>(width_)) | 399 else if (x >= static_cast<int>(width_)) |
405 { | 400 { |
406 pixelX = width_; | 401 imageX = width_; |
407 } | 402 } |
408 else | 403 else |
409 { | 404 { |
410 pixelX = static_cast<unsigned int>(x); | 405 imageX = static_cast<unsigned int>(x); |
411 } | 406 } |
412 | 407 |
413 if (y < 0) | 408 if (y < 0) |
414 { | 409 { |
415 pixelY = 0; | 410 imageY = 0; |
416 } | 411 } |
417 else if (y >= static_cast<int>(height_)) | 412 else if (y >= static_cast<int>(height_)) |
418 { | 413 { |
419 pixelY = height_; | 414 imageY = height_; |
420 } | 415 } |
421 else | 416 else |
422 { | 417 { |
423 pixelY = static_cast<unsigned int>(y); | 418 imageY = static_cast<unsigned int>(y); |
424 } | 419 } |
425 | 420 |
426 return true; | 421 return true; |
427 } | 422 } |
428 } | 423 } |
461 } | 456 } |
462 | 457 |
463 double GetPanY() const | 458 double GetPanY() const |
464 { | 459 { |
465 return panY_; | 460 return panY_; |
466 } | |
467 | |
468 virtual bool GetDefaultWindowing(float& center, | |
469 float& width) const | |
470 { | |
471 return false; | |
472 } | 461 } |
473 | 462 |
474 void GetCenter(double& centerX, | 463 void GetCenter(double& centerX, |
475 double& centerY) const | 464 double& centerY) const |
476 { | 465 { |
582 | 571 |
583 void SetResizeable(bool resizeable) | 572 void SetResizeable(bool resizeable) |
584 { | 573 { |
585 resizeable_ = resizeable; | 574 resizeable_ = resizeable; |
586 } | 575 } |
576 | |
577 virtual bool GetDefaultWindowing(float& center, | |
578 float& width) const | |
579 { | |
580 return false; | |
581 } | |
582 | |
583 virtual void Render(Orthanc::ImageAccessor& buffer, | |
584 const ViewportGeometry& view, | |
585 ImageInterpolation interpolation) const = 0; | |
586 | |
587 virtual bool GetRange(float& minValue, | |
588 float& maxValue) const = 0; | |
587 }; | 589 }; |
588 | 590 |
589 | 591 |
590 class BitmapAccessor : public boost::noncopyable | 592 class BitmapAccessor : public boost::noncopyable |
591 { | 593 { |
786 | 788 |
787 *q = (a * value + (1.0f - a) * (*q)); | 789 *q = (a * value + (1.0f - a) * (*q)); |
788 } | 790 } |
789 } | 791 } |
790 } | 792 } |
793 | |
794 virtual bool GetRange(float& minValue, | |
795 float& maxValue) const | |
796 { | |
797 if (useWindowing_) | |
798 { | |
799 return false; | |
800 } | |
801 else | |
802 { | |
803 minValue = 0; | |
804 maxValue = 0; | |
805 | |
806 if (foreground_ < 0) | |
807 { | |
808 minValue = foreground_; | |
809 } | |
810 | |
811 if (foreground_ > 0) | |
812 { | |
813 maxValue = foreground_; | |
814 } | |
815 | |
816 return true; | |
817 } | |
818 } | |
791 }; | 819 }; |
792 | 820 |
793 | 821 |
794 | 822 |
795 private: | 823 private: |
796 class DicomBitmap : public Bitmap | 824 class DicomBitmap : public Bitmap |
797 { | 825 { |
798 private: | 826 private: |
799 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData | 827 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData |
800 std::auto_ptr<DicomFrameConverter> converter_; | 828 std::auto_ptr<DicomFrameConverter> converter_; |
801 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 or RGB24 | 829 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 |
802 | 830 |
803 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) | 831 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) |
804 { | 832 { |
805 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); | 833 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); |
806 } | 834 } |
909 if (converter_.get() != NULL && | 937 if (converter_.get() != NULL && |
910 converter_->HasDefaultWindow()) | 938 converter_->HasDefaultWindow()) |
911 { | 939 { |
912 center = static_cast<float>(converter_->GetDefaultWindowCenter()); | 940 center = static_cast<float>(converter_->GetDefaultWindowCenter()); |
913 width = static_cast<float>(converter_->GetDefaultWindowWidth()); | 941 width = static_cast<float>(converter_->GetDefaultWindowWidth()); |
942 return true; | |
943 } | |
944 else | |
945 { | |
946 return false; | |
947 } | |
948 } | |
949 | |
950 | |
951 virtual bool GetRange(float& minValue, | |
952 float& maxValue) const | |
953 { | |
954 if (converted_.get() != NULL) | |
955 { | |
956 if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) | |
957 { | |
958 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
959 } | |
960 | |
961 Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_); | |
914 return true; | 962 return true; |
915 } | 963 } |
916 else | 964 else |
917 { | 965 { |
918 return false; | 966 return false; |
1001 return false; | 1049 return false; |
1002 } | 1050 } |
1003 } | 1051 } |
1004 | 1052 |
1005 | 1053 |
1054 void GetWindowingWithDefault(float& center, | |
1055 float& width) const | |
1056 { | |
1057 if (!GetWindowing(center, width)) | |
1058 { | |
1059 center = 128; | |
1060 width = 256; | |
1061 } | |
1062 } | |
1063 | |
1064 | |
1006 void SetWindowing(float center, | 1065 void SetWindowing(float center, |
1007 float width) | 1066 float width) |
1008 | 1067 |
1009 { | 1068 { |
1010 hasWindowing_ = true; | 1069 hasWindowing_ = true; |
1011 windowingCenter_ = center; | 1070 windowingCenter_ = center; |
1012 windowingWidth_ = width; | 1071 windowingWidth_ = width; |
1013 | 1072 |
1014 EmitMessage(ContentChangedMessage(*this)); | 1073 //EmitMessage(ContentChangedMessage(*this)); |
1015 } | 1074 } |
1016 | 1075 |
1017 | 1076 |
1018 Bitmap& LoadText(const Orthanc::Font& font, | 1077 Bitmap& LoadText(const Orthanc::Font& font, |
1019 const std::string& utf8) | 1078 const std::string& utf8) |
1023 std::auto_ptr<AlphaBitmap> alpha(new AlphaBitmap(bitmap, *this)); | 1082 std::auto_ptr<AlphaBitmap> alpha(new AlphaBitmap(bitmap, *this)); |
1024 alpha->LoadText(font, utf8); | 1083 alpha->LoadText(font, utf8); |
1025 | 1084 |
1026 AlphaBitmap* ptr = alpha.get(); | 1085 AlphaBitmap* ptr = alpha.get(); |
1027 bitmaps_[bitmap] = alpha.release(); | 1086 bitmaps_[bitmap] = alpha.release(); |
1028 | 1087 |
1029 return *ptr; | 1088 return *ptr; |
1030 } | 1089 } |
1031 | 1090 |
1032 | 1091 |
1033 Bitmap& LoadTestBlock(unsigned int width, | 1092 Bitmap& LoadTestBlock(unsigned int width, |
1229 | 1288 |
1230 context.SetSourceColor(255, 0, 0); | 1289 context.SetSourceColor(255, 0, 0); |
1231 view.ApplyTransform(context); | 1290 view.ApplyTransform(context); |
1232 bitmap->second->DrawBorders(context, view.GetZoom()); | 1291 bitmap->second->DrawBorders(context, view.GetZoom()); |
1233 } | 1292 } |
1293 } | |
1294 } | |
1295 | |
1296 | |
1297 void GetRange(float& minValue, | |
1298 float& maxValue) const | |
1299 { | |
1300 bool first = true; | |
1301 | |
1302 for (Bitmaps::const_iterator it = bitmaps_.begin(); | |
1303 it != bitmaps_.end(); it++) | |
1304 { | |
1305 assert(it->second != NULL); | |
1306 | |
1307 float a, b; | |
1308 if (it->second->GetRange(a, b)) | |
1309 { | |
1310 if (first) | |
1311 { | |
1312 minValue = a; | |
1313 maxValue = b; | |
1314 first = false; | |
1315 } | |
1316 else | |
1317 { | |
1318 minValue = std::min(a, minValue); | |
1319 maxValue = std::max(b, maxValue); | |
1320 } | |
1321 } | |
1322 } | |
1323 | |
1324 if (first) | |
1325 { | |
1326 minValue = 0; | |
1327 maxValue = 0; | |
1234 } | 1328 } |
1235 } | 1329 } |
1236 }; | 1330 }; |
1237 | 1331 |
1238 | 1332 |
1915 } | 2009 } |
1916 } | 2010 } |
1917 }; | 2011 }; |
1918 | 2012 |
1919 | 2013 |
2014 class WindowingTracker : public IWorldSceneMouseTracker | |
2015 { | |
2016 public: | |
2017 enum Action | |
2018 { | |
2019 Action_IncreaseWidth, | |
2020 Action_DecreaseWidth, | |
2021 Action_IncreaseCenter, | |
2022 Action_DecreaseCenter | |
2023 }; | |
2024 | |
2025 private: | |
2026 UndoRedoStack& undoRedoStack_; | |
2027 BitmapStack& stack_; | |
2028 int clickX_; | |
2029 int clickY_; | |
2030 Action leftAction_; | |
2031 Action rightAction_; | |
2032 Action upAction_; | |
2033 Action downAction_; | |
2034 float strength_; | |
2035 float sourceCenter_; | |
2036 float sourceWidth_; | |
2037 | |
2038 static void ComputeAxisEffect(int& deltaCenter, | |
2039 int& deltaWidth, | |
2040 int delta, | |
2041 Action actionNegative, | |
2042 Action actionPositive) | |
2043 { | |
2044 if (delta < 0) | |
2045 { | |
2046 switch (actionNegative) | |
2047 { | |
2048 case Action_IncreaseWidth: | |
2049 deltaWidth = -delta; | |
2050 break; | |
2051 | |
2052 case Action_DecreaseWidth: | |
2053 deltaWidth = delta; | |
2054 break; | |
2055 | |
2056 case Action_IncreaseCenter: | |
2057 deltaCenter = -delta; | |
2058 break; | |
2059 | |
2060 case Action_DecreaseCenter: | |
2061 deltaCenter = delta; | |
2062 break; | |
2063 | |
2064 default: | |
2065 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
2066 } | |
2067 } | |
2068 else if (delta > 0) | |
2069 { | |
2070 switch (actionPositive) | |
2071 { | |
2072 case Action_IncreaseWidth: | |
2073 deltaWidth = delta; | |
2074 break; | |
2075 | |
2076 case Action_DecreaseWidth: | |
2077 deltaWidth = -delta; | |
2078 break; | |
2079 | |
2080 case Action_IncreaseCenter: | |
2081 deltaCenter = delta; | |
2082 break; | |
2083 | |
2084 case Action_DecreaseCenter: | |
2085 deltaCenter = -delta; | |
2086 break; | |
2087 | |
2088 default: | |
2089 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
2090 } | |
2091 } | |
2092 } | |
2093 | |
2094 | |
2095 class UndoRedoCommand : public UndoRedoStack::ICommand | |
2096 { | |
2097 private: | |
2098 BitmapStack& stack_; | |
2099 float sourceCenter_; | |
2100 float sourceWidth_; | |
2101 float targetCenter_; | |
2102 float targetWidth_; | |
2103 | |
2104 public: | |
2105 UndoRedoCommand(const WindowingTracker& tracker) : | |
2106 stack_(tracker.stack_), | |
2107 sourceCenter_(tracker.sourceCenter_), | |
2108 sourceWidth_(tracker.sourceWidth_) | |
2109 { | |
2110 stack_.GetWindowingWithDefault(targetCenter_, targetWidth_); | |
2111 } | |
2112 | |
2113 virtual void Undo() const | |
2114 { | |
2115 stack_.SetWindowing(sourceCenter_, sourceWidth_); | |
2116 } | |
2117 | |
2118 virtual void Redo() const | |
2119 { | |
2120 stack_.SetWindowing(targetCenter_, targetWidth_); | |
2121 } | |
2122 }; | |
2123 | |
2124 | |
2125 public: | |
2126 WindowingTracker(UndoRedoStack& undoRedoStack, | |
2127 BitmapStack& stack, | |
2128 int x, | |
2129 int y, | |
2130 Action leftAction, | |
2131 Action rightAction, | |
2132 Action upAction, | |
2133 Action downAction) : | |
2134 undoRedoStack_(undoRedoStack), | |
2135 stack_(stack), | |
2136 clickX_(x), | |
2137 clickY_(y), | |
2138 leftAction_(leftAction), | |
2139 rightAction_(rightAction), | |
2140 upAction_(upAction), | |
2141 downAction_(downAction) | |
2142 { | |
2143 stack_.GetWindowingWithDefault(sourceCenter_, sourceWidth_); | |
2144 | |
2145 float minValue, maxValue; | |
2146 stack.GetRange(minValue, maxValue); | |
2147 | |
2148 assert(minValue <= maxValue); | |
2149 | |
2150 float tmp; | |
2151 | |
2152 float delta = (maxValue - minValue); | |
2153 if (delta <= 1) | |
2154 { | |
2155 tmp = 0; | |
2156 } | |
2157 else | |
2158 { | |
2159 tmp = log2(delta); | |
2160 } | |
2161 | |
2162 strength_ = tmp - 7; | |
2163 if (strength_ < 1) | |
2164 { | |
2165 strength_ = 1; | |
2166 } | |
2167 } | |
2168 | |
2169 virtual bool HasRender() const | |
2170 { | |
2171 return false; | |
2172 } | |
2173 | |
2174 virtual void Render(CairoContext& context, | |
2175 double zoom) | |
2176 { | |
2177 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
2178 } | |
2179 | |
2180 virtual void MouseUp() | |
2181 { | |
2182 undoRedoStack_.Add(new UndoRedoCommand(*this)); | |
2183 } | |
2184 | |
2185 | |
2186 virtual void MouseMove(int displayX, | |
2187 int displayY, | |
2188 double sceneX, | |
2189 double sceneY) | |
2190 { | |
2191 // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js | |
2192 | |
2193 static const float SCALE = 1.0; | |
2194 | |
2195 int deltaCenter = 0; | |
2196 int deltaWidth = 0; | |
2197 | |
2198 ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_); | |
2199 ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_); | |
2200 | |
2201 float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_); | |
2202 float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_); | |
2203 stack_.SetWindowing(newCenter, newWidth); | |
2204 } | |
2205 }; | |
2206 | |
2207 | |
1920 class BitmapStackInteractor : public IWorldSceneInteractor | 2208 class BitmapStackInteractor : public IWorldSceneInteractor |
1921 { | 2209 { |
1922 private: | 2210 private: |
1923 enum Tool | 2211 enum Tool |
1924 { | 2212 { |
1925 Tool_Move, | 2213 Tool_Move, |
1926 Tool_Rotate, | 2214 Tool_Rotate, |
1927 Tool_Crop, | 2215 Tool_Crop, |
1928 Tool_Resize | 2216 Tool_Resize, |
2217 Tool_Windowing | |
1929 }; | 2218 }; |
1930 | 2219 |
1931 static double GetHandleSize() | 2220 static double GetHandleSize() |
1932 { | 2221 { |
1933 return 10.0; | 2222 return 10.0; |
1948 | 2237 |
1949 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, | 2238 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, |
1950 const ViewportGeometry& view, | 2239 const ViewportGeometry& view, |
1951 MouseButton button, | 2240 MouseButton button, |
1952 KeyboardModifiers modifiers, | 2241 KeyboardModifiers modifiers, |
2242 int viewportX, | |
2243 int viewportY, | |
1953 double x, | 2244 double x, |
1954 double y, | 2245 double y, |
1955 IStatusBar* statusBar) | 2246 IStatusBar* statusBar) |
1956 { | 2247 { |
1957 if (button == MouseButton_Left) | 2248 if (button == MouseButton_Left) |
1958 { | 2249 { |
1959 size_t selected; | 2250 size_t selected; |
1960 | 2251 |
1961 if (!stack_.GetSelectedBitmap(selected)) | 2252 if (tool_ == Tool_Windowing) |
2253 { | |
2254 return new WindowingTracker(undoRedoStack_, stack_, | |
2255 viewportX, viewportY, | |
2256 WindowingTracker::Action_DecreaseWidth, | |
2257 WindowingTracker::Action_IncreaseWidth, | |
2258 WindowingTracker::Action_DecreaseCenter, | |
2259 WindowingTracker::Action_IncreaseCenter); | |
2260 } | |
2261 else if (!stack_.GetSelectedBitmap(selected)) | |
1962 { | 2262 { |
1963 size_t bitmap; | 2263 size_t bitmap; |
1964 if (stack_.LookupBitmap(bitmap, x, y)) | 2264 if (stack_.LookupBitmap(bitmap, x, y)) |
1965 { | 2265 { |
1966 printf("CLICK on bitmap %ld\n", bitmap); | 2266 printf("CLICK on bitmap %ld\n", bitmap); |
2106 | 2406 |
2107 case 's': | 2407 case 's': |
2108 tool_ = Tool_Resize; | 2408 tool_ = Tool_Resize; |
2109 break; | 2409 break; |
2110 | 2410 |
2411 case 'w': | |
2412 tool_ = Tool_Windowing; | |
2413 break; | |
2414 | |
2111 case 'z': | 2415 case 'z': |
2112 if (modifiers & KeyboardModifiers_Control) | 2416 if (modifiers & KeyboardModifiers_Control) |
2113 { | 2417 { |
2114 undoRedoStack_.Undo(); | 2418 undoRedoStack_.Undo(); |
2115 widget.NotifyContentChanged(); | 2419 widget.NotifyContentChanged(); |
2192 //stack_.Render(buffer, GetView(), ImageInterpolation_Bilinear); | 2496 //stack_.Render(buffer, GetView(), ImageInterpolation_Bilinear); |
2193 | 2497 |
2194 // As in GrayscaleFrameRenderer => TODO MERGE? | 2498 // As in GrayscaleFrameRenderer => TODO MERGE? |
2195 | 2499 |
2196 float windowCenter, windowWidth; | 2500 float windowCenter, windowWidth; |
2197 if (!stack_.GetWindowing(windowCenter, windowWidth)) | 2501 stack_.GetWindowingWithDefault(windowCenter, windowWidth); |
2198 { | |
2199 windowCenter = 128; | |
2200 windowWidth = 256; | |
2201 } | |
2202 | 2502 |
2203 float x0 = windowCenter - windowWidth / 2.0f; | 2503 float x0 = windowCenter - windowWidth / 2.0f; |
2204 float x1 = windowCenter + windowWidth / 2.0f; | 2504 float x1 = windowCenter + windowWidth / 2.0f; |
2205 | 2505 |
2206 if (windowWidth >= 0.001f) // Avoid division by zero at (*) | 2506 if (windowWidth >= 0.001f) // Avoid division by zero at (*) |
2295 | 2595 |
2296 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, | 2596 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, |
2297 const ViewportGeometry& view, | 2597 const ViewportGeometry& view, |
2298 MouseButton button, | 2598 MouseButton button, |
2299 KeyboardModifiers modifiers, | 2599 KeyboardModifiers modifiers, |
2600 int viewportX, | |
2601 int viewportY, | |
2300 double x, | 2602 double x, |
2301 double y, | 2603 double y, |
2302 IStatusBar* statusBar) | 2604 IStatusBar* statusBar) |
2303 { | 2605 { |
2304 switch (application_.currentTool_) { | 2606 switch (application_.currentTool_) { |