# HG changeset patch # User Sebastien Jodogne # Date 1540810295 -3600 # Node ID d95e65ebe0b9b749242761cc387467eef307c984 # Parent da25d2423314f7906fdd1df44c1fcaa2ace3a3ba ResizeBitmapTracker diff -r da25d2423314 -r d95e65ebe0b9 Applications/Samples/SingleFrameEditorApplication.h --- a/Applications/Samples/SingleFrameEditorApplication.h Fri Oct 26 17:04:24 2018 +0200 +++ b/Applications/Samples/SingleFrameEditorApplication.h Mon Oct 29 11:51:35 2018 +0100 @@ -62,7 +62,7 @@ class Bitmap : public boost::noncopyable { private: - bool visible_; + size_t index_; bool hasSize_; unsigned int width_; unsigned int height_; @@ -78,6 +78,7 @@ double panX_; double panY_; double angle_; + bool resizeable_; protected: @@ -112,6 +113,12 @@ } + const Matrix& GetTransform() const + { + return transform_; + } + + private: static void ApplyTransform(double& x /* inout */, double& y /* inout */, @@ -204,8 +211,8 @@ public: - Bitmap() : - visible_(true), + Bitmap(size_t index) : + index_(index), hasSize_(false), width_(0), height_(0), @@ -214,7 +221,8 @@ pixelSpacingY_(1), panX_(0), panY_(0), - angle_(0) + angle_(0), + resizeable_(false) { UpdateTransform(); } @@ -223,6 +231,11 @@ { } + size_t GetIndex() const + { + return index_; + } + void ResetCrop() { hasCrop_ = false; @@ -285,17 +298,6 @@ return angle_; } - bool IsVisible() const - { - return visible_; - } - - void SetVisible(bool visible) - { - visible_ = visible; - } - - void SetSize(unsigned int width, unsigned int height) { @@ -468,12 +470,6 @@ return false; } - const Matrix& GetTransform() const - { - return transform_; - } - - void GetCenter(double& centerX, double& centerY) const { @@ -586,6 +582,16 @@ return false; } + + bool IsResizeable() const + { + return resizeable_; + } + + void SetResizeable(bool resizeable) + { + resizeable_ = resizeable; + } }; @@ -681,6 +687,11 @@ std::auto_ptr converter_; std::auto_ptr converted_; // Float32 or RGB24 + static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) + { + return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); + } + void ApplyConverter() { @@ -692,9 +703,9 @@ } public: - static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) + DicomBitmap(size_t index) : + Bitmap(index) { - return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); } void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) @@ -808,7 +819,9 @@ float foreground_; public: - AlphaBitmap(const BitmapStack& stack) : + AlphaBitmap(size_t index, + const BitmapStack& stack) : + Bitmap(index), stack_(stack), useWindowing_(true), foreground_(0) @@ -995,27 +1008,27 @@ } - size_t LoadText(const Orthanc::Font& font, - const std::string& utf8, - float foreground) + Bitmap& LoadText(const Orthanc::Font& font, + const std::string& utf8) { - std::auto_ptr alpha(new AlphaBitmap(*this)); - alpha->LoadText(font, utf8); - //alpha->SetForegroundValue(foreground); - size_t bitmap = countBitmaps_++; + std::auto_ptr alpha(new AlphaBitmap(bitmap, *this)); + alpha->LoadText(font, utf8); + + AlphaBitmap* ptr = alpha.get(); bitmaps_[bitmap] = alpha.release(); - return bitmap; + return *ptr; } - size_t LoadTestBlock(unsigned int width, - unsigned int height, - float foreground) + Bitmap& LoadTestBlock(unsigned int width, + unsigned int height) { - std::auto_ptr alpha(new AlphaBitmap(*this)); + size_t bitmap = countBitmaps_++; + + std::auto_ptr alpha(new AlphaBitmap(bitmap, *this)); std::auto_ptr block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); @@ -1039,13 +1052,11 @@ } alpha->SetAlpha(block.release()); - //alpha->SetForegroundValue(foreground); - size_t bitmap = countBitmaps_++; - + AlphaBitmap* ptr = alpha.get(); bitmaps_[bitmap] = alpha.release(); - return bitmap; + return *ptr; } @@ -1055,7 +1066,7 @@ { size_t bitmap = countBitmaps_++; - bitmaps_[bitmap] = new DicomBitmap; + bitmaps_[bitmap] = new DicomBitmap(bitmap); { @@ -1226,7 +1237,8 @@ { Tool_Move, Tool_Rotate, - Tool_Crop + Tool_Crop, + Tool_Resize }; static double GetHandleSize() @@ -1409,7 +1421,7 @@ }; - class CornerBitmapTracker : public IWorldSceneMouseTracker + class CropBitmapTracker : public IWorldSceneMouseTracker { private: BitmapStack::BitmapAccessor accessor_; @@ -1420,7 +1432,7 @@ unsigned int cropHeight_; public: - CornerBitmapTracker(BitmapStack& stack, + CropBitmapTracker(BitmapStack& stack, const ViewportGeometry& view, size_t bitmap, double x, @@ -1455,10 +1467,10 @@ double sceneX, double sceneY) { - unsigned int x, y; - if (accessor_.IsValid()) { + unsigned int x, y; + BitmapStack::Bitmap& bitmap = accessor_.GetBitmap(); if (bitmap.GetPixel(x, y, sceneX, sceneY)) { @@ -1497,11 +1509,134 @@ }; + class ResizeBitmapTracker : public IWorldSceneMouseTracker + { + private: + BitmapStack::BitmapAccessor accessor_; + bool roundScaling_; + double originalSpacingX_; + double originalSpacingY_; + double originalPanX_; + double originalPanY_; + BitmapStack::Corner oppositeCorner_; + double oppositeX_; + double oppositeY_; + double baseScaling_; + + static double ComputeDistance(double x1, + double y1, + double x2, + double y2) + { + double dx = x1 - x2; + double dy = y1 - y2; + return sqrt(dx * dx + dy * dy); + } + + public: + ResizeBitmapTracker(BitmapStack& stack, + size_t bitmap, + double x, + double y, + BitmapStack::Corner corner, + bool roundScaling) : + accessor_(stack, bitmap), + roundScaling_(roundScaling) + { + if (accessor_.IsValid() && + accessor_.GetBitmap().IsResizeable()) + { + originalSpacingX_ = accessor_.GetBitmap().GetPixelSpacingX(); + originalSpacingY_ = accessor_.GetBitmap().GetPixelSpacingY(); + originalPanX_ = accessor_.GetBitmap().GetPanX(); + originalPanY_ = accessor_.GetBitmap().GetPanY(); + + switch (corner) + { + case BitmapStack::Corner_TopLeft: + oppositeCorner_ = BitmapStack::Corner_BottomRight; + break; + + case BitmapStack::Corner_TopRight: + oppositeCorner_ = BitmapStack::Corner_BottomLeft; + break; + + case BitmapStack::Corner_BottomLeft: + oppositeCorner_ = BitmapStack::Corner_TopRight; + break; + + case BitmapStack::Corner_BottomRight: + oppositeCorner_ = BitmapStack::Corner_TopLeft; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + accessor_.GetBitmap().GetCorner(oppositeX_, oppositeY_, oppositeCorner_); + + double d = ComputeDistance(x, y, oppositeX_, oppositeY_); + if (d >= std::numeric_limits::epsilon()) + { + baseScaling_ = 1.0 / d; + } + else + { + // Avoid division by zero in extreme cases + accessor_.Invalidate(); + } + } + } + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + virtual void MouseUp() + { + } + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY) + { + static const double ROUND_SCALING = 0.1; + + if (accessor_.IsValid() && + accessor_.GetBitmap().IsResizeable()) + { + double scaling = ComputeDistance(oppositeX_, oppositeY_, sceneX, sceneY) * baseScaling_; + + if (roundScaling_) + { + scaling = round(scaling / ROUND_SCALING) * ROUND_SCALING; + } + + BitmapStack::Bitmap& bitmap = accessor_.GetBitmap(); + bitmap.SetPixelSpacing(scaling * originalSpacingX_, scaling * originalSpacingY_); + + // Keep the opposite corner at a fixed location + double ox, oy; + bitmap.GetCorner(ox, oy, oppositeCorner_); + bitmap.SetPan((-ox + oppositeX_) + bitmap.GetPanX(), + (-oy + oppositeY_) + bitmap.GetPanY()); + } + } + }; + + public: BitmapStackInteractor(BitmapStack& stack) : stack_(stack), - //tool_(Tool_Move) - tool_(Tool_Crop) + tool_(Tool_Move) { } @@ -1528,13 +1663,25 @@ return NULL; } - else if (tool_ == Tool_Crop) + else if (tool_ == Tool_Crop || + tool_ == Tool_Resize) { BitmapStack::BitmapAccessor accessor(stack_, selected); BitmapStack::Corner corner; if (accessor.GetBitmap().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize())) { - return new CornerBitmapTracker(stack_, view, selected, x, y, corner); + switch (tool_) + { + case Tool_Crop: + return new CropBitmapTracker(stack_, view, selected, x, y, corner); + + case Tool_Resize: + return new ResizeBitmapTracker(stack_, selected, x, y, corner, + (modifiers & KeyboardModifiers_Shift)); + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } } else { @@ -1595,7 +1742,8 @@ { size_t selected; if (stack_.GetSelectedBitmap(selected) && - tool_ == Tool_Crop) + (tool_ == Tool_Crop || + tool_ == Tool_Resize)) { BitmapStack::BitmapAccessor accessor(stack_, selected); @@ -1638,7 +1786,7 @@ tool_ = Tool_Crop; break; - case 's': + case 'a': widget.FitContent(); break; @@ -1650,6 +1798,10 @@ tool_ = Tool_Rotate; break; + case 's': + tool_ = Tool_Resize; + break; + default: break; } @@ -1960,8 +2112,8 @@ stack_.reset(new BitmapStack(IObserver::broker_, *orthancApiClient_)); stack_->LoadFrame(instance, frame, false); stack_->LoadFrame("61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", frame, false); - stack_->LoadText(fonts.GetFont(0), "Hello\nworld\nBonjour, Alain", 256); - stack_->LoadTestBlock(100, 50, 256); + stack_->LoadText(fonts.GetFont(0), "Hello\nworld\nBonjour, Alain").SetResizeable(true); + stack_->LoadTestBlock(100, 50).SetResizeable(true); mainWidget_ = new BitmapStackWidget(IObserver::broker_, *stack_, "main-widget"); mainWidget_->SetTransmitMouseOver(true);