comparison Applications/Samples/SingleFrameEditorApplication.h @ 352:d95e65ebe0b9 am-2

ResizeBitmapTracker
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 29 Oct 2018 11:51:35 +0100
parents da25d2423314
children 92a159481900
comparison
equal deleted inserted replaced
351:da25d2423314 352:d95e65ebe0b9
60 60
61 61
62 class Bitmap : public boost::noncopyable 62 class Bitmap : public boost::noncopyable
63 { 63 {
64 private: 64 private:
65 bool visible_; 65 size_t index_;
66 bool hasSize_; 66 bool hasSize_;
67 unsigned int width_; 67 unsigned int width_;
68 unsigned int height_; 68 unsigned int height_;
69 bool hasCrop_; 69 bool hasCrop_;
70 unsigned int cropX_; 70 unsigned int cropX_;
76 double pixelSpacingX_; 76 double pixelSpacingX_;
77 double pixelSpacingY_; 77 double pixelSpacingY_;
78 double panX_; 78 double panX_;
79 double panY_; 79 double panY_;
80 double angle_; 80 double angle_;
81 bool resizeable_;
81 82
82 83
83 protected: 84 protected:
84 static Matrix CreateOffsetMatrix(double dx, 85 static Matrix CreateOffsetMatrix(double dx,
85 double dy) 86 double dy)
109 0, 0, 1 }; 110 0, 0, 1 };
110 LinearAlgebra::FillMatrix(m, 3, 3, v); 111 LinearAlgebra::FillMatrix(m, 3, 3, v);
111 return m; 112 return m;
112 } 113 }
113 114
115
116 const Matrix& GetTransform() const
117 {
118 return transform_;
119 }
120
114 121
115 private: 122 private:
116 static void ApplyTransform(double& x /* inout */, 123 static void ApplyTransform(double& x /* inout */,
117 double& y /* inout */, 124 double& y /* inout */,
118 const Matrix& transform) 125 const Matrix& transform)
202 ApplyTransform(x, y, transform_); 209 ApplyTransform(x, y, transform_);
203 } 210 }
204 211
205 212
206 public: 213 public:
207 Bitmap() : 214 Bitmap(size_t index) :
208 visible_(true), 215 index_(index),
209 hasSize_(false), 216 hasSize_(false),
210 width_(0), 217 width_(0),
211 height_(0), 218 height_(0),
212 hasCrop_(false), 219 hasCrop_(false),
213 pixelSpacingX_(1), 220 pixelSpacingX_(1),
214 pixelSpacingY_(1), 221 pixelSpacingY_(1),
215 panX_(0), 222 panX_(0),
216 panY_(0), 223 panY_(0),
217 angle_(0) 224 angle_(0),
225 resizeable_(false)
218 { 226 {
219 UpdateTransform(); 227 UpdateTransform();
220 } 228 }
221 229
222 virtual ~Bitmap() 230 virtual ~Bitmap()
223 { 231 {
232 }
233
234 size_t GetIndex() const
235 {
236 return index_;
224 } 237 }
225 238
226 void ResetCrop() 239 void ResetCrop()
227 { 240 {
228 hasCrop_ = false; 241 hasCrop_ = false;
283 double GetAngle() const 296 double GetAngle() const
284 { 297 {
285 return angle_; 298 return angle_;
286 } 299 }
287 300
288 bool IsVisible() const
289 {
290 return visible_;
291 }
292
293 void SetVisible(bool visible)
294 {
295 visible_ = visible;
296 }
297
298
299 void SetSize(unsigned int width, 301 void SetSize(unsigned int width,
300 unsigned int height) 302 unsigned int height)
301 { 303 {
302 if (hasSize_ && 304 if (hasSize_ &&
303 (width != width_ || 305 (width != width_ ||
465 virtual bool GetDefaultWindowing(float& center, 467 virtual bool GetDefaultWindowing(float& center,
466 float& width) const 468 float& width) const
467 { 469 {
468 return false; 470 return false;
469 } 471 }
470
471 const Matrix& GetTransform() const
472 {
473 return transform_;
474 }
475
476 472
477 void GetCenter(double& centerX, 473 void GetCenter(double& centerX,
478 double& centerY) const 474 double& centerY) const
479 { 475 {
480 #if 0 476 #if 0
584 } 580 }
585 } 581 }
586 582
587 return false; 583 return false;
588 } 584 }
585
586 bool IsResizeable() const
587 {
588 return resizeable_;
589 }
590
591 void SetResizeable(bool resizeable)
592 {
593 resizeable_ = resizeable;
594 }
589 }; 595 };
590 596
591 597
592 class BitmapAccessor : public boost::noncopyable 598 class BitmapAccessor : public boost::noncopyable
593 { 599 {
679 private: 685 private:
680 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData 686 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData
681 std::auto_ptr<DicomFrameConverter> converter_; 687 std::auto_ptr<DicomFrameConverter> converter_;
682 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 or RGB24 688 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 or RGB24
683 689
690 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag)
691 {
692 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement());
693 }
694
684 695
685 void ApplyConverter() 696 void ApplyConverter()
686 { 697 {
687 if (source_.get() != NULL && 698 if (source_.get() != NULL &&
688 converter_.get() != NULL) 699 converter_.get() != NULL)
690 converted_.reset(converter_->ConvertFrame(*source_)); 701 converted_.reset(converter_->ConvertFrame(*source_));
691 } 702 }
692 } 703 }
693 704
694 public: 705 public:
695 static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) 706 DicomBitmap(size_t index) :
696 { 707 Bitmap(index)
697 return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); 708 {
698 } 709 }
699 710
700 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) 711 void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset)
701 { 712 {
702 converter_.reset(new DicomFrameConverter); 713 converter_.reset(new DicomFrameConverter);
806 std::auto_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8 817 std::auto_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8
807 bool useWindowing_; 818 bool useWindowing_;
808 float foreground_; 819 float foreground_;
809 820
810 public: 821 public:
811 AlphaBitmap(const BitmapStack& stack) : 822 AlphaBitmap(size_t index,
823 const BitmapStack& stack) :
824 Bitmap(index),
812 stack_(stack), 825 stack_(stack),
813 useWindowing_(true), 826 useWindowing_(true),
814 foreground_(0) 827 foreground_(0)
815 { 828 {
816 } 829 }
993 1006
994 EmitMessage(ContentChangedMessage(*this)); 1007 EmitMessage(ContentChangedMessage(*this));
995 } 1008 }
996 1009
997 1010
998 size_t LoadText(const Orthanc::Font& font, 1011 Bitmap& LoadText(const Orthanc::Font& font,
999 const std::string& utf8, 1012 const std::string& utf8)
1000 float foreground) 1013 {
1001 { 1014 size_t bitmap = countBitmaps_++;
1002 std::auto_ptr<AlphaBitmap> alpha(new AlphaBitmap(*this)); 1015
1016 std::auto_ptr<AlphaBitmap> alpha(new AlphaBitmap(bitmap, *this));
1003 alpha->LoadText(font, utf8); 1017 alpha->LoadText(font, utf8);
1004 //alpha->SetForegroundValue(foreground); 1018
1005 1019 AlphaBitmap* ptr = alpha.get();
1020 bitmaps_[bitmap] = alpha.release();
1021
1022 return *ptr;
1023 }
1024
1025
1026 Bitmap& LoadTestBlock(unsigned int width,
1027 unsigned int height)
1028 {
1006 size_t bitmap = countBitmaps_++; 1029 size_t bitmap = countBitmaps_++;
1007 1030
1008 bitmaps_[bitmap] = alpha.release(); 1031 std::auto_ptr<AlphaBitmap> alpha(new AlphaBitmap(bitmap, *this));
1009
1010 return bitmap;
1011 }
1012
1013
1014 size_t LoadTestBlock(unsigned int width,
1015 unsigned int height,
1016 float foreground)
1017 {
1018 std::auto_ptr<AlphaBitmap> alpha(new AlphaBitmap(*this));
1019 1032
1020 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); 1033 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false));
1021 1034
1022 for (unsigned int padding = 0; 1035 for (unsigned int padding = 0;
1023 (width > 2 * padding) && (height > 2 * padding); 1036 (width > 2 * padding) && (height > 2 * padding);
1037 block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding); 1050 block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding);
1038 Orthanc::ImageProcessing::Set(region, color); 1051 Orthanc::ImageProcessing::Set(region, color);
1039 } 1052 }
1040 1053
1041 alpha->SetAlpha(block.release()); 1054 alpha->SetAlpha(block.release());
1042 //alpha->SetForegroundValue(foreground); 1055
1043 1056 AlphaBitmap* ptr = alpha.get();
1044 size_t bitmap = countBitmaps_++;
1045
1046 bitmaps_[bitmap] = alpha.release(); 1057 bitmaps_[bitmap] = alpha.release();
1047 1058
1048 return bitmap; 1059 return *ptr;
1049 } 1060 }
1050 1061
1051 1062
1052 size_t LoadFrame(const std::string& instance, 1063 size_t LoadFrame(const std::string& instance,
1053 unsigned int frame, 1064 unsigned int frame,
1054 bool httpCompression) 1065 bool httpCompression)
1055 { 1066 {
1056 size_t bitmap = countBitmaps_++; 1067 size_t bitmap = countBitmaps_++;
1057 1068
1058 bitmaps_[bitmap] = new DicomBitmap; 1069 bitmaps_[bitmap] = new DicomBitmap(bitmap);
1059 1070
1060 1071
1061 { 1072 {
1062 IWebService::Headers headers; 1073 IWebService::Headers headers;
1063 std::string uri = "/instances/" + instance + "/tags"; 1074 std::string uri = "/instances/" + instance + "/tags";
1224 private: 1235 private:
1225 enum Tool 1236 enum Tool
1226 { 1237 {
1227 Tool_Move, 1238 Tool_Move,
1228 Tool_Rotate, 1239 Tool_Rotate,
1229 Tool_Crop 1240 Tool_Crop,
1241 Tool_Resize
1230 }; 1242 };
1231 1243
1232 static double GetHandleSize() 1244 static double GetHandleSize()
1233 { 1245 {
1234 return 10.0; 1246 return 10.0;
1407 } 1419 }
1408 } 1420 }
1409 }; 1421 };
1410 1422
1411 1423
1412 class CornerBitmapTracker : public IWorldSceneMouseTracker 1424 class CropBitmapTracker : public IWorldSceneMouseTracker
1413 { 1425 {
1414 private: 1426 private:
1415 BitmapStack::BitmapAccessor accessor_; 1427 BitmapStack::BitmapAccessor accessor_;
1416 BitmapStack::Corner corner_; 1428 BitmapStack::Corner corner_;
1417 unsigned int cropX_; 1429 unsigned int cropX_;
1418 unsigned int cropY_; 1430 unsigned int cropY_;
1419 unsigned int cropWidth_; 1431 unsigned int cropWidth_;
1420 unsigned int cropHeight_; 1432 unsigned int cropHeight_;
1421 1433
1422 public: 1434 public:
1423 CornerBitmapTracker(BitmapStack& stack, 1435 CropBitmapTracker(BitmapStack& stack,
1424 const ViewportGeometry& view, 1436 const ViewportGeometry& view,
1425 size_t bitmap, 1437 size_t bitmap,
1426 double x, 1438 double x,
1427 double y, 1439 double y,
1428 BitmapStack::Corner corner) : 1440 BitmapStack::Corner corner) :
1453 virtual void MouseMove(int displayX, 1465 virtual void MouseMove(int displayX,
1454 int displayY, 1466 int displayY,
1455 double sceneX, 1467 double sceneX,
1456 double sceneY) 1468 double sceneY)
1457 { 1469 {
1458 unsigned int x, y;
1459
1460 if (accessor_.IsValid()) 1470 if (accessor_.IsValid())
1461 { 1471 {
1472 unsigned int x, y;
1473
1462 BitmapStack::Bitmap& bitmap = accessor_.GetBitmap(); 1474 BitmapStack::Bitmap& bitmap = accessor_.GetBitmap();
1463 if (bitmap.GetPixel(x, y, sceneX, sceneY)) 1475 if (bitmap.GetPixel(x, y, sceneX, sceneY))
1464 { 1476 {
1465 unsigned int targetX, targetWidth; 1477 unsigned int targetX, targetWidth;
1466 1478
1495 } 1507 }
1496 } 1508 }
1497 }; 1509 };
1498 1510
1499 1511
1512 class ResizeBitmapTracker : public IWorldSceneMouseTracker
1513 {
1514 private:
1515 BitmapStack::BitmapAccessor accessor_;
1516 bool roundScaling_;
1517 double originalSpacingX_;
1518 double originalSpacingY_;
1519 double originalPanX_;
1520 double originalPanY_;
1521 BitmapStack::Corner oppositeCorner_;
1522 double oppositeX_;
1523 double oppositeY_;
1524 double baseScaling_;
1525
1526 static double ComputeDistance(double x1,
1527 double y1,
1528 double x2,
1529 double y2)
1530 {
1531 double dx = x1 - x2;
1532 double dy = y1 - y2;
1533 return sqrt(dx * dx + dy * dy);
1534 }
1535
1536 public:
1537 ResizeBitmapTracker(BitmapStack& stack,
1538 size_t bitmap,
1539 double x,
1540 double y,
1541 BitmapStack::Corner corner,
1542 bool roundScaling) :
1543 accessor_(stack, bitmap),
1544 roundScaling_(roundScaling)
1545 {
1546 if (accessor_.IsValid() &&
1547 accessor_.GetBitmap().IsResizeable())
1548 {
1549 originalSpacingX_ = accessor_.GetBitmap().GetPixelSpacingX();
1550 originalSpacingY_ = accessor_.GetBitmap().GetPixelSpacingY();
1551 originalPanX_ = accessor_.GetBitmap().GetPanX();
1552 originalPanY_ = accessor_.GetBitmap().GetPanY();
1553
1554 switch (corner)
1555 {
1556 case BitmapStack::Corner_TopLeft:
1557 oppositeCorner_ = BitmapStack::Corner_BottomRight;
1558 break;
1559
1560 case BitmapStack::Corner_TopRight:
1561 oppositeCorner_ = BitmapStack::Corner_BottomLeft;
1562 break;
1563
1564 case BitmapStack::Corner_BottomLeft:
1565 oppositeCorner_ = BitmapStack::Corner_TopRight;
1566 break;
1567
1568 case BitmapStack::Corner_BottomRight:
1569 oppositeCorner_ = BitmapStack::Corner_TopLeft;
1570 break;
1571
1572 default:
1573 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
1574 }
1575
1576 accessor_.GetBitmap().GetCorner(oppositeX_, oppositeY_, oppositeCorner_);
1577
1578 double d = ComputeDistance(x, y, oppositeX_, oppositeY_);
1579 if (d >= std::numeric_limits<float>::epsilon())
1580 {
1581 baseScaling_ = 1.0 / d;
1582 }
1583 else
1584 {
1585 // Avoid division by zero in extreme cases
1586 accessor_.Invalidate();
1587 }
1588 }
1589 }
1590
1591 virtual bool HasRender() const
1592 {
1593 return false;
1594 }
1595
1596 virtual void Render(CairoContext& context,
1597 double zoom)
1598 {
1599 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
1600 }
1601
1602 virtual void MouseUp()
1603 {
1604 }
1605
1606 virtual void MouseMove(int displayX,
1607 int displayY,
1608 double sceneX,
1609 double sceneY)
1610 {
1611 static const double ROUND_SCALING = 0.1;
1612
1613 if (accessor_.IsValid() &&
1614 accessor_.GetBitmap().IsResizeable())
1615 {
1616 double scaling = ComputeDistance(oppositeX_, oppositeY_, sceneX, sceneY) * baseScaling_;
1617
1618 if (roundScaling_)
1619 {
1620 scaling = round(scaling / ROUND_SCALING) * ROUND_SCALING;
1621 }
1622
1623 BitmapStack::Bitmap& bitmap = accessor_.GetBitmap();
1624 bitmap.SetPixelSpacing(scaling * originalSpacingX_, scaling * originalSpacingY_);
1625
1626 // Keep the opposite corner at a fixed location
1627 double ox, oy;
1628 bitmap.GetCorner(ox, oy, oppositeCorner_);
1629 bitmap.SetPan((-ox + oppositeX_) + bitmap.GetPanX(),
1630 (-oy + oppositeY_) + bitmap.GetPanY());
1631 }
1632 }
1633 };
1634
1635
1500 public: 1636 public:
1501 BitmapStackInteractor(BitmapStack& stack) : 1637 BitmapStackInteractor(BitmapStack& stack) :
1502 stack_(stack), 1638 stack_(stack),
1503 //tool_(Tool_Move) 1639 tool_(Tool_Move)
1504 tool_(Tool_Crop)
1505 { 1640 {
1506 } 1641 }
1507 1642
1508 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, 1643 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget,
1509 const ViewportGeometry& view, 1644 const ViewportGeometry& view,
1526 stack_.Select(bitmap); 1661 stack_.Select(bitmap);
1527 } 1662 }
1528 1663
1529 return NULL; 1664 return NULL;
1530 } 1665 }
1531 else if (tool_ == Tool_Crop) 1666 else if (tool_ == Tool_Crop ||
1667 tool_ == Tool_Resize)
1532 { 1668 {
1533 BitmapStack::BitmapAccessor accessor(stack_, selected); 1669 BitmapStack::BitmapAccessor accessor(stack_, selected);
1534 BitmapStack::Corner corner; 1670 BitmapStack::Corner corner;
1535 if (accessor.GetBitmap().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize())) 1671 if (accessor.GetBitmap().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize()))
1536 { 1672 {
1537 return new CornerBitmapTracker(stack_, view, selected, x, y, corner); 1673 switch (tool_)
1674 {
1675 case Tool_Crop:
1676 return new CropBitmapTracker(stack_, view, selected, x, y, corner);
1677
1678 case Tool_Resize:
1679 return new ResizeBitmapTracker(stack_, selected, x, y, corner,
1680 (modifiers & KeyboardModifiers_Shift));
1681
1682 default:
1683 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
1684 }
1538 } 1685 }
1539 else 1686 else
1540 { 1687 {
1541 size_t bitmap; 1688 size_t bitmap;
1542 1689
1593 double y, 1740 double y,
1594 IStatusBar* statusBar) 1741 IStatusBar* statusBar)
1595 { 1742 {
1596 size_t selected; 1743 size_t selected;
1597 if (stack_.GetSelectedBitmap(selected) && 1744 if (stack_.GetSelectedBitmap(selected) &&
1598 tool_ == Tool_Crop) 1745 (tool_ == Tool_Crop ||
1746 tool_ == Tool_Resize))
1599 { 1747 {
1600 BitmapStack::BitmapAccessor accessor(stack_, selected); 1748 BitmapStack::BitmapAccessor accessor(stack_, selected);
1601 1749
1602 BitmapStack::Corner corner; 1750 BitmapStack::Corner corner;
1603 if (accessor.GetBitmap().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize())) 1751 if (accessor.GetBitmap().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize()))
1636 { 1784 {
1637 case 'c': 1785 case 'c':
1638 tool_ = Tool_Crop; 1786 tool_ = Tool_Crop;
1639 break; 1787 break;
1640 1788
1641 case 's': 1789 case 'a':
1642 widget.FitContent(); 1790 widget.FitContent();
1643 break; 1791 break;
1644 1792
1645 case 'm': 1793 case 'm':
1646 tool_ = Tool_Move; 1794 tool_ = Tool_Move;
1647 break; 1795 break;
1648 1796
1649 case 'r': 1797 case 'r':
1650 tool_ = Tool_Rotate; 1798 tool_ = Tool_Rotate;
1799 break;
1800
1801 case 's':
1802 tool_ = Tool_Resize;
1651 break; 1803 break;
1652 1804
1653 default: 1805 default:
1654 break; 1806 break;
1655 } 1807 }
1958 fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); 2110 fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
1959 2111
1960 stack_.reset(new BitmapStack(IObserver::broker_, *orthancApiClient_)); 2112 stack_.reset(new BitmapStack(IObserver::broker_, *orthancApiClient_));
1961 stack_->LoadFrame(instance, frame, false); 2113 stack_->LoadFrame(instance, frame, false);
1962 stack_->LoadFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", frame, false); 2114 stack_->LoadFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", frame, false);
1963 stack_->LoadText(fonts.GetFont(0), "Hello\nworld\nBonjour, Alain", 256); 2115 stack_->LoadText(fonts.GetFont(0), "Hello\nworld\nBonjour, Alain").SetResizeable(true);
1964 stack_->LoadTestBlock(100, 50, 256); 2116 stack_->LoadTestBlock(100, 50).SetResizeable(true);
1965 2117
1966 mainWidget_ = new BitmapStackWidget(IObserver::broker_, *stack_, "main-widget"); 2118 mainWidget_ = new BitmapStackWidget(IObserver::broker_, *stack_, "main-widget");
1967 mainWidget_->SetTransmitMouseOver(true); 2119 mainWidget_->SetTransmitMouseOver(true);
1968 2120
1969 //stack_->SetWindowing(128, 256); 2121 //stack_->SetWindowing(128, 256);