comparison Applications/Samples/SingleFrameEditorApplication.h @ 407:842a3c7cfdc0

renames
author Sebastien Jodogne <s.jodogne@gmail.com>
date Mon, 12 Nov 2018 11:44:20 +0100
parents 99e31898910e
children 6834c236b36d
comparison
equal deleted inserted replaced
406:5d359b115b29 407:842a3c7cfdc0
37 #include <Core/OrthancException.h> 37 #include <Core/OrthancException.h>
38 #include <Core/Toolbox.h> 38 #include <Core/Toolbox.h>
39 #include <Plugins/Samples/Common/DicomDatasetReader.h> 39 #include <Plugins/Samples/Common/DicomDatasetReader.h>
40 #include <Plugins/Samples/Common/FullOrthancDataset.h> 40 #include <Plugins/Samples/Common/FullOrthancDataset.h>
41 41
42
43 // Export using PAM is faster than using PNG, but requires Orthanc
44 // core >= 1.4.3
42 #define EXPORT_USING_PAM 1 45 #define EXPORT_USING_PAM 1
43 46
44 47
45 #include <boost/math/constants/constants.hpp> 48 #include <boost/math/constants/constants.hpp>
46 #include <boost/math/special_functions/round.hpp> 49 #include <boost/math/special_functions/round.hpp>
47 50
48 51
49 namespace OrthancStone 52 namespace OrthancStone
50 { 53 {
51 class BitmapStack : 54 class RadiologyScene :
52 public IObserver, 55 public IObserver,
53 public IObservable 56 public IObservable
54 { 57 {
55 public: 58 public:
56 typedef OriginMessage<MessageType_Widget_GeometryChanged, BitmapStack> GeometryChangedMessage; 59 typedef OriginMessage<MessageType_Widget_GeometryChanged, RadiologyScene> GeometryChangedMessage;
57 typedef OriginMessage<MessageType_Widget_ContentChanged, BitmapStack> ContentChangedMessage; 60 typedef OriginMessage<MessageType_Widget_ContentChanged, RadiologyScene> ContentChangedMessage;
58 61
59 62
60 enum Corner 63 enum Corner
61 { 64 {
62 Corner_TopLeft, 65 Corner_TopLeft,
65 Corner_BottomRight 68 Corner_BottomRight
66 }; 69 };
67 70
68 71
69 72
70 class Bitmap : public boost::noncopyable 73 class Layer : public boost::noncopyable
71 { 74 {
75 friend class RadiologyScene;
76
72 private: 77 private:
73 size_t index_; 78 size_t index_;
74 bool hasSize_; 79 bool hasSize_;
75 unsigned int width_; 80 unsigned int width_;
76 unsigned int height_; 81 unsigned int height_;
185 190
186 ApplyTransform(x, y, transform_); 191 ApplyTransform(x, y, transform_);
187 } 192 }
188 193
189 194
195 void SetIndex(size_t index)
196 {
197 index_ = index;
198 }
199
200
201 bool Contains(double x,
202 double y) const
203 {
204 ApplyTransform(x, y, transformInverse_);
205
206 unsigned int cropX, cropY, cropWidth, cropHeight;
207 GetCrop(cropX, cropY, cropWidth, cropHeight);
208
209 return (x >= cropX && x <= cropX + cropWidth &&
210 y >= cropY && y <= cropY + cropHeight);
211 }
212
213
214 void DrawBorders(CairoContext& context,
215 double zoom)
216 {
217 unsigned int cx, cy, width, height;
218 GetCrop(cx, cy, width, height);
219
220 double dx = static_cast<double>(cx);
221 double dy = static_cast<double>(cy);
222 double dwidth = static_cast<double>(width);
223 double dheight = static_cast<double>(height);
224
225 cairo_t* cr = context.GetObject();
226 cairo_set_line_width(cr, 2.0 / zoom);
227
228 double x, y;
229 x = dx;
230 y = dy;
231 ApplyTransform(x, y, transform_);
232 cairo_move_to(cr, x, y);
233
234 x = dx + dwidth;
235 y = dy;
236 ApplyTransform(x, y, transform_);
237 cairo_line_to(cr, x, y);
238
239 x = dx + dwidth;
240 y = dy + dheight;
241 ApplyTransform(x, y, transform_);
242 cairo_line_to(cr, x, y);
243
244 x = dx;
245 y = dy + dheight;
246 ApplyTransform(x, y, transform_);
247 cairo_line_to(cr, x, y);
248
249 x = dx;
250 y = dy;
251 ApplyTransform(x, y, transform_);
252 cairo_line_to(cr, x, y);
253
254 cairo_stroke(cr);
255 }
256
257
258 static double Square(double x)
259 {
260 return x * x;
261 }
262
263
190 public: 264 public:
191 Bitmap() : 265 Layer() :
192 index_(0), 266 index_(0),
193 hasSize_(false), 267 hasSize_(false),
194 width_(0), 268 width_(0),
195 height_(0), 269 height_(0),
196 hasCrop_(false), 270 hasCrop_(false),
202 resizeable_(false) 276 resizeable_(false)
203 { 277 {
204 UpdateTransform(); 278 UpdateTransform();
205 } 279 }
206 280
207 virtual ~Bitmap() 281 virtual ~Layer()
208 { 282 {
209 }
210
211 void SetIndex(size_t index)
212 {
213 index_ = index;
214 } 283 }
215 284
216 size_t GetIndex() const 285 size_t GetIndex() const
217 { 286 {
218 return index_; 287 return index_;
308 { 377 {
309 return height_; 378 return height_;
310 } 379 }
311 380
312 381
313 void CheckSize(unsigned int width,
314 unsigned int height)
315 {
316 if (hasSize_ &&
317 (width != width_ ||
318 height != height_))
319 {
320 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
321 }
322 }
323
324
325 Extent2D GetExtent() const 382 Extent2D GetExtent() const
326 { 383 {
327 Extent2D extent; 384 Extent2D extent;
328 385
329 unsigned int x, y, width, height; 386 unsigned int x, y, width, height;
341 398
342 return extent; 399 return extent;
343 } 400 }
344 401
345 402
346 bool Contains(double x,
347 double y) const
348 {
349 ApplyTransform(x, y, transformInverse_);
350
351 unsigned int cropX, cropY, cropWidth, cropHeight;
352 GetCrop(cropX, cropY, cropWidth, cropHeight);
353
354 return (x >= cropX && x <= cropX + cropWidth &&
355 y >= cropY && y <= cropY + cropHeight);
356 }
357
358
359 bool GetPixel(unsigned int& imageX, 403 bool GetPixel(unsigned int& imageX,
360 unsigned int& imageY, 404 unsigned int& imageY,
361 double sceneX, 405 double sceneX,
362 double sceneY) const 406 double sceneY) const
363 { 407 {
445 double& centerY) const 489 double& centerY) const
446 { 490 {
447 centerX = static_cast<double>(width_) / 2.0; 491 centerX = static_cast<double>(width_) / 2.0;
448 centerY = static_cast<double>(height_) / 2.0; 492 centerY = static_cast<double>(height_) / 2.0;
449 ApplyTransform(centerX, centerY, transform_); 493 ApplyTransform(centerX, centerY, transform_);
450 }
451
452
453 void DrawBorders(CairoContext& context,
454 double zoom)
455 {
456 unsigned int cx, cy, width, height;
457 GetCrop(cx, cy, width, height);
458
459 double dx = static_cast<double>(cx);
460 double dy = static_cast<double>(cy);
461 double dwidth = static_cast<double>(width);
462 double dheight = static_cast<double>(height);
463
464 cairo_t* cr = context.GetObject();
465 cairo_set_line_width(cr, 2.0 / zoom);
466
467 double x, y;
468 x = dx;
469 y = dy;
470 ApplyTransform(x, y, transform_);
471 cairo_move_to(cr, x, y);
472
473 x = dx + dwidth;
474 y = dy;
475 ApplyTransform(x, y, transform_);
476 cairo_line_to(cr, x, y);
477
478 x = dx + dwidth;
479 y = dy + dheight;
480 ApplyTransform(x, y, transform_);
481 cairo_line_to(cr, x, y);
482
483 x = dx;
484 y = dy + dheight;
485 ApplyTransform(x, y, transform_);
486 cairo_line_to(cr, x, y);
487
488 x = dx;
489 y = dy;
490 ApplyTransform(x, y, transform_);
491 cairo_line_to(cr, x, y);
492
493 cairo_stroke(cr);
494 }
495
496
497 static double Square(double x)
498 {
499 return x * x;
500 } 494 }
501 495
502 496
503 void GetCorner(double& x /* out */, 497 void GetCorner(double& x /* out */,
504 double& y /* out */, 498 double& y /* out */,
554 { 548 {
555 resizeable_ = resizeable; 549 resizeable_ = resizeable;
556 } 550 }
557 551
558 virtual bool GetDefaultWindowing(float& center, 552 virtual bool GetDefaultWindowing(float& center,
559 float& width) const 553 float& width) const = 0;
560 {
561 return false;
562 }
563 554
564 virtual void Render(Orthanc::ImageAccessor& buffer, 555 virtual void Render(Orthanc::ImageAccessor& buffer,
565 const Matrix& viewTransform, 556 const Matrix& viewTransform,
566 ImageInterpolation interpolation) const = 0; 557 ImageInterpolation interpolation) const = 0;
567 558
568 virtual bool GetRange(float& minValue, 559 virtual bool GetRange(float& minValue,
569 float& maxValue) const = 0; 560 float& maxValue) const = 0;
570 }; 561 };
571 562
572 563
573 class BitmapAccessor : public boost::noncopyable 564 class LayerAccessor : public boost::noncopyable
574 { 565 {
575 private: 566 private:
576 BitmapStack& stack_; 567 RadiologyScene& scene_;
577 size_t index_; 568 size_t index_;
578 Bitmap* bitmap_; 569 Layer* layer_;
579 570
580 public: 571 public:
581 BitmapAccessor(BitmapStack& stack, 572 LayerAccessor(RadiologyScene& scene,
582 size_t index) : 573 size_t index) :
583 stack_(stack), 574 scene_(scene),
584 index_(index) 575 index_(index)
585 { 576 {
586 Bitmaps::iterator bitmap = stack.bitmaps_.find(index); 577 Layers::iterator layer = scene.layers_.find(index);
587 if (bitmap == stack.bitmaps_.end()) 578 if (layer == scene.layers_.end())
588 { 579 {
589 bitmap_ = NULL; 580 layer_ = NULL;
590 } 581 }
591 else 582 else
592 { 583 {
593 assert(bitmap->second != NULL); 584 assert(layer->second != NULL);
594 bitmap_ = bitmap->second; 585 layer_ = layer->second;
595 } 586 }
596 } 587 }
597 588
598 BitmapAccessor(BitmapStack& stack, 589 LayerAccessor(RadiologyScene& scene,
599 double x, 590 double x,
600 double y) : 591 double y) :
601 stack_(stack), 592 scene_(scene),
602 index_(0) // Dummy initialization 593 index_(0) // Dummy initialization
603 { 594 {
604 if (stack.LookupBitmap(index_, x, y)) 595 if (scene.LookupLayer(index_, x, y))
605 { 596 {
606 Bitmaps::iterator bitmap = stack.bitmaps_.find(index_); 597 Layers::iterator layer = scene.layers_.find(index_);
607 598
608 if (bitmap == stack.bitmaps_.end()) 599 if (layer == scene.layers_.end())
609 { 600 {
610 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 601 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
611 } 602 }
612 else 603 else
613 { 604 {
614 assert(bitmap->second != NULL); 605 assert(layer->second != NULL);
615 bitmap_ = bitmap->second; 606 layer_ = layer->second;
616 } 607 }
617 } 608 }
618 else 609 else
619 { 610 {
620 bitmap_ = NULL; 611 layer_ = NULL;
621 } 612 }
622 } 613 }
623 614
624 void Invalidate() 615 void Invalidate()
625 { 616 {
626 bitmap_ = NULL; 617 layer_ = NULL;
627 } 618 }
628 619
629 bool IsValid() const 620 bool IsValid() const
630 { 621 {
631 return bitmap_ != NULL; 622 return layer_ != NULL;
632 } 623 }
633 624
634 BitmapStack& GetStack() const 625 RadiologyScene& GetScene() const
635 { 626 {
636 if (IsValid()) 627 if (IsValid())
637 { 628 {
638 return stack_; 629 return scene_;
639 } 630 }
640 else 631 else
641 { 632 {
642 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 633 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
643 } 634 }
653 { 644 {
654 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 645 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
655 } 646 }
656 } 647 }
657 648
658 Bitmap& GetBitmap() const 649 Layer& GetLayer() const
659 { 650 {
660 if (IsValid()) 651 if (IsValid())
661 { 652 {
662 return *bitmap_; 653 return *layer_;
663 } 654 }
664 else 655 else
665 { 656 {
666 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); 657 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
667 } 658 }
668 } 659 }
669 }; 660 };
670 661
671 662
672 class AlphaBitmap : public Bitmap 663 private:
664 class AlphaLayer : public Layer
673 { 665 {
674 private: 666 private:
675 const BitmapStack& stack_; 667 const RadiologyScene& scene_;
676 std::auto_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8 668 std::auto_ptr<Orthanc::ImageAccessor> alpha_; // Grayscale8
677 bool useWindowing_; 669 bool useWindowing_;
678 float foreground_; 670 float foreground_;
679 671
680 public: 672 public:
681 AlphaBitmap(const BitmapStack& stack) : 673 AlphaLayer(const RadiologyScene& scene) :
682 stack_(stack), 674 scene_(scene),
683 useWindowing_(true), 675 useWindowing_(true),
684 foreground_(0) 676 foreground_(0)
685 { 677 {
686 } 678 }
687 679
716 const std::string& utf8) 708 const std::string& utf8)
717 { 709 {
718 SetAlpha(font.RenderAlpha(utf8)); 710 SetAlpha(font.RenderAlpha(utf8));
719 } 711 }
720 712
713
714 virtual bool GetDefaultWindowing(float& center,
715 float& width) const
716 {
717 return false;
718 }
719
721 720
722 virtual void Render(Orthanc::ImageAccessor& buffer, 721 virtual void Render(Orthanc::ImageAccessor& buffer,
723 const Matrix& viewTransform, 722 const Matrix& viewTransform,
724 ImageInterpolation interpolation) const 723 ImageInterpolation interpolation) const
725 { 724 {
753 float value = foreground_; 752 float value = foreground_;
754 753
755 if (useWindowing_) 754 if (useWindowing_)
756 { 755 {
757 float center, width; 756 float center, width;
758 if (stack_.GetWindowing(center, width)) 757 if (scene_.GetWindowing(center, width))
759 { 758 {
760 value = center + width / 2.0f; 759 value = center + width / 2.0f;
761 } 760 }
762 } 761 }
763 762
773 *q = (a * value + (1.0f - a) * (*q)); 772 *q = (a * value + (1.0f - a) * (*q));
774 } 773 }
775 } 774 }
776 } 775 }
777 776
777
778 virtual bool GetRange(float& minValue, 778 virtual bool GetRange(float& minValue,
779 float& maxValue) const 779 float& maxValue) const
780 { 780 {
781 if (useWindowing_) 781 if (useWindowing_)
782 { 782 {
834 LinearAlgebra::FillMatrix(m, 3, 3, v); 834 LinearAlgebra::FillMatrix(m, 3, 3, v);
835 return m; 835 return m;
836 } 836 }
837 837
838 838
839 class DicomBitmap : public Bitmap 839 class DicomLayer : public Layer
840 { 840 {
841 private: 841 private:
842 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData 842 std::auto_ptr<Orthanc::ImageAccessor> source_; // Content of PixelData
843 std::auto_ptr<DicomFrameConverter> converter_; 843 std::auto_ptr<DicomFrameConverter> converter_;
844 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32 844 std::auto_ptr<Orthanc::ImageAccessor> converted_; // Float32
972 }; 972 };
973 973
974 974
975 975
976 976
977 typedef std::map<size_t, Bitmap*> Bitmaps; 977 typedef std::map<size_t, Layer*> Layers;
978 978
979 OrthancApiClient& orthanc_; 979 OrthancApiClient& orthanc_;
980 size_t countBitmaps_; 980 size_t countLayers_;
981 bool hasWindowing_; 981 bool hasWindowing_;
982 float windowingCenter_; 982 float windowingCenter_;
983 float windowingWidth_; 983 float windowingWidth_;
984 Bitmaps bitmaps_; 984 Layers layers_;
985
986
987 Layer& RegisterLayer(Layer* layer)
988 {
989 if (layer == NULL)
990 {
991 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer);
992 }
993
994 std::auto_ptr<Layer> raii(layer);
995
996 size_t index = countLayers_++;
997 raii->SetIndex(index);
998 layers_[index] = raii.release();
999
1000 EmitMessage(GeometryChangedMessage(*this));
1001 EmitMessage(ContentChangedMessage(*this));
1002
1003 return *layer;
1004 }
1005
985 1006
986 public: 1007 public:
987 BitmapStack(MessageBroker& broker, 1008 RadiologyScene(MessageBroker& broker,
988 OrthancApiClient& orthanc) : 1009 OrthancApiClient& orthanc) :
989 IObserver(broker), 1010 IObserver(broker),
990 IObservable(broker), 1011 IObservable(broker),
991 orthanc_(orthanc), 1012 orthanc_(orthanc),
992 countBitmaps_(0), 1013 countLayers_(0),
993 hasWindowing_(false), 1014 hasWindowing_(false),
994 windowingCenter_(0), // Dummy initialization 1015 windowingCenter_(0), // Dummy initialization
995 windowingWidth_(0) // Dummy initialization 1016 windowingWidth_(0) // Dummy initialization
996 { 1017 {
997 } 1018 }
998 1019
999 1020
1000 virtual ~BitmapStack() 1021 virtual ~RadiologyScene()
1001 { 1022 {
1002 for (Bitmaps::iterator it = bitmaps_.begin(); it != bitmaps_.end(); it++) 1023 for (Layers::iterator it = layers_.begin(); it != layers_.end(); it++)
1003 { 1024 {
1004 assert(it->second != NULL); 1025 assert(it->second != NULL);
1005 delete it->second; 1026 delete it->second;
1006 } 1027 }
1007 } 1028 }
1042 windowingCenter_ = center; 1063 windowingCenter_ = center;
1043 windowingWidth_ = width; 1064 windowingWidth_ = width;
1044 } 1065 }
1045 1066
1046 1067
1047 Bitmap& RegisterBitmap(Bitmap* bitmap) 1068 Layer& LoadText(const Orthanc::Font& font,
1048 { 1069 const std::string& utf8)
1049 if (bitmap == NULL) 1070 {
1050 { 1071 std::auto_ptr<AlphaLayer> alpha(new AlphaLayer(*this));
1051 throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); 1072 alpha->LoadText(font, utf8);
1052 } 1073
1053 1074 return RegisterLayer(alpha.release());
1054 std::auto_ptr<Bitmap> raii(bitmap); 1075 }
1055 1076
1056 size_t index = countBitmaps_++;
1057 raii->SetIndex(index);
1058 bitmaps_[index] = raii.release();
1059
1060 EmitMessage(GeometryChangedMessage(*this));
1061 EmitMessage(ContentChangedMessage(*this));
1062
1063 return *bitmap;
1064 }
1065 1077
1066 1078 Layer& LoadTestBlock(unsigned int width,
1067 Bitmap& LoadText(const Orthanc::Font& font, 1079 unsigned int height)
1068 const std::string& utf8)
1069 {
1070 std::auto_ptr<AlphaBitmap> alpha(new AlphaBitmap(*this));
1071 alpha->LoadText(font, utf8);
1072
1073 return RegisterBitmap(alpha.release());
1074 }
1075
1076
1077 Bitmap& LoadTestBlock(unsigned int width,
1078 unsigned int height)
1079 { 1080 {
1080 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); 1081 std::auto_ptr<Orthanc::Image> block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false));
1081 1082
1082 for (unsigned int padding = 0; 1083 for (unsigned int padding = 0;
1083 (width > 2 * padding) && (height > 2 * padding); 1084 (width > 2 * padding) && (height > 2 * padding);
1096 Orthanc::ImageAccessor region; 1097 Orthanc::ImageAccessor region;
1097 block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding); 1098 block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding);
1098 Orthanc::ImageProcessing::Set(region, color); 1099 Orthanc::ImageProcessing::Set(region, color);
1099 } 1100 }
1100 1101
1101 std::auto_ptr<AlphaBitmap> alpha(new AlphaBitmap(*this)); 1102 std::auto_ptr<AlphaLayer> alpha(new AlphaLayer(*this));
1102 alpha->SetAlpha(block.release()); 1103 alpha->SetAlpha(block.release());
1103 1104
1104 return RegisterBitmap(alpha.release()); 1105 return RegisterLayer(alpha.release());
1105 } 1106 }
1106 1107
1107 1108
1108 Bitmap& LoadFrame(const std::string& instance, 1109 Layer& LoadDicomFrame(const std::string& instance,
1109 unsigned int frame, 1110 unsigned int frame,
1110 bool httpCompression) 1111 bool httpCompression)
1111 { 1112 {
1112 Bitmap& bitmap = RegisterBitmap(new DicomBitmap); 1113 Layer& layer = RegisterLayer(new DicomLayer);
1113 1114
1114 { 1115 {
1115 IWebService::Headers headers; 1116 IWebService::Headers headers;
1116 std::string uri = "/instances/" + instance + "/tags"; 1117 std::string uri = "/instances/" + instance + "/tags";
1117 orthanc_.GetBinaryAsync(uri, headers, 1118 orthanc_.GetBinaryAsync(uri, headers,
1118 new Callable<BitmapStack, OrthancApiClient::BinaryResponseReadyMessage> 1119 new Callable<RadiologyScene, OrthancApiClient::BinaryResponseReadyMessage>
1119 (*this, &BitmapStack::OnTagsReceived), NULL, 1120 (*this, &RadiologyScene::OnTagsReceived), NULL,
1120 new Orthanc::SingleValueObject<size_t>(bitmap.GetIndex())); 1121 new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
1121 } 1122 }
1122 1123
1123 { 1124 {
1124 IWebService::Headers headers; 1125 IWebService::Headers headers;
1125 headers["Accept"] = "image/x-portable-arbitrarymap"; 1126 headers["Accept"] = "image/x-portable-arbitrarymap";
1129 headers["Accept-Encoding"] = "gzip"; 1130 headers["Accept-Encoding"] = "gzip";
1130 } 1131 }
1131 1132
1132 std::string uri = "/instances/" + instance + "/frames/" + boost::lexical_cast<std::string>(frame) + "/image-uint16"; 1133 std::string uri = "/instances/" + instance + "/frames/" + boost::lexical_cast<std::string>(frame) + "/image-uint16";
1133 orthanc_.GetBinaryAsync(uri, headers, 1134 orthanc_.GetBinaryAsync(uri, headers,
1134 new Callable<BitmapStack, OrthancApiClient::BinaryResponseReadyMessage> 1135 new Callable<RadiologyScene, OrthancApiClient::BinaryResponseReadyMessage>
1135 (*this, &BitmapStack::OnFrameReceived), NULL, 1136 (*this, &RadiologyScene::OnFrameReceived), NULL,
1136 new Orthanc::SingleValueObject<size_t>(bitmap.GetIndex())); 1137 new Orthanc::SingleValueObject<size_t>(layer.GetIndex()));
1137 } 1138 }
1138 1139
1139 return bitmap; 1140 return layer;
1140 } 1141 }
1141 1142
1142 1143
1143 void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) 1144 void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
1144 { 1145 {
1145 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue(); 1146 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
1146 1147
1147 LOG(INFO) << "JSON received: " << message.GetUri().c_str() 1148 LOG(INFO) << "JSON received: " << message.GetUri().c_str()
1148 << " (" << message.GetAnswerSize() << " bytes) for bitmap " << index; 1149 << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
1149 1150
1150 Bitmaps::iterator bitmap = bitmaps_.find(index); 1151 Layers::iterator layer = layers_.find(index);
1151 if (bitmap != bitmaps_.end()) 1152 if (layer != layers_.end())
1152 { 1153 {
1153 assert(bitmap->second != NULL); 1154 assert(layer->second != NULL);
1154 1155
1155 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); 1156 OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize());
1156 dynamic_cast<DicomBitmap*>(bitmap->second)->SetDicomTags(dicom); 1157 dynamic_cast<DicomLayer*>(layer->second)->SetDicomTags(dicom);
1157 1158
1158 float c, w; 1159 float c, w;
1159 if (!hasWindowing_ && 1160 if (!hasWindowing_ &&
1160 bitmap->second->GetDefaultWindowing(c, w)) 1161 layer->second->GetDefaultWindowing(c, w))
1161 { 1162 {
1162 hasWindowing_ = true; 1163 hasWindowing_ = true;
1163 windowingCenter_ = c; 1164 windowingCenter_ = c;
1164 windowingWidth_ = w; 1165 windowingWidth_ = w;
1165 } 1166 }
1172 void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message) 1173 void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message)
1173 { 1174 {
1174 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue(); 1175 size_t index = dynamic_cast<const Orthanc::SingleValueObject<size_t>&>(message.GetPayload()).GetValue();
1175 1176
1176 LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str() 1177 LOG(INFO) << "DICOM frame received: " << message.GetUri().c_str()
1177 << " (" << message.GetAnswerSize() << " bytes) for bitmap " << index; 1178 << " (" << message.GetAnswerSize() << " bytes) for layer " << index;
1178 1179
1179 Bitmaps::iterator bitmap = bitmaps_.find(index); 1180 Layers::iterator layer = layers_.find(index);
1180 if (bitmap != bitmaps_.end()) 1181 if (layer != layers_.end())
1181 { 1182 {
1182 assert(bitmap->second != NULL); 1183 assert(layer->second != NULL);
1183 1184
1184 std::string content; 1185 std::string content;
1185 if (message.GetAnswerSize() > 0) 1186 if (message.GetAnswerSize() > 0)
1186 { 1187 {
1187 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize()); 1188 content.assign(reinterpret_cast<const char*>(message.GetAnswer()), message.GetAnswerSize());
1188 } 1189 }
1189 1190
1190 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader); 1191 std::auto_ptr<Orthanc::PamReader> reader(new Orthanc::PamReader);
1191 reader->ReadFromMemory(content); 1192 reader->ReadFromMemory(content);
1192 dynamic_cast<DicomBitmap*>(bitmap->second)->SetSourceImage(reader.release()); 1193 dynamic_cast<DicomLayer*>(layer->second)->SetSourceImage(reader.release());
1193 1194
1194 EmitMessage(ContentChangedMessage(*this)); 1195 EmitMessage(ContentChangedMessage(*this));
1195 } 1196 }
1196 } 1197 }
1197 1198
1198 1199
1199 Extent2D GetSceneExtent() const 1200 Extent2D GetSceneExtent() const
1200 { 1201 {
1201 Extent2D extent; 1202 Extent2D extent;
1202 1203
1203 for (Bitmaps::const_iterator it = bitmaps_.begin(); 1204 for (Layers::const_iterator it = layers_.begin();
1204 it != bitmaps_.end(); ++it) 1205 it != layers_.end(); ++it)
1205 { 1206 {
1206 assert(it->second != NULL); 1207 assert(it->second != NULL);
1207 extent.Union(it->second->GetExtent()); 1208 extent.Union(it->second->GetExtent());
1208 } 1209 }
1209 1210
1216 ImageInterpolation interpolation) const 1217 ImageInterpolation interpolation) const
1217 { 1218 {
1218 Orthanc::ImageProcessing::Set(buffer, 0); 1219 Orthanc::ImageProcessing::Set(buffer, 0);
1219 1220
1220 // Render layers in the background-to-foreground order 1221 // Render layers in the background-to-foreground order
1221 for (size_t index = 0; index < countBitmaps_; index++) 1222 for (size_t index = 0; index < countLayers_; index++)
1222 { 1223 {
1223 Bitmaps::const_iterator it = bitmaps_.find(index); 1224 Layers::const_iterator it = layers_.find(index);
1224 if (it != bitmaps_.end()) 1225 if (it != layers_.end())
1225 { 1226 {
1226 assert(it->second != NULL); 1227 assert(it->second != NULL);
1227 it->second->Render(buffer, viewTransform, interpolation); 1228 it->second->Render(buffer, viewTransform, interpolation);
1228 } 1229 }
1229 } 1230 }
1230 } 1231 }
1231 1232
1232 1233
1233 bool LookupBitmap(size_t& index /* out */, 1234 bool LookupLayer(size_t& index /* out */,
1234 double x, 1235 double x,
1235 double y) const 1236 double y) const
1236 { 1237 {
1237 // Render layers in the foreground-to-background order 1238 // Render layers in the foreground-to-background order
1238 for (size_t i = countBitmaps_; i > 0; i--) 1239 for (size_t i = countLayers_; i > 0; i--)
1239 { 1240 {
1240 index = i - 1; 1241 index = i - 1;
1241 Bitmaps::const_iterator it = bitmaps_.find(index); 1242 Layers::const_iterator it = layers_.find(index);
1242 if (it != bitmaps_.end()) 1243 if (it != layers_.end())
1243 { 1244 {
1244 assert(it->second != NULL); 1245 assert(it->second != NULL);
1245 if (it->second->Contains(x, y)) 1246 if (it->second->Contains(x, y))
1246 { 1247 {
1247 return true; 1248 return true;
1252 return false; 1253 return false;
1253 } 1254 }
1254 1255
1255 1256
1256 void DrawBorder(CairoContext& context, 1257 void DrawBorder(CairoContext& context,
1257 unsigned int bitmap, 1258 unsigned int layer,
1258 double zoom) 1259 double zoom)
1259 { 1260 {
1260 Bitmaps::const_iterator found = bitmaps_.find(bitmap); 1261 Layers::const_iterator found = layers_.find(layer);
1261 1262
1262 if (found != bitmaps_.end()) 1263 if (found != layers_.end())
1263 { 1264 {
1264 context.SetSourceColor(255, 0, 0); 1265 context.SetSourceColor(255, 0, 0);
1265 found->second->DrawBorders(context, zoom); 1266 found->second->DrawBorders(context, zoom);
1266 } 1267 }
1267 } 1268 }
1270 void GetRange(float& minValue, 1271 void GetRange(float& minValue,
1271 float& maxValue) const 1272 float& maxValue) const
1272 { 1273 {
1273 bool first = true; 1274 bool first = true;
1274 1275
1275 for (Bitmaps::const_iterator it = bitmaps_.begin(); 1276 for (Layers::const_iterator it = layers_.begin();
1276 it != bitmaps_.end(); it++) 1277 it != layers_.end(); it++)
1277 { 1278 {
1278 assert(it->second != NULL); 1279 assert(it->second != NULL);
1279 1280
1280 float a, b; 1281 float a, b;
1281 if (it->second->GetRange(a, b)) 1282 if (it->second->GetRange(a, b))
1300 maxValue = 0; 1301 maxValue = 0;
1301 } 1302 }
1302 } 1303 }
1303 1304
1304 1305
1306 // Export using PAM is faster than using PNG, but requires Orthanc
1307 // core >= 1.4.3
1305 void Export(const Orthanc::DicomMap& dicom, 1308 void Export(const Orthanc::DicomMap& dicom,
1306 double pixelSpacingX, 1309 double pixelSpacingX,
1307 double pixelSpacingY, 1310 double pixelSpacingY,
1308 bool invert, 1311 bool invert,
1309 ImageInterpolation interpolation) 1312 ImageInterpolation interpolation,
1313 bool usePam)
1310 { 1314 {
1311 if (pixelSpacingX <= 0 || 1315 if (pixelSpacingX <= 0 ||
1312 pixelSpacingY <= 0) 1316 pixelSpacingY <= 0)
1313 { 1317 {
1314 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); 1318 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
1315 } 1319 }
1316 1320
1317 LOG(WARNING) << "Exporting DICOM"; 1321 LOG(INFO) << "Exporting DICOM";
1318 1322
1319 Extent2D extent = GetSceneExtent(); 1323 Extent2D extent = GetSceneExtent();
1320 1324
1321 int w = std::ceil(extent.GetWidth() / pixelSpacingX); 1325 int w = std::ceil(extent.GetWidth() / pixelSpacingX);
1322 int h = std::ceil(extent.GetHeight() / pixelSpacingY); 1326 int h = std::ceil(extent.GetHeight() / pixelSpacingY);
1343 std::string base64; 1347 std::string base64;
1344 1348
1345 { 1349 {
1346 std::string content; 1350 std::string content;
1347 1351
1348 #if EXPORT_USING_PAM == 1 1352 if (usePam)
1349 { 1353 {
1350 Orthanc::PamWriter writer; 1354 Orthanc::PamWriter writer;
1351 writer.WriteToMemory(content, rendered); 1355 writer.WriteToMemory(content, rendered);
1352 } 1356 }
1353 #else 1357 else
1354 { 1358 {
1355 Orthanc::PngWriter writer; 1359 Orthanc::PngWriter writer;
1356 writer.WriteToMemory(content, rendered); 1360 writer.WriteToMemory(content, rendered);
1357 } 1361 }
1358 #endif
1359 1362
1360 Orthanc::Toolbox::EncodeBase64(base64, content); 1363 Orthanc::Toolbox::EncodeBase64(base64, content);
1361 } 1364 }
1362 1365
1363 std::set<Orthanc::DicomTag> tags; 1366 std::set<Orthanc::DicomTag> tags;
1378 } 1381 }
1379 1382
1380 json["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] = 1383 json["Tags"][Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION.Format()] =
1381 (invert ? "MONOCHROME1" : "MONOCHROME2"); 1384 (invert ? "MONOCHROME1" : "MONOCHROME2");
1382 1385
1383 // WARNING: The order of PixelSpacing is Y/X 1386 // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to
1387 // avoid floating-point numbers to grow over 16 characters,
1388 // which would be invalid according to DICOM standard
1389 // ("dciodvfy" would complain).
1384 char buf[32]; 1390 char buf[32];
1385 sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX); 1391 sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX);
1386 1392
1387 json["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf; 1393 json["Tags"][Orthanc::DICOM_TAG_PIXEL_SPACING.Format()] = buf;
1388 1394
1394 1400
1395 json["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] = 1401 json["Tags"][Orthanc::DICOM_TAG_WINDOW_WIDTH.Format()] =
1396 boost::lexical_cast<std::string>(boost::math::iround(width)); 1402 boost::lexical_cast<std::string>(boost::math::iround(width));
1397 } 1403 }
1398 1404
1399 #if EXPORT_USING_PAM == 1 1405 // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme
1400 json["Content"] = "data:" + std::string(Orthanc::MIME_PAM) + ";base64," + base64; 1406 json["Content"] = ("data:" +
1401 #else 1407 std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) +
1402 json["Content"] = "data:" + std::string(Orthanc::MIME_PNG) + ";base64," + base64; 1408 ";base64," + base64);
1403 #endif
1404 1409
1405 orthanc_.PostJsonAsyncExpectJson( 1410 orthanc_.PostJsonAsyncExpectJson(
1406 "/tools/create-dicom", json, 1411 "/tools/create-dicom", json,
1407 new Callable<BitmapStack, OrthancApiClient::JsonResponseReadyMessage> 1412 new Callable<RadiologyScene, OrthancApiClient::JsonResponseReadyMessage>
1408 (*this, &BitmapStack::OnDicomExported), 1413 (*this, &RadiologyScene::OnDicomExported),
1409 NULL, NULL); 1414 NULL, NULL);
1410 } 1415 }
1411 1416
1412 1417
1413 void OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message) 1418 void OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message)
1414 { 1419 {
1415 LOG(WARNING) << "DICOM export was successful:" 1420 LOG(INFO) << "DICOM export was successful:"
1416 << message.GetJson().toStyledString(); 1421 << message.GetJson().toStyledString();
1417 } 1422 }
1418 }; 1423 };
1419 1424
1420 1425
1421 class UndoRedoStack : public boost::noncopyable 1426 class UndoRedoStack : public boost::noncopyable
1496 } 1501 }
1497 } 1502 }
1498 }; 1503 };
1499 1504
1500 1505
1501 class BitmapCommandBase : public UndoRedoStack::ICommand 1506 class RadiologyLayerCommand : public UndoRedoStack::ICommand
1502 { 1507 {
1503 private: 1508 private:
1504 BitmapStack& stack_; 1509 RadiologyScene& scene_;
1505 size_t bitmap_; 1510 size_t layer_;
1506 1511
1507 protected: 1512 protected:
1508 virtual void UndoInternal(BitmapStack::Bitmap& bitmap) const = 0; 1513 virtual void UndoInternal(RadiologyScene::Layer& layer) const = 0;
1509 1514
1510 virtual void RedoInternal(BitmapStack::Bitmap& bitmap) const = 0; 1515 virtual void RedoInternal(RadiologyScene::Layer& layer) const = 0;
1511 1516
1512 public: 1517 public:
1513 BitmapCommandBase(BitmapStack& stack, 1518 RadiologyLayerCommand(RadiologyScene& scene,
1514 size_t bitmap) : 1519 size_t layer) :
1515 stack_(stack), 1520 scene_(scene),
1516 bitmap_(bitmap) 1521 layer_(layer)
1517 { 1522 {
1518 } 1523 }
1519 1524
1520 BitmapCommandBase(const BitmapStack::BitmapAccessor& accessor) : 1525 RadiologyLayerCommand(const RadiologyScene::LayerAccessor& accessor) :
1521 stack_(accessor.GetStack()), 1526 scene_(accessor.GetScene()),
1522 bitmap_(accessor.GetIndex()) 1527 layer_(accessor.GetIndex())
1523 { 1528 {
1524 } 1529 }
1525 1530
1526 virtual void Undo() const 1531 virtual void Undo() const
1527 { 1532 {
1528 BitmapStack::BitmapAccessor accessor(stack_, bitmap_); 1533 RadiologyScene::LayerAccessor accessor(scene_, layer_);
1529 1534
1530 if (accessor.IsValid()) 1535 if (accessor.IsValid())
1531 { 1536 {
1532 UndoInternal(accessor.GetBitmap()); 1537 UndoInternal(accessor.GetLayer());
1533 } 1538 }
1534 } 1539 }
1535 1540
1536 virtual void Redo() const 1541 virtual void Redo() const
1537 { 1542 {
1538 BitmapStack::BitmapAccessor accessor(stack_, bitmap_); 1543 RadiologyScene::LayerAccessor accessor(scene_, layer_);
1539 1544
1540 if (accessor.IsValid()) 1545 if (accessor.IsValid())
1541 { 1546 {
1542 RedoInternal(accessor.GetBitmap()); 1547 RedoInternal(accessor.GetLayer());
1543 } 1548 }
1544 } 1549 }
1545 }; 1550 };
1546 1551
1547 1552
1548 class RotateBitmapTracker : public IWorldSceneMouseTracker 1553 class RadiologyLayerRotateTracker : public IWorldSceneMouseTracker
1549 { 1554 {
1550 private: 1555 private:
1551 UndoRedoStack& undoRedoStack_; 1556 UndoRedoStack& undoRedoStack_;
1552 BitmapStack::BitmapAccessor accessor_; 1557 RadiologyScene::LayerAccessor accessor_;
1553 double centerX_; 1558 double centerX_;
1554 double centerY_; 1559 double centerY_;
1555 double originalAngle_; 1560 double originalAngle_;
1556 double clickAngle_; 1561 double clickAngle_;
1557 bool roundAngles_; 1562 bool roundAngles_;
1558 1563
1559 bool ComputeAngle(double& angle /* out */, 1564 bool ComputeAngle(double& angle /* out */,
1560 double sceneX, 1565 double sceneX,
1561 double sceneY) const 1566 double sceneY) const
1562 { 1567 {
1576 return false; 1581 return false;
1577 } 1582 }
1578 } 1583 }
1579 1584
1580 1585
1581 class UndoRedoCommand : public BitmapCommandBase 1586 class UndoRedoCommand : public RadiologyLayerCommand
1582 { 1587 {
1583 private: 1588 private:
1584 double sourceAngle_; 1589 double sourceAngle_;
1585 double targetAngle_; 1590 double targetAngle_;
1586 1591
1588 { 1593 {
1589 return boost::math::iround(angle * 180.0 / boost::math::constants::pi<double>()); 1594 return boost::math::iround(angle * 180.0 / boost::math::constants::pi<double>());
1590 } 1595 }
1591 1596
1592 protected: 1597 protected:
1593 virtual void UndoInternal(BitmapStack::Bitmap& bitmap) const 1598 virtual void UndoInternal(RadiologyScene::Layer& layer) const
1594 { 1599 {
1595 LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; 1600 LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees";
1596 bitmap.SetAngle(sourceAngle_); 1601 layer.SetAngle(sourceAngle_);
1597 } 1602 }
1598 1603
1599 virtual void RedoInternal(BitmapStack::Bitmap& bitmap) const 1604 virtual void RedoInternal(RadiologyScene::Layer& layer) const
1600 { 1605 {
1601 LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; 1606 LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees";
1602 bitmap.SetAngle(targetAngle_); 1607 layer.SetAngle(targetAngle_);
1603 } 1608 }
1604 1609
1605 public: 1610 public:
1606 UndoRedoCommand(const RotateBitmapTracker& tracker) : 1611 UndoRedoCommand(const RadiologyLayerRotateTracker& tracker) :
1607 BitmapCommandBase(tracker.accessor_), 1612 RadiologyLayerCommand(tracker.accessor_),
1608 sourceAngle_(tracker.originalAngle_), 1613 sourceAngle_(tracker.originalAngle_),
1609 targetAngle_(tracker.accessor_.GetBitmap().GetAngle()) 1614 targetAngle_(tracker.accessor_.GetLayer().GetAngle())
1610 { 1615 {
1611 } 1616 }
1612 }; 1617 };
1613 1618
1614 1619
1615 public: 1620 public:
1616 RotateBitmapTracker(UndoRedoStack& undoRedoStack, 1621 RadiologyLayerRotateTracker(UndoRedoStack& undoRedoStack,
1617 BitmapStack& stack, 1622 RadiologyScene& scene,
1618 const ViewportGeometry& view, 1623 const ViewportGeometry& view,
1619 size_t bitmap, 1624 size_t layer,
1620 double x, 1625 double x,
1621 double y, 1626 double y,
1622 bool roundAngles) : 1627 bool roundAngles) :
1623 undoRedoStack_(undoRedoStack), 1628 undoRedoStack_(undoRedoStack),
1624 accessor_(stack, bitmap), 1629 accessor_(scene, layer),
1625 roundAngles_(roundAngles) 1630 roundAngles_(roundAngles)
1626 { 1631 {
1627 if (accessor_.IsValid()) 1632 if (accessor_.IsValid())
1628 { 1633 {
1629 accessor_.GetBitmap().GetCenter(centerX_, centerY_); 1634 accessor_.GetLayer().GetCenter(centerX_, centerY_);
1630 originalAngle_ = accessor_.GetBitmap().GetAngle(); 1635 originalAngle_ = accessor_.GetLayer().GetAngle();
1631 1636
1632 double sceneX, sceneY; 1637 double sceneX, sceneY;
1633 view.MapDisplayToScene(sceneX, sceneY, x, y); 1638 view.MapDisplayToScene(sceneX, sceneY, x, y);
1634 1639
1635 if (!ComputeAngle(clickAngle_, x, y)) 1640 if (!ComputeAngle(clickAngle_, x, y))
1675 if (roundAngles_) 1680 if (roundAngles_)
1676 { 1681 {
1677 angle = boost::math::round<double>((angle / ROUND_ANGLE) * ROUND_ANGLE); 1682 angle = boost::math::round<double>((angle / ROUND_ANGLE) * ROUND_ANGLE);
1678 } 1683 }
1679 1684
1680 accessor_.GetBitmap().SetAngle(angle); 1685 accessor_.GetLayer().SetAngle(angle);
1681 } 1686 }
1682 } 1687 }
1683 }; 1688 };
1684 1689
1685 1690
1686 class MoveBitmapTracker : public IWorldSceneMouseTracker 1691 class RadiologyLayerMoveTracker : public IWorldSceneMouseTracker
1687 { 1692 {
1688 private: 1693 private:
1689 UndoRedoStack& undoRedoStack_; 1694 UndoRedoStack& undoRedoStack_;
1690 BitmapStack::BitmapAccessor accessor_; 1695 RadiologyScene::LayerAccessor accessor_;
1691 double clickX_; 1696 double clickX_;
1692 double clickY_; 1697 double clickY_;
1693 double panX_; 1698 double panX_;
1694 double panY_; 1699 double panY_;
1695 bool oneAxis_; 1700 bool oneAxis_;
1696 1701
1697 class UndoRedoCommand : public BitmapCommandBase 1702 class UndoRedoCommand : public RadiologyLayerCommand
1698 { 1703 {
1699 private: 1704 private:
1700 double sourceX_; 1705 double sourceX_;
1701 double sourceY_; 1706 double sourceY_;
1702 double targetX_; 1707 double targetX_;
1703 double targetY_; 1708 double targetY_;
1704 1709
1705 protected: 1710 protected:
1706 virtual void UndoInternal(BitmapStack::Bitmap& bitmap) const 1711 virtual void UndoInternal(RadiologyScene::Layer& layer) const
1707 { 1712 {
1708 bitmap.SetPan(sourceX_, sourceY_); 1713 layer.SetPan(sourceX_, sourceY_);
1709 } 1714 }
1710 1715
1711 virtual void RedoInternal(BitmapStack::Bitmap& bitmap) const 1716 virtual void RedoInternal(RadiologyScene::Layer& layer) const
1712 { 1717 {
1713 bitmap.SetPan(targetX_, targetY_); 1718 layer.SetPan(targetX_, targetY_);
1714 } 1719 }
1715 1720
1716 public: 1721 public:
1717 UndoRedoCommand(const MoveBitmapTracker& tracker) : 1722 UndoRedoCommand(const RadiologyLayerMoveTracker& tracker) :
1718 BitmapCommandBase(tracker.accessor_), 1723 RadiologyLayerCommand(tracker.accessor_),
1719 sourceX_(tracker.panX_), 1724 sourceX_(tracker.panX_),
1720 sourceY_(tracker.panY_), 1725 sourceY_(tracker.panY_),
1721 targetX_(tracker.accessor_.GetBitmap().GetPanX()), 1726 targetX_(tracker.accessor_.GetLayer().GetPanX()),
1722 targetY_(tracker.accessor_.GetBitmap().GetPanY()) 1727 targetY_(tracker.accessor_.GetLayer().GetPanY())
1723 { 1728 {
1724 } 1729 }
1725 }; 1730 };
1726 1731
1727 1732
1728 public: 1733 public:
1729 MoveBitmapTracker(UndoRedoStack& undoRedoStack, 1734 RadiologyLayerMoveTracker(UndoRedoStack& undoRedoStack,
1730 BitmapStack& stack, 1735 RadiologyScene& scene,
1731 size_t bitmap, 1736 size_t layer,
1732 double x, 1737 double x,
1733 double y, 1738 double y,
1734 bool oneAxis) : 1739 bool oneAxis) :
1735 undoRedoStack_(undoRedoStack), 1740 undoRedoStack_(undoRedoStack),
1736 accessor_(stack, bitmap), 1741 accessor_(scene, layer),
1737 clickX_(x), 1742 clickX_(x),
1738 clickY_(y), 1743 clickY_(y),
1739 oneAxis_(oneAxis) 1744 oneAxis_(oneAxis)
1740 { 1745 {
1741 if (accessor_.IsValid()) 1746 if (accessor_.IsValid())
1742 { 1747 {
1743 panX_ = accessor_.GetBitmap().GetPanX(); 1748 panX_ = accessor_.GetLayer().GetPanX();
1744 panY_ = accessor_.GetBitmap().GetPanY(); 1749 panY_ = accessor_.GetLayer().GetPanY();
1745 } 1750 }
1746 } 1751 }
1747 1752
1748 virtual bool HasRender() const 1753 virtual bool HasRender() const
1749 { 1754 {
1776 1781
1777 if (oneAxis_) 1782 if (oneAxis_)
1778 { 1783 {
1779 if (fabs(dx) > fabs(dy)) 1784 if (fabs(dx) > fabs(dy))
1780 { 1785 {
1781 accessor_.GetBitmap().SetPan(dx + panX_, panY_); 1786 accessor_.GetLayer().SetPan(dx + panX_, panY_);
1782 } 1787 }
1783 else 1788 else
1784 { 1789 {
1785 accessor_.GetBitmap().SetPan(panX_, dy + panY_); 1790 accessor_.GetLayer().SetPan(panX_, dy + panY_);
1786 } 1791 }
1787 } 1792 }
1788 else 1793 else
1789 { 1794 {
1790 accessor_.GetBitmap().SetPan(dx + panX_, dy + panY_); 1795 accessor_.GetLayer().SetPan(dx + panX_, dy + panY_);
1791 } 1796 }
1792 } 1797 }
1793 } 1798 }
1794 }; 1799 };
1795 1800
1796 1801
1797 class CropBitmapTracker : public IWorldSceneMouseTracker 1802 class RadiologyLayerCropTracker : public IWorldSceneMouseTracker
1798 { 1803 {
1799 private: 1804 private:
1800 UndoRedoStack& undoRedoStack_; 1805 UndoRedoStack& undoRedoStack_;
1801 BitmapStack::BitmapAccessor accessor_; 1806 RadiologyScene::LayerAccessor accessor_;
1802 BitmapStack::Corner corner_; 1807 RadiologyScene::Corner corner_;
1803 unsigned int cropX_; 1808 unsigned int cropX_;
1804 unsigned int cropY_; 1809 unsigned int cropY_;
1805 unsigned int cropWidth_; 1810 unsigned int cropWidth_;
1806 unsigned int cropHeight_; 1811 unsigned int cropHeight_;
1807 1812
1808 class UndoRedoCommand : public BitmapCommandBase 1813 class UndoRedoCommand : public RadiologyLayerCommand
1809 { 1814 {
1810 private: 1815 private:
1811 unsigned int sourceCropX_; 1816 unsigned int sourceCropX_;
1812 unsigned int sourceCropY_; 1817 unsigned int sourceCropY_;
1813 unsigned int sourceCropWidth_; 1818 unsigned int sourceCropWidth_;
1816 unsigned int targetCropY_; 1821 unsigned int targetCropY_;
1817 unsigned int targetCropWidth_; 1822 unsigned int targetCropWidth_;
1818 unsigned int targetCropHeight_; 1823 unsigned int targetCropHeight_;
1819 1824
1820 protected: 1825 protected:
1821 virtual void UndoInternal(BitmapStack::Bitmap& bitmap) const 1826 virtual void UndoInternal(RadiologyScene::Layer& layer) const
1822 { 1827 {
1823 bitmap.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_); 1828 layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_);
1824 } 1829 }
1825 1830
1826 virtual void RedoInternal(BitmapStack::Bitmap& bitmap) const 1831 virtual void RedoInternal(RadiologyScene::Layer& layer) const
1827 { 1832 {
1828 bitmap.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_); 1833 layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_);
1829 } 1834 }
1830 1835
1831 public: 1836 public:
1832 UndoRedoCommand(const CropBitmapTracker& tracker) : 1837 UndoRedoCommand(const RadiologyLayerCropTracker& tracker) :
1833 BitmapCommandBase(tracker.accessor_), 1838 RadiologyLayerCommand(tracker.accessor_),
1834 sourceCropX_(tracker.cropX_), 1839 sourceCropX_(tracker.cropX_),
1835 sourceCropY_(tracker.cropY_), 1840 sourceCropY_(tracker.cropY_),
1836 sourceCropWidth_(tracker.cropWidth_), 1841 sourceCropWidth_(tracker.cropWidth_),
1837 sourceCropHeight_(tracker.cropHeight_) 1842 sourceCropHeight_(tracker.cropHeight_)
1838 { 1843 {
1839 tracker.accessor_.GetBitmap().GetCrop(targetCropX_, targetCropY_, 1844 tracker.accessor_.GetLayer().GetCrop(targetCropX_, targetCropY_,
1840 targetCropWidth_, targetCropHeight_); 1845 targetCropWidth_, targetCropHeight_);
1841 } 1846 }
1842 }; 1847 };
1843 1848
1844 1849
1845 public: 1850 public:
1846 CropBitmapTracker(UndoRedoStack& undoRedoStack, 1851 RadiologyLayerCropTracker(UndoRedoStack& undoRedoStack,
1847 BitmapStack& stack, 1852 RadiologyScene& scene,
1848 const ViewportGeometry& view, 1853 const ViewportGeometry& view,
1849 size_t bitmap, 1854 size_t layer,
1850 double x, 1855 double x,
1851 double y, 1856 double y,
1852 BitmapStack::Corner corner) : 1857 RadiologyScene::Corner corner) :
1853 undoRedoStack_(undoRedoStack), 1858 undoRedoStack_(undoRedoStack),
1854 accessor_(stack, bitmap), 1859 accessor_(scene, layer),
1855 corner_(corner) 1860 corner_(corner)
1856 { 1861 {
1857 if (accessor_.IsValid()) 1862 if (accessor_.IsValid())
1858 { 1863 {
1859 accessor_.GetBitmap().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_); 1864 accessor_.GetLayer().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_);
1860 } 1865 }
1861 } 1866 }
1862 1867
1863 virtual bool HasRender() const 1868 virtual bool HasRender() const
1864 { 1869 {
1886 { 1891 {
1887 if (accessor_.IsValid()) 1892 if (accessor_.IsValid())
1888 { 1893 {
1889 unsigned int x, y; 1894 unsigned int x, y;
1890 1895
1891 BitmapStack::Bitmap& bitmap = accessor_.GetBitmap(); 1896 RadiologyScene::Layer& layer = accessor_.GetLayer();
1892 if (bitmap.GetPixel(x, y, sceneX, sceneY)) 1897 if (layer.GetPixel(x, y, sceneX, sceneY))
1893 { 1898 {
1894 unsigned int targetX, targetWidth; 1899 unsigned int targetX, targetWidth;
1895 1900
1896 if (corner_ == BitmapStack::Corner_TopLeft || 1901 if (corner_ == RadiologyScene::Corner_TopLeft ||
1897 corner_ == BitmapStack::Corner_BottomLeft) 1902 corner_ == RadiologyScene::Corner_BottomLeft)
1898 { 1903 {
1899 targetX = std::min(x, cropX_ + cropWidth_); 1904 targetX = std::min(x, cropX_ + cropWidth_);
1900 targetWidth = cropX_ + cropWidth_ - targetX; 1905 targetWidth = cropX_ + cropWidth_ - targetX;
1901 } 1906 }
1902 else 1907 else
1905 targetWidth = std::max(x, cropX_) - cropX_; 1910 targetWidth = std::max(x, cropX_) - cropX_;
1906 } 1911 }
1907 1912
1908 unsigned int targetY, targetHeight; 1913 unsigned int targetY, targetHeight;
1909 1914
1910 if (corner_ == BitmapStack::Corner_TopLeft || 1915 if (corner_ == RadiologyScene::Corner_TopLeft ||
1911 corner_ == BitmapStack::Corner_TopRight) 1916 corner_ == RadiologyScene::Corner_TopRight)
1912 { 1917 {
1913 targetY = std::min(y, cropY_ + cropHeight_); 1918 targetY = std::min(y, cropY_ + cropHeight_);
1914 targetHeight = cropY_ + cropHeight_ - targetY; 1919 targetHeight = cropY_ + cropHeight_ - targetY;
1915 } 1920 }
1916 else 1921 else
1917 { 1922 {
1918 targetY = cropY_; 1923 targetY = cropY_;
1919 targetHeight = std::max(y, cropY_) - cropY_; 1924 targetHeight = std::max(y, cropY_) - cropY_;
1920 } 1925 }
1921 1926
1922 bitmap.SetCrop(targetX, targetY, targetWidth, targetHeight); 1927 layer.SetCrop(targetX, targetY, targetWidth, targetHeight);
1923 } 1928 }
1924 } 1929 }
1925 } 1930 }
1926 }; 1931 };
1927 1932
1928 1933
1929 class ResizeBitmapTracker : public IWorldSceneMouseTracker 1934 class RadiologyLayerResizeTracker : public IWorldSceneMouseTracker
1930 { 1935 {
1931 private: 1936 private:
1932 UndoRedoStack& undoRedoStack_; 1937 UndoRedoStack& undoRedoStack_;
1933 BitmapStack::BitmapAccessor accessor_; 1938 RadiologyScene::LayerAccessor accessor_;
1934 bool roundScaling_; 1939 bool roundScaling_;
1935 double originalSpacingX_; 1940 double originalSpacingX_;
1936 double originalSpacingY_; 1941 double originalSpacingY_;
1937 double originalPanX_; 1942 double originalPanX_;
1938 double originalPanY_; 1943 double originalPanY_;
1939 BitmapStack::Corner oppositeCorner_; 1944 RadiologyScene::Corner oppositeCorner_;
1940 double oppositeX_; 1945 double oppositeX_;
1941 double oppositeY_; 1946 double oppositeY_;
1942 double baseScaling_; 1947 double baseScaling_;
1943 1948
1944 static double ComputeDistance(double x1, 1949 static double ComputeDistance(double x1,
1945 double y1, 1950 double y1,
1946 double x2, 1951 double x2,
1947 double y2) 1952 double y2)
1949 double dx = x1 - x2; 1954 double dx = x1 - x2;
1950 double dy = y1 - y2; 1955 double dy = y1 - y2;
1951 return sqrt(dx * dx + dy * dy); 1956 return sqrt(dx * dx + dy * dy);
1952 } 1957 }
1953 1958
1954 class UndoRedoCommand : public BitmapCommandBase 1959 class UndoRedoCommand : public RadiologyLayerCommand
1955 { 1960 {
1956 private: 1961 private:
1957 double sourceSpacingX_; 1962 double sourceSpacingX_;
1958 double sourceSpacingY_; 1963 double sourceSpacingY_;
1959 double sourcePanX_; 1964 double sourcePanX_;
1962 double targetSpacingY_; 1967 double targetSpacingY_;
1963 double targetPanX_; 1968 double targetPanX_;
1964 double targetPanY_; 1969 double targetPanY_;
1965 1970
1966 protected: 1971 protected:
1967 virtual void UndoInternal(BitmapStack::Bitmap& bitmap) const 1972 virtual void UndoInternal(RadiologyScene::Layer& layer) const
1968 { 1973 {
1969 bitmap.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_); 1974 layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_);
1970 bitmap.SetPan(sourcePanX_, sourcePanY_); 1975 layer.SetPan(sourcePanX_, sourcePanY_);
1971 } 1976 }
1972 1977
1973 virtual void RedoInternal(BitmapStack::Bitmap& bitmap) const 1978 virtual void RedoInternal(RadiologyScene::Layer& layer) const
1974 { 1979 {
1975 bitmap.SetPixelSpacing(targetSpacingX_, targetSpacingY_); 1980 layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_);
1976 bitmap.SetPan(targetPanX_, targetPanY_); 1981 layer.SetPan(targetPanX_, targetPanY_);
1977 } 1982 }
1978 1983
1979 public: 1984 public:
1980 UndoRedoCommand(const ResizeBitmapTracker& tracker) : 1985 UndoRedoCommand(const RadiologyLayerResizeTracker& tracker) :
1981 BitmapCommandBase(tracker.accessor_), 1986 RadiologyLayerCommand(tracker.accessor_),
1982 sourceSpacingX_(tracker.originalSpacingX_), 1987 sourceSpacingX_(tracker.originalSpacingX_),
1983 sourceSpacingY_(tracker.originalSpacingY_), 1988 sourceSpacingY_(tracker.originalSpacingY_),
1984 sourcePanX_(tracker.originalPanX_), 1989 sourcePanX_(tracker.originalPanX_),
1985 sourcePanY_(tracker.originalPanY_), 1990 sourcePanY_(tracker.originalPanY_),
1986 targetSpacingX_(tracker.accessor_.GetBitmap().GetPixelSpacingX()), 1991 targetSpacingX_(tracker.accessor_.GetLayer().GetPixelSpacingX()),
1987 targetSpacingY_(tracker.accessor_.GetBitmap().GetPixelSpacingY()), 1992 targetSpacingY_(tracker.accessor_.GetLayer().GetPixelSpacingY()),
1988 targetPanX_(tracker.accessor_.GetBitmap().GetPanX()), 1993 targetPanX_(tracker.accessor_.GetLayer().GetPanX()),
1989 targetPanY_(tracker.accessor_.GetBitmap().GetPanY()) 1994 targetPanY_(tracker.accessor_.GetLayer().GetPanY())
1990 { 1995 {
1991 } 1996 }
1992 }; 1997 };
1993 1998
1994 1999
1995 public: 2000 public:
1996 ResizeBitmapTracker(UndoRedoStack& undoRedoStack, 2001 RadiologyLayerResizeTracker(UndoRedoStack& undoRedoStack,
1997 BitmapStack& stack, 2002 RadiologyScene& scene,
1998 size_t bitmap, 2003 size_t layer,
1999 double x, 2004 double x,
2000 double y, 2005 double y,
2001 BitmapStack::Corner corner, 2006 RadiologyScene::Corner corner,
2002 bool roundScaling) : 2007 bool roundScaling) :
2003 undoRedoStack_(undoRedoStack), 2008 undoRedoStack_(undoRedoStack),
2004 accessor_(stack, bitmap), 2009 accessor_(scene, layer),
2005 roundScaling_(roundScaling) 2010 roundScaling_(roundScaling)
2006 { 2011 {
2007 if (accessor_.IsValid() && 2012 if (accessor_.IsValid() &&
2008 accessor_.GetBitmap().IsResizeable()) 2013 accessor_.GetLayer().IsResizeable())
2009 { 2014 {
2010 originalSpacingX_ = accessor_.GetBitmap().GetPixelSpacingX(); 2015 originalSpacingX_ = accessor_.GetLayer().GetPixelSpacingX();
2011 originalSpacingY_ = accessor_.GetBitmap().GetPixelSpacingY(); 2016 originalSpacingY_ = accessor_.GetLayer().GetPixelSpacingY();
2012 originalPanX_ = accessor_.GetBitmap().GetPanX(); 2017 originalPanX_ = accessor_.GetLayer().GetPanX();
2013 originalPanY_ = accessor_.GetBitmap().GetPanY(); 2018 originalPanY_ = accessor_.GetLayer().GetPanY();
2014 2019
2015 switch (corner) 2020 switch (corner)
2016 { 2021 {
2017 case BitmapStack::Corner_TopLeft: 2022 case RadiologyScene::Corner_TopLeft:
2018 oppositeCorner_ = BitmapStack::Corner_BottomRight; 2023 oppositeCorner_ = RadiologyScene::Corner_BottomRight;
2019 break; 2024 break;
2020 2025
2021 case BitmapStack::Corner_TopRight: 2026 case RadiologyScene::Corner_TopRight:
2022 oppositeCorner_ = BitmapStack::Corner_BottomLeft; 2027 oppositeCorner_ = RadiologyScene::Corner_BottomLeft;
2023 break; 2028 break;
2024 2029
2025 case BitmapStack::Corner_BottomLeft: 2030 case RadiologyScene::Corner_BottomLeft:
2026 oppositeCorner_ = BitmapStack::Corner_TopRight; 2031 oppositeCorner_ = RadiologyScene::Corner_TopRight;
2027 break; 2032 break;
2028 2033
2029 case BitmapStack::Corner_BottomRight: 2034 case RadiologyScene::Corner_BottomRight:
2030 oppositeCorner_ = BitmapStack::Corner_TopLeft; 2035 oppositeCorner_ = RadiologyScene::Corner_TopLeft;
2031 break; 2036 break;
2032 2037
2033 default: 2038 default:
2034 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); 2039 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
2035 } 2040 }
2036 2041
2037 accessor_.GetBitmap().GetCorner(oppositeX_, oppositeY_, oppositeCorner_); 2042 accessor_.GetLayer().GetCorner(oppositeX_, oppositeY_, oppositeCorner_);
2038 2043
2039 double d = ComputeDistance(x, y, oppositeX_, oppositeY_); 2044 double d = ComputeDistance(x, y, oppositeX_, oppositeY_);
2040 if (d >= std::numeric_limits<float>::epsilon()) 2045 if (d >= std::numeric_limits<float>::epsilon())
2041 { 2046 {
2042 baseScaling_ = 1.0 / d; 2047 baseScaling_ = 1.0 / d;
2061 } 2066 }
2062 2067
2063 virtual void MouseUp() 2068 virtual void MouseUp()
2064 { 2069 {
2065 if (accessor_.IsValid() && 2070 if (accessor_.IsValid() &&
2066 accessor_.GetBitmap().IsResizeable()) 2071 accessor_.GetLayer().IsResizeable())
2067 { 2072 {
2068 undoRedoStack_.Add(new UndoRedoCommand(*this)); 2073 undoRedoStack_.Add(new UndoRedoCommand(*this));
2069 } 2074 }
2070 } 2075 }
2071 2076
2075 double sceneY) 2080 double sceneY)
2076 { 2081 {
2077 static const double ROUND_SCALING = 0.1; 2082 static const double ROUND_SCALING = 0.1;
2078 2083
2079 if (accessor_.IsValid() && 2084 if (accessor_.IsValid() &&
2080 accessor_.GetBitmap().IsResizeable()) 2085 accessor_.GetLayer().IsResizeable())
2081 { 2086 {
2082 double scaling = ComputeDistance(oppositeX_, oppositeY_, sceneX, sceneY) * baseScaling_; 2087 double scaling = ComputeDistance(oppositeX_, oppositeY_, sceneX, sceneY) * baseScaling_;
2083 2088
2084 if (roundScaling_) 2089 if (roundScaling_)
2085 { 2090 {
2086 scaling = boost::math::round<double>((scaling / ROUND_SCALING) * ROUND_SCALING); 2091 scaling = boost::math::round<double>((scaling / ROUND_SCALING) * ROUND_SCALING);
2087 } 2092 }
2088 2093
2089 BitmapStack::Bitmap& bitmap = accessor_.GetBitmap(); 2094 RadiologyScene::Layer& layer = accessor_.GetLayer();
2090 bitmap.SetPixelSpacing(scaling * originalSpacingX_, 2095 layer.SetPixelSpacing(scaling * originalSpacingX_,
2091 scaling * originalSpacingY_); 2096 scaling * originalSpacingY_);
2092 2097
2093 // Keep the opposite corner at a fixed location 2098 // Keep the opposite corner at a fixed location
2094 double ox, oy; 2099 double ox, oy;
2095 bitmap.GetCorner(ox, oy, oppositeCorner_); 2100 layer.GetCorner(ox, oy, oppositeCorner_);
2096 bitmap.SetPan(bitmap.GetPanX() + oppositeX_ - ox, 2101 layer.SetPan(layer.GetPanX() + oppositeX_ - ox,
2097 bitmap.GetPanY() + oppositeY_ - oy); 2102 layer.GetPanY() + oppositeY_ - oy);
2098 } 2103 }
2099 } 2104 }
2100 }; 2105 };
2101 2106
2102 2107
2103 class WindowingTracker : public IWorldSceneMouseTracker 2108 class RadiologyWindowingTracker : public IWorldSceneMouseTracker
2104 { 2109 {
2105 public: 2110 public:
2106 enum Action 2111 enum Action
2107 { 2112 {
2108 Action_IncreaseWidth, 2113 Action_IncreaseWidth,
2110 Action_IncreaseCenter, 2115 Action_IncreaseCenter,
2111 Action_DecreaseCenter 2116 Action_DecreaseCenter
2112 }; 2117 };
2113 2118
2114 private: 2119 private:
2115 UndoRedoStack& undoRedoStack_; 2120 UndoRedoStack& undoRedoStack_;
2116 BitmapStack& stack_; 2121 RadiologyScene& scene_;
2117 int clickX_; 2122 int clickX_;
2118 int clickY_; 2123 int clickY_;
2119 Action leftAction_; 2124 Action leftAction_;
2120 Action rightAction_; 2125 Action rightAction_;
2121 Action upAction_; 2126 Action upAction_;
2122 Action downAction_; 2127 Action downAction_;
2123 float strength_; 2128 float strength_;
2124 float sourceCenter_; 2129 float sourceCenter_;
2125 float sourceWidth_; 2130 float sourceWidth_;
2126 2131
2127 static void ComputeAxisEffect(int& deltaCenter, 2132 static void ComputeAxisEffect(int& deltaCenter,
2128 int& deltaWidth, 2133 int& deltaWidth,
2129 int delta, 2134 int delta,
2130 Action actionNegative, 2135 Action actionNegative,
2182 2187
2183 2188
2184 class UndoRedoCommand : public UndoRedoStack::ICommand 2189 class UndoRedoCommand : public UndoRedoStack::ICommand
2185 { 2190 {
2186 private: 2191 private:
2187 BitmapStack& stack_; 2192 RadiologyScene& scene_;
2188 float sourceCenter_; 2193 float sourceCenter_;
2189 float sourceWidth_; 2194 float sourceWidth_;
2190 float targetCenter_; 2195 float targetCenter_;
2191 float targetWidth_; 2196 float targetWidth_;
2192 2197
2193 public: 2198 public:
2194 UndoRedoCommand(const WindowingTracker& tracker) : 2199 UndoRedoCommand(const RadiologyWindowingTracker& tracker) :
2195 stack_(tracker.stack_), 2200 scene_(tracker.scene_),
2196 sourceCenter_(tracker.sourceCenter_), 2201 sourceCenter_(tracker.sourceCenter_),
2197 sourceWidth_(tracker.sourceWidth_) 2202 sourceWidth_(tracker.sourceWidth_)
2198 { 2203 {
2199 stack_.GetWindowingWithDefault(targetCenter_, targetWidth_); 2204 scene_.GetWindowingWithDefault(targetCenter_, targetWidth_);
2200 } 2205 }
2201 2206
2202 virtual void Undo() const 2207 virtual void Undo() const
2203 { 2208 {
2204 stack_.SetWindowing(sourceCenter_, sourceWidth_); 2209 scene_.SetWindowing(sourceCenter_, sourceWidth_);
2205 } 2210 }
2206 2211
2207 virtual void Redo() const 2212 virtual void Redo() const
2208 { 2213 {
2209 stack_.SetWindowing(targetCenter_, targetWidth_); 2214 scene_.SetWindowing(targetCenter_, targetWidth_);
2210 } 2215 }
2211 }; 2216 };
2212 2217
2213 2218
2214 public: 2219 public:
2215 WindowingTracker(UndoRedoStack& undoRedoStack, 2220 RadiologyWindowingTracker(UndoRedoStack& undoRedoStack,
2216 BitmapStack& stack, 2221 RadiologyScene& scene,
2217 int x, 2222 int x,
2218 int y, 2223 int y,
2219 Action leftAction, 2224 Action leftAction,
2220 Action rightAction, 2225 Action rightAction,
2221 Action upAction, 2226 Action upAction,
2222 Action downAction) : 2227 Action downAction) :
2223 undoRedoStack_(undoRedoStack), 2228 undoRedoStack_(undoRedoStack),
2224 stack_(stack), 2229 scene_(scene),
2225 clickX_(x), 2230 clickX_(x),
2226 clickY_(y), 2231 clickY_(y),
2227 leftAction_(leftAction), 2232 leftAction_(leftAction),
2228 rightAction_(rightAction), 2233 rightAction_(rightAction),
2229 upAction_(upAction), 2234 upAction_(upAction),
2230 downAction_(downAction) 2235 downAction_(downAction)
2231 { 2236 {
2232 stack_.GetWindowingWithDefault(sourceCenter_, sourceWidth_); 2237 scene_.GetWindowingWithDefault(sourceCenter_, sourceWidth_);
2233 2238
2234 float minValue, maxValue; 2239 float minValue, maxValue;
2235 stack.GetRange(minValue, maxValue); 2240 scene.GetRange(minValue, maxValue);
2236 2241
2237 assert(minValue <= maxValue); 2242 assert(minValue <= maxValue);
2238 2243
2239 float tmp; 2244 float tmp;
2240 2245
2289 ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_); 2294 ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_);
2290 ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_); 2295 ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_);
2291 2296
2292 float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_); 2297 float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_);
2293 float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_); 2298 float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_);
2294 stack_.SetWindowing(newCenter, newWidth); 2299 scene_.SetWindowing(newCenter, newWidth);
2295 } 2300 }
2296 }; 2301 };
2297 2302
2298 2303
2299 class BitmapStackWidget : 2304 class RadiologyWidget :
2300 public WorldSceneWidget, 2305 public WorldSceneWidget,
2301 public IObserver 2306 public IObserver
2302 { 2307 {
2303 private: 2308 private:
2304 BitmapStack& stack_; 2309 RadiologyScene& scene_;
2305 std::auto_ptr<Orthanc::Image> floatBuffer_; 2310 std::auto_ptr<Orthanc::Image> floatBuffer_;
2306 std::auto_ptr<CairoSurface> cairoBuffer_; 2311 std::auto_ptr<CairoSurface> cairoBuffer_;
2307 bool invert_; 2312 bool invert_;
2308 ImageInterpolation interpolation_; 2313 ImageInterpolation interpolation_;
2309 bool hasSelection_; 2314 bool hasSelection_;
2310 size_t selectedBitmap_; 2315 size_t selectedLayer_;
2311 2316
2312 virtual bool RenderInternal(unsigned int width, 2317 virtual bool RenderInternal(unsigned int width,
2313 unsigned int height, 2318 unsigned int height,
2314 ImageInterpolation interpolation) 2319 ImageInterpolation interpolation)
2315 { 2320 {
2316 float windowCenter, windowWidth; 2321 float windowCenter, windowWidth;
2317 stack_.GetWindowingWithDefault(windowCenter, windowWidth); 2322 scene_.GetWindowingWithDefault(windowCenter, windowWidth);
2318 2323
2319 float x0 = windowCenter - windowWidth / 2.0f; 2324 float x0 = windowCenter - windowWidth / 2.0f;
2320 float x1 = windowCenter + windowWidth / 2.0f; 2325 float x1 = windowCenter + windowWidth / 2.0f;
2321 2326
2322 if (windowWidth <= 0.001f) // Avoid division by zero at (*) 2327 if (windowWidth <= 0.001f) // Avoid division by zero at (*)
2337 cairoBuffer_->GetHeight() != height) 2342 cairoBuffer_->GetHeight() != height)
2338 { 2343 {
2339 cairoBuffer_.reset(new CairoSurface(width, height)); 2344 cairoBuffer_.reset(new CairoSurface(width, height));
2340 } 2345 }
2341 2346
2342 stack_.Render(*floatBuffer_, GetView().GetMatrix(), interpolation); 2347 scene_.Render(*floatBuffer_, GetView().GetMatrix(), interpolation);
2343 2348
2344 // Conversion from Float32 to BGRA32 (cairo). Very similar to 2349 // Conversion from Float32 to BGRA32 (cairo). Very similar to
2345 // GrayscaleFrameRenderer => TODO MERGE? 2350 // GrayscaleFrameRenderer => TODO MERGE?
2346 2351
2347 Orthanc::ImageAccessor target; 2352 Orthanc::ImageAccessor target;
2388 } 2393 }
2389 2394
2390 protected: 2395 protected:
2391 virtual Extent2D GetSceneExtent() 2396 virtual Extent2D GetSceneExtent()
2392 { 2397 {
2393 return stack_.GetSceneExtent(); 2398 return scene_.GetSceneExtent();
2394 } 2399 }
2395 2400
2396 virtual bool RenderScene(CairoContext& context, 2401 virtual bool RenderScene(CairoContext& context,
2397 const ViewportGeometry& view) 2402 const ViewportGeometry& view)
2398 { 2403 {
2414 cairo_paint(cr); 2419 cairo_paint(cr);
2415 } 2420 }
2416 2421
2417 if (hasSelection_) 2422 if (hasSelection_)
2418 { 2423 {
2419 stack_.DrawBorder(context, selectedBitmap_, view.GetZoom()); 2424 scene_.DrawBorder(context, selectedLayer_, view.GetZoom());
2420 } 2425 }
2421 2426
2422 return true; 2427 return true;
2423 } 2428 }
2424 2429
2425 public: 2430 public:
2426 BitmapStackWidget(MessageBroker& broker, 2431 RadiologyWidget(MessageBroker& broker,
2427 BitmapStack& stack, 2432 RadiologyScene& scene,
2428 const std::string& name) : 2433 const std::string& name) :
2429 WorldSceneWidget(name), 2434 WorldSceneWidget(name),
2430 IObserver(broker), 2435 IObserver(broker),
2431 stack_(stack), 2436 scene_(scene),
2432 invert_(false), 2437 invert_(false),
2433 interpolation_(ImageInterpolation_Nearest), 2438 interpolation_(ImageInterpolation_Nearest),
2434 hasSelection_(false), 2439 hasSelection_(false),
2435 selectedBitmap_(0) // Dummy initialization 2440 selectedLayer_(0) // Dummy initialization
2436 { 2441 {
2437 stack.RegisterObserverCallback(new Callable<BitmapStackWidget, BitmapStack::GeometryChangedMessage>(*this, &BitmapStackWidget::OnGeometryChanged)); 2442 scene.RegisterObserverCallback(
2438 stack.RegisterObserverCallback(new Callable<BitmapStackWidget, BitmapStack::ContentChangedMessage>(*this, &BitmapStackWidget::OnContentChanged)); 2443 new Callable<RadiologyWidget, RadiologyScene::GeometryChangedMessage>
2439 } 2444 (*this, &RadiologyWidget::OnGeometryChanged));
2440 2445
2441 BitmapStack& GetStack() const 2446 scene.RegisterObserverCallback(
2442 { 2447 new Callable<RadiologyWidget, RadiologyScene::ContentChangedMessage>
2443 return stack_; 2448 (*this, &RadiologyWidget::OnContentChanged));
2449 }
2450
2451 RadiologyScene& GetScene() const
2452 {
2453 return scene_;
2444 } 2454 }
2445 2455
2446 void Unselect() 2456 void Unselect()
2447 { 2457 {
2448 hasSelection_ = false; 2458 hasSelection_ = false;
2449 } 2459 }
2450 2460
2451 void Select(size_t bitmap) 2461 void Select(size_t layer)
2452 { 2462 {
2453 hasSelection_ = true; 2463 hasSelection_ = true;
2454 selectedBitmap_ = bitmap; 2464 selectedLayer_ = layer;
2455 } 2465 }
2456 2466
2457 bool LookupSelectedBitmap(size_t& bitmap) 2467 bool LookupSelectedLayer(size_t& layer)
2458 { 2468 {
2459 if (hasSelection_) 2469 if (hasSelection_)
2460 { 2470 {
2461 bitmap = selectedBitmap_; 2471 layer = selectedLayer_;
2462 return true; 2472 return true;
2463 } 2473 }
2464 else 2474 else
2465 { 2475 {
2466 return false; 2476 return false;
2467 } 2477 }
2468 } 2478 }
2469 2479
2470 void OnGeometryChanged(const BitmapStack::GeometryChangedMessage& message) 2480 void OnGeometryChanged(const RadiologyScene::GeometryChangedMessage& message)
2471 { 2481 {
2472 LOG(INFO) << "Geometry has changed"; 2482 LOG(INFO) << "Geometry has changed";
2473 FitContent(); 2483 FitContent();
2474 } 2484 }
2475 2485
2476 void OnContentChanged(const BitmapStack::ContentChangedMessage& message) 2486 void OnContentChanged(const RadiologyScene::ContentChangedMessage& message)
2477 { 2487 {
2478 LOG(INFO) << "Content has changed"; 2488 LOG(INFO) << "Content has changed";
2479 NotifyContentChanged(); 2489 NotifyContentChanged();
2480 } 2490 }
2481 2491
2513 return interpolation_; 2523 return interpolation_;
2514 } 2524 }
2515 }; 2525 };
2516 2526
2517 2527
2518 class BitmapStackInteractor : 2528 namespace Samples
2519 public IWorldSceneInteractor,
2520 public IObserver
2521 { 2529 {
2522 private: 2530 class RadiologyEditorInteractor :
2523 enum Tool 2531 public IWorldSceneInteractor,
2524 { 2532 public IObserver
2525 Tool_Move, 2533 {
2526 Tool_Rotate, 2534 private:
2527 Tool_Crop, 2535 enum Tool
2528 Tool_Resize, 2536 {
2529 Tool_Windowing 2537 Tool_Move,
2530 }; 2538 Tool_Rotate,
2531 2539 Tool_Crop,
2532 2540 Tool_Resize,
2533 UndoRedoStack undoRedoStack_; 2541 Tool_Windowing
2534 Tool tool_; 2542 };
2535 2543
2536 2544
2537 static double GetHandleSize() 2545 UndoRedoStack undoRedoStack_;
2538 { 2546 Tool tool_;
2539 return 10.0; 2547
2540 } 2548
2549 static double GetHandleSize()
2550 {
2551 return 10.0;
2552 }
2541 2553
2542 2554
2543 public: 2555 public:
2544 BitmapStackInteractor(MessageBroker& broker) : 2556 RadiologyEditorInteractor(MessageBroker& broker) :
2545 IObserver(broker), 2557 IObserver(broker),
2546 tool_(Tool_Move) 2558 tool_(Tool_Move)
2547 { 2559 {
2548 } 2560 }
2549 2561
2550 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& worldWidget, 2562 virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& worldWidget,
2551 const ViewportGeometry& view, 2563 const ViewportGeometry& view,
2552 MouseButton button, 2564 MouseButton button,
2553 KeyboardModifiers modifiers, 2565 KeyboardModifiers modifiers,
2554 int viewportX, 2566 int viewportX,
2555 int viewportY, 2567 int viewportY,
2556 double x, 2568 double x,
2557 double y, 2569 double y,
2558 IStatusBar* statusBar) 2570 IStatusBar* statusBar)
2559 { 2571 {
2560 BitmapStackWidget& widget = dynamic_cast<BitmapStackWidget&>(worldWidget); 2572 RadiologyWidget& widget = dynamic_cast<RadiologyWidget&>(worldWidget);
2561 2573
2562 if (button == MouseButton_Left) 2574 if (button == MouseButton_Left)
2563 { 2575 {
2564 size_t selected; 2576 size_t selected;
2565 2577
2566 if (tool_ == Tool_Windowing) 2578 if (tool_ == Tool_Windowing)
2567 { 2579 {
2568 return new WindowingTracker(undoRedoStack_, widget.GetStack(), 2580 return new RadiologyWindowingTracker(undoRedoStack_, widget.GetScene(),
2569 viewportX, viewportY, 2581 viewportX, viewportY,
2570 WindowingTracker::Action_DecreaseWidth, 2582 RadiologyWindowingTracker::Action_DecreaseWidth,
2571 WindowingTracker::Action_IncreaseWidth, 2583 RadiologyWindowingTracker::Action_IncreaseWidth,
2572 WindowingTracker::Action_DecreaseCenter, 2584 RadiologyWindowingTracker::Action_DecreaseCenter,
2573 WindowingTracker::Action_IncreaseCenter); 2585 RadiologyWindowingTracker::Action_IncreaseCenter);
2574 } 2586 }
2575 else if (!widget.LookupSelectedBitmap(selected)) 2587 else if (!widget.LookupSelectedLayer(selected))
2576 { 2588 {
2577 // No bitmap is currently selected 2589 // No layer is currently selected
2578 size_t bitmap; 2590 size_t layer;
2579 if (widget.GetStack().LookupBitmap(bitmap, x, y)) 2591 if (widget.GetScene().LookupLayer(layer, x, y))
2580 {
2581 widget.Select(bitmap);
2582 }
2583
2584 return NULL;
2585 }
2586 else if (tool_ == Tool_Crop ||
2587 tool_ == Tool_Resize)
2588 {
2589 BitmapStack::BitmapAccessor accessor(widget.GetStack(), selected);
2590 BitmapStack::Corner corner;
2591 if (accessor.GetBitmap().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize()))
2592 {
2593 switch (tool_)
2594 { 2592 {
2595 case Tool_Crop: 2593 widget.Select(layer);
2596 return new CropBitmapTracker(undoRedoStack_, widget.GetStack(), view, selected, x, y, corner);
2597
2598 case Tool_Resize:
2599 return new ResizeBitmapTracker(undoRedoStack_, widget.GetStack(), selected, x, y, corner,
2600 (modifiers & KeyboardModifiers_Shift));
2601
2602 default:
2603 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
2604 } 2594 }
2595
2596 return NULL;
2597 }
2598 else if (tool_ == Tool_Crop ||
2599 tool_ == Tool_Resize)
2600 {
2601 RadiologyScene::LayerAccessor accessor(widget.GetScene(), selected);
2602 RadiologyScene::Corner corner;
2603 if (accessor.GetLayer().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize()))
2604 {
2605 switch (tool_)
2606 {
2607 case Tool_Crop:
2608 return new RadiologyLayerCropTracker(undoRedoStack_, widget.GetScene(), view, selected, x, y, corner);
2609
2610 case Tool_Resize:
2611 return new RadiologyLayerResizeTracker(undoRedoStack_, widget.GetScene(), selected, x, y, corner,
2612 (modifiers & KeyboardModifiers_Shift));
2613
2614 default:
2615 throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
2616 }
2617 }
2618 else
2619 {
2620 size_t layer;
2621
2622 if (widget.GetScene().LookupLayer(layer, x, y))
2623 {
2624 widget.Select(layer);
2625 }
2626 else
2627 {
2628 widget.Unselect();
2629 }
2630
2631 return NULL;
2632 }
2605 } 2633 }
2606 else 2634 else
2607 { 2635 {
2608 size_t bitmap; 2636 size_t layer;
2609 2637
2610 if (widget.GetStack().LookupBitmap(bitmap, x, y)) 2638 if (widget.GetScene().LookupLayer(layer, x, y))
2611 { 2639 {
2612 widget.Select(bitmap); 2640 if (layer == selected)
2641 {
2642 switch (tool_)
2643 {
2644 case Tool_Move:
2645 return new RadiologyLayerMoveTracker(undoRedoStack_, widget.GetScene(), layer, x, y,
2646 (modifiers & KeyboardModifiers_Shift));
2647
2648 case Tool_Rotate:
2649 return new RadiologyLayerRotateTracker(undoRedoStack_, widget.GetScene(), view, layer, x, y,
2650 (modifiers & KeyboardModifiers_Shift));
2651
2652 default:
2653 break;
2654 }
2655
2656 return NULL;
2657 }
2658 else
2659 {
2660 widget.Select(layer);
2661 return NULL;
2662 }
2613 } 2663 }
2614 else 2664 else
2615 { 2665 {
2616 widget.Unselect(); 2666 widget.Unselect();
2617 }
2618
2619 return NULL;
2620 }
2621 }
2622 else
2623 {
2624 size_t bitmap;
2625
2626 if (widget.GetStack().LookupBitmap(bitmap, x, y))
2627 {
2628 if (bitmap == selected)
2629 {
2630 switch (tool_)
2631 {
2632 case Tool_Move:
2633 return new MoveBitmapTracker(undoRedoStack_, widget.GetStack(), bitmap, x, y,
2634 (modifiers & KeyboardModifiers_Shift));
2635
2636 case Tool_Rotate:
2637 return new RotateBitmapTracker(undoRedoStack_, widget.GetStack(), view, bitmap, x, y,
2638 (modifiers & KeyboardModifiers_Shift));
2639
2640 default:
2641 break;
2642 }
2643
2644 return NULL; 2667 return NULL;
2645 } 2668 }
2646 else 2669 }
2670 }
2671 else
2672 {
2673 return NULL;
2674 }
2675 }
2676
2677 virtual void MouseOver(CairoContext& context,
2678 WorldSceneWidget& worldWidget,
2679 const ViewportGeometry& view,
2680 double x,
2681 double y,
2682 IStatusBar* statusBar)
2683 {
2684 RadiologyWidget& widget = dynamic_cast<RadiologyWidget&>(worldWidget);
2685
2686 #if 0
2687 if (statusBar != NULL)
2688 {
2689 char buf[64];
2690 sprintf(buf, "X = %.02f Y = %.02f (in cm)", x / 10.0, y / 10.0);
2691 statusBar->SetMessage(buf);
2692 }
2693 #endif
2694
2695 size_t selected;
2696
2697 if (widget.LookupSelectedLayer(selected) &&
2698 (tool_ == Tool_Crop ||
2699 tool_ == Tool_Resize))
2700 {
2701 RadiologyScene::LayerAccessor accessor(widget.GetScene(), selected);
2702
2703 RadiologyScene::Corner corner;
2704 if (accessor.GetLayer().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize()))
2705 {
2706 accessor.GetLayer().GetCorner(x, y, corner);
2707
2708 double z = 1.0 / view.GetZoom();
2709
2710 context.SetSourceColor(255, 0, 0);
2711 cairo_t* cr = context.GetObject();
2712 cairo_set_line_width(cr, 2.0 * z);
2713 cairo_move_to(cr, x - GetHandleSize() * z, y - GetHandleSize() * z);
2714 cairo_line_to(cr, x + GetHandleSize() * z, y - GetHandleSize() * z);
2715 cairo_line_to(cr, x + GetHandleSize() * z, y + GetHandleSize() * z);
2716 cairo_line_to(cr, x - GetHandleSize() * z, y + GetHandleSize() * z);
2717 cairo_line_to(cr, x - GetHandleSize() * z, y - GetHandleSize() * z);
2718 cairo_stroke(cr);
2719 }
2720 }
2721 }
2722
2723 virtual void MouseWheel(WorldSceneWidget& widget,
2724 MouseWheelDirection direction,
2725 KeyboardModifiers modifiers,
2726 IStatusBar* statusBar)
2727 {
2728 }
2729
2730 virtual void KeyPressed(WorldSceneWidget& worldWidget,
2731 KeyboardKeys key,
2732 char keyChar,
2733 KeyboardModifiers modifiers,
2734 IStatusBar* statusBar)
2735 {
2736 RadiologyWidget& widget = dynamic_cast<RadiologyWidget&>(worldWidget);
2737
2738 switch (keyChar)
2739 {
2740 case 'a':
2741 widget.FitContent();
2742 break;
2743
2744 case 'c':
2745 tool_ = Tool_Crop;
2746 break;
2747
2748 case 'e':
2749 {
2750 Orthanc::DicomMap tags;
2751
2752 // Minimal set of tags to generate a valid CR image
2753 tags.SetValue(Orthanc::DICOM_TAG_ACCESSION_NUMBER, "NOPE", false);
2754 tags.SetValue(Orthanc::DICOM_TAG_BODY_PART_EXAMINED, "PELVIS", false);
2755 tags.SetValue(Orthanc::DICOM_TAG_INSTANCE_NUMBER, "1", false);
2756 //tags.SetValue(Orthanc::DICOM_TAG_LATERALITY, "", false);
2757 tags.SetValue(Orthanc::DICOM_TAG_MANUFACTURER, "OSIMIS", false);
2758 tags.SetValue(Orthanc::DICOM_TAG_MODALITY, "CR", false);
2759 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_BIRTH_DATE, "20000101", false);
2760 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ID, "hello", false);
2761 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_NAME, "HELLO^WORLD", false);
2762 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ORIENTATION, "", false);
2763 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_SEX, "M", false);
2764 tags.SetValue(Orthanc::DICOM_TAG_REFERRING_PHYSICIAN_NAME, "HOUSE^MD", false);
2765 tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false);
2766 tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false);
2767 tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false);
2768 tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false);
2769
2770 widget.GetScene().Export(tags, 0.1, 0.1, widget.IsInverted(),
2771 widget.GetInterpolation(), EXPORT_USING_PAM);
2772 break;
2773 }
2774
2775 case 'i':
2776 widget.SwitchInvert();
2777 break;
2778
2779 case 'm':
2780 tool_ = Tool_Move;
2781 break;
2782
2783 case 'n':
2784 {
2785 switch (widget.GetInterpolation())
2647 { 2786 {
2648 widget.Select(bitmap); 2787 case ImageInterpolation_Nearest:
2649 return NULL; 2788 LOG(INFO) << "Switching to bilinear interpolation";
2789 widget.SetInterpolation(ImageInterpolation_Bilinear);
2790 break;
2791
2792 case ImageInterpolation_Bilinear:
2793 LOG(INFO) << "Switching to nearest neighbor interpolation";
2794 widget.SetInterpolation(ImageInterpolation_Nearest);
2795 break;
2796
2797 default:
2798 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
2650 } 2799 }
2651 }
2652 else
2653 {
2654 widget.Unselect();
2655 return NULL;
2656 }
2657 }
2658 }
2659 else
2660 {
2661 return NULL;
2662 }
2663 }
2664
2665 virtual void MouseOver(CairoContext& context,
2666 WorldSceneWidget& worldWidget,
2667 const ViewportGeometry& view,
2668 double x,
2669 double y,
2670 IStatusBar* statusBar)
2671 {
2672 BitmapStackWidget& widget = dynamic_cast<BitmapStackWidget&>(worldWidget);
2673
2674 #if 0
2675 if (statusBar != NULL)
2676 {
2677 char buf[64];
2678 sprintf(buf, "X = %.02f Y = %.02f (in cm)", x / 10.0, y / 10.0);
2679 statusBar->SetMessage(buf);
2680 }
2681 #endif
2682
2683 size_t selected;
2684
2685 if (widget.LookupSelectedBitmap(selected) &&
2686 (tool_ == Tool_Crop ||
2687 tool_ == Tool_Resize))
2688 {
2689 BitmapStack::BitmapAccessor accessor(widget.GetStack(), selected);
2690
2691 BitmapStack::Corner corner;
2692 if (accessor.GetBitmap().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize()))
2693 {
2694 accessor.GetBitmap().GetCorner(x, y, corner);
2695 2800
2696 double z = 1.0 / view.GetZoom(); 2801 break;
2697 2802 }
2698 context.SetSourceColor(255, 0, 0); 2803
2699 cairo_t* cr = context.GetObject(); 2804 case 'r':
2700 cairo_set_line_width(cr, 2.0 * z); 2805 tool_ = Tool_Rotate;
2701 cairo_move_to(cr, x - GetHandleSize() * z, y - GetHandleSize() * z); 2806 break;
2702 cairo_line_to(cr, x + GetHandleSize() * z, y - GetHandleSize() * z); 2807
2703 cairo_line_to(cr, x + GetHandleSize() * z, y + GetHandleSize() * z); 2808 case 's':
2704 cairo_line_to(cr, x - GetHandleSize() * z, y + GetHandleSize() * z); 2809 tool_ = Tool_Resize;
2705 cairo_line_to(cr, x - GetHandleSize() * z, y - GetHandleSize() * z); 2810 break;
2706 cairo_stroke(cr); 2811
2707 } 2812 case 'w':
2708 } 2813 tool_ = Tool_Windowing;
2709 } 2814 break;
2710 2815
2711 virtual void MouseWheel(WorldSceneWidget& widget, 2816 case 'y':
2712 MouseWheelDirection direction, 2817 if (modifiers & KeyboardModifiers_Control)
2713 KeyboardModifiers modifiers, 2818 {
2714 IStatusBar* statusBar) 2819 undoRedoStack_.Redo();
2715 { 2820 widget.NotifyContentChanged();
2716 } 2821 }
2717 2822 break;
2718 virtual void KeyPressed(WorldSceneWidget& worldWidget, 2823
2719 KeyboardKeys key, 2824 case 'z':
2720 char keyChar, 2825 if (modifiers & KeyboardModifiers_Control)
2721 KeyboardModifiers modifiers, 2826 {
2722 IStatusBar* statusBar) 2827 undoRedoStack_.Undo();
2723 { 2828 widget.NotifyContentChanged();
2724 BitmapStackWidget& widget = dynamic_cast<BitmapStackWidget&>(worldWidget); 2829 }
2725 2830 break;
2726 switch (keyChar) 2831
2727 { 2832 default:
2728 case 'a': 2833 break;
2729 widget.FitContent(); 2834 }
2730 break; 2835 }
2731 2836 };
2732 case 'c':
2733 tool_ = Tool_Crop;
2734 break;
2735
2736 case 'e':
2737 {
2738 Orthanc::DicomMap tags;
2739
2740 // Minimal set of tags to generate a valid CR image
2741 tags.SetValue(Orthanc::DICOM_TAG_ACCESSION_NUMBER, "NOPE", false);
2742 tags.SetValue(Orthanc::DICOM_TAG_BODY_PART_EXAMINED, "PELVIS", false);
2743 tags.SetValue(Orthanc::DICOM_TAG_INSTANCE_NUMBER, "1", false);
2744 //tags.SetValue(Orthanc::DICOM_TAG_LATERALITY, "", false);
2745 tags.SetValue(Orthanc::DICOM_TAG_MANUFACTURER, "OSIMIS", false);
2746 tags.SetValue(Orthanc::DICOM_TAG_MODALITY, "CR", false);
2747 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_BIRTH_DATE, "20000101", false);
2748 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ID, "hello", false);
2749 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_NAME, "HELLO^WORLD", false);
2750 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ORIENTATION, "", false);
2751 tags.SetValue(Orthanc::DICOM_TAG_PATIENT_SEX, "M", false);
2752 tags.SetValue(Orthanc::DICOM_TAG_REFERRING_PHYSICIAN_NAME, "HOUSE^MD", false);
2753 tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false);
2754 tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false);
2755 tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false);
2756 tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false);
2757
2758 widget.GetStack().Export(tags, 0.1, 0.1, widget.IsInverted(), widget.GetInterpolation());
2759 break;
2760 }
2761
2762 case 'i':
2763 widget.SwitchInvert();
2764 break;
2765
2766 case 'm':
2767 tool_ = Tool_Move;
2768 break;
2769
2770 case 'n':
2771 {
2772 switch (widget.GetInterpolation())
2773 {
2774 case ImageInterpolation_Nearest:
2775 LOG(INFO) << "Switching to bilinear interpolation";
2776 widget.SetInterpolation(ImageInterpolation_Bilinear);
2777 break;
2778
2779 case ImageInterpolation_Bilinear:
2780 LOG(INFO) << "Switching to nearest neighbor interpolation";
2781 widget.SetInterpolation(ImageInterpolation_Nearest);
2782 break;
2783
2784 default:
2785 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
2786 }
2787
2788 break;
2789 }
2790
2791 case 'r':
2792 tool_ = Tool_Rotate;
2793 break;
2794
2795 case 's':
2796 tool_ = Tool_Resize;
2797 break;
2798
2799 case 'w':
2800 tool_ = Tool_Windowing;
2801 break;
2802
2803 case 'y':
2804 if (modifiers & KeyboardModifiers_Control)
2805 {
2806 undoRedoStack_.Redo();
2807 widget.NotifyContentChanged();
2808 }
2809 break;
2810
2811 case 'z':
2812 if (modifiers & KeyboardModifiers_Control)
2813 {
2814 undoRedoStack_.Undo();
2815 widget.NotifyContentChanged();
2816 }
2817 break;
2818
2819 default:
2820 break;
2821 }
2822 }
2823 };
2824 2837
2825 2838
2826 2839
2827 namespace Samples
2828 {
2829 class SingleFrameEditorApplication : 2840 class SingleFrameEditorApplication :
2830 public SampleSingleCanvasApplicationBase, 2841 public SampleSingleCanvasApplicationBase,
2831 public IObserver 2842 public IObserver
2832 { 2843 {
2833 private: 2844 private:
2834 std::auto_ptr<OrthancApiClient> orthancApiClient_; 2845 std::auto_ptr<OrthancApiClient> orthancApiClient_;
2835 std::auto_ptr<BitmapStack> stack_; 2846 std::auto_ptr<RadiologyScene> scene_;
2836 BitmapStackInteractor interactor_; 2847 RadiologyEditorInteractor interactor_;
2837 2848
2838 public: 2849 public:
2839 SingleFrameEditorApplication(MessageBroker& broker) : 2850 SingleFrameEditorApplication(MessageBroker& broker) :
2840 IObserver(broker), 2851 IObserver(broker),
2841 interactor_(broker) 2852 interactor_(broker)
2874 statusBar.SetMessage("Use the key \"f\" to switch full screen"); 2885 statusBar.SetMessage("Use the key \"f\" to switch full screen");
2875 statusBar.SetMessage("Use the key \"i\" to invert contrast"); 2886 statusBar.SetMessage("Use the key \"i\" to invert contrast");
2876 statusBar.SetMessage("Use the key \"m\" to move objects"); 2887 statusBar.SetMessage("Use the key \"m\" to move objects");
2877 statusBar.SetMessage("Use the key \"n\" to switch between nearest neighbor and bilinear interpolation"); 2888 statusBar.SetMessage("Use the key \"n\" to switch between nearest neighbor and bilinear interpolation");
2878 statusBar.SetMessage("Use the key \"r\" to rotate objects"); 2889 statusBar.SetMessage("Use the key \"r\" to rotate objects");
2879 statusBar.SetMessage("Use the key \"s\" to resize objects (not applicable to DICOM bitmaps)"); 2890 statusBar.SetMessage("Use the key \"s\" to resize objects (not applicable to DICOM layers)");
2880 statusBar.SetMessage("Use the key \"w\" to change windowing"); 2891 statusBar.SetMessage("Use the key \"w\" to change windowing");
2881 2892
2882 statusBar.SetMessage("Use the key \"ctrl-z\" to undo action"); 2893 statusBar.SetMessage("Use the key \"ctrl-z\" to undo action");
2883 statusBar.SetMessage("Use the key \"ctrl-y\" to redo action"); 2894 statusBar.SetMessage("Use the key \"ctrl-y\" to redo action");
2884 2895
2894 orthancApiClient_.reset(new OrthancApiClient(GetBroker(), context_->GetWebService())); 2905 orthancApiClient_.reset(new OrthancApiClient(GetBroker(), context_->GetWebService()));
2895 2906
2896 Orthanc::FontRegistry fonts; 2907 Orthanc::FontRegistry fonts;
2897 fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); 2908 fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16);
2898 2909
2899 stack_.reset(new BitmapStack(GetBroker(), *orthancApiClient_)); 2910 scene_.reset(new RadiologyScene(GetBroker(), *orthancApiClient_));
2900 stack_->LoadFrame(instance, frame, false); //.SetPan(200, 0); 2911 scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0);
2901 //stack_->LoadFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false); 2912 //scene_->LoadDicomFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false);
2902 2913
2903 { 2914 {
2904 BitmapStack::Bitmap& bitmap = stack_->LoadText(fonts.GetFont(0), "Hello\nworld"); 2915 RadiologyScene::Layer& layer = scene_->LoadText(fonts.GetFont(0), "Hello\nworld");
2905 //dynamic_cast<BitmapStack::AlphaBitmap&>(bitmap).SetForegroundValue(256); 2916 //dynamic_cast<RadiologyScene::Layer&>(layer).SetForegroundValue(256);
2906 dynamic_cast<BitmapStack::AlphaBitmap&>(bitmap).SetResizeable(true); 2917 dynamic_cast<RadiologyScene::Layer&>(layer).SetResizeable(true);
2907 } 2918 }
2908 2919
2909 { 2920 {
2910 BitmapStack::Bitmap& bitmap = stack_->LoadTestBlock(100, 50); 2921 RadiologyScene::Layer& layer = scene_->LoadTestBlock(100, 50);
2911 //dynamic_cast<BitmapStack::AlphaBitmap&>(bitmap).SetForegroundValue(256); 2922 //dynamic_cast<RadiologyScene::Layer&>(layer).SetForegroundValue(256);
2912 dynamic_cast<BitmapStack::AlphaBitmap&>(bitmap).SetResizeable(true); 2923 dynamic_cast<RadiologyScene::Layer&>(layer).SetResizeable(true);
2913 dynamic_cast<BitmapStack::AlphaBitmap&>(bitmap).SetPan(0, 200); 2924 dynamic_cast<RadiologyScene::Layer&>(layer).SetPan(0, 200);
2914 } 2925 }
2915 2926
2916 2927
2917 mainWidget_ = new BitmapStackWidget(GetBroker(), *stack_, "main-widget"); 2928 mainWidget_ = new RadiologyWidget(GetBroker(), *scene_, "main-widget");
2918 mainWidget_->SetTransmitMouseOver(true); 2929 mainWidget_->SetTransmitMouseOver(true);
2919 mainWidget_->SetInteractor(interactor_); 2930 mainWidget_->SetInteractor(interactor_);
2920 2931
2921 //stack_->SetWindowing(128, 256); 2932 //scene_->SetWindowing(128, 256);
2922 } 2933 }
2923 }; 2934 };
2924 } 2935 }
2925 } 2936 }