# HG changeset patch # User Sebastien Jodogne # Date 1596808559 -7200 # Node ID 82279abb92d01f8779bc50e9cb17539817f245fe # Parent c7a37c3a0b8e44510a4300c2c70326b8674fe39d GrayscaleWindowingSceneTracker diff -r c7a37c3a0b8e -r 82279abb92d0 OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake --- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Fri Aug 07 15:00:52 2020 +0200 +++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Fri Aug 07 15:55:59 2020 +0200 @@ -338,12 +338,14 @@ ${ORTHANC_STONE_ROOT}/Sources/Scene2D/FloatTextureSceneLayer.h ${ORTHANC_STONE_ROOT}/Sources/Scene2D/GrayscaleStyleConfigurator.cpp ${ORTHANC_STONE_ROOT}/Sources/Scene2D/GrayscaleStyleConfigurator.h + ${ORTHANC_STONE_ROOT}/Sources/Scene2D/GrayscaleWindowingSceneTracker.cpp + ${ORTHANC_STONE_ROOT}/Sources/Scene2D/GrayscaleWindowingSceneTracker.h ${ORTHANC_STONE_ROOT}/Sources/Scene2D/ICompositor.h ${ORTHANC_STONE_ROOT}/Sources/Scene2D/ILayerStyleConfigurator.h + ${ORTHANC_STONE_ROOT}/Sources/Scene2D/IPointerTracker.h + ${ORTHANC_STONE_ROOT}/Sources/Scene2D/ISceneLayer.h ${ORTHANC_STONE_ROOT}/Sources/Scene2D/InfoPanelSceneLayer.cpp ${ORTHANC_STONE_ROOT}/Sources/Scene2D/InfoPanelSceneLayer.h - ${ORTHANC_STONE_ROOT}/Sources/Scene2D/IPointerTracker.h - ${ORTHANC_STONE_ROOT}/Sources/Scene2D/ISceneLayer.h ${ORTHANC_STONE_ROOT}/Sources/Scene2D/LookupTableStyleConfigurator.cpp ${ORTHANC_STONE_ROOT}/Sources/Scene2D/LookupTableStyleConfigurator.h ${ORTHANC_STONE_ROOT}/Sources/Scene2D/LookupTableTextureSceneLayer.cpp diff -r c7a37c3a0b8e -r 82279abb92d0 OrthancStone/Sources/Scene2D/FloatTextureSceneLayer.cpp --- a/OrthancStone/Sources/Scene2D/FloatTextureSceneLayer.cpp Fri Aug 07 15:00:52 2020 +0200 +++ b/OrthancStone/Sources/Scene2D/FloatTextureSceneLayer.cpp Fri Aug 07 15:55:59 2020 +0200 @@ -31,7 +31,10 @@ { FloatTextureSceneLayer::FloatTextureSceneLayer(const Orthanc::ImageAccessor& texture) : inverted_(false), - applyLog_(false) + applyLog_(false), + isRangeComputed_(false), + minValue_(0), + maxValue_(0) { { std::unique_ptr t( @@ -107,11 +110,11 @@ void FloatTextureSceneLayer::FitRange() { float minValue, maxValue; - Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, GetTexture()); + GetRange(minValue, maxValue); + assert(minValue <= maxValue); float width; - assert(minValue <= maxValue); if (LinearAlgebra::IsCloseToZero(maxValue - minValue)) { width = 1; @@ -125,6 +128,22 @@ } + void FloatTextureSceneLayer::GetRange(float& minValue, + float& maxValue) + { + if (!isRangeComputed_) + { + Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue_, maxValue_, GetTexture()); + isRangeComputed_ = true; + } + + assert(minValue_ <= maxValue_); + + minValue = minValue_; + maxValue = maxValue_; + } + + ISceneLayer* FloatTextureSceneLayer::Clone() const { std::unique_ptr cloned @@ -136,6 +155,9 @@ cloned->customWidth_ = customWidth_; cloned->inverted_ = inverted_; cloned->applyLog_ = applyLog_; + cloned->isRangeComputed_ = isRangeComputed_; + cloned->minValue_ = minValue_; + cloned->maxValue_ = maxValue_; return cloned.release(); } diff -r c7a37c3a0b8e -r 82279abb92d0 OrthancStone/Sources/Scene2D/FloatTextureSceneLayer.h --- a/OrthancStone/Sources/Scene2D/FloatTextureSceneLayer.h Fri Aug 07 15:00:52 2020 +0200 +++ b/OrthancStone/Sources/Scene2D/FloatTextureSceneLayer.h Fri Aug 07 15:55:59 2020 +0200 @@ -33,6 +33,9 @@ float customWidth_; bool inverted_; bool applyLog_; + bool isRangeComputed_; + float minValue_; + float maxValue_; public: // The pixel format must be convertible to "Float32" @@ -68,6 +71,9 @@ return applyLog_; } + void GetRange(float& minValue, + float& maxValue); + virtual ISceneLayer* Clone() const; virtual Type GetType() const diff -r c7a37c3a0b8e -r 82279abb92d0 OrthancStone/Sources/Scene2D/GrayscaleWindowingSceneTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Scene2D/GrayscaleWindowingSceneTracker.cpp Fri Aug 07 15:55:59 2020 +0200 @@ -0,0 +1,161 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "GrayscaleWindowingSceneTracker.h" + +#include "../Scene2DViewport/ViewportController.h" +#include "FloatTextureSceneLayer.h" + +#include + +namespace OrthancStone +{ + namespace + { + class GrayscaleLayerAccessor : public boost::noncopyable + { + private: + std::unique_ptr lock_; + FloatTextureSceneLayer* layer_; + + public: + GrayscaleLayerAccessor(boost::shared_ptr viewport, + int layerIndex) : + layer_(NULL) + { + if (viewport != NULL) + { + lock_.reset(viewport->Lock()); + + if (lock_->GetController().GetScene().HasLayer(layerIndex)) + { + ISceneLayer& layer = lock_->GetController().GetScene().GetLayer(layerIndex); + if (layer.GetType() == ISceneLayer::Type_FloatTexture) + { + layer_ = &dynamic_cast(layer); + } + } + } + } + + bool IsValid() const + { + return layer_ != NULL; + } + + FloatTextureSceneLayer& GetLayer() const + { + if (layer_ == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return *layer_; + } + } + + void Invalidate() + { + if (lock_.get() != NULL) + { + lock_->Invalidate(); + } + } + }; + } + + void GrayscaleWindowingSceneTracker::SetWindowing(float center, + float width) + { + if (active_) + { + GrayscaleLayerAccessor accessor(viewport_, layerIndex_); + + if (accessor.IsValid()) + { + accessor.GetLayer().SetCustomWindowing(center, width); + accessor.Invalidate(); + } + } + } + + + GrayscaleWindowingSceneTracker::GrayscaleWindowingSceneTracker(boost::shared_ptr viewport, + int layerIndex, + const PointerEvent& event, + unsigned int canvasWidth, + unsigned int canvasHeight) : + OneGesturePointerTracker(viewport), + layerIndex_(layerIndex), + clickX_(event.GetMainPosition().GetX()), + clickY_(event.GetMainPosition().GetY()) + { + active_ = false; + + if (canvasWidth > 3 && + canvasHeight > 3) + { + GrayscaleLayerAccessor accessor(viewport_, layerIndex_); + + if (accessor.IsValid()) + { + FloatTextureSceneLayer& layer = accessor.GetLayer(); + + layer.GetWindowing(originalCenter_, originalWidth_); + + float minValue, maxValue; + layer.GetRange(minValue, maxValue); + + normalization_ = (maxValue - minValue) / static_cast(std::min(canvasWidth, canvasHeight) - 1); + active_ = true; + } + else + { + LOG(INFO) << "Cannot create GrayscaleWindowingSceneTracker on a non-float texture"; + } + } + } + + void GrayscaleWindowingSceneTracker::PointerMove(const PointerEvent& event) + { + if (active_) + { + const double x = event.GetMainPosition().GetX(); + const double y = event.GetMainPosition().GetY(); + + float center = originalCenter_ + (x - clickX_) * normalization_; + float width = originalWidth_ + (y - clickY_) * normalization_; + + if (width <= 1) + { + width = 1; + } + + SetWindowing(center, width); + } + } + + void GrayscaleWindowingSceneTracker::Cancel() + { + SetWindowing(originalCenter_, originalWidth_); + } +} diff -r c7a37c3a0b8e -r 82279abb92d0 OrthancStone/Sources/Scene2D/GrayscaleWindowingSceneTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrthancStone/Sources/Scene2D/GrayscaleWindowingSceneTracker.h Fri Aug 07 15:55:59 2020 +0200 @@ -0,0 +1,58 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + + +#include "../Scene2DViewport/OneGesturePointerTracker.h" +#include "../Viewport/IViewport.h" +#include "Internals/FixedPointAligner.h" + +#include + +namespace OrthancStone +{ + class GrayscaleWindowingSceneTracker : public OneGesturePointerTracker + { + private: + bool active_; + int layerIndex_; + double normalization_; + float originalCenter_; + float originalWidth_; + double clickX_; + double clickY_; + + void SetWindowing(float center, + float width); + + public: + GrayscaleWindowingSceneTracker(boost::shared_ptr viewport, + int layerIndex, + const PointerEvent& event, + unsigned int canvasWidth, + unsigned int canvasHeight); + + virtual void PointerMove(const PointerEvent& event) ORTHANC_OVERRIDE; + + virtual void Cancel() ORTHANC_OVERRIDE; + }; +} diff -r c7a37c3a0b8e -r 82279abb92d0 OrthancStone/Sources/Scene2DViewport/ViewportController.cpp --- a/OrthancStone/Sources/Scene2DViewport/ViewportController.cpp Fri Aug 07 15:00:52 2020 +0200 +++ b/OrthancStone/Sources/Scene2DViewport/ViewportController.cpp Fri Aug 07 15:55:59 2020 +0200 @@ -23,10 +23,11 @@ #include "UndoStack.h" #include "MeasureCommands.h" -#include "../StoneException.h" +#include "../Scene2D/GrayscaleWindowingSceneTracker.h" #include "../Scene2D/PanSceneTracker.h" #include "../Scene2D/RotateSceneTracker.h" #include "../Scene2D/ZoomSceneTracker.h" +#include "../StoneException.h" #include @@ -41,22 +42,16 @@ switch (event.GetMouseButton()) { case MouseButton_Left: - return new RotateSceneTracker(viewport, event); + //return new RotateSceneTracker(viewport, event); + + // Assumes that the layer whose windowing is controlled, is the one with index "0" + return new GrayscaleWindowingSceneTracker(viewport, 0, event, viewportWidth, viewportHeight); case MouseButton_Middle: return new PanSceneTracker(viewport, event); case MouseButton_Right: - { - if (viewportWidth != 0) - { - return new ZoomSceneTracker(viewport, event, viewportWidth); - } - else - { - return NULL; - } - } + return new ZoomSceneTracker(viewport, event, viewportHeight); default: return NULL;