# HG changeset patch # User Sebastien Jodogne # Date 1493297440 -7200 # Node ID 37e504582af6a9d7dbf1e7b7a372c24c2e47f199 # Parent 369fe11606fa0c8ad5f2d481765649a1e897b1f1# Parent b340879da9bd2d8444c9373aa447d8d92fcb6c29 integration mainline->wasm diff -r 369fe11606fa -r 37e504582af6 Applications/BasicApplicationContext.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/BasicApplicationContext.cpp Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,135 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "BasicApplicationContext.h" + +#include "../../Framework/Toolbox/OrthancSeriesLoader.h" +#include "../../Framework/Volumes/VolumeImageSimplePolicy.h" +#include "../../Framework/Volumes/VolumeImageProgressivePolicy.h" + +namespace OrthancStone +{ + BasicApplicationContext::BasicApplicationContext(OrthancPlugins::IOrthancConnection& orthanc) : + orthanc_(orthanc) + { + } + + + BasicApplicationContext::~BasicApplicationContext() + { + for (Interactors::iterator it = interactors_.begin(); it != interactors_.end(); ++it) + { + assert(*it != NULL); + delete *it; + } + + for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it) + { + assert(*it != NULL); + delete *it; + } + + for (StructureSets::iterator it = structureSets_.begin(); it != structureSets_.end(); ++it) + { + assert(*it != NULL); + delete *it; + } + } + + + IWidget& BasicApplicationContext::SetCentralWidget(IWidget* widget) // Takes ownership + { + viewport_.SetCentralWidget(widget); + return *widget; + } + + + VolumeImage& BasicApplicationContext::AddSeriesVolume(const std::string& series, + bool isProgressiveDownload, + size_t downloadThreadCount) + { + std::auto_ptr volume(new VolumeImage(new OrthancSeriesLoader(orthanc_, series))); + + if (isProgressiveDownload) + { + volume->SetDownloadPolicy(new VolumeImageProgressivePolicy); + } + else + { + volume->SetDownloadPolicy(new VolumeImageSimplePolicy); + } + + volume->SetThreadCount(downloadThreadCount); + + VolumeImage& result = *volume; + volumes_.push_back(volume.release()); + + return result; + } + + + DicomStructureSet& BasicApplicationContext::AddStructureSet(const std::string& instance) + { + std::auto_ptr structureSet(new DicomStructureSet(orthanc_, instance)); + + DicomStructureSet& result = *structureSet; + structureSets_.push_back(structureSet.release()); + + return result; + } + + + IWorldSceneInteractor& BasicApplicationContext::AddInteractor(IWorldSceneInteractor* interactor) + { + if (interactor == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + interactors_.push_back(interactor); + + return *interactor; + } + + + void BasicApplicationContext::Start() + { + for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it) + { + assert(*it != NULL); + (*it)->Start(); + } + + viewport_.Start(); + } + + + void BasicApplicationContext::Stop() + { + viewport_.Stop(); + + for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it) + { + assert(*it != NULL); + (*it)->Stop(); + } + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/BasicApplicationContext.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/BasicApplicationContext.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,76 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "../../Framework/Volumes/VolumeImage.h" +#include "../../Framework/Viewport/WidgetViewport.h" +#include "../../Framework/Widgets/IWorldSceneInteractor.h" +#include "../../Framework/Toolbox/DicomStructureSet.h" + +#include + +namespace OrthancStone +{ + class BasicApplicationContext : public boost::noncopyable + { + private: + typedef std::list Volumes; + typedef std::list Interactors; + typedef std::list StructureSets; + + OrthancPlugins::IOrthancConnection& orthanc_; + + WidgetViewport viewport_; + Volumes volumes_; + Interactors interactors_; + StructureSets structureSets_; + + public: + BasicApplicationContext(OrthancPlugins::IOrthancConnection& orthanc); + + ~BasicApplicationContext(); + + IWidget& SetCentralWidget(IWidget* widget); // Takes ownership + + IViewport& GetViewport() + { + return viewport_; + } + + OrthancPlugins::IOrthancConnection& GetOrthancConnection() + { + return orthanc_; + } + + VolumeImage& AddSeriesVolume(const std::string& series, + bool isProgressiveDownload, + size_t downloadThreadCount); + + DicomStructureSet& AddStructureSet(const std::string& instance); + + IWorldSceneInteractor& AddInteractor(IWorldSceneInteractor* interactor); + + void Start(); + + void Stop(); + }; +} diff -r 369fe11606fa -r 37e504582af6 Applications/BinarySemaphore.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/BinarySemaphore.cpp Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,50 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "BinarySemaphore.h" + +namespace OrthancStone +{ + BinarySemaphore::BinarySemaphore() : + proceed_(false) + { + } + + void BinarySemaphore::Signal() + { + //boost::mutex::scoped_lock lock(mutex_); + + proceed_ = true; + condition_.notify_one(); + } + + void BinarySemaphore::Wait() + { + boost::mutex::scoped_lock lock(mutex_); + + while (!proceed_) + { + condition_.wait(lock); + } + + proceed_ = false; + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/BinarySemaphore.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/BinarySemaphore.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,43 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 +#include + +namespace OrthancStone +{ + class BinarySemaphore : public boost::noncopyable + { + private: + bool proceed_; + boost::mutex mutex_; + boost::condition_variable condition_; + + public: + explicit BinarySemaphore(); + + void Signal(); + + void Wait(); + }; +} diff -r 369fe11606fa -r 37e504582af6 Applications/IBasicApplication.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/IBasicApplication.cpp Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,273 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "IBasicApplication.h" + +#include "../../Resources/Orthanc/Core/Logging.h" +#include "../../Resources/Orthanc/Core/HttpClient.h" +#include "../../Resources/Orthanc/Plugins/Samples/Common/OrthancHttpConnection.h" +#include "Sdl/SdlEngine.h" + +namespace OrthancStone +{ + // Anonymous namespace to avoid clashes against other compilation modules + namespace + { + class LogStatusBar : public IStatusBar + { + public: + virtual void ClearMessage() + { + } + + virtual void SetMessage(const std::string& message) + { + LOG(WARNING) << message; + } + }; + } + + +#if ORTHANC_ENABLE_SDL == 1 + static void DeclareSdlCommandLineOptions(boost::program_options::options_description& options) + { + // Declare the supported parameters + boost::program_options::options_description generic("Generic options"); + generic.add_options() + ("help", "Display this help and exit") + ("verbose", "Be verbose in logs") + ("orthanc", boost::program_options::value()->default_value("http://localhost:8042/"), + "URL to the Orthanc server") + ("username", "Username for the Orthanc server") + ("password", "Password for the Orthanc server") + ("https-verify", boost::program_options::value()->default_value(true), "Check HTTPS certificates") + ; + + options.add(generic); + + boost::program_options::options_description sdl("SDL options"); + sdl.add_options() + ("width", boost::program_options::value()->default_value(1024), "Initial width of the SDL window") + ("height", boost::program_options::value()->default_value(768), "Initial height of the SDL window") + ("opengl", boost::program_options::value()->default_value(true), "Enable OpenGL in SDL") + ; + + options.add(sdl); + } + + + int IBasicApplication::ExecuteWithSdl(IBasicApplication& application, + int argc, + char* argv[]) + { + /****************************************************************** + * Initialize all the subcomponents of Orthanc Stone + ******************************************************************/ + + Orthanc::Logging::Initialize(); + Orthanc::HttpClient::InitializeOpenSsl(); + Orthanc::HttpClient::GlobalInitialize(); + SdlEngine::GlobalInitialize(); + + + /****************************************************************** + * Declare and parse the command-line options of the application + ******************************************************************/ + + boost::program_options::options_description options; + DeclareSdlCommandLineOptions(options); + application.DeclareCommandLineOptions(options); + + boost::program_options::variables_map parameters; + bool error = false; + + try + { + boost::program_options::store(boost::program_options::command_line_parser(argc, argv). + options(options).run(), parameters); + boost::program_options::notify(parameters); + } + catch (boost::program_options::error& e) + { + LOG(ERROR) << "Error while parsing the command-line arguments: " << e.what(); + error = true; + } + + + /****************************************************************** + * Configure the application with the command-line parameters + ******************************************************************/ + + if (error || parameters.count("help")) + { + std::cout << std::endl + << "Usage: " << argv[0] << " [OPTION]..." + << std::endl + << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research." + << std::endl << std::endl + << "Demonstration application of Orthanc Stone using SDL." + << std::endl; + + std::cout << options << "\n"; + return error ? -1 : 0; + } + + if (parameters.count("https-verify") && + !parameters["https-verify"].as()) + { + LOG(WARNING) << "Turning off verification of HTTPS certificates (unsafe)"; + Orthanc::HttpClient::ConfigureSsl(false, ""); + } + + if (parameters.count("verbose")) + { + Orthanc::Logging::EnableInfoLevel(true); + } + + if (!parameters.count("width") || + !parameters.count("height") || + !parameters.count("opengl")) + { + LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing"; + return -1; + } + + int w = parameters["width"].as(); + int h = parameters["height"].as(); + if (w <= 0 || h <= 0) + { + LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive"; + return -1; + } + + unsigned int width = static_cast(w); + unsigned int height = static_cast(h); + LOG(WARNING) << "Initial display size: " << width << "x" << height; + + bool opengl = parameters["opengl"].as(); + if (opengl) + { + LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes"; + } + else + { + LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance"; + } + + bool success = true; + try + { + /**************************************************************** + * Initialize the connection to the Orthanc server + ****************************************************************/ + + Orthanc::WebServiceParameters webService; + + if (parameters.count("orthanc")) + { + webService.SetUrl(parameters["orthanc"].as()); + } + + if (parameters.count("username")) + { + webService.SetUsername(parameters["username"].as()); + } + + if (parameters.count("password")) + { + webService.SetPassword(parameters["password"].as()); + } + + LOG(WARNING) << "URL to the Orthanc REST API: " << webService.GetUrl(); + OrthancPlugins::OrthancHttpConnection orthanc(webService); + + if (!MessagingToolbox::CheckOrthancVersion(orthanc)) + { + LOG(ERROR) << "Your version of Orthanc is incompatible with Orthanc Stone, please upgrade"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + + /**************************************************************** + * Initialize the application + ****************************************************************/ + + LogStatusBar statusBar; + BasicApplicationContext context(orthanc); + + application.Initialize(context, statusBar, parameters); + context.GetViewport().SetStatusBar(statusBar); + + std::string title = application.GetTitle(); + if (title.empty()) + { + title = "Stone of Orthanc"; + } + + context.Start(); + + { + /************************************************************** + * Run the application inside a SDL window + **************************************************************/ + + LOG(WARNING) << "Starting the application"; + + SdlWindow window(title.c_str(), width, height, opengl); + SdlEngine sdl(window, context.GetViewport()); + + sdl.Run(); + + LOG(WARNING) << "Stopping the application"; + } + + + /**************************************************************** + * Finalize the application + ****************************************************************/ + + context.Stop(); + + LOG(WARNING) << "The application has stopped"; + + context.GetViewport().ResetStatusBar(); + application.Finalize(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + success = false; + } + + + /****************************************************************** + * Finalize all the subcomponents of Orthanc Stone + ******************************************************************/ + + SdlEngine::GlobalFinalize(); + Orthanc::HttpClient::GlobalFinalize(); + Orthanc::HttpClient::FinalizeOpenSsl(); + + return (success ? 0 : -1); + } +#endif + +} diff -r 369fe11606fa -r 37e504582af6 Applications/IBasicApplication.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/IBasicApplication.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,57 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "BasicApplicationContext.h" + +#include + +#if ORTHANC_ENABLE_SDL == 1 +# include // Necessary to avoid undefined reference to `SDL_main' +#endif + +namespace OrthancStone +{ + class IBasicApplication : public boost::noncopyable + { + public: + virtual ~IBasicApplication() + { + } + + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0; + + virtual std::string GetTitle() const = 0; + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) = 0; + + virtual void Finalize() = 0; + +#if ORTHANC_ENABLE_SDL == 1 + static int ExecuteWithSdl(IBasicApplication& application, + int argc, + char* argv[]); +#endif + }; +} diff -r 369fe11606fa -r 37e504582af6 Applications/Samples/BasicPetCtFusionApplication.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/BasicPetCtFusionApplication.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,202 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SampleInteractor.h" + +#include "../../Resources/Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + namespace Samples + { + class BasicPetCtFusionApplication : public SampleApplicationBase + { + private: + class Interactor : public SampleInteractor + { + public: + static void SetStyle(LayeredSceneWidget& widget, + bool ct, + bool pet) + { + if (ct) + { + RenderStyle style; + style.windowing_ = ImageWindowing_Bone; + widget.SetLayerStyle(0, style); + } + else + { + RenderStyle style; + style.visible_ = false; + widget.SetLayerStyle(0, style); + } + + if (ct && pet) + { + RenderStyle style; + style.applyLut_ = true; + style.alpha_ = 0.5; + widget.SetLayerStyle(1, style); + } + else if (pet) + { + RenderStyle style; + style.applyLut_ = true; + widget.SetLayerStyle(1, style); + } + else + { + RenderStyle style; + style.visible_ = false; + widget.SetLayerStyle(1, style); + } + } + + + static bool IsVisible(LayeredSceneWidget& widget, + size_t layer) + { + RenderStyle style = widget.GetLayerStyle(layer); + return style.visible_; + } + + + static void ToggleInterpolation(LayeredSceneWidget& widget, + size_t layer) + { + RenderStyle style = widget.GetLayerStyle(layer); + + if (style.interpolation_ == ImageInterpolation_Linear) + { + style.interpolation_ = ImageInterpolation_Nearest; + } + else + { + style.interpolation_ = ImageInterpolation_Linear; + } + + widget.SetLayerStyle(layer, style); + } + + + Interactor(VolumeImage& volume, + VolumeProjection projection, + bool reverse) : + SampleInteractor(volume, projection, reverse) + { + } + + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + LayeredSceneWidget& layered = dynamic_cast(widget); + + switch (key) + { + case 'c': + // Toggle the visibility of the CT layer + SetStyle(layered, !IsVisible(layered, 0), IsVisible(layered, 1)); + break; + + case 'p': + // Toggle the visibility of the PET layer + SetStyle(layered, IsVisible(layered, 0), !IsVisible(layered, 1)); + break; + + case 'i': + { + // Toggle on/off the interpolation + ToggleInterpolation(layered, 0); + ToggleInterpolation(layered, 1); + break; + } + + default: + break; + } + } + }; + + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("ct", boost::program_options::value(), + "Orthanc ID of the CT series") + ("pet", boost::program_options::value(), + "Orthanc ID of the PET series") + ("threads", boost::program_options::value()->default_value(3), + "Number of download threads for the CT series") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + if (parameters.count("ct") != 1 || + parameters.count("pet") != 1) + { + LOG(ERROR) << "The series ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string ct = parameters["ct"].as(); + std::string pet = parameters["pet"].as(); + unsigned int threads = parameters["threads"].as(); + + VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads); + VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1); + + // Take the PET volume as the reference for the slices + std::auto_ptr interactor(new Interactor(petVolume, VolumeProjection_Axial, false /* don't reverse normal */)); + + std::auto_ptr widget(new LayeredSceneWidget); + widget->AddLayer(new VolumeImage::LayerFactory(ctVolume)); + widget->AddLayer(new VolumeImage::LayerFactory(petVolume)); + widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); + widget->SetInteractor(*interactor); + + Interactor::SetStyle(*widget, true, true); // Initially, show both CT and PET layers + + context.AddInteractor(interactor.release()); + context.SetCentralWidget(widget.release()); + + statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode"); + statusBar.SetMessage("Use the key \"c\" to show/hide the CT layer"); + statusBar.SetMessage("Use the key \"p\" to show/hide the PET layer"); + statusBar.SetMessage("Use the key \"i\" to toggle the smoothing of the images"); + } + }; + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/Samples/EmptyApplication.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/EmptyApplication.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,59 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SampleApplicationBase.h" + +#include "../../Framework/Widgets/EmptyWidget.h" + +namespace OrthancStone +{ + namespace Samples + { + class EmptyApplication : public SampleApplicationBase + { + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("red", boost::program_options::value()->default_value(255), "Background color: red channel") + ("green", boost::program_options::value()->default_value(0), "Background color: green channel") + ("blue", boost::program_options::value()->default_value(0), "Background color: blue channel") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + int red = parameters["red"].as(); + int green = parameters["green"].as(); + int blue = parameters["blue"].as(); + + context.SetCentralWidget(new EmptyWidget(red, green, blue)); + } + }; + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/Samples/LayoutPetCtFusionApplication.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/LayoutPetCtFusionApplication.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,398 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SampleInteractor.h" + +#include "../../Framework/Layers/SiblingSliceLocationFactory.h" +#include "../../Framework/Layers/DicomStructureSetRendererFactory.h" +#include "../../Framework/Widgets/LayoutWidget.h" + +#include "../../Resources/Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + namespace Samples + { + class LayoutPetCtFusionApplication : + public SampleApplicationBase, + public LayeredSceneWidget::ISliceObserver, + public WorldSceneWidget::IWorldObserver + { + private: + class Interactor : public SampleInteractor + { + private: + LayoutPetCtFusionApplication& that_; + + public: + Interactor(LayoutPetCtFusionApplication& that, + VolumeImage& volume, + VolumeProjection projection, + bool reverse) : + SampleInteractor(volume, projection, reverse), + that_(that) + { + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + MouseButton button, + double x, + double y, + IStatusBar* statusBar) + { + if (button == MouseButton_Left) + { + // Center the sibling views over the clicked point + Vector p = slice.MapSliceToWorldCoordinates(x, y); + + if (statusBar != NULL) + { + char buf[64]; + sprintf(buf, "Click on coordinates (%.02f,%.02f,%.02f) in cm", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); + statusBar->SetMessage(buf); + } + + that_.interactorAxial_->LookupSliceContainingPoint(*that_.ctAxial_, p); + that_.interactorCoronal_->LookupSliceContainingPoint(*that_.ctCoronal_, p); + that_.interactorSagittal_->LookupSliceContainingPoint(*that_.ctSagittal_, p); + } + + return NULL; + } + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + if (key == 's') + { + that_.SetDefaultView(); + } + } + }; + + bool processingEvent_; + Interactor* interactorAxial_; + Interactor* interactorCoronal_; + Interactor* interactorSagittal_; + LayeredSceneWidget* ctAxial_; + LayeredSceneWidget* ctCoronal_; + LayeredSceneWidget* ctSagittal_; + LayeredSceneWidget* petAxial_; + LayeredSceneWidget* petCoronal_; + LayeredSceneWidget* petSagittal_; + LayeredSceneWidget* fusionAxial_; + LayeredSceneWidget* fusionCoronal_; + LayeredSceneWidget* fusionSagittal_; + + + void SetDefaultView() + { + petAxial_->SetDefaultView(); + petCoronal_->SetDefaultView(); + petSagittal_->SetDefaultView(); + } + + + void AddLayer(LayeredSceneWidget& widget, + VolumeImage& volume, + bool isCt) + { + size_t layer; + widget.AddLayer(layer, new VolumeImage::LayerFactory(volume)); + + if (isCt) + { + RenderStyle style; + style.windowing_ = ImageWindowing_Bone; + widget.SetLayerStyle(layer, style); + } + else + { + RenderStyle style; + style.applyLut_ = true; + style.alpha_ = (layer == 0 ? 1.0f : 0.5f); + widget.SetLayerStyle(layer, style); + } + } + + + void ConnectSiblingLocations(LayeredSceneWidget& axial, + LayeredSceneWidget& coronal, + LayeredSceneWidget& sagittal) + { + SiblingSliceLocationFactory::Configure(axial, coronal); + SiblingSliceLocationFactory::Configure(axial, sagittal); + SiblingSliceLocationFactory::Configure(coronal, sagittal); + } + + + void SynchronizeView(const WorldSceneWidget& source, + const ViewportGeometry& view, + LayeredSceneWidget& widget1, + LayeredSceneWidget& widget2, + LayeredSceneWidget& widget3) + { + if (&source == &widget1 || + &source == &widget2 || + &source == &widget3) + { + if (&source != &widget1) + { + widget1.SetView(view); + } + + if (&source != &widget2) + { + widget2.SetView(view); + } + + if (&source != &widget3) + { + widget3.SetView(view); + } + } + } + + + void SynchronizeSlice(const LayeredSceneWidget& source, + const SliceGeometry& slice, + LayeredSceneWidget& widget1, + LayeredSceneWidget& widget2, + LayeredSceneWidget& widget3) + { + if (&source == &widget1 || + &source == &widget2 || + &source == &widget3) + { + if (&source != &widget1) + { + widget1.SetSlice(slice); + } + + if (&source != &widget2) + { + widget2.SetSlice(slice); + } + + if (&source != &widget3) + { + widget3.SetSlice(slice); + } + } + } + + + LayeredSceneWidget* CreateWidget() + { + std::auto_ptr widget(new LayeredSceneWidget); + widget->Register(dynamic_cast(*this)); + widget->Register(dynamic_cast(*this)); + return widget.release(); + } + + + void CreateLayout(BasicApplicationContext& context) + { + std::auto_ptr layout(new OrthancStone::LayoutWidget); + layout->SetBackgroundCleared(true); + //layout->SetBackgroundColor(255,0,0); + layout->SetPadding(5); + + OrthancStone::LayoutWidget& layoutA = dynamic_cast + (layout->AddWidget(new OrthancStone::LayoutWidget)); + layoutA.SetPadding(0, 0, 0, 0, 5); + layoutA.SetVertical(); + petAxial_ = &dynamic_cast(layoutA.AddWidget(CreateWidget())); + OrthancStone::LayoutWidget& layoutA2 = dynamic_cast + (layoutA.AddWidget(new OrthancStone::LayoutWidget)); + layoutA2.SetPadding(0, 0, 0, 0, 5); + petSagittal_ = &dynamic_cast(layoutA2.AddWidget(CreateWidget())); + petCoronal_ = &dynamic_cast(layoutA2.AddWidget(CreateWidget())); + + OrthancStone::LayoutWidget& layoutB = dynamic_cast + (layout->AddWidget(new OrthancStone::LayoutWidget)); + layoutB.SetPadding(0, 0, 0, 0, 5); + layoutB.SetVertical(); + ctAxial_ = &dynamic_cast(layoutB.AddWidget(CreateWidget())); + OrthancStone::LayoutWidget& layoutB2 = dynamic_cast + (layoutB.AddWidget(new OrthancStone::LayoutWidget)); + layoutB2.SetPadding(0, 0, 0, 0, 5); + ctSagittal_ = &dynamic_cast(layoutB2.AddWidget(CreateWidget())); + ctCoronal_ = &dynamic_cast(layoutB2.AddWidget(CreateWidget())); + + OrthancStone::LayoutWidget& layoutC = dynamic_cast + (layout->AddWidget(new OrthancStone::LayoutWidget)); + layoutC.SetPadding(0, 0, 0, 0, 5); + layoutC.SetVertical(); + fusionAxial_ = &dynamic_cast(layoutC.AddWidget(CreateWidget())); + OrthancStone::LayoutWidget& layoutC2 = dynamic_cast + (layoutC.AddWidget(new OrthancStone::LayoutWidget)); + layoutC2.SetPadding(0, 0, 0, 0, 5); + fusionSagittal_ = &dynamic_cast(layoutC2.AddWidget(CreateWidget())); + fusionCoronal_ = &dynamic_cast(layoutC2.AddWidget(CreateWidget())); + + context.SetCentralWidget(layout.release()); + } + + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("ct", boost::program_options::value(), + "Orthanc ID of the CT series") + ("pet", boost::program_options::value(), + "Orthanc ID of the PET series") + ("rt", boost::program_options::value(), + "Orthanc ID of the DICOM RT-STRUCT series (optional)") + ("threads", boost::program_options::value()->default_value(3), + "Number of download threads for the CT series") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + processingEvent_ = true; + + if (parameters.count("ct") != 1 || + parameters.count("pet") != 1) + { + LOG(ERROR) << "The series ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string ct = parameters["ct"].as(); + std::string pet = parameters["pet"].as(); + unsigned int threads = parameters["threads"].as(); + + VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads); + VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1); + + // Take the PET volume as the reference for the slices + interactorAxial_ = &dynamic_cast + (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Axial, false))); + interactorCoronal_ = &dynamic_cast + (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Coronal, false))); + interactorSagittal_ = &dynamic_cast + (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Sagittal, true))); + + CreateLayout(context); + + AddLayer(*ctAxial_, ctVolume, true); + AddLayer(*ctCoronal_, ctVolume, true); + AddLayer(*ctSagittal_, ctVolume, true); + + AddLayer(*petAxial_, petVolume, false); + AddLayer(*petCoronal_, petVolume, false); + AddLayer(*petSagittal_, petVolume, false); + + AddLayer(*fusionAxial_, ctVolume, true); + AddLayer(*fusionAxial_, petVolume, false); + AddLayer(*fusionCoronal_, ctVolume, true); + AddLayer(*fusionCoronal_, petVolume, false); + AddLayer(*fusionSagittal_, ctVolume, true); + AddLayer(*fusionSagittal_, petVolume, false); + + if (parameters.count("rt") == 1) + { + DicomStructureSet& rtStruct = context.AddStructureSet(parameters["rt"].as()); + + Vector p = rtStruct.GetStructureCenter(0); + interactorAxial_->GetCursor().LookupSliceContainingPoint(p); + + ctAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct)); + petAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct)); + fusionAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct)); + } + + ConnectSiblingLocations(*ctAxial_, *ctCoronal_, *ctSagittal_); + ConnectSiblingLocations(*petAxial_, *petCoronal_, *petSagittal_); + ConnectSiblingLocations(*fusionAxial_, *fusionCoronal_, *fusionSagittal_); + + interactorAxial_->AddWidget(*ctAxial_); + interactorAxial_->AddWidget(*petAxial_); + interactorAxial_->AddWidget(*fusionAxial_); + + interactorCoronal_->AddWidget(*ctCoronal_); + interactorCoronal_->AddWidget(*petCoronal_); + interactorCoronal_->AddWidget(*fusionCoronal_); + + interactorSagittal_->AddWidget(*ctSagittal_); + interactorSagittal_->AddWidget(*petSagittal_); + interactorSagittal_->AddWidget(*fusionSagittal_); + + processingEvent_ = false; + + statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode"); + statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); + } + + virtual void NotifySizeChange(const WorldSceneWidget& source, + ViewportGeometry& view) + { + view.SetDefaultView(); + } + + virtual void NotifyViewChange(const WorldSceneWidget& source, + const ViewportGeometry& view) + { + if (!processingEvent_) // Avoid reentrant calls + { + processingEvent_ = true; + + SynchronizeView(source, view, *ctAxial_, *petAxial_, *fusionAxial_); + SynchronizeView(source, view, *ctCoronal_, *petCoronal_, *fusionCoronal_); + SynchronizeView(source, view, *ctSagittal_, *petSagittal_, *fusionSagittal_); + + processingEvent_ = false; + } + } + + virtual void NotifySliceChange(const LayeredSceneWidget& source, + const SliceGeometry& slice) + { + if (!processingEvent_) // Avoid reentrant calls + { + processingEvent_ = true; + + SynchronizeSlice(source, slice, *ctAxial_, *petAxial_, *fusionAxial_); + SynchronizeSlice(source, slice, *ctCoronal_, *petCoronal_, *fusionCoronal_); + SynchronizeSlice(source, slice, *ctSagittal_, *petSagittal_, *fusionSagittal_); + + processingEvent_ = false; + } + } + }; + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/Samples/SampleApplicationBase.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/SampleApplicationBase.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,47 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "../IBasicApplication.h" + +namespace OrthancStone +{ + namespace Samples + { + class SampleApplicationBase : public IBasicApplication + { + public: + virtual std::string GetTitle() const + { + return "Stone of Orthanc - Sample"; + } + + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + } + + virtual void Finalize() + { + } + }; + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/Samples/SampleInteractor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/SampleInteractor.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,133 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SampleApplicationBase.h" + +#include "../../Framework/Widgets/LayeredSceneWidget.h" +#include "../../Framework/Widgets/IWorldSceneInteractor.h" +#include "../../Framework/Toolbox/ParallelSlicesCursor.h" + +namespace OrthancStone +{ + namespace Samples + { + /** + * This is a basic mouse interactor for sample applications. It + * contains a set of parallel slices in the 3D space. The mouse + * wheel events make the widget change the slice that is + * displayed. + **/ + class SampleInteractor : public IWorldSceneInteractor + { + private: + ParallelSlicesCursor cursor_; + + public: + SampleInteractor(VolumeImage& volume, + VolumeProjection projection, + bool reverse) + { + std::auto_ptr slices(volume.GetGeometry(projection, reverse)); + cursor_.SetGeometry(*slices); + } + + SampleInteractor(ISeriesLoader& series, + bool reverse) + { + if (reverse) + { + std::auto_ptr slices(series.GetGeometry().Reverse()); + cursor_.SetGeometry(*slices); + } + else + { + cursor_.SetGeometry(series.GetGeometry()); + } + } + + SampleInteractor(const ParallelSlices& slices) + { + cursor_.SetGeometry(slices); + } + + ParallelSlicesCursor& GetCursor() + { + return cursor_; + } + + void AddWidget(LayeredSceneWidget& widget) + { + widget.SetInteractor(*this); + widget.SetSlice(cursor_.GetCurrentSlice()); + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + MouseButton button, + double x, + double y, + IStatusBar* statusBar) + { + return NULL; + } + + virtual void MouseOver(CairoContext& context, + WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) + { + } + + virtual void MouseWheel(WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + if (cursor_.ApplyWheelEvent(direction, modifiers)) + { + dynamic_cast(widget).SetSlice(cursor_.GetCurrentSlice()); + } + } + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + } + + void LookupSliceContainingPoint(LayeredSceneWidget& widget, + const Vector& p) + { + if (cursor_.LookupSliceContainingPoint(p)) + { + widget.SetSlice(cursor_.GetCurrentSlice()); + } + } + }; + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/Samples/SampleMainSdl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/SampleMainSdl.cpp Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,62 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 . + **/ + + +// The macro "ORTHANC_STONE_SAMPLE" must be set by the CMake script + +#if ORTHANC_STONE_SAMPLE == 1 +#include "EmptyApplication.h" +typedef OrthancStone::Samples::EmptyApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 2 +#include "TestPatternApplication.h" +typedef OrthancStone::Samples::TestPatternApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 3 +#include "SingleFrameApplication.h" +typedef OrthancStone::Samples::SingleFrameApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 4 +#include "SingleVolumeApplication.h" +typedef OrthancStone::Samples::SingleVolumeApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 5 +#include "BasicPetCtFusionApplication.h" +typedef OrthancStone::Samples::BasicPetCtFusionApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 6 +#include "SynchronizedSeriesApplication.h" +typedef OrthancStone::Samples::SynchronizedSeriesApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 7 +#include "LayoutPetCtFusionApplication.h" +typedef OrthancStone::Samples::LayoutPetCtFusionApplication Application; + +#else +#error Please set the ORTHANC_STONE_SAMPLE macro +#endif + + +int main(int argc, char* argv[]) +{ + Application application; + + return OrthancStone::IBasicApplication::ExecuteWithSdl(application, argc, argv); +} diff -r 369fe11606fa -r 37e504582af6 Applications/Samples/SingleFrameApplication.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/SingleFrameApplication.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,85 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SampleApplicationBase.h" + +#include "../../Framework/Layers/SingleFrameRendererFactory.h" +#include "../../Framework/Widgets/LayeredSceneWidget.h" +#include "../../Resources/Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + namespace Samples + { + class SingleFrameApplication : public SampleApplicationBase + { + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("instance", boost::program_options::value(), + "Orthanc ID of the instance") + ("frame", boost::program_options::value()->default_value(0), + "Number of the frame, for multi-frame DICOM instances") + ("smooth", boost::program_options::value()->default_value(true), + "Enable linear interpolation to smooth the image") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + if (parameters.count("instance") != 1) + { + LOG(ERROR) << "The instance ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string instance = parameters["instance"].as(); + int frame = parameters["frame"].as(); + + std::auto_ptr renderer; + renderer.reset(new SingleFrameRendererFactory(context.GetOrthancConnection(), instance, frame)); + + std::auto_ptr widget(new LayeredSceneWidget); + widget->SetSlice(renderer->GetSliceGeometry()); + widget->AddLayer(renderer.release()); + + if (parameters["smooth"].as()) + { + RenderStyle s; + s.interpolation_ = ImageInterpolation_Linear; + widget->SetLayerStyle(0, s); + } + + context.SetCentralWidget(widget.release()); + } + }; + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/Samples/SingleVolumeApplication.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/SingleVolumeApplication.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,284 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SampleInteractor.h" + +#include "../../Resources/Orthanc/Core/Toolbox.h" +#include "../../Framework/Layers/LineMeasureTracker.h" +#include "../../Framework/Layers/CircleMeasureTracker.h" +#include "../../Resources/Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + namespace Samples + { + class SingleVolumeApplication : public SampleApplicationBase + { + private: + class Interactor : public SampleInteractor + { + private: + enum MouseMode + { + MouseMode_None, + MouseMode_TrackCoordinates, + MouseMode_LineMeasure, + MouseMode_CircleMeasure + }; + + MouseMode mouseMode_; + + void SetMouseMode(MouseMode mode, + IStatusBar* statusBar) + { + if (mouseMode_ == mode) + { + mouseMode_ = MouseMode_None; + } + else + { + mouseMode_ = mode; + } + + if (statusBar) + { + switch (mouseMode_) + { + case MouseMode_None: + statusBar->SetMessage("Disabling the mouse tools"); + break; + + case MouseMode_TrackCoordinates: + statusBar->SetMessage("Tracking the mouse coordinates"); + break; + + case MouseMode_LineMeasure: + statusBar->SetMessage("Mouse clicks will now measure the distances"); + break; + + case MouseMode_CircleMeasure: + statusBar->SetMessage("Mouse clicks will now draw circles"); + break; + + default: + break; + } + } + } + + public: + Interactor(VolumeImage& volume, + VolumeProjection projection, + bool reverse) : + SampleInteractor(volume, projection, reverse), + mouseMode_(MouseMode_None) + { + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + MouseButton button, + double x, + double y, + IStatusBar* statusBar) + { + if (button == MouseButton_Left) + { + switch (mouseMode_) + { + case MouseMode_LineMeasure: + return new LineMeasureTracker(NULL, slice, x, y, 255, 0, 0, 14 /* font size */); + + case MouseMode_CircleMeasure: + return new CircleMeasureTracker(NULL, slice, x, y, 255, 0, 0, 14 /* font size */); + + default: + break; + } + } + + return NULL; + } + + virtual void MouseOver(CairoContext& context, + WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) + { + if (mouseMode_ == MouseMode_TrackCoordinates && + statusBar != NULL) + { + Vector p = slice.MapSliceToWorldCoordinates(x, y); + + char buf[64]; + sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); + statusBar->SetMessage(buf); + } + } + + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + switch (key) + { + case 't': + SetMouseMode(MouseMode_TrackCoordinates, statusBar); + break; + + case 'm': + SetMouseMode(MouseMode_LineMeasure, statusBar); + break; + + case 'c': + SetMouseMode(MouseMode_CircleMeasure, statusBar); + break; + + case 'b': + { + if (statusBar) + { + statusBar->SetMessage("Setting Hounsfield window to bones"); + } + + RenderStyle style; + style.windowing_ = ImageWindowing_Bone; + dynamic_cast(widget).SetLayerStyle(0, style); + break; + } + + case 'l': + { + if (statusBar) + { + statusBar->SetMessage("Setting Hounsfield window to lung"); + } + + RenderStyle style; + style.windowing_ = ImageWindowing_Lung; + dynamic_cast(widget).SetLayerStyle(0, style); + break; + } + + case 'd': + { + if (statusBar) + { + statusBar->SetMessage("Setting Hounsfield window to what is written in the DICOM file"); + } + + RenderStyle style; + style.windowing_ = ImageWindowing_Default; + dynamic_cast(widget).SetLayerStyle(0, style); + break; + } + + default: + break; + } + } + }; + + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("series", boost::program_options::value(), + "Orthanc ID of the series") + ("threads", boost::program_options::value()->default_value(3), + "Number of download threads") + ("projection", boost::program_options::value()->default_value("axial"), + "Projection of interest (can be axial, sagittal or coronal)") + ("reverse", boost::program_options::value()->default_value(false), + "Reverse the normal direction of the volume") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + if (parameters.count("series") != 1) + { + LOG(ERROR) << "The series ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string series = parameters["series"].as(); + unsigned int threads = parameters["threads"].as(); + bool reverse = parameters["reverse"].as(); + + std::string tmp = parameters["projection"].as(); + Orthanc::Toolbox::ToLowerCase(tmp); + + VolumeProjection projection; + if (tmp == "axial") + { + projection = VolumeProjection_Axial; + } + else if (tmp == "sagittal") + { + projection = VolumeProjection_Sagittal; + } + else if (tmp == "coronal") + { + projection = VolumeProjection_Coronal; + } + else + { + LOG(ERROR) << "Unknown projection: " << tmp; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + VolumeImage& volume = context.AddSeriesVolume(series, true /* progressive download */, threads); + + std::auto_ptr interactor(new Interactor(volume, projection, reverse)); + + std::auto_ptr widget(new LayeredSceneWidget); + widget->AddLayer(new VolumeImage::LayerFactory(volume)); + widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); + widget->SetInteractor(*interactor); + + context.AddInteractor(interactor.release()); + context.SetCentralWidget(widget.release()); + + statusBar.SetMessage("Use the keys \"b\", \"l\" and \"d\" to change Hounsfield windowing"); + statusBar.SetMessage("Use the keys \"t\" to track the (X,Y,Z) mouse coordinates"); + statusBar.SetMessage("Use the keys \"m\" to measure distances"); + statusBar.SetMessage("Use the keys \"c\" to draw circles"); + } + }; + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/Samples/SynchronizedSeriesApplication.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/SynchronizedSeriesApplication.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,107 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SampleInteractor.h" + +#include "../../Framework/Toolbox/OrthancSeriesLoader.h" +#include "../../Framework/Layers/SeriesFrameRendererFactory.h" +#include "../../Framework/Layers/SiblingSliceLocationFactory.h" +#include "../../Framework/Widgets/LayoutWidget.h" +#include "../../Resources/Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + namespace Samples + { + class SynchronizedSeriesApplication : public SampleApplicationBase + { + private: + LayeredSceneWidget* CreateSeriesWidget(BasicApplicationContext& context, + const std::string& series) + { + std::auto_ptr loader(new OrthancSeriesLoader(context.GetOrthancConnection(), series)); + + std::auto_ptr interactor(new SampleInteractor(*loader, false)); + + std::auto_ptr widget(new LayeredSceneWidget); + widget->AddLayer(new SeriesFrameRendererFactory(loader.release(), false)); + widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); + widget->SetInteractor(*interactor); + + context.AddInteractor(interactor.release()); + + return widget.release(); + } + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("a", boost::program_options::value(), + "Orthanc ID of the 1st series") + ("b", boost::program_options::value(), + "Orthanc ID of the 2nd series") + ("c", boost::program_options::value(), + "Orthanc ID of the 3rd series") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + if (parameters.count("a") != 1 || + parameters.count("b") != 1 || + parameters.count("c") != 1) + { + LOG(ERROR) << "At least one of the three series IDs is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::auto_ptr a(CreateSeriesWidget(context, parameters["a"].as())); + std::auto_ptr b(CreateSeriesWidget(context, parameters["b"].as())); + std::auto_ptr c(CreateSeriesWidget(context, parameters["c"].as())); + + SiblingSliceLocationFactory::Configure(*a, *b); + SiblingSliceLocationFactory::Configure(*a, *c); + SiblingSliceLocationFactory::Configure(*b, *c); + + std::auto_ptr layout(new LayoutWidget); + layout->SetPadding(5); + layout->AddWidget(a.release()); + + std::auto_ptr layoutB(new LayoutWidget); + layoutB->SetVertical(); + layoutB->SetPadding(5); + layoutB->AddWidget(b.release()); + layoutB->AddWidget(c.release()); + layout->AddWidget(layoutB.release()); + + context.SetCentralWidget(layout.release()); + } + }; + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/Samples/TestPatternApplication.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Samples/TestPatternApplication.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,63 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SampleApplicationBase.h" + +#include "../../Framework/Widgets/TestCairoWidget.h" +#include "../../Framework/Widgets/TestWorldSceneWidget.h" +#include "../../Framework/Widgets/LayoutWidget.h" + +namespace OrthancStone +{ + namespace Samples + { + class TestPatternApplication : public SampleApplicationBase + { + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("animate", boost::program_options::value()->default_value(true), "Animate the test pattern") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + std::auto_ptr layout(new LayoutWidget); + layout->SetPadding(10); + layout->SetBackgroundCleared(true); + layout->AddWidget(new TestCairoWidget(parameters["animate"].as())); + layout->AddWidget(new TestWorldSceneWidget); + + context.SetCentralWidget(layout.release()); + } + }; + } +} diff -r 369fe11606fa -r 37e504582af6 Applications/Sdl/SdlBuffering.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Sdl/SdlBuffering.cpp Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,134 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SdlBuffering.h" + +#if ORTHANC_ENABLE_SDL == 1 + +#include "../../../Resources/Orthanc/Core/Logging.h" +#include "../../../Resources/Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + SdlBuffering::SdlBuffering() : + sdlSurface_(NULL), + pendingFrame_(false) + { + } + + + SdlBuffering::~SdlBuffering() + { + if (sdlSurface_) + { + SDL_FreeSurface(sdlSurface_); + } + } + + + void SdlBuffering::SetSize(unsigned int width, + unsigned int height, + IViewport& viewport) + { + boost::mutex::scoped_lock lock(mutex_); + + viewport.SetSize(width, height); + + if (offscreenSurface_.get() == NULL || + offscreenSurface_->GetWidth() != width || + offscreenSurface_->GetHeight() != height) + { + offscreenSurface_.reset(new CairoSurface(width, height)); + } + + if (onscreenSurface_.get() == NULL || + onscreenSurface_->GetWidth() != width || + onscreenSurface_->GetHeight() != height) + { + onscreenSurface_.reset(new CairoSurface(width, height)); + + // TODO Big endian? + static const uint32_t rmask = 0x00ff0000; + static const uint32_t gmask = 0x0000ff00; + static const uint32_t bmask = 0x000000ff; + + if (sdlSurface_) + { + SDL_FreeSurface(sdlSurface_); + } + + sdlSurface_ = SDL_CreateRGBSurfaceFrom(onscreenSurface_->GetBuffer(), width, height, 32, + onscreenSurface_->GetPitch(), rmask, gmask, bmask, 0); + if (!sdlSurface_) + { + LOG(ERROR) << "Cannot create a SDL surface from a Cairo surface"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + pendingFrame_ = false; + } + + + bool SdlBuffering::RenderOffscreen(IViewport& viewport) + { + boost::mutex::scoped_lock lock(mutex_); + + if (offscreenSurface_.get() == NULL) + { + return false; + } + + Orthanc::ImageAccessor target = offscreenSurface_->GetAccessor(); + + if (viewport.Render(target) && + !pendingFrame_) + { + pendingFrame_ = true; + return true; + } + else + { + return false; + } + } + + + void SdlBuffering::SwapToScreen(SdlWindow& window) + { + if (!pendingFrame_ || + offscreenSurface_.get() == NULL || + onscreenSurface_.get() == NULL) + { + return; + } + + { + boost::mutex::scoped_lock lock(mutex_); + onscreenSurface_->Copy(*offscreenSurface_); + } + + window.Render(sdlSurface_); + pendingFrame_ = false; + } +} + +#endif diff -r 369fe11606fa -r 37e504582af6 Applications/Sdl/SdlBuffering.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Sdl/SdlBuffering.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,60 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 + +#if ORTHANC_ENABLE_SDL == 1 + +#include "SdlWindow.h" +#include "../../Framework/Viewport/CairoSurface.h" +#include "../../Framework/Viewport/IViewport.h" + +#include + +namespace OrthancStone +{ + class SdlBuffering : public boost::noncopyable + { + private: + boost::mutex mutex_; + std::auto_ptr offscreenSurface_; + std::auto_ptr onscreenSurface_; + SDL_Surface* sdlSurface_; + bool pendingFrame_; + + public: + SdlBuffering(); + + ~SdlBuffering(); + + void SetSize(unsigned int width, + unsigned int height, + IViewport& viewport); + + // Returns "true" if a new refresh of the display should be + // triggered afterwards + bool RenderOffscreen(IViewport& viewport); + + void SwapToScreen(SdlWindow& window); + }; +} + +#endif diff -r 369fe11606fa -r 37e504582af6 Applications/Sdl/SdlEngine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Sdl/SdlEngine.cpp Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,319 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SdlEngine.h" + +#if ORTHANC_ENABLE_SDL == 1 + +#include "../../../Resources/Orthanc/Core/Logging.h" + +#include + +namespace OrthancStone +{ + void SdlEngine::RenderFrame() + { + if (!viewportChanged_) + { + return; + } + + viewportChanged_ = false; + + if (buffering_.RenderOffscreen(viewport_)) + { + // Do not notify twice when a new frame was rendered, to avoid + // spoiling the SDL event queue + SDL_Event event; + SDL_memset(&event, 0, sizeof(event)); + event.type = refreshEvent_; + event.user.code = 0; + event.user.data1 = 0; + event.user.data2 = 0; + SDL_PushEvent(&event); + } + } + + + void SdlEngine::RenderThread(SdlEngine* that) + { + for (;;) + { + that->renderFrame_.Wait(); + + if (that->continue_) + { + that->RenderFrame(); + } + else + { + return; + } + } + } + + + KeyboardModifiers SdlEngine::GetKeyboardModifiers(const uint8_t* keyboardState, + const int scancodeCount) + { + int result = KeyboardModifiers_None; + + if (keyboardState != NULL) + { + if (SDL_SCANCODE_LSHIFT < scancodeCount && + keyboardState[SDL_SCANCODE_LSHIFT]) + { + result |= KeyboardModifiers_Shift; + } + + if (SDL_SCANCODE_RSHIFT < scancodeCount && + keyboardState[SDL_SCANCODE_RSHIFT]) + { + result |= KeyboardModifiers_Shift; + } + + if (SDL_SCANCODE_LCTRL < scancodeCount && + keyboardState[SDL_SCANCODE_LCTRL]) + { + result |= KeyboardModifiers_Control; + } + + if (SDL_SCANCODE_RCTRL < scancodeCount && + keyboardState[SDL_SCANCODE_RCTRL]) + { + result |= KeyboardModifiers_Control; + } + + if (SDL_SCANCODE_LALT < scancodeCount && + keyboardState[SDL_SCANCODE_LALT]) + { + result |= KeyboardModifiers_Alt; + } + + if (SDL_SCANCODE_RALT < scancodeCount && + keyboardState[SDL_SCANCODE_RALT]) + { + result |= KeyboardModifiers_Alt; + } + } + + return static_cast(result); + } + + + void SdlEngine::SetSize(unsigned int width, + unsigned int height) + { + buffering_.SetSize(width, height, viewport_); + viewportChanged_ = true; + Refresh(); + } + + + void SdlEngine::Stop() + { + if (continue_) + { + continue_ = false; + renderFrame_.Signal(); // Unlock the render thread + renderThread_.join(); + } + } + + + void SdlEngine::Refresh() + { + renderFrame_.Signal(); + } + + + SdlEngine::SdlEngine(SdlWindow& window, + IViewport& viewport) : + window_(window), + viewport_(viewport), + continue_(true) + { + refreshEvent_ = SDL_RegisterEvents(1); + + SetSize(window_.GetWidth(), window_.GetHeight()); + + viewport_.Register(*this); + + renderThread_ = boost::thread(RenderThread, this); + } + + + SdlEngine::~SdlEngine() + { + Stop(); + + viewport_.Unregister(*this); + } + + + void SdlEngine::Run() + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + bool stop = false; + while (!stop) + { + Refresh(); + + SDL_Event event; + + while (SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + stop = true; + break; + } + else if (event.type == refreshEvent_) + { + buffering_.SwapToScreen(window_); + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + switch (event.button.button) + { + case SDL_BUTTON_LEFT: + viewport_.MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers); + break; + + case SDL_BUTTON_RIGHT: + viewport_.MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers); + break; + + case SDL_BUTTON_MIDDLE: + viewport_.MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers); + break; + + default: + break; + } + } + else if (event.type == SDL_MOUSEMOTION) + { + viewport_.MouseMove(event.button.x, event.button.y); + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + viewport_.MouseUp(); + } + else if (event.type == SDL_WINDOWEVENT) + { + switch (event.window.event) + { + case SDL_WINDOWEVENT_LEAVE: + viewport_.MouseLeave(); + break; + + case SDL_WINDOWEVENT_ENTER: + viewport_.MouseEnter(); + break; + + case SDL_WINDOWEVENT_SIZE_CHANGED: + SetSize(event.window.data1, event.window.data2); + break; + + default: + break; + } + } + else if (event.type == SDL_MOUSEWHEEL) + { + KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + int x, y; + SDL_GetMouseState(&x, &y); + + if (event.wheel.y > 0) + { + viewport_.MouseWheel(MouseWheelDirection_Up, x, y, modifiers); + } + else if (event.wheel.y < 0) + { + viewport_.MouseWheel(MouseWheelDirection_Down, x, y, modifiers); + } + } + else if (event.type == SDL_KEYDOWN) + { + KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + switch (event.key.keysym.sym) + { + case SDLK_a: viewport_.KeyPressed('a', modifiers); break; + case SDLK_b: viewport_.KeyPressed('b', modifiers); break; + case SDLK_c: viewport_.KeyPressed('c', modifiers); break; + case SDLK_d: viewport_.KeyPressed('d', modifiers); break; + case SDLK_e: viewport_.KeyPressed('e', modifiers); break; + case SDLK_f: window_.ToggleMaximize(); break; + case SDLK_g: viewport_.KeyPressed('g', modifiers); break; + case SDLK_h: viewport_.KeyPressed('h', modifiers); break; + case SDLK_i: viewport_.KeyPressed('i', modifiers); break; + case SDLK_j: viewport_.KeyPressed('j', modifiers); break; + case SDLK_k: viewport_.KeyPressed('k', modifiers); break; + case SDLK_l: viewport_.KeyPressed('l', modifiers); break; + case SDLK_m: viewport_.KeyPressed('m', modifiers); break; + case SDLK_n: viewport_.KeyPressed('n', modifiers); break; + case SDLK_o: viewport_.KeyPressed('o', modifiers); break; + case SDLK_p: viewport_.KeyPressed('p', modifiers); break; + case SDLK_q: stop = true; break; + case SDLK_r: viewport_.KeyPressed('r', modifiers); break; + case SDLK_s: viewport_.KeyPressed('s', modifiers); break; + case SDLK_t: viewport_.KeyPressed('t', modifiers); break; + case SDLK_u: viewport_.KeyPressed('u', modifiers); break; + case SDLK_v: viewport_.KeyPressed('v', modifiers); break; + case SDLK_w: viewport_.KeyPressed('w', modifiers); break; + case SDLK_x: viewport_.KeyPressed('x', modifiers); break; + case SDLK_y: viewport_.KeyPressed('y', modifiers); break; + case SDLK_z: viewport_.KeyPressed('z', modifiers); break; + + default: + break; + } + } + } + + SDL_Delay(10); // Necessary for mouse wheel events to work + } + + Stop(); + } + + + void SdlEngine::GlobalInitialize() + { + SDL_Init(SDL_INIT_VIDEO); + } + + + void SdlEngine::GlobalFinalize() + { + SDL_Quit(); + } +} + +#endif diff -r 369fe11606fa -r 37e504582af6 Applications/Sdl/SdlEngine.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Sdl/SdlEngine.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,78 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 + +#if ORTHANC_ENABLE_SDL == 1 + +#include "SdlBuffering.h" +#include "../BinarySemaphore.h" + +#include + +namespace OrthancStone +{ + class SdlEngine : public IViewport::IChangeObserver + { + private: + SdlWindow& window_; + IViewport& viewport_; + SdlBuffering buffering_; + boost::thread renderThread_; + bool continue_; + BinarySemaphore renderFrame_; + uint32_t refreshEvent_; + bool viewportChanged_; + + void RenderFrame(); + + static void RenderThread(SdlEngine* that); + + static KeyboardModifiers GetKeyboardModifiers(const uint8_t* keyboardState, + const int scancodeCount); + + void SetSize(unsigned int width, + unsigned int height); + + void Stop(); + + void Refresh(); + + public: + SdlEngine(SdlWindow& window, + IViewport& viewport); + + virtual ~SdlEngine(); + + virtual void NotifyChange(const IViewport& viewport) + { + viewportChanged_ = true; + } + + void Run(); + + static void GlobalInitialize(); + + static void GlobalFinalize(); + }; +} + +#endif diff -r 369fe11606fa -r 37e504582af6 Applications/Sdl/SdlWindow.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Sdl/SdlWindow.cpp Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,150 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 "SdlWindow.h" + +#if ORTHANC_ENABLE_SDL == 1 + +#include "../../../Resources/Orthanc/Core/Logging.h" +#include "../../../Resources/Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + SdlWindow::SdlWindow(const char* title, + unsigned int width, + unsigned int height, + bool enableOpenGl) : + maximized_(false) + { + // TODO Understand why, with SDL_WINDOW_OPENGL + MinGW32 + Release + // build mode, the application crashes whenever the SDL window is + // resized or maximized + + uint32_t windowFlags, rendererFlags; + if (enableOpenGl) + { + windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + rendererFlags = SDL_RENDERER_ACCELERATED; + } + else + { + windowFlags = SDL_WINDOW_RESIZABLE; + rendererFlags = SDL_RENDERER_SOFTWARE; + } + + window_ = SDL_CreateWindow(title, + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width, height, windowFlags); + + if (window_ == NULL) + { + LOG(ERROR) << "Cannot create the SDL window: " << SDL_GetError(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + renderer_ = SDL_CreateRenderer(window_, -1, rendererFlags); + if (!renderer_) + { + LOG(ERROR) << "Cannot create the SDL renderer: " << SDL_GetError(); + SDL_DestroyWindow(window_); + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + SdlWindow::~SdlWindow() + { + if (renderer_ != NULL) + { + SDL_DestroyRenderer(renderer_); + } + + if (window_ != NULL) + { + SDL_DestroyWindow(window_); + } + } + + + unsigned int SdlWindow::GetWidth() const + { + int w = -1; + SDL_GetWindowSize(window_, &w, NULL); + + if (w < 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + else + { + return static_cast(w); + } + } + + + unsigned int SdlWindow::GetHeight() const + { + int h = -1; + SDL_GetWindowSize(window_, NULL, &h); + + if (h < 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + else + { + return static_cast(h); + } + } + + + void SdlWindow::Render(SDL_Surface* surface) + { + //SDL_RenderClear(renderer_); + + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer_, surface); + if (texture != NULL) + { + SDL_RenderCopy(renderer_, texture, NULL, NULL); + SDL_DestroyTexture(texture); + } + + SDL_RenderPresent(renderer_); + } + + + void SdlWindow::ToggleMaximize() + { + if (maximized_) + { + SDL_RestoreWindow(window_); + maximized_ = false; + } + else + { + SDL_MaximizeWindow(window_); + maximized_ = true; + } + } +} + +#endif diff -r 369fe11606fa -r 37e504582af6 Applications/Sdl/SdlWindow.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Sdl/SdlWindow.h Thu Apr 27 14:50:40 2017 +0200 @@ -0,0 +1,57 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017 Osimis, 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 + +#if ORTHANC_ENABLE_SDL == 1 + +#include +#include +#include + +namespace OrthancStone +{ + class SdlWindow : public boost::noncopyable + { + private: + SDL_Window *window_; + SDL_Renderer *renderer_; + bool maximized_; + + public: + SdlWindow(const char* title, + unsigned int width, + unsigned int height, + bool enableOpenGl); + + ~SdlWindow(); + + unsigned int GetWidth() const; + + unsigned int GetHeight() const; + + void Render(SDL_Surface* surface); + + void ToggleMaximize(); + }; +} + +#endif diff -r 369fe11606fa -r 37e504582af6 CMakeLists.txt --- a/CMakeLists.txt Thu Apr 27 11:09:58 2017 +0200 +++ b/CMakeLists.txt Thu Apr 27 14:50:40 2017 +0200 @@ -26,7 +26,7 @@ ##################################################################### macro(BuildSample Target Sample) - add_executable(${Target} Samples/SampleMainSdl.cpp) + add_executable(${Target} Applications/Samples/SampleMainSdl.cpp) set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample}) target_link_libraries(${Target} OrthancStone) endmacro() diff -r 369fe11606fa -r 37e504582af6 Framework/Applications/BasicApplicationContext.cpp --- a/Framework/Applications/BasicApplicationContext.cpp Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "BasicApplicationContext.h" - -#include "../Toolbox/OrthancSeriesLoader.h" -#include "../Volumes/VolumeImageSimplePolicy.h" -#include "../Volumes/VolumeImageProgressivePolicy.h" - -namespace OrthancStone -{ - BasicApplicationContext::BasicApplicationContext(OrthancPlugins::IOrthancConnection& orthanc) : - orthanc_(orthanc) - { - } - - - BasicApplicationContext::~BasicApplicationContext() - { - for (Interactors::iterator it = interactors_.begin(); it != interactors_.end(); ++it) - { - assert(*it != NULL); - delete *it; - } - - for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it) - { - assert(*it != NULL); - delete *it; - } - - for (StructureSets::iterator it = structureSets_.begin(); it != structureSets_.end(); ++it) - { - assert(*it != NULL); - delete *it; - } - } - - - IWidget& BasicApplicationContext::SetCentralWidget(IWidget* widget) // Takes ownership - { - viewport_.SetCentralWidget(widget); - return *widget; - } - - - VolumeImage& BasicApplicationContext::AddSeriesVolume(const std::string& series, - bool isProgressiveDownload, - size_t downloadThreadCount) - { - std::auto_ptr volume(new VolumeImage(new OrthancSeriesLoader(orthanc_, series))); - - if (isProgressiveDownload) - { - volume->SetDownloadPolicy(new VolumeImageProgressivePolicy); - } - else - { - volume->SetDownloadPolicy(new VolumeImageSimplePolicy); - } - - volume->SetThreadCount(downloadThreadCount); - - VolumeImage& result = *volume; - volumes_.push_back(volume.release()); - - return result; - } - - - DicomStructureSet& BasicApplicationContext::AddStructureSet(const std::string& instance) - { - std::auto_ptr structureSet(new DicomStructureSet(orthanc_, instance)); - - DicomStructureSet& result = *structureSet; - structureSets_.push_back(structureSet.release()); - - return result; - } - - - IWorldSceneInteractor& BasicApplicationContext::AddInteractor(IWorldSceneInteractor* interactor) - { - if (interactor == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - interactors_.push_back(interactor); - - return *interactor; - } - - - void BasicApplicationContext::Start() - { - for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it) - { - assert(*it != NULL); - (*it)->Start(); - } - - viewport_.Start(); - } - - - void BasicApplicationContext::Stop() - { - viewport_.Stop(); - - for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it) - { - assert(*it != NULL); - (*it)->Stop(); - } - } -} diff -r 369fe11606fa -r 37e504582af6 Framework/Applications/BasicApplicationContext.h --- a/Framework/Applications/BasicApplicationContext.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "../Volumes/VolumeImage.h" -#include "../Viewport/WidgetViewport.h" -#include "../Widgets/IWorldSceneInteractor.h" -#include "../Toolbox/DicomStructureSet.h" - -#include - -namespace OrthancStone -{ - class BasicApplicationContext : public boost::noncopyable - { - private: - typedef std::list Volumes; - typedef std::list Interactors; - typedef std::list StructureSets; - - OrthancPlugins::IOrthancConnection& orthanc_; - - WidgetViewport viewport_; - Volumes volumes_; - Interactors interactors_; - StructureSets structureSets_; - - public: - BasicApplicationContext(OrthancPlugins::IOrthancConnection& orthanc); - - ~BasicApplicationContext(); - - IWidget& SetCentralWidget(IWidget* widget); // Takes ownership - - IViewport& GetViewport() - { - return viewport_; - } - - OrthancPlugins::IOrthancConnection& GetOrthancConnection() - { - return orthanc_; - } - - VolumeImage& AddSeriesVolume(const std::string& series, - bool isProgressiveDownload, - size_t downloadThreadCount); - - DicomStructureSet& AddStructureSet(const std::string& instance); - - IWorldSceneInteractor& AddInteractor(IWorldSceneInteractor* interactor); - - void Start(); - - void Stop(); - }; -} diff -r 369fe11606fa -r 37e504582af6 Framework/Applications/IBasicApplication.cpp --- a/Framework/Applications/IBasicApplication.cpp Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,273 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "IBasicApplication.h" - -#include "../../Resources/Orthanc/Core/Logging.h" -#include "../../Resources/Orthanc/Core/HttpClient.h" -#include "../../Resources/Orthanc/Plugins/Samples/Common/OrthancHttpConnection.h" -#include "Sdl/SdlEngine.h" - -namespace OrthancStone -{ - // Anonymous namespace to avoid clashes against other compilation modules - namespace - { - class LogStatusBar : public IStatusBar - { - public: - virtual void ClearMessage() - { - } - - virtual void SetMessage(const std::string& message) - { - LOG(WARNING) << message; - } - }; - } - - -#if ORTHANC_ENABLE_SDL == 1 - static void DeclareSdlCommandLineOptions(boost::program_options::options_description& options) - { - // Declare the supported parameters - boost::program_options::options_description generic("Generic options"); - generic.add_options() - ("help", "Display this help and exit") - ("verbose", "Be verbose in logs") - ("orthanc", boost::program_options::value()->default_value("http://localhost:8042/"), - "URL to the Orthanc server") - ("username", "Username for the Orthanc server") - ("password", "Password for the Orthanc server") - ("https-verify", boost::program_options::value()->default_value(true), "Check HTTPS certificates") - ; - - options.add(generic); - - boost::program_options::options_description sdl("SDL options"); - sdl.add_options() - ("width", boost::program_options::value()->default_value(1024), "Initial width of the SDL window") - ("height", boost::program_options::value()->default_value(768), "Initial height of the SDL window") - ("opengl", boost::program_options::value()->default_value(true), "Enable OpenGL in SDL") - ; - - options.add(sdl); - } - - - int IBasicApplication::ExecuteWithSdl(IBasicApplication& application, - int argc, - char* argv[]) - { - /****************************************************************** - * Initialize all the subcomponents of Orthanc Stone - ******************************************************************/ - - Orthanc::Logging::Initialize(); - Orthanc::HttpClient::InitializeOpenSsl(); - Orthanc::HttpClient::GlobalInitialize(); - SdlEngine::GlobalInitialize(); - - - /****************************************************************** - * Declare and parse the command-line options of the application - ******************************************************************/ - - boost::program_options::options_description options; - DeclareSdlCommandLineOptions(options); - application.DeclareCommandLineOptions(options); - - boost::program_options::variables_map parameters; - bool error = false; - - try - { - boost::program_options::store(boost::program_options::command_line_parser(argc, argv). - options(options).run(), parameters); - boost::program_options::notify(parameters); - } - catch (boost::program_options::error& e) - { - LOG(ERROR) << "Error while parsing the command-line arguments: " << e.what(); - error = true; - } - - - /****************************************************************** - * Configure the application with the command-line parameters - ******************************************************************/ - - if (error || parameters.count("help")) - { - std::cout << std::endl - << "Usage: " << argv[0] << " [OPTION]..." - << std::endl - << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research." - << std::endl << std::endl - << "Demonstration application of Orthanc Stone using SDL." - << std::endl; - - std::cout << options << "\n"; - return error ? -1 : 0; - } - - if (parameters.count("https-verify") && - !parameters["https-verify"].as()) - { - LOG(WARNING) << "Turning off verification of HTTPS certificates (unsafe)"; - Orthanc::HttpClient::ConfigureSsl(false, ""); - } - - if (parameters.count("verbose")) - { - Orthanc::Logging::EnableInfoLevel(true); - } - - if (!parameters.count("width") || - !parameters.count("height") || - !parameters.count("opengl")) - { - LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing"; - return -1; - } - - int w = parameters["width"].as(); - int h = parameters["height"].as(); - if (w <= 0 || h <= 0) - { - LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive"; - return -1; - } - - unsigned int width = static_cast(w); - unsigned int height = static_cast(h); - LOG(WARNING) << "Initial display size: " << width << "x" << height; - - bool opengl = parameters["opengl"].as(); - if (opengl) - { - LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes"; - } - else - { - LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance"; - } - - bool success = true; - try - { - /**************************************************************** - * Initialize the connection to the Orthanc server - ****************************************************************/ - - Orthanc::WebServiceParameters webService; - - if (parameters.count("orthanc")) - { - webService.SetUrl(parameters["orthanc"].as()); - } - - if (parameters.count("username")) - { - webService.SetUsername(parameters["username"].as()); - } - - if (parameters.count("password")) - { - webService.SetPassword(parameters["password"].as()); - } - - LOG(WARNING) << "URL to the Orthanc REST API: " << webService.GetUrl(); - OrthancPlugins::OrthancHttpConnection orthanc(webService); - - if (!MessagingToolbox::CheckOrthancVersion(orthanc)) - { - LOG(ERROR) << "Your version of Orthanc is incompatible with Orthanc Stone, please upgrade"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); - } - - - /**************************************************************** - * Initialize the application - ****************************************************************/ - - LogStatusBar statusBar; - BasicApplicationContext context(orthanc); - - application.Initialize(context, statusBar, parameters); - context.GetViewport().SetStatusBar(statusBar); - - std::string title = application.GetTitle(); - if (title.empty()) - { - title = "Stone of Orthanc"; - } - - context.Start(); - - { - /************************************************************** - * Run the application inside a SDL window - **************************************************************/ - - LOG(WARNING) << "Starting the application"; - - SdlWindow window(title.c_str(), width, height, opengl); - SdlEngine sdl(window, context.GetViewport()); - - sdl.Run(); - - LOG(WARNING) << "Stopping the application"; - } - - - /**************************************************************** - * Finalize the application - ****************************************************************/ - - context.Stop(); - - LOG(WARNING) << "The application has stopped"; - - context.GetViewport().ResetStatusBar(); - application.Finalize(); - } - catch (Orthanc::OrthancException& e) - { - LOG(ERROR) << "EXCEPTION: " << e.What(); - success = false; - } - - - /****************************************************************** - * Finalize all the subcomponents of Orthanc Stone - ******************************************************************/ - - SdlEngine::GlobalFinalize(); - Orthanc::HttpClient::GlobalFinalize(); - Orthanc::HttpClient::FinalizeOpenSsl(); - - return (success ? 0 : -1); - } -#endif - -} diff -r 369fe11606fa -r 37e504582af6 Framework/Applications/IBasicApplication.h --- a/Framework/Applications/IBasicApplication.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "BasicApplicationContext.h" - -#include - -#if ORTHANC_ENABLE_SDL == 1 -# include // Necessary to avoid undefined reference to `SDL_main' -#endif - -namespace OrthancStone -{ - class IBasicApplication : public boost::noncopyable - { - public: - virtual ~IBasicApplication() - { - } - - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0; - - virtual std::string GetTitle() const = 0; - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) = 0; - - virtual void Finalize() = 0; - -#if ORTHANC_ENABLE_SDL == 1 - static int ExecuteWithSdl(IBasicApplication& application, - int argc, - char* argv[]); -#endif - }; -} diff -r 369fe11606fa -r 37e504582af6 Framework/Applications/Sdl/SdlBuffering.cpp --- a/Framework/Applications/Sdl/SdlBuffering.cpp Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,134 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SdlBuffering.h" - -#if ORTHANC_ENABLE_SDL == 1 - -#include "../../../Resources/Orthanc/Core/Logging.h" -#include "../../../Resources/Orthanc/Core/OrthancException.h" - -namespace OrthancStone -{ - SdlBuffering::SdlBuffering() : - sdlSurface_(NULL), - pendingFrame_(false) - { - } - - - SdlBuffering::~SdlBuffering() - { - if (sdlSurface_) - { - SDL_FreeSurface(sdlSurface_); - } - } - - - void SdlBuffering::SetSize(unsigned int width, - unsigned int height, - IViewport& viewport) - { - boost::mutex::scoped_lock lock(mutex_); - - viewport.SetSize(width, height); - - if (offscreenSurface_.get() == NULL || - offscreenSurface_->GetWidth() != width || - offscreenSurface_->GetHeight() != height) - { - offscreenSurface_.reset(new CairoSurface(width, height)); - } - - if (onscreenSurface_.get() == NULL || - onscreenSurface_->GetWidth() != width || - onscreenSurface_->GetHeight() != height) - { - onscreenSurface_.reset(new CairoSurface(width, height)); - - // TODO Big endian? - static const uint32_t rmask = 0x00ff0000; - static const uint32_t gmask = 0x0000ff00; - static const uint32_t bmask = 0x000000ff; - - if (sdlSurface_) - { - SDL_FreeSurface(sdlSurface_); - } - - sdlSurface_ = SDL_CreateRGBSurfaceFrom(onscreenSurface_->GetBuffer(), width, height, 32, - onscreenSurface_->GetPitch(), rmask, gmask, bmask, 0); - if (!sdlSurface_) - { - LOG(ERROR) << "Cannot create a SDL surface from a Cairo surface"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - pendingFrame_ = false; - } - - - bool SdlBuffering::RenderOffscreen(IViewport& viewport) - { - boost::mutex::scoped_lock lock(mutex_); - - if (offscreenSurface_.get() == NULL) - { - return false; - } - - Orthanc::ImageAccessor target = offscreenSurface_->GetAccessor(); - - if (viewport.Render(target) && - !pendingFrame_) - { - pendingFrame_ = true; - return true; - } - else - { - return false; - } - } - - - void SdlBuffering::SwapToScreen(SdlWindow& window) - { - if (!pendingFrame_ || - offscreenSurface_.get() == NULL || - onscreenSurface_.get() == NULL) - { - return; - } - - { - boost::mutex::scoped_lock lock(mutex_); - onscreenSurface_->Copy(*offscreenSurface_); - } - - window.Render(sdlSurface_); - pendingFrame_ = false; - } -} - -#endif diff -r 369fe11606fa -r 37e504582af6 Framework/Applications/Sdl/SdlBuffering.h --- a/Framework/Applications/Sdl/SdlBuffering.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 - -#if ORTHANC_ENABLE_SDL == 1 - -#include "SdlWindow.h" -#include "../../Viewport/CairoSurface.h" -#include "../../Viewport/IViewport.h" - -#include - -namespace OrthancStone -{ - class SdlBuffering : public boost::noncopyable - { - private: - boost::mutex mutex_; - std::auto_ptr offscreenSurface_; - std::auto_ptr onscreenSurface_; - SDL_Surface* sdlSurface_; - bool pendingFrame_; - - public: - SdlBuffering(); - - ~SdlBuffering(); - - void SetSize(unsigned int width, - unsigned int height, - IViewport& viewport); - - // Returns "true" if a new refresh of the display should be - // triggered afterwards - bool RenderOffscreen(IViewport& viewport); - - void SwapToScreen(SdlWindow& window); - }; -} - -#endif diff -r 369fe11606fa -r 37e504582af6 Framework/Applications/Sdl/SdlEngine.cpp --- a/Framework/Applications/Sdl/SdlEngine.cpp Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,319 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SdlEngine.h" - -#if ORTHANC_ENABLE_SDL == 1 - -#include "../../../Resources/Orthanc/Core/Logging.h" - -#include - -namespace OrthancStone -{ - void SdlEngine::RenderFrame() - { - if (!viewportChanged_) - { - return; - } - - viewportChanged_ = false; - - if (buffering_.RenderOffscreen(viewport_)) - { - // Do not notify twice when a new frame was rendered, to avoid - // spoiling the SDL event queue - SDL_Event event; - SDL_memset(&event, 0, sizeof(event)); - event.type = refreshEvent_; - event.user.code = 0; - event.user.data1 = 0; - event.user.data2 = 0; - SDL_PushEvent(&event); - } - } - - - void SdlEngine::RenderThread(SdlEngine* that) - { - for (;;) - { - that->renderFrame_.Wait(); - - if (that->continue_) - { - that->RenderFrame(); - } - else - { - return; - } - } - } - - - KeyboardModifiers SdlEngine::GetKeyboardModifiers(const uint8_t* keyboardState, - const int scancodeCount) - { - int result = KeyboardModifiers_None; - - if (keyboardState != NULL) - { - if (SDL_SCANCODE_LSHIFT < scancodeCount && - keyboardState[SDL_SCANCODE_LSHIFT]) - { - result |= KeyboardModifiers_Shift; - } - - if (SDL_SCANCODE_RSHIFT < scancodeCount && - keyboardState[SDL_SCANCODE_RSHIFT]) - { - result |= KeyboardModifiers_Shift; - } - - if (SDL_SCANCODE_LCTRL < scancodeCount && - keyboardState[SDL_SCANCODE_LCTRL]) - { - result |= KeyboardModifiers_Control; - } - - if (SDL_SCANCODE_RCTRL < scancodeCount && - keyboardState[SDL_SCANCODE_RCTRL]) - { - result |= KeyboardModifiers_Control; - } - - if (SDL_SCANCODE_LALT < scancodeCount && - keyboardState[SDL_SCANCODE_LALT]) - { - result |= KeyboardModifiers_Alt; - } - - if (SDL_SCANCODE_RALT < scancodeCount && - keyboardState[SDL_SCANCODE_RALT]) - { - result |= KeyboardModifiers_Alt; - } - } - - return static_cast(result); - } - - - void SdlEngine::SetSize(unsigned int width, - unsigned int height) - { - buffering_.SetSize(width, height, viewport_); - viewportChanged_ = true; - Refresh(); - } - - - void SdlEngine::Stop() - { - if (continue_) - { - continue_ = false; - renderFrame_.Signal(); // Unlock the render thread - renderThread_.join(); - } - } - - - void SdlEngine::Refresh() - { - renderFrame_.Signal(); - } - - - SdlEngine::SdlEngine(SdlWindow& window, - IViewport& viewport) : - window_(window), - viewport_(viewport), - continue_(true) - { - refreshEvent_ = SDL_RegisterEvents(1); - - SetSize(window_.GetWidth(), window_.GetHeight()); - - viewport_.Register(*this); - - renderThread_ = boost::thread(RenderThread, this); - } - - - SdlEngine::~SdlEngine() - { - Stop(); - - viewport_.Unregister(*this); - } - - - void SdlEngine::Run() - { - int scancodeCount = 0; - const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); - - bool stop = false; - while (!stop) - { - Refresh(); - - SDL_Event event; - - while (SDL_PollEvent(&event)) - { - if (event.type == SDL_QUIT) - { - stop = true; - break; - } - else if (event.type == refreshEvent_) - { - buffering_.SwapToScreen(window_); - } - else if (event.type == SDL_MOUSEBUTTONDOWN) - { - KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); - - switch (event.button.button) - { - case SDL_BUTTON_LEFT: - viewport_.MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers); - break; - - case SDL_BUTTON_RIGHT: - viewport_.MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers); - break; - - case SDL_BUTTON_MIDDLE: - viewport_.MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers); - break; - - default: - break; - } - } - else if (event.type == SDL_MOUSEMOTION) - { - viewport_.MouseMove(event.button.x, event.button.y); - } - else if (event.type == SDL_MOUSEBUTTONUP) - { - viewport_.MouseUp(); - } - else if (event.type == SDL_WINDOWEVENT) - { - switch (event.window.event) - { - case SDL_WINDOWEVENT_LEAVE: - viewport_.MouseLeave(); - break; - - case SDL_WINDOWEVENT_ENTER: - viewport_.MouseEnter(); - break; - - case SDL_WINDOWEVENT_SIZE_CHANGED: - SetSize(event.window.data1, event.window.data2); - break; - - default: - break; - } - } - else if (event.type == SDL_MOUSEWHEEL) - { - KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); - - int x, y; - SDL_GetMouseState(&x, &y); - - if (event.wheel.y > 0) - { - viewport_.MouseWheel(MouseWheelDirection_Up, x, y, modifiers); - } - else if (event.wheel.y < 0) - { - viewport_.MouseWheel(MouseWheelDirection_Down, x, y, modifiers); - } - } - else if (event.type == SDL_KEYDOWN) - { - KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); - - switch (event.key.keysym.sym) - { - case SDLK_a: viewport_.KeyPressed('a', modifiers); break; - case SDLK_b: viewport_.KeyPressed('b', modifiers); break; - case SDLK_c: viewport_.KeyPressed('c', modifiers); break; - case SDLK_d: viewport_.KeyPressed('d', modifiers); break; - case SDLK_e: viewport_.KeyPressed('e', modifiers); break; - case SDLK_f: window_.ToggleMaximize(); break; - case SDLK_g: viewport_.KeyPressed('g', modifiers); break; - case SDLK_h: viewport_.KeyPressed('h', modifiers); break; - case SDLK_i: viewport_.KeyPressed('i', modifiers); break; - case SDLK_j: viewport_.KeyPressed('j', modifiers); break; - case SDLK_k: viewport_.KeyPressed('k', modifiers); break; - case SDLK_l: viewport_.KeyPressed('l', modifiers); break; - case SDLK_m: viewport_.KeyPressed('m', modifiers); break; - case SDLK_n: viewport_.KeyPressed('n', modifiers); break; - case SDLK_o: viewport_.KeyPressed('o', modifiers); break; - case SDLK_p: viewport_.KeyPressed('p', modifiers); break; - case SDLK_q: stop = true; break; - case SDLK_r: viewport_.KeyPressed('r', modifiers); break; - case SDLK_s: viewport_.KeyPressed('s', modifiers); break; - case SDLK_t: viewport_.KeyPressed('t', modifiers); break; - case SDLK_u: viewport_.KeyPressed('u', modifiers); break; - case SDLK_v: viewport_.KeyPressed('v', modifiers); break; - case SDLK_w: viewport_.KeyPressed('w', modifiers); break; - case SDLK_x: viewport_.KeyPressed('x', modifiers); break; - case SDLK_y: viewport_.KeyPressed('y', modifiers); break; - case SDLK_z: viewport_.KeyPressed('z', modifiers); break; - - default: - break; - } - } - } - - SDL_Delay(10); // Necessary for mouse wheel events to work - } - - Stop(); - } - - - void SdlEngine::GlobalInitialize() - { - SDL_Init(SDL_INIT_VIDEO); - } - - - void SdlEngine::GlobalFinalize() - { - SDL_Quit(); - } -} - -#endif diff -r 369fe11606fa -r 37e504582af6 Framework/Applications/Sdl/SdlEngine.h --- a/Framework/Applications/Sdl/SdlEngine.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 - -#if ORTHANC_ENABLE_SDL == 1 - -#include "SdlBuffering.h" -#include "../../Toolbox/BinarySemaphore.h" - -#include - -namespace OrthancStone -{ - class SdlEngine : public IViewport::IChangeObserver - { - private: - SdlWindow& window_; - IViewport& viewport_; - SdlBuffering buffering_; - boost::thread renderThread_; - bool continue_; - BinarySemaphore renderFrame_; - uint32_t refreshEvent_; - bool viewportChanged_; - - void RenderFrame(); - - static void RenderThread(SdlEngine* that); - - static KeyboardModifiers GetKeyboardModifiers(const uint8_t* keyboardState, - const int scancodeCount); - - void SetSize(unsigned int width, - unsigned int height); - - void Stop(); - - void Refresh(); - - public: - SdlEngine(SdlWindow& window, - IViewport& viewport); - - virtual ~SdlEngine(); - - virtual void NotifyChange(const IViewport& viewport) - { - viewportChanged_ = true; - } - - void Run(); - - static void GlobalInitialize(); - - static void GlobalFinalize(); - }; -} - -#endif diff -r 369fe11606fa -r 37e504582af6 Framework/Applications/Sdl/SdlWindow.cpp --- a/Framework/Applications/Sdl/SdlWindow.cpp Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,150 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SdlWindow.h" - -#if ORTHANC_ENABLE_SDL == 1 - -#include "../../../Resources/Orthanc/Core/Logging.h" -#include "../../../Resources/Orthanc/Core/OrthancException.h" - -namespace OrthancStone -{ - SdlWindow::SdlWindow(const char* title, - unsigned int width, - unsigned int height, - bool enableOpenGl) : - maximized_(false) - { - // TODO Understand why, with SDL_WINDOW_OPENGL + MinGW32 + Release - // build mode, the application crashes whenever the SDL window is - // resized or maximized - - uint32_t windowFlags, rendererFlags; - if (enableOpenGl) - { - windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; - rendererFlags = SDL_RENDERER_ACCELERATED; - } - else - { - windowFlags = SDL_WINDOW_RESIZABLE; - rendererFlags = SDL_RENDERER_SOFTWARE; - } - - window_ = SDL_CreateWindow(title, - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - width, height, windowFlags); - - if (window_ == NULL) - { - LOG(ERROR) << "Cannot create the SDL window: " << SDL_GetError(); - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - renderer_ = SDL_CreateRenderer(window_, -1, rendererFlags); - if (!renderer_) - { - LOG(ERROR) << "Cannot create the SDL renderer: " << SDL_GetError(); - SDL_DestroyWindow(window_); - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - - SdlWindow::~SdlWindow() - { - if (renderer_ != NULL) - { - SDL_DestroyRenderer(renderer_); - } - - if (window_ != NULL) - { - SDL_DestroyWindow(window_); - } - } - - - unsigned int SdlWindow::GetWidth() const - { - int w = -1; - SDL_GetWindowSize(window_, &w, NULL); - - if (w < 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - else - { - return static_cast(w); - } - } - - - unsigned int SdlWindow::GetHeight() const - { - int h = -1; - SDL_GetWindowSize(window_, NULL, &h); - - if (h < 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - else - { - return static_cast(h); - } - } - - - void SdlWindow::Render(SDL_Surface* surface) - { - //SDL_RenderClear(renderer_); - - SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer_, surface); - if (texture != NULL) - { - SDL_RenderCopy(renderer_, texture, NULL, NULL); - SDL_DestroyTexture(texture); - } - - SDL_RenderPresent(renderer_); - } - - - void SdlWindow::ToggleMaximize() - { - if (maximized_) - { - SDL_RestoreWindow(window_); - maximized_ = false; - } - else - { - SDL_MaximizeWindow(window_); - maximized_ = true; - } - } -} - -#endif diff -r 369fe11606fa -r 37e504582af6 Framework/Applications/Sdl/SdlWindow.h --- a/Framework/Applications/Sdl/SdlWindow.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 - -#if ORTHANC_ENABLE_SDL == 1 - -#include -#include -#include - -namespace OrthancStone -{ - class SdlWindow : public boost::noncopyable - { - private: - SDL_Window *window_; - SDL_Renderer *renderer_; - bool maximized_; - - public: - SdlWindow(const char* title, - unsigned int width, - unsigned int height, - bool enableOpenGl); - - ~SdlWindow(); - - unsigned int GetWidth() const; - - unsigned int GetHeight() const; - - void Render(SDL_Surface* surface); - - void ToggleMaximize(); - }; -} - -#endif diff -r 369fe11606fa -r 37e504582af6 Framework/Toolbox/BinarySemaphore.cpp --- a/Framework/Toolbox/BinarySemaphore.cpp Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "BinarySemaphore.h" - -namespace OrthancStone -{ - BinarySemaphore::BinarySemaphore() : - proceed_(false) - { - } - - void BinarySemaphore::Signal() - { - //boost::mutex::scoped_lock lock(mutex_); - - proceed_ = true; - condition_.notify_one(); - } - - void BinarySemaphore::Wait() - { - boost::mutex::scoped_lock lock(mutex_); - - while (!proceed_) - { - condition_.wait(lock); - } - - proceed_ = false; - } -} diff -r 369fe11606fa -r 37e504582af6 Framework/Toolbox/BinarySemaphore.h --- a/Framework/Toolbox/BinarySemaphore.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 -#include - -namespace OrthancStone -{ - class BinarySemaphore : public boost::noncopyable - { - private: - bool proceed_; - boost::mutex mutex_; - boost::condition_variable condition_; - - public: - explicit BinarySemaphore(); - - void Signal(); - - void Wait(); - }; -} diff -r 369fe11606fa -r 37e504582af6 Resources/CMake/OrthancStone.cmake --- a/Resources/CMake/OrthancStone.cmake Thu Apr 27 11:09:58 2017 +0200 +++ b/Resources/CMake/OrthancStone.cmake Thu Apr 27 14:50:40 2017 +0200 @@ -176,11 +176,13 @@ ##################################################################### list(APPEND ORTHANC_STONE_SOURCES - ${ORTHANC_STONE_DIR}/Framework/Applications/BasicApplicationContext.cpp - ${ORTHANC_STONE_DIR}/Framework/Applications/IBasicApplication.cpp - ${ORTHANC_STONE_DIR}/Framework/Applications/Sdl/SdlBuffering.cpp - ${ORTHANC_STONE_DIR}/Framework/Applications/Sdl/SdlEngine.cpp - ${ORTHANC_STONE_DIR}/Framework/Applications/Sdl/SdlWindow.cpp + ${ORTHANC_STONE_DIR}/Applications/BasicApplicationContext.cpp + ${ORTHANC_STONE_DIR}/Applications/BinarySemaphore.cpp + ${ORTHANC_STONE_DIR}/Applications/IBasicApplication.cpp + ${ORTHANC_STONE_DIR}/Applications/Sdl/SdlBuffering.cpp + ${ORTHANC_STONE_DIR}/Applications/Sdl/SdlEngine.cpp + ${ORTHANC_STONE_DIR}/Applications/Sdl/SdlWindow.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/CircleMeasureTracker.cpp ${ORTHANC_STONE_DIR}/Framework/Layers/ColorFrameRenderer.cpp ${ORTHANC_STONE_DIR}/Framework/Layers/DicomStructureSetRendererFactory.cpp @@ -192,7 +194,6 @@ ${ORTHANC_STONE_DIR}/Framework/Layers/SeriesFrameRendererFactory.cpp ${ORTHANC_STONE_DIR}/Framework/Layers/SiblingSliceLocationFactory.cpp ${ORTHANC_STONE_DIR}/Framework/Layers/SingleFrameRendererFactory.cpp - ${ORTHANC_STONE_DIR}/Framework/Toolbox/BinarySemaphore.cpp ${ORTHANC_STONE_DIR}/Framework/Toolbox/DicomFrameConverter.cpp ${ORTHANC_STONE_DIR}/Framework/Toolbox/DicomStructureSet.cpp ${ORTHANC_STONE_DIR}/Framework/Toolbox/DownloadStack.cpp diff -r 369fe11606fa -r 37e504582af6 Samples/BasicPetCtFusionApplication.h --- a/Samples/BasicPetCtFusionApplication.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SampleInteractor.h" - -#include "../Resources/Orthanc/Core/Logging.h" - -namespace OrthancStone -{ - namespace Samples - { - class BasicPetCtFusionApplication : public SampleApplicationBase - { - private: - class Interactor : public SampleInteractor - { - public: - static void SetStyle(LayeredSceneWidget& widget, - bool ct, - bool pet) - { - if (ct) - { - RenderStyle style; - style.windowing_ = ImageWindowing_Bone; - widget.SetLayerStyle(0, style); - } - else - { - RenderStyle style; - style.visible_ = false; - widget.SetLayerStyle(0, style); - } - - if (ct && pet) - { - RenderStyle style; - style.applyLut_ = true; - style.alpha_ = 0.5; - widget.SetLayerStyle(1, style); - } - else if (pet) - { - RenderStyle style; - style.applyLut_ = true; - widget.SetLayerStyle(1, style); - } - else - { - RenderStyle style; - style.visible_ = false; - widget.SetLayerStyle(1, style); - } - } - - - static bool IsVisible(LayeredSceneWidget& widget, - size_t layer) - { - RenderStyle style = widget.GetLayerStyle(layer); - return style.visible_; - } - - - static void ToggleInterpolation(LayeredSceneWidget& widget, - size_t layer) - { - RenderStyle style = widget.GetLayerStyle(layer); - - if (style.interpolation_ == ImageInterpolation_Linear) - { - style.interpolation_ = ImageInterpolation_Nearest; - } - else - { - style.interpolation_ = ImageInterpolation_Linear; - } - - widget.SetLayerStyle(layer, style); - } - - - Interactor(VolumeImage& volume, - VolumeProjection projection, - bool reverse) : - SampleInteractor(volume, projection, reverse) - { - } - - - virtual void KeyPressed(WorldSceneWidget& widget, - char key, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - LayeredSceneWidget& layered = dynamic_cast(widget); - - switch (key) - { - case 'c': - // Toggle the visibility of the CT layer - SetStyle(layered, !IsVisible(layered, 0), IsVisible(layered, 1)); - break; - - case 'p': - // Toggle the visibility of the PET layer - SetStyle(layered, IsVisible(layered, 0), !IsVisible(layered, 1)); - break; - - case 'i': - { - // Toggle on/off the interpolation - ToggleInterpolation(layered, 0); - ToggleInterpolation(layered, 1); - break; - } - - default: - break; - } - } - }; - - - public: - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("ct", boost::program_options::value(), - "Orthanc ID of the CT series") - ("pet", boost::program_options::value(), - "Orthanc ID of the PET series") - ("threads", boost::program_options::value()->default_value(3), - "Number of download threads for the CT series") - ; - - options.add(generic); - } - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - if (parameters.count("ct") != 1 || - parameters.count("pet") != 1) - { - LOG(ERROR) << "The series ID is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::string ct = parameters["ct"].as(); - std::string pet = parameters["pet"].as(); - unsigned int threads = parameters["threads"].as(); - - VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads); - VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1); - - // Take the PET volume as the reference for the slices - std::auto_ptr interactor(new Interactor(petVolume, VolumeProjection_Axial, false /* don't reverse normal */)); - - std::auto_ptr widget(new LayeredSceneWidget); - widget->AddLayer(new VolumeImage::LayerFactory(ctVolume)); - widget->AddLayer(new VolumeImage::LayerFactory(petVolume)); - widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); - widget->SetInteractor(*interactor); - - Interactor::SetStyle(*widget, true, true); // Initially, show both CT and PET layers - - context.AddInteractor(interactor.release()); - context.SetCentralWidget(widget.release()); - - statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode"); - statusBar.SetMessage("Use the key \"c\" to show/hide the CT layer"); - statusBar.SetMessage("Use the key \"p\" to show/hide the PET layer"); - statusBar.SetMessage("Use the key \"i\" to toggle the smoothing of the images"); - } - }; - } -} diff -r 369fe11606fa -r 37e504582af6 Samples/EmptyApplication.h --- a/Samples/EmptyApplication.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SampleApplicationBase.h" - -#include "../Framework/Widgets/EmptyWidget.h" - -namespace OrthancStone -{ - namespace Samples - { - class EmptyApplication : public SampleApplicationBase - { - public: - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("red", boost::program_options::value()->default_value(255), "Background color: red channel") - ("green", boost::program_options::value()->default_value(0), "Background color: green channel") - ("blue", boost::program_options::value()->default_value(0), "Background color: blue channel") - ; - - options.add(generic); - } - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - int red = parameters["red"].as(); - int green = parameters["green"].as(); - int blue = parameters["blue"].as(); - - context.SetCentralWidget(new EmptyWidget(red, green, blue)); - } - }; - } -} diff -r 369fe11606fa -r 37e504582af6 Samples/LayoutPetCtFusionApplication.h --- a/Samples/LayoutPetCtFusionApplication.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,398 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SampleInteractor.h" - -#include "../Framework/Layers/SiblingSliceLocationFactory.h" -#include "../Framework/Layers/DicomStructureSetRendererFactory.h" -#include "../Framework/Widgets/LayoutWidget.h" - -#include "../Resources/Orthanc/Core/Logging.h" - -namespace OrthancStone -{ - namespace Samples - { - class LayoutPetCtFusionApplication : - public SampleApplicationBase, - public LayeredSceneWidget::ISliceObserver, - public WorldSceneWidget::IWorldObserver - { - private: - class Interactor : public SampleInteractor - { - private: - LayoutPetCtFusionApplication& that_; - - public: - Interactor(LayoutPetCtFusionApplication& that, - VolumeImage& volume, - VolumeProjection projection, - bool reverse) : - SampleInteractor(volume, projection, reverse), - that_(that) - { - } - - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const SliceGeometry& slice, - const ViewportGeometry& view, - MouseButton button, - double x, - double y, - IStatusBar* statusBar) - { - if (button == MouseButton_Left) - { - // Center the sibling views over the clicked point - Vector p = slice.MapSliceToWorldCoordinates(x, y); - - if (statusBar != NULL) - { - char buf[64]; - sprintf(buf, "Click on coordinates (%.02f,%.02f,%.02f) in cm", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); - statusBar->SetMessage(buf); - } - - that_.interactorAxial_->LookupSliceContainingPoint(*that_.ctAxial_, p); - that_.interactorCoronal_->LookupSliceContainingPoint(*that_.ctCoronal_, p); - that_.interactorSagittal_->LookupSliceContainingPoint(*that_.ctSagittal_, p); - } - - return NULL; - } - - virtual void KeyPressed(WorldSceneWidget& widget, - char key, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - if (key == 's') - { - that_.SetDefaultView(); - } - } - }; - - bool processingEvent_; - Interactor* interactorAxial_; - Interactor* interactorCoronal_; - Interactor* interactorSagittal_; - LayeredSceneWidget* ctAxial_; - LayeredSceneWidget* ctCoronal_; - LayeredSceneWidget* ctSagittal_; - LayeredSceneWidget* petAxial_; - LayeredSceneWidget* petCoronal_; - LayeredSceneWidget* petSagittal_; - LayeredSceneWidget* fusionAxial_; - LayeredSceneWidget* fusionCoronal_; - LayeredSceneWidget* fusionSagittal_; - - - void SetDefaultView() - { - petAxial_->SetDefaultView(); - petCoronal_->SetDefaultView(); - petSagittal_->SetDefaultView(); - } - - - void AddLayer(LayeredSceneWidget& widget, - VolumeImage& volume, - bool isCt) - { - size_t layer; - widget.AddLayer(layer, new VolumeImage::LayerFactory(volume)); - - if (isCt) - { - RenderStyle style; - style.windowing_ = ImageWindowing_Bone; - widget.SetLayerStyle(layer, style); - } - else - { - RenderStyle style; - style.applyLut_ = true; - style.alpha_ = (layer == 0 ? 1.0f : 0.5f); - widget.SetLayerStyle(layer, style); - } - } - - - void ConnectSiblingLocations(LayeredSceneWidget& axial, - LayeredSceneWidget& coronal, - LayeredSceneWidget& sagittal) - { - SiblingSliceLocationFactory::Configure(axial, coronal); - SiblingSliceLocationFactory::Configure(axial, sagittal); - SiblingSliceLocationFactory::Configure(coronal, sagittal); - } - - - void SynchronizeView(const WorldSceneWidget& source, - const ViewportGeometry& view, - LayeredSceneWidget& widget1, - LayeredSceneWidget& widget2, - LayeredSceneWidget& widget3) - { - if (&source == &widget1 || - &source == &widget2 || - &source == &widget3) - { - if (&source != &widget1) - { - widget1.SetView(view); - } - - if (&source != &widget2) - { - widget2.SetView(view); - } - - if (&source != &widget3) - { - widget3.SetView(view); - } - } - } - - - void SynchronizeSlice(const LayeredSceneWidget& source, - const SliceGeometry& slice, - LayeredSceneWidget& widget1, - LayeredSceneWidget& widget2, - LayeredSceneWidget& widget3) - { - if (&source == &widget1 || - &source == &widget2 || - &source == &widget3) - { - if (&source != &widget1) - { - widget1.SetSlice(slice); - } - - if (&source != &widget2) - { - widget2.SetSlice(slice); - } - - if (&source != &widget3) - { - widget3.SetSlice(slice); - } - } - } - - - LayeredSceneWidget* CreateWidget() - { - std::auto_ptr widget(new LayeredSceneWidget); - widget->Register(dynamic_cast(*this)); - widget->Register(dynamic_cast(*this)); - return widget.release(); - } - - - void CreateLayout(BasicApplicationContext& context) - { - std::auto_ptr layout(new OrthancStone::LayoutWidget); - layout->SetBackgroundCleared(true); - //layout->SetBackgroundColor(255,0,0); - layout->SetPadding(5); - - OrthancStone::LayoutWidget& layoutA = dynamic_cast - (layout->AddWidget(new OrthancStone::LayoutWidget)); - layoutA.SetPadding(0, 0, 0, 0, 5); - layoutA.SetVertical(); - petAxial_ = &dynamic_cast(layoutA.AddWidget(CreateWidget())); - OrthancStone::LayoutWidget& layoutA2 = dynamic_cast - (layoutA.AddWidget(new OrthancStone::LayoutWidget)); - layoutA2.SetPadding(0, 0, 0, 0, 5); - petSagittal_ = &dynamic_cast(layoutA2.AddWidget(CreateWidget())); - petCoronal_ = &dynamic_cast(layoutA2.AddWidget(CreateWidget())); - - OrthancStone::LayoutWidget& layoutB = dynamic_cast - (layout->AddWidget(new OrthancStone::LayoutWidget)); - layoutB.SetPadding(0, 0, 0, 0, 5); - layoutB.SetVertical(); - ctAxial_ = &dynamic_cast(layoutB.AddWidget(CreateWidget())); - OrthancStone::LayoutWidget& layoutB2 = dynamic_cast - (layoutB.AddWidget(new OrthancStone::LayoutWidget)); - layoutB2.SetPadding(0, 0, 0, 0, 5); - ctSagittal_ = &dynamic_cast(layoutB2.AddWidget(CreateWidget())); - ctCoronal_ = &dynamic_cast(layoutB2.AddWidget(CreateWidget())); - - OrthancStone::LayoutWidget& layoutC = dynamic_cast - (layout->AddWidget(new OrthancStone::LayoutWidget)); - layoutC.SetPadding(0, 0, 0, 0, 5); - layoutC.SetVertical(); - fusionAxial_ = &dynamic_cast(layoutC.AddWidget(CreateWidget())); - OrthancStone::LayoutWidget& layoutC2 = dynamic_cast - (layoutC.AddWidget(new OrthancStone::LayoutWidget)); - layoutC2.SetPadding(0, 0, 0, 0, 5); - fusionSagittal_ = &dynamic_cast(layoutC2.AddWidget(CreateWidget())); - fusionCoronal_ = &dynamic_cast(layoutC2.AddWidget(CreateWidget())); - - context.SetCentralWidget(layout.release()); - } - - - public: - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("ct", boost::program_options::value(), - "Orthanc ID of the CT series") - ("pet", boost::program_options::value(), - "Orthanc ID of the PET series") - ("rt", boost::program_options::value(), - "Orthanc ID of the DICOM RT-STRUCT series (optional)") - ("threads", boost::program_options::value()->default_value(3), - "Number of download threads for the CT series") - ; - - options.add(generic); - } - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - processingEvent_ = true; - - if (parameters.count("ct") != 1 || - parameters.count("pet") != 1) - { - LOG(ERROR) << "The series ID is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::string ct = parameters["ct"].as(); - std::string pet = parameters["pet"].as(); - unsigned int threads = parameters["threads"].as(); - - VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads); - VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1); - - // Take the PET volume as the reference for the slices - interactorAxial_ = &dynamic_cast - (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Axial, false))); - interactorCoronal_ = &dynamic_cast - (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Coronal, false))); - interactorSagittal_ = &dynamic_cast - (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Sagittal, true))); - - CreateLayout(context); - - AddLayer(*ctAxial_, ctVolume, true); - AddLayer(*ctCoronal_, ctVolume, true); - AddLayer(*ctSagittal_, ctVolume, true); - - AddLayer(*petAxial_, petVolume, false); - AddLayer(*petCoronal_, petVolume, false); - AddLayer(*petSagittal_, petVolume, false); - - AddLayer(*fusionAxial_, ctVolume, true); - AddLayer(*fusionAxial_, petVolume, false); - AddLayer(*fusionCoronal_, ctVolume, true); - AddLayer(*fusionCoronal_, petVolume, false); - AddLayer(*fusionSagittal_, ctVolume, true); - AddLayer(*fusionSagittal_, petVolume, false); - - if (parameters.count("rt") == 1) - { - DicomStructureSet& rtStruct = context.AddStructureSet(parameters["rt"].as()); - - Vector p = rtStruct.GetStructureCenter(0); - interactorAxial_->GetCursor().LookupSliceContainingPoint(p); - - ctAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct)); - petAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct)); - fusionAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct)); - } - - ConnectSiblingLocations(*ctAxial_, *ctCoronal_, *ctSagittal_); - ConnectSiblingLocations(*petAxial_, *petCoronal_, *petSagittal_); - ConnectSiblingLocations(*fusionAxial_, *fusionCoronal_, *fusionSagittal_); - - interactorAxial_->AddWidget(*ctAxial_); - interactorAxial_->AddWidget(*petAxial_); - interactorAxial_->AddWidget(*fusionAxial_); - - interactorCoronal_->AddWidget(*ctCoronal_); - interactorCoronal_->AddWidget(*petCoronal_); - interactorCoronal_->AddWidget(*fusionCoronal_); - - interactorSagittal_->AddWidget(*ctSagittal_); - interactorSagittal_->AddWidget(*petSagittal_); - interactorSagittal_->AddWidget(*fusionSagittal_); - - processingEvent_ = false; - - statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode"); - statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); - } - - virtual void NotifySizeChange(const WorldSceneWidget& source, - ViewportGeometry& view) - { - view.SetDefaultView(); - } - - virtual void NotifyViewChange(const WorldSceneWidget& source, - const ViewportGeometry& view) - { - if (!processingEvent_) // Avoid reentrant calls - { - processingEvent_ = true; - - SynchronizeView(source, view, *ctAxial_, *petAxial_, *fusionAxial_); - SynchronizeView(source, view, *ctCoronal_, *petCoronal_, *fusionCoronal_); - SynchronizeView(source, view, *ctSagittal_, *petSagittal_, *fusionSagittal_); - - processingEvent_ = false; - } - } - - virtual void NotifySliceChange(const LayeredSceneWidget& source, - const SliceGeometry& slice) - { - if (!processingEvent_) // Avoid reentrant calls - { - processingEvent_ = true; - - SynchronizeSlice(source, slice, *ctAxial_, *petAxial_, *fusionAxial_); - SynchronizeSlice(source, slice, *ctCoronal_, *petCoronal_, *fusionCoronal_); - SynchronizeSlice(source, slice, *ctSagittal_, *petSagittal_, *fusionSagittal_); - - processingEvent_ = false; - } - } - }; - } -} diff -r 369fe11606fa -r 37e504582af6 Samples/SampleApplicationBase.h --- a/Samples/SampleApplicationBase.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "../Framework/Applications/IBasicApplication.h" - -namespace OrthancStone -{ - namespace Samples - { - class SampleApplicationBase : public IBasicApplication - { - public: - virtual std::string GetTitle() const - { - return "Stone of Orthanc - Sample"; - } - - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - } - - virtual void Finalize() - { - } - }; - } -} diff -r 369fe11606fa -r 37e504582af6 Samples/SampleInteractor.h --- a/Samples/SampleInteractor.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SampleApplicationBase.h" - -#include "../Framework/Widgets/LayeredSceneWidget.h" -#include "../Framework/Widgets/IWorldSceneInteractor.h" -#include "../Framework/Toolbox/ParallelSlicesCursor.h" - -namespace OrthancStone -{ - namespace Samples - { - /** - * This is a basic mouse interactor for sample applications. It - * contains a set of parallel slices in the 3D space. The mouse - * wheel events make the widget change the slice that is - * displayed. - **/ - class SampleInteractor : public IWorldSceneInteractor - { - private: - ParallelSlicesCursor cursor_; - - public: - SampleInteractor(VolumeImage& volume, - VolumeProjection projection, - bool reverse) - { - std::auto_ptr slices(volume.GetGeometry(projection, reverse)); - cursor_.SetGeometry(*slices); - } - - SampleInteractor(ISeriesLoader& series, - bool reverse) - { - if (reverse) - { - std::auto_ptr slices(series.GetGeometry().Reverse()); - cursor_.SetGeometry(*slices); - } - else - { - cursor_.SetGeometry(series.GetGeometry()); - } - } - - SampleInteractor(const ParallelSlices& slices) - { - cursor_.SetGeometry(slices); - } - - ParallelSlicesCursor& GetCursor() - { - return cursor_; - } - - void AddWidget(LayeredSceneWidget& widget) - { - widget.SetInteractor(*this); - widget.SetSlice(cursor_.GetCurrentSlice()); - } - - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const SliceGeometry& slice, - const ViewportGeometry& view, - MouseButton button, - double x, - double y, - IStatusBar* statusBar) - { - return NULL; - } - - virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const SliceGeometry& slice, - const ViewportGeometry& view, - double x, - double y, - IStatusBar* statusBar) - { - } - - virtual void MouseWheel(WorldSceneWidget& widget, - MouseWheelDirection direction, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - if (cursor_.ApplyWheelEvent(direction, modifiers)) - { - dynamic_cast(widget).SetSlice(cursor_.GetCurrentSlice()); - } - } - - virtual void KeyPressed(WorldSceneWidget& widget, - char key, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - } - - void LookupSliceContainingPoint(LayeredSceneWidget& widget, - const Vector& p) - { - if (cursor_.LookupSliceContainingPoint(p)) - { - widget.SetSlice(cursor_.GetCurrentSlice()); - } - } - }; - } -} diff -r 369fe11606fa -r 37e504582af6 Samples/SampleMainSdl.cpp --- a/Samples/SampleMainSdl.cpp Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 . - **/ - - -// The macro "ORTHANC_STONE_SAMPLE" must be set by the CMake script - -#if ORTHANC_STONE_SAMPLE == 1 -#include "EmptyApplication.h" -typedef OrthancStone::Samples::EmptyApplication Application; - -#elif ORTHANC_STONE_SAMPLE == 2 -#include "TestPatternApplication.h" -typedef OrthancStone::Samples::TestPatternApplication Application; - -#elif ORTHANC_STONE_SAMPLE == 3 -#include "SingleFrameApplication.h" -typedef OrthancStone::Samples::SingleFrameApplication Application; - -#elif ORTHANC_STONE_SAMPLE == 4 -#include "SingleVolumeApplication.h" -typedef OrthancStone::Samples::SingleVolumeApplication Application; - -#elif ORTHANC_STONE_SAMPLE == 5 -#include "BasicPetCtFusionApplication.h" -typedef OrthancStone::Samples::BasicPetCtFusionApplication Application; - -#elif ORTHANC_STONE_SAMPLE == 6 -#include "SynchronizedSeriesApplication.h" -typedef OrthancStone::Samples::SynchronizedSeriesApplication Application; - -#elif ORTHANC_STONE_SAMPLE == 7 -#include "LayoutPetCtFusionApplication.h" -typedef OrthancStone::Samples::LayoutPetCtFusionApplication Application; - -#else -#error Please set the ORTHANC_STONE_SAMPLE macro -#endif - - -int main(int argc, char* argv[]) -{ - Application application; - - return OrthancStone::IBasicApplication::ExecuteWithSdl(application, argc, argv); -} diff -r 369fe11606fa -r 37e504582af6 Samples/SingleFrameApplication.h --- a/Samples/SingleFrameApplication.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SampleApplicationBase.h" - -#include "../Framework/Layers/SingleFrameRendererFactory.h" -#include "../Framework/Widgets/LayeredSceneWidget.h" -#include "../Resources/Orthanc/Core/Logging.h" - -namespace OrthancStone -{ - namespace Samples - { - class SingleFrameApplication : public SampleApplicationBase - { - public: - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("instance", boost::program_options::value(), - "Orthanc ID of the instance") - ("frame", boost::program_options::value()->default_value(0), - "Number of the frame, for multi-frame DICOM instances") - ("smooth", boost::program_options::value()->default_value(true), - "Enable linear interpolation to smooth the image") - ; - - options.add(generic); - } - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - if (parameters.count("instance") != 1) - { - LOG(ERROR) << "The instance ID is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::string instance = parameters["instance"].as(); - int frame = parameters["frame"].as(); - - std::auto_ptr renderer; - renderer.reset(new SingleFrameRendererFactory(context.GetOrthancConnection(), instance, frame)); - - std::auto_ptr widget(new LayeredSceneWidget); - widget->SetSlice(renderer->GetSliceGeometry()); - widget->AddLayer(renderer.release()); - - if (parameters["smooth"].as()) - { - RenderStyle s; - s.interpolation_ = ImageInterpolation_Linear; - widget->SetLayerStyle(0, s); - } - - context.SetCentralWidget(widget.release()); - } - }; - } -} diff -r 369fe11606fa -r 37e504582af6 Samples/SingleVolumeApplication.h --- a/Samples/SingleVolumeApplication.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,284 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SampleInteractor.h" - -#include "../Resources/Orthanc/Core/Toolbox.h" -#include "../Framework/Layers/LineMeasureTracker.h" -#include "../Framework/Layers/CircleMeasureTracker.h" -#include "../Resources/Orthanc/Core/Logging.h" - -namespace OrthancStone -{ - namespace Samples - { - class SingleVolumeApplication : public SampleApplicationBase - { - private: - class Interactor : public SampleInteractor - { - private: - enum MouseMode - { - MouseMode_None, - MouseMode_TrackCoordinates, - MouseMode_LineMeasure, - MouseMode_CircleMeasure - }; - - MouseMode mouseMode_; - - void SetMouseMode(MouseMode mode, - IStatusBar* statusBar) - { - if (mouseMode_ == mode) - { - mouseMode_ = MouseMode_None; - } - else - { - mouseMode_ = mode; - } - - if (statusBar) - { - switch (mouseMode_) - { - case MouseMode_None: - statusBar->SetMessage("Disabling the mouse tools"); - break; - - case MouseMode_TrackCoordinates: - statusBar->SetMessage("Tracking the mouse coordinates"); - break; - - case MouseMode_LineMeasure: - statusBar->SetMessage("Mouse clicks will now measure the distances"); - break; - - case MouseMode_CircleMeasure: - statusBar->SetMessage("Mouse clicks will now draw circles"); - break; - - default: - break; - } - } - } - - public: - Interactor(VolumeImage& volume, - VolumeProjection projection, - bool reverse) : - SampleInteractor(volume, projection, reverse), - mouseMode_(MouseMode_None) - { - } - - virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, - const SliceGeometry& slice, - const ViewportGeometry& view, - MouseButton button, - double x, - double y, - IStatusBar* statusBar) - { - if (button == MouseButton_Left) - { - switch (mouseMode_) - { - case MouseMode_LineMeasure: - return new LineMeasureTracker(NULL, slice, x, y, 255, 0, 0, 14 /* font size */); - - case MouseMode_CircleMeasure: - return new CircleMeasureTracker(NULL, slice, x, y, 255, 0, 0, 14 /* font size */); - - default: - break; - } - } - - return NULL; - } - - virtual void MouseOver(CairoContext& context, - WorldSceneWidget& widget, - const SliceGeometry& slice, - const ViewportGeometry& view, - double x, - double y, - IStatusBar* statusBar) - { - if (mouseMode_ == MouseMode_TrackCoordinates && - statusBar != NULL) - { - Vector p = slice.MapSliceToWorldCoordinates(x, y); - - char buf[64]; - sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); - statusBar->SetMessage(buf); - } - } - - - virtual void KeyPressed(WorldSceneWidget& widget, - char key, - KeyboardModifiers modifiers, - IStatusBar* statusBar) - { - switch (key) - { - case 't': - SetMouseMode(MouseMode_TrackCoordinates, statusBar); - break; - - case 'm': - SetMouseMode(MouseMode_LineMeasure, statusBar); - break; - - case 'c': - SetMouseMode(MouseMode_CircleMeasure, statusBar); - break; - - case 'b': - { - if (statusBar) - { - statusBar->SetMessage("Setting Hounsfield window to bones"); - } - - RenderStyle style; - style.windowing_ = ImageWindowing_Bone; - dynamic_cast(widget).SetLayerStyle(0, style); - break; - } - - case 'l': - { - if (statusBar) - { - statusBar->SetMessage("Setting Hounsfield window to lung"); - } - - RenderStyle style; - style.windowing_ = ImageWindowing_Lung; - dynamic_cast(widget).SetLayerStyle(0, style); - break; - } - - case 'd': - { - if (statusBar) - { - statusBar->SetMessage("Setting Hounsfield window to what is written in the DICOM file"); - } - - RenderStyle style; - style.windowing_ = ImageWindowing_Default; - dynamic_cast(widget).SetLayerStyle(0, style); - break; - } - - default: - break; - } - } - }; - - - public: - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("series", boost::program_options::value(), - "Orthanc ID of the series") - ("threads", boost::program_options::value()->default_value(3), - "Number of download threads") - ("projection", boost::program_options::value()->default_value("axial"), - "Projection of interest (can be axial, sagittal or coronal)") - ("reverse", boost::program_options::value()->default_value(false), - "Reverse the normal direction of the volume") - ; - - options.add(generic); - } - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - if (parameters.count("series") != 1) - { - LOG(ERROR) << "The series ID is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::string series = parameters["series"].as(); - unsigned int threads = parameters["threads"].as(); - bool reverse = parameters["reverse"].as(); - - std::string tmp = parameters["projection"].as(); - Orthanc::Toolbox::ToLowerCase(tmp); - - VolumeProjection projection; - if (tmp == "axial") - { - projection = VolumeProjection_Axial; - } - else if (tmp == "sagittal") - { - projection = VolumeProjection_Sagittal; - } - else if (tmp == "coronal") - { - projection = VolumeProjection_Coronal; - } - else - { - LOG(ERROR) << "Unknown projection: " << tmp; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - VolumeImage& volume = context.AddSeriesVolume(series, true /* progressive download */, threads); - - std::auto_ptr interactor(new Interactor(volume, projection, reverse)); - - std::auto_ptr widget(new LayeredSceneWidget); - widget->AddLayer(new VolumeImage::LayerFactory(volume)); - widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); - widget->SetInteractor(*interactor); - - context.AddInteractor(interactor.release()); - context.SetCentralWidget(widget.release()); - - statusBar.SetMessage("Use the keys \"b\", \"l\" and \"d\" to change Hounsfield windowing"); - statusBar.SetMessage("Use the keys \"t\" to track the (X,Y,Z) mouse coordinates"); - statusBar.SetMessage("Use the keys \"m\" to measure distances"); - statusBar.SetMessage("Use the keys \"c\" to draw circles"); - } - }; - } -} diff -r 369fe11606fa -r 37e504582af6 Samples/SynchronizedSeriesApplication.h --- a/Samples/SynchronizedSeriesApplication.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,107 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SampleInteractor.h" - -#include "../Framework/Toolbox/OrthancSeriesLoader.h" -#include "../Framework/Layers/SeriesFrameRendererFactory.h" -#include "../Framework/Layers/SiblingSliceLocationFactory.h" -#include "../Framework/Widgets/LayoutWidget.h" -#include "../Resources/Orthanc/Core/Logging.h" - -namespace OrthancStone -{ - namespace Samples - { - class SynchronizedSeriesApplication : public SampleApplicationBase - { - private: - LayeredSceneWidget* CreateSeriesWidget(BasicApplicationContext& context, - const std::string& series) - { - std::auto_ptr loader(new OrthancSeriesLoader(context.GetOrthancConnection(), series)); - - std::auto_ptr interactor(new SampleInteractor(*loader, false)); - - std::auto_ptr widget(new LayeredSceneWidget); - widget->AddLayer(new SeriesFrameRendererFactory(loader.release(), false)); - widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); - widget->SetInteractor(*interactor); - - context.AddInteractor(interactor.release()); - - return widget.release(); - } - - public: - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("a", boost::program_options::value(), - "Orthanc ID of the 1st series") - ("b", boost::program_options::value(), - "Orthanc ID of the 2nd series") - ("c", boost::program_options::value(), - "Orthanc ID of the 3rd series") - ; - - options.add(generic); - } - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - if (parameters.count("a") != 1 || - parameters.count("b") != 1 || - parameters.count("c") != 1) - { - LOG(ERROR) << "At least one of the three series IDs is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - std::auto_ptr a(CreateSeriesWidget(context, parameters["a"].as())); - std::auto_ptr b(CreateSeriesWidget(context, parameters["b"].as())); - std::auto_ptr c(CreateSeriesWidget(context, parameters["c"].as())); - - SiblingSliceLocationFactory::Configure(*a, *b); - SiblingSliceLocationFactory::Configure(*a, *c); - SiblingSliceLocationFactory::Configure(*b, *c); - - std::auto_ptr layout(new LayoutWidget); - layout->SetPadding(5); - layout->AddWidget(a.release()); - - std::auto_ptr layoutB(new LayoutWidget); - layoutB->SetVertical(); - layoutB->SetPadding(5); - layoutB->AddWidget(b.release()); - layoutB->AddWidget(c.release()); - layout->AddWidget(layoutB.release()); - - context.SetCentralWidget(layout.release()); - } - }; - } -} diff -r 369fe11606fa -r 37e504582af6 Samples/TestPatternApplication.h --- a/Samples/TestPatternApplication.h Thu Apr 27 11:09:58 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017 Osimis, 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 "SampleApplicationBase.h" - -#include "../Framework/Widgets/TestCairoWidget.h" -#include "../Framework/Widgets/TestWorldSceneWidget.h" -#include "../Framework/Widgets/LayoutWidget.h" - -namespace OrthancStone -{ - namespace Samples - { - class TestPatternApplication : public SampleApplicationBase - { - public: - virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) - { - boost::program_options::options_description generic("Sample options"); - generic.add_options() - ("animate", boost::program_options::value()->default_value(true), "Animate the test pattern") - ; - - options.add(generic); - } - - virtual void Initialize(BasicApplicationContext& context, - IStatusBar& statusBar, - const boost::program_options::variables_map& parameters) - { - using namespace OrthancStone; - - std::auto_ptr layout(new LayoutWidget); - layout->SetPadding(10); - layout->SetBackgroundCleared(true); - layout->AddWidget(new TestCairoWidget(parameters["animate"].as())); - layout->AddWidget(new TestWorldSceneWidget); - - context.SetCentralWidget(layout.release()); - } - }; - } -}