# HG changeset patch # User Alain Mazy # Date 1562333582 -7200 # Node ID 9953f16c304dbe2c10adf2fdc8cfa363ef6eb369 # Parent 12b591d5d63cf45e086d7f1fba16f9488ce2a127# Parent 4bc8d9609447376985c129780b22c597503d8805 Merge diff -r 4bc8d9609447 -r 9953f16c304d .hgignore --- a/.hgignore Tue Jun 25 18:17:33 2019 +0200 +++ b/.hgignore Fri Jul 05 15:33:02 2019 +0200 @@ -33,6 +33,7 @@ Resources/CommandTool/protoc-tests/node_modules/ Samples/Sdl/ThirdPartyDownloads/ Samples/Sdl/CMakeLists.txt.orig +Samples/Qt/ThirdPartyDownloads/ Samples/WebAssembly/build/ Samples/WebAssembly/ThirdPartyDownloads/ diff -r 4bc8d9609447 -r 9953f16c304d Applications/Generic/GuiAdapter.cpp --- a/Applications/Generic/GuiAdapter.cpp Tue Jun 25 18:17:33 2019 +0200 +++ b/Applications/Generic/GuiAdapter.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -41,14 +41,14 @@ widgets_.push_back(widget); } - std::ostream& operator<<( - std::ostream& os, const GuiAdapterKeyboardEvent& event) - { - os << "sym: " << event.sym << " (" << (int)(event.sym[0]) << ") ctrl: " << event.ctrlKey << ", " << - "shift: " << event.shiftKey << ", " << - "alt: " << event.altKey; - return os; - } + std::ostream& operator<<( + std::ostream& os, const GuiAdapterKeyboardEvent& event) + { + os << "sym: " << event.sym << " (" << (int)(event.sym[0]) << ") ctrl: " << event.ctrlKey << ", " << + "shift: " << event.shiftKey << ", " << + "alt: " << event.altKey; + return os; + } #if ORTHANC_ENABLE_WASM == 1 void GuiAdapter::Run() @@ -428,15 +428,15 @@ switch (source.button.button) { case SDL_BUTTON_MIDDLE: - dest.button = 1; + dest.button =GUIADAPTER_MOUSEBUTTON_MIDDLE; break; case SDL_BUTTON_RIGHT: - dest.button = 2; + dest.button = GUIADAPTER_MOUSEBUTTON_RIGHT; break; case SDL_BUTTON_LEFT: - dest.button = 0; + dest.button = GUIADAPTER_MOUSEBUTTON_LEFT; break; default: @@ -573,14 +573,14 @@ // the SDL window name IS the canvas name ("canvas" is used because this lib // is designed for Wasm - SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); - ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); + SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); + ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); - ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); + ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); std::string windowTitle(windowTitleSz); - ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); + ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); switch (event.mouse.type) { @@ -604,14 +604,14 @@ ORTHANC_ASSERT(event.sym[0] != 0); ORTHANC_ASSERT(event.sym[1] == 0); - SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); - ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); + SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); + ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); - ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); + ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); std::string windowTitle(windowTitleSz); - ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); + ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); switch (event.type) { @@ -636,23 +636,23 @@ // SDL ONLY void GuiAdapter::OnMouseEvent(uint32_t windowID, const GuiAdapterMouseEvent& event) { - if (windowID == 0) - { - LOG(WARNING) << "GuiAdapter::OnMouseEvent -- windowID == 0 and event won't be routed!"; - } - else - { + if (windowID == 0) + { + LOG(WARNING) << "GuiAdapter::OnMouseEvent -- windowID == 0 and event won't be routed!"; + } + else + { // the SDL window name IS the canvas name ("canvas" is used because this lib // is designed for Wasm - SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); - - ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); + SDL_Window* sdlWindow = SDL_GetWindowFromID(windowID); + + ORTHANC_ASSERT(sdlWindow != NULL, "Window ID \"" << windowID << "\" is not a valid SDL window ID!"); const char* windowTitleSz = SDL_GetWindowTitle(sdlWindow); - ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); + ORTHANC_ASSERT(windowTitleSz != NULL, "Window ID \"" << windowID << "\" has a NULL window title!"); std::string windowTitle(windowTitleSz); - ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); + ORTHANC_ASSERT(windowTitle != "", "Window ID \"" << windowID << "\" has an empty window title!"); switch (event.type) { @@ -692,7 +692,7 @@ //ORTHANC_ASSERT(foundWidget, "WindowID " << windowID << " was not found in the registered widgets!"); //if(foundWidget) // foundWidget-> - } + } } // SDL ONLY @@ -788,8 +788,8 @@ } #endif } - else if (event.type == SDL_MOUSEWHEEL) - { + else if (event.type == SDL_MOUSEWHEEL) + { int scancodeCount = 0; const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); @@ -810,21 +810,21 @@ GuiAdapterWheelEvent dest; ConvertFromPlatform(dest, ctrlPressed, shiftPressed, altPressed, event); - OnMouseWheelEvent(event.window.windowID, dest); - - //KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); - - //int x, y; - //SDL_GetMouseState(&x, &y); - - //if (event.wheel.y > 0) - //{ - // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers); - //} - //else if (event.wheel.y < 0) - //{ - // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers); - //} + OnMouseWheelEvent(event.window.windowID, dest); + + //KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + //int x, y; + //SDL_GetMouseState(&x, &y); + + //if (event.wheel.y > 0) + //{ + // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers); + //} + //else if (event.wheel.y < 0) + //{ + // locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers); + //} } else if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { diff -r 4bc8d9609447 -r 9953f16c304d Applications/Generic/GuiAdapter.h --- a/Applications/Generic/GuiAdapter.h Tue Jun 25 18:17:33 2019 +0200 +++ b/Applications/Generic/GuiAdapter.h Fri Jul 05 15:33:02 2019 +0200 @@ -67,6 +67,14 @@ }; + enum GuiAdapterMouseButtonType + { + GUIADAPTER_MOUSEBUTTON_LEFT = 0, + GUIADAPTER_MOUSEBUTTON_MIDDLE = 1, + GUIADAPTER_MOUSEBUTTON_RIGHT = 2 + }; + + enum GuiAdapterHidEventType { GUIADAPTER_EVENT_MOUSEDOWN = 1973, @@ -140,6 +148,14 @@ //long canvasX; //long canvasY; //long padding; + + public: + GuiAdapterMouseEvent() + : ctrlKey(false), + shiftKey(false), + altKey(false) + { + } }; struct GuiAdapterWheelEvent { @@ -162,7 +178,7 @@ bool altKey; }; - std::ostream& operator<<(std::ostream& os, const GuiAdapterKeyboardEvent& event); + std::ostream& operator<<(std::ostream& os, const GuiAdapterKeyboardEvent& event); /* Mousedown event trigger when either the left or right (or middle) mouse is pressed diff -r 4bc8d9609447 -r 9953f16c304d Applications/Qt/QCairoWidget.h --- a/Applications/Qt/QCairoWidget.h Tue Jun 25 18:17:33 2019 +0200 +++ b/Applications/Qt/QCairoWidget.h Fri Jul 05 15:33:02 2019 +0200 @@ -21,8 +21,8 @@ #pragma once #include "../../Applications/Generic/NativeStoneApplicationContext.h" -#include "../../Framework/Viewport/CairoSurface.h" -#include "../../Framework/Widgets/IWidget.h" +#include "../../Framework/Wrappers/CairoSurface.h" +#include "../../Framework/Deprecated/Widgets/IWidget.h" #include #include diff -r 4bc8d9609447 -r 9953f16c304d Applications/Qt/QtStoneApplicationRunner.cpp --- a/Applications/Qt/QtStoneApplicationRunner.cpp Tue Jun 25 18:17:33 2019 +0200 +++ b/Applications/Qt/QtStoneApplicationRunner.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -27,7 +27,7 @@ #include #include -#include "../../Framework/Toolbox/MessagingToolbox.h" +#include "../../Framework/Deprecated/Toolbox/MessagingToolbox.h" #include #include diff -r 4bc8d9609447 -r 9953f16c304d Framework/Deprecated/Toolbox/BaseWebService.cpp --- a/Framework/Deprecated/Toolbox/BaseWebService.cpp Tue Jun 25 18:17:33 2019 +0200 +++ b/Framework/Deprecated/Toolbox/BaseWebService.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -27,6 +27,8 @@ #include #include +#include +#include namespace Deprecated { @@ -89,7 +91,7 @@ OrthancStone::MessageHandler* failureCallback, unsigned int timeoutInSeconds) { - if (cache_.find(uri) == cache_.end()) + if (!cacheEnabled_ || cache_.find(uri) == cache_.end()) { GetAsyncInternal(uri, headers, new BaseWebService::BaseWebServicePayload(successCallback, failureCallback, payload), // ownership is transfered @@ -101,6 +103,15 @@ } else { + // put the uri on top of the most recently accessed list + std::deque::iterator it = std::find(orderedCacheKeys_.begin(), orderedCacheKeys_.end(), uri); + if (it != orderedCacheKeys_.end()) + { + std::string uri = *it; + orderedCacheKeys_.erase(it); + orderedCacheKeys_.push_front(uri); + } + // create a command and "post" it to the Oracle so it is executed and commited "later" NotifyHttpSuccessLater(cache_[uri], payload, successCallback); } @@ -123,7 +134,28 @@ void BaseWebService::CacheAndNotifyHttpSuccess(const IWebService::HttpRequestSuccessMessage& message) { - cache_[message.GetUri()] = boost::shared_ptr(new CachedHttpRequestSuccessMessage(message)); + if (cacheEnabled_) + { + while (cacheCurrentSize_ + message.GetAnswerSize() > cacheMaxSize_ && orderedCacheKeys_.size() > 0) + { + VLOG(1) << "BaseWebService: clearing cache: " << cacheCurrentSize_ << "/" << cacheMaxSize_ << "(" << message.GetAnswerSize() << ")"; + const std::string& oldestUri = orderedCacheKeys_.back(); + HttpCache::iterator it = cache_.find(oldestUri); + if (it != cache_.end()) + { + cacheCurrentSize_ -= it->second->GetAnswerSize(); + cache_.erase(it); + } + orderedCacheKeys_.pop_back(); + + } + + boost::shared_ptr cachedMessage(new CachedHttpRequestSuccessMessage(message)); + cache_[message.GetUri()] = cachedMessage; + orderedCacheKeys_.push_front(message.GetUri()); + cacheCurrentSize_ += message.GetAnswerSize(); + } + NotifyHttpSuccess(message); } diff -r 4bc8d9609447 -r 9953f16c304d Framework/Deprecated/Toolbox/BaseWebService.h --- a/Framework/Deprecated/Toolbox/BaseWebService.h Tue Jun 25 18:17:33 2019 +0200 +++ b/Framework/Deprecated/Toolbox/BaseWebService.h Fri Jul 05 15:33:02 2019 +0200 @@ -25,6 +25,7 @@ #include #include +#include namespace Deprecated { @@ -81,14 +82,21 @@ class BaseWebServicePayload; bool cacheEnabled_; - std::map > cache_; // TODO: this is currently an infinite cache ! + size_t cacheCurrentSize_; + size_t cacheMaxSize_; + + typedef std::map > HttpCache; + HttpCache cache_; + std::deque orderedCacheKeys_; public: BaseWebService(OrthancStone::MessageBroker& broker) : IWebService(broker), IObserver(broker), - cacheEnabled_(true) + cacheEnabled_(false), + cacheCurrentSize_(0), + cacheMaxSize_(100*1024*1024) { } diff -r 4bc8d9609447 -r 9953f16c304d Framework/Deprecated/Toolbox/OrthancApiClient.cpp --- a/Framework/Deprecated/Toolbox/OrthancApiClient.cpp Tue Jun 25 18:17:33 2019 +0200 +++ b/Framework/Deprecated/Toolbox/OrthancApiClient.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -73,7 +73,7 @@ std::auto_ptr< OrthancStone::MessageHandler > binaryHandler_; std::auto_ptr< OrthancStone::MessageHandler > failureHandler_; std::auto_ptr< Orthanc::IDynamicObject > userPayload_; - + OrthancStone::MessageBroker& broker_; void NotifyConversionError(const IWebService::HttpRequestSuccessMessage& message) const { if (failureHandler_.get() != NULL) @@ -84,12 +84,15 @@ } public: - WebServicePayload(OrthancStone::MessageHandler* handler, + WebServicePayload(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* handler, OrthancStone::MessageHandler* failureHandler, Orthanc::IDynamicObject* userPayload) : emptyHandler_(handler), failureHandler_(failureHandler), - userPayload_(userPayload) + userPayload_(userPayload), + broker_(broker) + { if (handler == NULL) { @@ -97,12 +100,14 @@ } } - WebServicePayload(OrthancStone::MessageHandler* handler, + WebServicePayload(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* handler, OrthancStone::MessageHandler* failureHandler, Orthanc::IDynamicObject* userPayload) : binaryHandler_(handler), failureHandler_(failureHandler), - userPayload_(userPayload) + userPayload_(userPayload), + broker_(broker) { if (handler == NULL) { @@ -110,12 +115,14 @@ } } - WebServicePayload(OrthancStone::MessageHandler* handler, + WebServicePayload(OrthancStone::MessageBroker& broker, + OrthancStone::MessageHandler* handler, OrthancStone::MessageHandler* failureHandler, Orthanc::IDynamicObject* userPayload) : jsonHandler_(handler), failureHandler_(failureHandler), - userPayload_(userPayload) + userPayload_(userPayload), + broker_(broker) { if (handler == NULL) { @@ -127,26 +134,35 @@ { if (emptyHandler_.get() != NULL) { - emptyHandler_->Apply(OrthancApiClient::EmptyResponseReadyMessage - (message.GetUri(), userPayload_.get())); + if (broker_.IsActive(*(emptyHandler_->GetObserver()))) + { + emptyHandler_->Apply(OrthancApiClient::EmptyResponseReadyMessage + (message.GetUri(), userPayload_.get())); + } } else if (binaryHandler_.get() != NULL) { - binaryHandler_->Apply(OrthancApiClient::BinaryResponseReadyMessage - (message.GetUri(), message.GetAnswer(), - message.GetAnswerSize(), userPayload_.get())); + if (broker_.IsActive(*(binaryHandler_->GetObserver()))) + { + binaryHandler_->Apply(OrthancApiClient::BinaryResponseReadyMessage + (message.GetUri(), message.GetAnswer(), + message.GetAnswerSize(), userPayload_.get())); + } } else if (jsonHandler_.get() != NULL) { - Json::Value response; - if (MessagingToolbox::ParseJson(response, message.GetAnswer(), message.GetAnswerSize())) + if (broker_.IsActive(*(jsonHandler_->GetObserver()))) { - jsonHandler_->Apply(OrthancApiClient::JsonResponseReadyMessage - (message.GetUri(), response, userPayload_.get())); - } - else - { - NotifyConversionError(message); + Json::Value response; + if (MessagingToolbox::ParseJson(response, message.GetAnswer(), message.GetAnswerSize())) + { + jsonHandler_->Apply(OrthancApiClient::JsonResponseReadyMessage + (message.GetUri(), response, userPayload_.get())); + } + else + { + NotifyConversionError(message); + } } } else @@ -186,7 +202,7 @@ IWebService::HttpHeaders emptyHeaders; web_.GetAsync(baseUrl_ + uri, emptyHeaders, - new WebServicePayload(successCallback, failureCallback, payload), + new WebServicePayload(IObservable::GetBroker(), successCallback, failureCallback, payload), new OrthancStone::Callable (*this, &OrthancApiClient::NotifyHttpSuccess), new OrthancStone::Callable @@ -216,7 +232,7 @@ // printf("GET [%s] [%s]\n", baseUrl_.c_str(), uri.c_str()); web_.GetAsync(baseUrl_ + uri, headers, - new WebServicePayload(successCallback, failureCallback, payload), + new WebServicePayload(IObservable::GetBroker(), successCallback, failureCallback, payload), new OrthancStone::Callable (*this, &OrthancApiClient::NotifyHttpSuccess), new OrthancStone::Callable @@ -232,7 +248,7 @@ Orthanc::IDynamicObject* payload) { web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body, - new WebServicePayload(successCallback, failureCallback, payload), + new WebServicePayload(IObservable::GetBroker(), successCallback, failureCallback, payload), new OrthancStone::Callable (*this, &OrthancApiClient::NotifyHttpSuccess), new OrthancStone::Callable @@ -255,7 +271,7 @@ Orthanc::IDynamicObject* payload /* takes ownership */) { web_.PostAsync(baseUrl_ + uri, IWebService::HttpHeaders(), body, - new WebServicePayload(successCallback, failureCallback, payload), + new WebServicePayload(IObservable::GetBroker(), successCallback, failureCallback, payload), new OrthancStone::Callable (*this, &OrthancApiClient::NotifyHttpSuccess), new OrthancStone::Callable @@ -302,7 +318,7 @@ Orthanc::IDynamicObject* payload) { web_.DeleteAsync(baseUrl_ + uri, IWebService::HttpHeaders(), - new WebServicePayload(successCallback, failureCallback, payload), + new WebServicePayload(IObservable::GetBroker(), successCallback, failureCallback, payload), new OrthancStone::Callable (*this, &OrthancApiClient::NotifyHttpSuccess), new OrthancStone::Callable diff -r 4bc8d9609447 -r 9953f16c304d Framework/Radiography/RadiographyDicomLayer.h --- a/Framework/Radiography/RadiographyDicomLayer.h Tue Jun 25 18:17:33 2019 +0200 +++ b/Framework/Radiography/RadiographyDicomLayer.h Fri Jul 05 15:33:02 2019 +0200 @@ -60,6 +60,22 @@ return frame_; } + virtual size_t GetApproximateMemoryUsage() const + { + size_t size = 0; + if (source_.get() != NULL) + { + size += source_->GetPitch() * source_->GetHeight(); + } + if (converted_.get() != NULL) + { + size += converted_->GetPitch() * converted_->GetHeight(); + } + + return size; + } + + void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset); void SetSourceImage(Orthanc::ImageAccessor* image); // Takes ownership diff -r 4bc8d9609447 -r 9953f16c304d Framework/Radiography/RadiographyLayer.h --- a/Framework/Radiography/RadiographyLayer.h Tue Jun 25 18:17:33 2019 +0200 +++ b/Framework/Radiography/RadiographyLayer.h Fri Jul 05 15:33:02 2019 +0200 @@ -355,5 +355,10 @@ float& maxValue) const = 0; friend class RadiographyMaskLayer; // because it needs to GetTransform on the dicomLayer it relates to + + virtual size_t GetApproximateMemoryUsage() const // this is used to limit the number of scenes loaded in RAM when resources are limited (we actually only count the size used by the images, not the C structs) + { + return 0; + } }; } diff -r 4bc8d9609447 -r 9953f16c304d Framework/Radiography/RadiographyMaskLayer.h --- a/Framework/Radiography/RadiographyMaskLayer.h Tue Jun 25 18:17:33 2019 +0200 +++ b/Framework/Radiography/RadiographyMaskLayer.h Fri Jul 05 15:33:02 2019 +0200 @@ -49,6 +49,18 @@ { } + virtual size_t GetApproximateMemoryUsage() const + { + size_t size = 0; + if (mask_.get() != NULL) + { + size += mask_->GetPitch() * mask_->GetHeight(); + } + + return size; + } + + void SetCorners(const std::vector& corners); void SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index); diff -r 4bc8d9609447 -r 9953f16c304d Framework/Radiography/RadiographyScene.cpp --- a/Framework/Radiography/RadiographyScene.cpp Tue Jun 25 18:17:33 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -147,6 +147,16 @@ return *layer; } + size_t RadiographyScene::GetApproximateMemoryUsage() const + { + size_t size = 0; + for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) + { + size += it->second->GetApproximateMemoryUsage(); + } + return size; + } + void RadiographyScene::OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message) { BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, message.GetOrigin())); diff -r 4bc8d9609447 -r 9953f16c304d Framework/Radiography/RadiographyScene.h --- a/Framework/Radiography/RadiographyScene.h Tue Jun 25 18:17:33 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.h Fri Jul 05 15:33:02 2019 +0200 @@ -163,6 +163,8 @@ virtual ~RadiographyScene(); + virtual size_t GetApproximateMemoryUsage() const; + bool GetWindowing(float& center, float& width) const; diff -r 4bc8d9609447 -r 9953f16c304d Framework/Radiography/RadiographySceneReader.cpp --- a/Framework/Radiography/RadiographySceneReader.cpp Tue Jun 25 18:17:33 2019 +0200 +++ b/Framework/Radiography/RadiographySceneReader.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -60,6 +60,11 @@ if (version != 1) throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + if (input.isMember("hasWindowing") && input["hasWindowing"].asBool()) + { + scene_.SetWindowing(input["windowCenter"].asFloat(), input["windowWidth"].asFloat()); + } + RadiographyDicomLayer* dicomLayer = NULL; for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) { @@ -143,6 +148,11 @@ if (version != 1) throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + if (input.isMember("hasWindowing") && input["hasWindowing"].asBool()) + { + scene_.SetWindowing(input["windowCenter"].asFloat(), input["windowWidth"].asFloat()); + } + RadiographyDicomLayer* dicomLayer = NULL; for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) { diff -r 4bc8d9609447 -r 9953f16c304d Framework/Radiography/RadiographySceneWriter.cpp --- a/Framework/Radiography/RadiographySceneWriter.cpp Tue Jun 25 18:17:33 2019 +0200 +++ b/Framework/Radiography/RadiographySceneWriter.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -30,6 +30,14 @@ void RadiographySceneWriter::Write(Json::Value& output, const RadiographyScene& scene) { output["version"] = 1; + float windowCenter, windowWidth; + bool hasWindowing = scene.GetWindowing(windowCenter, windowWidth); + output["hasWindowing"] = hasWindowing; + if (hasWindowing) + { + output["windowCenter"] = windowCenter; + output["windowWidth"] = windowWidth; + } output["layers"] = Json::arrayValue; std::vector layersIndexes; diff -r 4bc8d9609447 -r 9953f16c304d Framework/Scene2D/Internals/CairoFloatTextureRenderer.cpp diff -r 4bc8d9609447 -r 9953f16c304d Platforms/Wasm/Defaults.cpp --- a/Platforms/Wasm/Defaults.cpp Tue Jun 25 18:17:33 2019 +0200 +++ b/Platforms/Wasm/Defaults.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -41,6 +41,41 @@ extern "C" { #endif +#if 0 + // rewrite malloc/free in order to monitor allocations. We actually only monitor large allocations (like images ...) + + size_t bigChunksTotalSize = 0; + std::map allocatedBigChunks; + + extern void* emscripten_builtin_malloc(size_t bytes); + extern void emscripten_builtin_free(void* mem); + + void * __attribute__((noinline)) malloc(size_t size) + { + void *ptr = emscripten_builtin_malloc(size); + if (size > 100000) + { + bigChunksTotalSize += size; + printf("++ Allocated %zu bytes, got %p. (%zu MB consumed by big chunks)\n", size, ptr, bigChunksTotalSize/(1024*1024)); + allocatedBigChunks[ptr] = size; + } + return ptr; + } + + void __attribute__((noinline)) free(void *ptr) + { + emscripten_builtin_free(ptr); + + std::map::iterator it = allocatedBigChunks.find(ptr); + if (it != allocatedBigChunks.end()) + { + bigChunksTotalSize -= it->second; + printf("-- Freed %zu bytes at %p. (%zu MB consumed by big chunks)\n", it->second, ptr, bigChunksTotalSize/(1024*1024)); + allocatedBigChunks.erase(it); + } + } +#endif // 0 + using namespace OrthancStone; // when WASM needs a C++ viewport @@ -275,7 +310,7 @@ float x2, float y2) { - printf("touch start with %d touches\n", touchCount); + // printf("touch start with %d touches\n", touchCount); std::vector touches; GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); @@ -291,7 +326,7 @@ float x2, float y2) { - printf("touch move with %d touches\n", touchCount); + // printf("touch move with %d touches\n", touchCount); std::vector touches; GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); @@ -307,7 +342,7 @@ float x2, float y2) { - printf("touch end with %d touches remaining\n", touchCount); + // printf("touch end with %d touches remaining\n", touchCount); std::vector touches; GetTouchVector(touches, touchCount, x0, y0, x1, y1, x2, y2); @@ -362,14 +397,14 @@ { static std::string output; // we don't want the string to be deallocated when we return to JS code so we always use the same string (this is fine since JS is single-thread) - printf("SendSerializedMessageToStoneApplication\n"); - printf("%s", message); + //printf("SendSerializedMessageToStoneApplication\n"); + //printf("%s", message); if (applicationWasmAdapter.get() != NULL) { applicationWasmAdapter->HandleSerializedMessageFromWeb(output, std::string(message)); return output.c_str(); } - printf("This Stone application does not have a Web Adapter"); + printf("This Stone application does not have a Web Adapter, unable to send messages"); return NULL; } diff -r 4bc8d9609447 -r 9953f16c304d Platforms/Wasm/logger.ts --- a/Platforms/Wasm/logger.ts Tue Jun 25 18:17:33 2019 +0200 +++ b/Platforms/Wasm/logger.ts Fri Jul 05 15:33:02 2019 +0200 @@ -73,7 +73,7 @@ private getOutput(source: LogSource, args: any[]): any[] { var prefix = this.getPrefix(); - var prefixAndSource = []; + var prefixAndSource = Array(); if (prefix != null) { prefixAndSource = [prefix]; @@ -94,7 +94,7 @@ return [...prefixAndSource, ...args]; } - protected getPrefix(): string { + protected getPrefix(): string | null { return null; } } diff -r 4bc8d9609447 -r 9953f16c304d Platforms/Wasm/wasm-application-runner.ts --- a/Platforms/Wasm/wasm-application-runner.ts Tue Jun 25 18:17:33 2019 +0200 +++ b/Platforms/Wasm/wasm-application-runner.ts Fri Jul 05 15:33:02 2019 +0200 @@ -1,5 +1,5 @@ -import Stone = require('./stone-framework-loader'); -import StoneViewport = require('./wasm-viewport'); +import * as Stone from './stone-framework-loader' +import * as StoneViewport from './wasm-viewport' import * as Logger from './logger' if (!('WebAssembly' in window)) { @@ -130,11 +130,6 @@ Logger.defaultLogger.debug("Connecting C++ methods to JS methods - done"); - // Prevent scrolling - document.body.addEventListener('touchmove', function (event) { - event.preventDefault(); - }, { passive: false}); // must not be passive if calling event.preventDefault, ie to cancel scroll or zoom of the whole interface - _InitializeWasmApplication(orthancBaseUrl); }); } diff -r 4bc8d9609447 -r 9953f16c304d Platforms/Wasm/wasm-viewport.ts --- a/Platforms/Wasm/wasm-viewport.ts Tue Jun 25 18:17:33 2019 +0200 +++ b/Platforms/Wasm/wasm-viewport.ts Fri Jul 05 15:33:02 2019 +0200 @@ -1,4 +1,4 @@ -import wasmApplicationRunner = require('./wasm-application-runner'); +import * as wasmApplicationRunner from './wasm-application-runner' import * as Logger from './logger' var isPendingRedraw = false; @@ -10,14 +10,17 @@ Logger.defaultLogger.debug('Scheduling a refresh of the viewport, as its content changed'); window.requestAnimationFrame(function() { isPendingRedraw = false; - WasmViewport.GetFromCppViewport(cppViewportHandle).Redraw(); + let viewport = WasmViewport.GetFromCppViewport(cppViewportHandle); + if (viewport) { + viewport.Redraw(); + } }); } } (window).ScheduleWebViewportRedraw = ScheduleWebViewportRedraw; -declare function UTF8ToString(any): string; +declare function UTF8ToString(v: any): string; function CreateWasmViewport(htmlCanvasId: string) : any { var cppViewportHandle = wasmApplicationRunner.CreateCppViewport(); @@ -38,7 +41,7 @@ private module_ : any; private canvasId_ : string; private htmlCanvas_ : HTMLCanvasElement; - private context_ : CanvasRenderingContext2D; + private context_ : CanvasRenderingContext2D | null; private imageData_ : any = null; private renderingBuffer_ : any = null; @@ -96,20 +99,20 @@ return this.pimpl_; } - public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport { + public static GetFromCppViewport(cppViewportHandle: number) : WasmViewport | null { if (WasmViewport.viewportsMapByCppHandle_[cppViewportHandle] !== undefined) { return WasmViewport.viewportsMapByCppHandle_[cppViewportHandle]; } Logger.defaultLogger.error("WasmViewport not found !"); - return undefined; + return null; } - public static GetFromCanvasId(canvasId: string) : WasmViewport { + public static GetFromCanvasId(canvasId: string) : WasmViewport | null { if (WasmViewport.viewportsMapByCanvasId_[canvasId] !== undefined) { return WasmViewport.viewportsMapByCanvasId_[canvasId]; } Logger.defaultLogger.error("WasmViewport not found !"); - return undefined; + return null; } public static ResizeAll() { @@ -135,7 +138,9 @@ this.renderingBuffer_, this.imageData_.width * this.imageData_.height * 4)); - this.context_.putImageData(this.imageData_, 0, 0); + if (this.context_) { + this.context_.putImageData(this.imageData_, 0, 0); + } } } @@ -147,25 +152,27 @@ } // width/height is defined by the parent width/height - this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth; - this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight; + if (this.htmlCanvas_.parentElement) { + this.htmlCanvas_.width = this.htmlCanvas_.parentElement.offsetWidth; + this.htmlCanvas_.height = this.htmlCanvas_.parentElement.offsetHeight; - Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height); + Logger.defaultLogger.debug("resizing WasmViewport: ", this.htmlCanvas_.width, "x", this.htmlCanvas_.height); - if (this.imageData_ === null) { - this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height); - this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); - - if (this.renderingBuffer_ != null) { - this.module_._free(this.renderingBuffer_); + if (this.imageData_ === null && this.context_) { + this.imageData_ = this.context_.getImageData(0, 0, this.htmlCanvas_.width, this.htmlCanvas_.height); + this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); + + if (this.renderingBuffer_ != null) { + this.module_._free(this.renderingBuffer_); + } + + this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4); + } else { + this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); } - this.renderingBuffer_ = this.module_._malloc(this.imageData_.width * this.imageData_.height * 4); - } else { - this.ViewportSetSize(this.pimpl_, this.htmlCanvas_.width, this.htmlCanvas_.height); + this.Redraw(); } - - this.Redraw(); } public Initialize() { @@ -211,7 +218,7 @@ }); window.addEventListener('keydown', function(event) { - var keyChar = event.key; + var keyChar: string | null = event.key; var keyCode = event.keyCode if (keyChar.length == 1) { keyCode = 0; // maps to OrthancStone::KeyboardKeys_Generic @@ -328,7 +335,7 @@ this.touchZoom_ = false; } - public GetTouchTranslation(event) { + public GetTouchTranslation(event: any) { var touch = event.targetTouches[0]; return [ touch.pageX, @@ -336,7 +343,7 @@ ]; } - public GetTouchZoom(event) { + public GetTouchZoom(event: any) { var touch1 = event.targetTouches[0]; var touch2 = event.targetTouches[1]; var dx = (touch1.pageX - touch2.pageX); diff -r 4bc8d9609447 -r 9953f16c304d Resources/CMake/OrthancStoneConfiguration.cmake diff -r 4bc8d9609447 -r 9953f16c304d Resources/CodeGeneration/template.in.h.j2 --- a/Resources/CodeGeneration/template.in.h.j2 Tue Jun 25 18:17:33 2019 +0200 +++ b/Resources/CodeGeneration/template.in.h.j2 Fri Jul 05 15:33:02 2019 +0200 @@ -51,7 +51,7 @@ inline Json::Value _StoneSerializeValue(int64_t value) { - Json::Value result(value); + Json::Value result(static_cast(value)); return result; } @@ -79,7 +79,7 @@ inline Json::Value _StoneSerializeValue(uint64_t value) { - Json::Value result(value); + Json::Value result(static_cast(value)); return result; } diff -r 4bc8d9609447 -r 9953f16c304d Samples/Qt/BasicScene.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Qt/BasicScene.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -0,0 +1,463 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + +#define GLEW_STATIC 1 +// From Stone +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include "../../Applications/Sdl/SdlOpenGLWindow.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/Scene2D.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Framework/Scene2DViewport/UndoStack.h" + +#include "../../Framework/StoneInitialization.h" +#include "../../Framework/Messages/MessageBroker.h" + +// From Orthanc framework +#include +#include +#include +#include +#include + +#include +#include +#include "EmbeddedResources.h" + +//#include +#include +#include +#include + +static const unsigned int FONT_SIZE = 32; +static const int LAYER_POSITION = 150; + +using namespace OrthancStone; + +void PrepareScene(boost::shared_ptr controller) +{ + Scene2D& scene(*controller->GetScene()); + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t *p = reinterpret_cast(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast(i.GetRow(1)); + p[0] = 0; + p[1] = 0; + p[2] = 255; + + p[3] = 255; + p[4] = 0; + p[5] = 0; + + scene.SetLayer(12, new ColorTextureSceneLayer(i)); + + std::auto_ptr l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * 3.14); + scene.SetLayer(14, l.release()); + } + + // Texture of 1x1 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t *p = reinterpret_cast(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::auto_ptr l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * 3.14); + scene.SetLayer(13, l.release()); + } + + // Some lines + { + std::auto_ptr layer(new PolylineSceneLayer); + + layer->SetThickness(1); + + PolylineSceneLayer::Chain chain; + chain.push_back(ScenePoint2D(0 - 0.5, 0 - 0.5)); + chain.push_back(ScenePoint2D(0 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 2 - 0.5)); + chain.push_back(ScenePoint2D(2 - 0.5, 0 - 0.5)); + layer->AddChain(chain, true, 255, 0, 0); + + chain.clear(); + chain.push_back(ScenePoint2D(-5, -5)); + chain.push_back(ScenePoint2D(5, -5)); + chain.push_back(ScenePoint2D(5, 5)); + chain.push_back(ScenePoint2D(-5, 5)); + layer->AddChain(chain, true, 0, 255, 0); + + double dy = 1.01; + chain.clear(); + chain.push_back(ScenePoint2D(-4, -4)); + chain.push_back(ScenePoint2D(4, -4 + dy)); + chain.push_back(ScenePoint2D(-4, -4 + 2.0 * dy)); + chain.push_back(ScenePoint2D(4, 2)); + layer->AddChain(chain, false, 0, 0, 255); + +// layer->SetColor(0,255, 255); + scene.SetLayer(50, layer.release()); + } + + // Some text + { + std::auto_ptr layer(new TextSceneLayer); + layer->SetText("Hello"); + scene.SetLayer(100, layer.release()); + } +} + + +//void TakeScreenshot(const std::string& target, +// const Scene2D& scene, +// unsigned int canvasWidth, +// unsigned int canvasHeight) +//{ +// // Take a screenshot, then save it as PNG file +// CairoCompositor compositor(scene, canvasWidth, canvasHeight); +// compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, FONT_SIZE, Orthanc::Encoding_Latin1); +// compositor.Refresh(); + +// Orthanc::ImageAccessor canvas; +// compositor.GetCanvas().GetReadOnlyAccessor(canvas); + +// Orthanc::Image png(Orthanc::PixelFormat_RGB24, canvas.GetWidth(), canvas.GetHeight(), false); +// Orthanc::ImageProcessing::Convert(png, canvas); + +// Orthanc::PngWriter writer; +// writer.WriteToFile(target, png); +//} + + +//void HandleApplicationEvent(ViewportControllerPtr controller, +// const OpenGLCompositor& compositor, +// const SDL_Event& event, +// FlexiblePointerTrackerPtr& activeTracker) +//{ +// Scene2D& scene(*controller->GetScene()); +// if (event.type == SDL_MOUSEMOTION) +// { +// int scancodeCount = 0; +// const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + +// if (activeTracker.get() == NULL && +// SDL_SCANCODE_LCTRL < scancodeCount && +// keyboardState[SDL_SCANCODE_LCTRL]) +// { +// // The "left-ctrl" key is down, while no tracker is present + +// PointerEvent e; +// e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); + +// ScenePoint2D p = e.GetMainPosition().Apply(scene.GetCanvasToSceneTransform()); + +// char buf[64]; +// sprintf(buf, "(%0.02f,%0.02f)", p.GetX(), p.GetY()); + +// if (scene.HasLayer(LAYER_POSITION)) +// { +// TextSceneLayer& layer = +// dynamic_cast(scene.GetLayer(LAYER_POSITION)); +// layer.SetText(buf); +// layer.SetPosition(p.GetX(), p.GetY()); +// } +// else +// { +// std::auto_ptr +// layer(new TextSceneLayer); +// layer->SetColor(0, 255, 0); +// layer->SetText(buf); +// layer->SetBorder(20); +// layer->SetAnchor(BitmapAnchor_BottomCenter); +// layer->SetPosition(p.GetX(), p.GetY()); +// scene.SetLayer(LAYER_POSITION, layer.release()); +// } +// } +// else +// { +// scene.DeleteLayer(LAYER_POSITION); +// } +// } +// else if (event.type == SDL_MOUSEBUTTONDOWN) +// { +// PointerEvent e; +// e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y)); + +// switch (event.button.button) +// { +// case SDL_BUTTON_MIDDLE: +// activeTracker = boost::make_shared(controller, e); +// break; + +// case SDL_BUTTON_RIGHT: +// activeTracker = boost::make_shared(controller, +// e, compositor.GetCanvasHeight()); +// break; + +// case SDL_BUTTON_LEFT: +// activeTracker = boost::make_shared(controller, e); +// break; + +// default: +// break; +// } +// } +// else if (event.type == SDL_KEYDOWN && +// event.key.repeat == 0 /* Ignore key bounce */) +// { +// switch (event.key.keysym.sym) +// { +// case SDLK_s: +// controller->FitContent(compositor.GetCanvasWidth(), +// compositor.GetCanvasHeight()); +// break; + +// case SDLK_c: +// TakeScreenshot("screenshot.png", scene, +// compositor.GetCanvasWidth(), +// compositor.GetCanvasHeight()); +// break; + +// default: +// break; +// } +// } +//} + + +static void GLAPIENTRY OpenGLMessageCallback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam ) +{ + if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) + { + fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), + type, severity, message ); + } +} + + +//void Run(ViewportControllerPtr controller) +//{ +// SdlOpenGLWindow window("Hello", 1024, 768); + +// controller->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight()); + +// glEnable(GL_DEBUG_OUTPUT); +// glDebugMessageCallback(OpenGLMessageCallback, 0); + +// OpenGLCompositor compositor(window, *controller->GetScene()); +// compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, +// FONT_SIZE, Orthanc::Encoding_Latin1); + +// FlexiblePointerTrackerPtr tracker; + +// bool stop = false; +// while (!stop) +// { +// compositor.Refresh(); + +// SDL_Event event; +// while (!stop && +// SDL_PollEvent(&event)) +// { +// if (event.type == SDL_QUIT) +// { +// stop = true; +// break; +// } +// else if (event.type == SDL_MOUSEMOTION) +// { +// if (tracker) +// { +// PointerEvent e; +// e.AddPosition(compositor.GetPixelCenterCoordinates( +// event.button.x, event.button.y)); +// tracker->PointerMove(e); +// } +// } +// else if (event.type == SDL_MOUSEBUTTONUP) +// { +// if (tracker) +// { +// PointerEvent e; +// e.AddPosition(compositor.GetPixelCenterCoordinates( +// event.button.x, event.button.y)); +// tracker->PointerUp(e); +// if(!tracker->IsAlive()) +// tracker.reset(); +// } +// } +// else if (event.type == SDL_WINDOWEVENT && +// event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) +// { +// tracker.reset(); +// compositor.UpdateSize(); +// } +// else if (event.type == SDL_KEYDOWN && +// event.key.repeat == 0 /* Ignore key bounce */) +// { +// switch (event.key.keysym.sym) +// { +// case SDLK_f: +// window.GetWindow().ToggleMaximize(); +// break; + +// case SDLK_q: +// stop = true; +// break; + +// default: +// break; +// } +// } + +// HandleApplicationEvent(controller, compositor, event, tracker); +// } + +// SDL_Delay(1); +// } +//} + +extern void InitGL(); + +#include +#include "BasicSceneWindow.h" +#include "Scene2DInteractor.h" + +int main(int argc, char* argv[]) +{ + { + QApplication a(argc, argv); + + QSurfaceFormat requestedFormat; + requestedFormat.setVersion( 2, 0 ); + + OrthancStone::Samples::BasicSceneWindow window; + window.show(); + + MessageBroker broker; + boost::shared_ptr undoStack(new UndoStack); + boost::shared_ptr controller = boost::make_shared( + undoStack, boost::ref(broker)); + PrepareScene(controller); + + boost::shared_ptr interactor(new BasicScene2DInteractor(controller)); + window.GetOpenGlWidget().SetInteractor(interactor); + + QOpenGLContext * context = new QOpenGLContext; + context->setFormat( requestedFormat ); + context->create(); + context->makeCurrent(window.GetOpenGlWidget().context()->surface()); + + boost::shared_ptr compositor = boost::make_shared(window.GetOpenGlWidget(), *controller->GetScene()); + compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + + window.GetOpenGlWidget().SetCompositor(compositor); + + return a.exec(); + } + + + + + + + + + + + + +// StoneInitialize(); +// Orthanc::Logging::EnableInfoLevel(true); + +// QApplication app(argc, argv); + +// OrthancStone::Samples::BasicSceneWindow window; + +// QSurfaceFormat requestedFormat; +// requestedFormat.setVersion( 3, 3 ); + +// window.show(); + +// QOpenGLContext * context = new QOpenGLContext; +// context->setFormat( requestedFormat ); +// context->create(); + +// GLenum err = glewInit(); +// if( GLEW_OK != err ){ +// qDebug() << "[Error] GLEW failed to initialize. " << (const char*)glewGetErrorString(err); +// } + +// try +// { +// MessageBroker broker; +// ViewportControllerPtr controller = boost::make_shared( +// boost::ref(broker)); +// PrepareScene(controller); + +// boost::shared_ptr compositor(new OpenGLCompositor(window.GetOpenGlWidget(), *controller->GetScene())); + +// compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, +// FONT_SIZE, Orthanc::Encoding_Latin1); + +// window.SetCompositor(compositor); + +// app.exec(); +// } +// catch (Orthanc::OrthancException& e) +// { +// LOG(ERROR) << "EXCEPTION: " << e.What(); +// } + + + +// StoneFinalize(); + +// return 0; +} diff -r 4bc8d9609447 -r 9953f16c304d Samples/Qt/BasicSceneWindow.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Qt/BasicSceneWindow.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -0,0 +1,61 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include "BasicSceneWindow.h" + +/** + * Don't use "ui_MainWindow.h" instead of below, as + * this makes CMake unable to detect when the UI file changes. + **/ +#include +#include "../../Applications/Samples/SampleApplicationBase.h" + +namespace OrthancStone +{ + namespace Samples + { + + BasicSceneWindow::BasicSceneWindow( + QWidget *parent) : +// QStoneMainWindow(context, parent), + ui_(new Ui::BasicSceneWindow) + //stoneSampleApplication_(stoneSampleApplication) + { + ui_->setupUi(this); + //SetCentralStoneWidget(*ui_->cairoCentralWidget); + } + + BasicSceneWindow::~BasicSceneWindow() + { + delete ui_; + } + + QStoneOpenGlWidget& BasicSceneWindow::GetOpenGlWidget() + { + return *(ui_->centralWidget); + } + + void BasicSceneWindow::SetCompositor(boost::shared_ptr compositor) + { + ui_->centralWidget->SetCompositor(compositor); + } + + } +} diff -r 4bc8d9609447 -r 9953f16c304d Samples/Qt/BasicSceneWindow.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Qt/BasicSceneWindow.h Fri Jul 05 15:33:02 2019 +0200 @@ -0,0 +1,55 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2019 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ +#pragma once +#include +#include +// #include "../../Qt/QCairoWidget.h" +// #include "../../Qt/QStoneMainWindow.h" + +namespace Ui +{ + class BasicSceneWindow; +} + +namespace OrthancStone +{ + namespace Samples + { + + //class SampleSingleCanvasApplicationBase; + + class BasicSceneWindow : public QMainWindow + { + Q_OBJECT + + private: + Ui::BasicSceneWindow* ui_; + //SampleSingleCanvasApplicationBase& stoneSampleApplication_; + + public: + explicit BasicSceneWindow(QWidget *parent = 0); + ~BasicSceneWindow(); + + QStoneOpenGlWidget& GetOpenGlWidget(); + + void SetCompositor(boost::shared_ptr compositor); + }; + } +} diff -r 4bc8d9609447 -r 9953f16c304d Samples/Qt/BasicSceneWindow.ui --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Qt/BasicSceneWindow.ui Fri Jul 05 15:33:02 2019 +0200 @@ -0,0 +1,84 @@ + + + BasicSceneWindow + + + + 0 + 0 + 903 + 634 + + + + + 500 + 300 + + + + + 500 + 300 + + + + Stone of Orthanc + + + Qt::LeftToRight + + + + + 0 + 0 + + + + Qt::LeftToRight + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 500 + + + + + + + + + + 0 + 0 + 903 + 21 + + + + + Test + + + + + + + + + QStoneOpenGlWidget + QWidget +
QStoneOpenGlWidget.h
+
+
+ + +
diff -r 4bc8d9609447 -r 9953f16c304d Samples/Qt/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Qt/CMakeLists.txt Fri Jul 05 15:33:02 2019 +0200 @@ -0,0 +1,83 @@ +cmake_minimum_required(VERSION 2.8.3) + +##################################################################### +## Configuration of the Orthanc framework +##################################################################### + +# This CMake file defines the "ORTHANC_STONE_VERSION" macro, so it +# must be the first inclusion +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/Version.cmake) + +if (ORTHANC_STONE_VERSION STREQUAL "mainline") + set(ORTHANC_FRAMEWORK_VERSION "mainline") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "hg") +else() + set(ORTHANC_FRAMEWORK_VERSION "1.5.7") + set(ORTHANC_FRAMEWORK_DEFAULT_SOURCE "web") +endif() + +set(ORTHANC_FRAMEWORK_SOURCE "${ORTHANC_FRAMEWORK_DEFAULT_SOURCE}" CACHE STRING "Source of the Orthanc source code (can be \"hg\", \"archive\", \"web\" or \"path\")") +set(ORTHANC_FRAMEWORK_ARCHIVE "" CACHE STRING "Path to the Orthanc archive, if ORTHANC_FRAMEWORK_SOURCE is \"archive\"") +set(ORTHANC_FRAMEWORK_ROOT "" CACHE STRING "Path to the Orthanc source directory, if ORTHANC_FRAMEWORK_SOURCE is \"path\"") + + +##################################################################### +## Configuration of the Stone framework +##################################################################### + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneParameters.cmake) +include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) + +DownloadPackage( + "a24b8136b8f3bb93f166baf97d9328de" + "http://orthanc.osimis.io/ThirdPartyDownloads/ubuntu-font-family-0.83.zip" + "${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83") + +set(ORTHANC_STONE_APPLICATION_RESOURCES + UBUNTU_FONT ${CMAKE_BINARY_DIR}/ubuntu-font-family-0.83/Ubuntu-R.ttf + ) + +SET(ENABLE_GOOGLE_TEST OFF) +SET(ENABLE_LOCALE ON) +SET(ENABLE_QT ON) +SET(ENABLE_SDL OFF) +SET(ENABLE_WEB_CLIENT ON) +SET(ORTHANC_SANDBOXED OFF) +LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) + +add_definitions( + -DORTHANC_ENABLE_LOGGING_PLUGIN=0 + ) +##################################################################### +## Build the samples +##################################################################### + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + +list(APPEND BASIC_SCENE_APPLICATIONS_SOURCES + BasicSceneWindow.cpp + ) + +ORTHANC_QT_WRAP_UI(BASIC_SCENE_APPLICATIONS_SOURCES + BasicSceneWindow.ui + ) + +ORTHANC_QT_WRAP_CPP(BASIC_SCENE_APPLICATIONS_SOURCES + BasicSceneWindow.h + QStoneOpenGlWidget.h + ) + +add_executable(BasicScene + BasicScene.cpp + QStoneOpenGlWidget.cpp + Scene2DInteractor.cpp + ${BASIC_SCENE_APPLICATIONS_SOURCES} + ) + +target_include_directories(BasicScene PUBLIC ${CMAKE_SOURCE_DIR} ${STONE_SOURCES_DIR}) + +target_link_libraries(BasicScene OrthancStone) diff -r 4bc8d9609447 -r 9953f16c304d Samples/Qt/QStoneOpenGlWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Qt/QStoneOpenGlWidget.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -0,0 +1,81 @@ +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include "QStoneOpenGlWidget.h" + +#include + +using namespace OrthancStone; + +void QStoneOpenGlWidget::initializeGL() +{ + glewInit(); +} + +void QStoneOpenGlWidget::MakeCurrent() +{ + this->makeCurrent(); +} + +void QStoneOpenGlWidget::resizeGL(int w, int h) +{ + +} + +void QStoneOpenGlWidget::paintGL() +{ + if (compositor_) + { + compositor_->Refresh(); + } + doneCurrent(); +} + +void ConvertFromPlatform( + OrthancStone::GuiAdapterMouseEvent& dest, + const QMouseEvent& qtEvent) +{ + dest.targetX = qtEvent.x(); + dest.targetY = qtEvent.y(); + + switch (qtEvent.button()) + { + case Qt::LeftButton: dest.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; break; + case Qt::MiddleButton: dest.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_MIDDLE; break; + case Qt::RightButton: dest.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_RIGHT; break; + default: + dest.button = OrthancStone::GUIADAPTER_MOUSEBUTTON_LEFT; + } + + if (qtEvent.modifiers().testFlag(Qt::ShiftModifier)) + { + dest.shiftKey = true; + } + if (qtEvent.modifiers().testFlag(Qt::ControlModifier)) + { + dest.ctrlKey = true; + } + if (qtEvent.modifiers().testFlag(Qt::AltModifier)) + { + dest.altKey = true; + } + +} + + + +void QStoneOpenGlWidget::mousePressEvent(QMouseEvent* qtEvent) +{ + OrthancStone::GuiAdapterMouseEvent event; + ConvertFromPlatform(event, *qtEvent); + + if (sceneInteractor_.get() != NULL) + { + sceneInteractor_->OnMouseEvent(event); + } + + + // convert +//TODO event-> + +// sceneInteractor_->OnMouseEvent(event); +} + diff -r 4bc8d9609447 -r 9953f16c304d Samples/Qt/QStoneOpenGlWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Qt/QStoneOpenGlWidget.h Fri Jul 05 15:33:02 2019 +0200 @@ -0,0 +1,60 @@ +#pragma once +#include "../../Framework/OpenGL/OpenGLIncludes.h" +#include +#include + +#include +#include "../../Framework/OpenGL/IOpenGLContext.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "Scene2DInteractor.h" + +namespace OrthancStone +{ + class QStoneOpenGlWidget : public QOpenGLWidget, public OrthancStone::OpenGL::IOpenGLContext + { + boost::shared_ptr compositor_; + boost::shared_ptr sceneInteractor_; + + public: + QStoneOpenGlWidget(QWidget *parent) : + QOpenGLWidget(parent) + { + } + + protected: + + //**** QWidget overrides + void initializeGL() override; + void resizeGL(int w, int h) override; + void paintGL() override; + + void mousePressEvent(QMouseEvent* event) override; + + //**** IOpenGLContext overrides + + virtual void MakeCurrent() override; + virtual void SwapBuffer() override {} + + virtual unsigned int GetCanvasWidth() const override + { + return this->width(); + } + + virtual unsigned int GetCanvasHeight() const override + { + return this->height(); + } + + public: + void SetInteractor(boost::shared_ptr sceneInteractor) + { + sceneInteractor_ = sceneInteractor; + } + + void SetCompositor(boost::shared_ptr compositor) + { + compositor_ = compositor; + } + + }; +} diff -r 4bc8d9609447 -r 9953f16c304d Samples/Qt/Scene2DInteractor.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Qt/Scene2DInteractor.cpp Fri Jul 05 15:33:02 2019 +0200 @@ -0,0 +1,17 @@ +#include "Scene2DInteractor.h" + + +namespace OrthancStone +{ + +} + +using namespace OrthancStone; + + +void BasicScene2DInteractor::OnMouseEvent(const GuiAdapterMouseEvent& event) +{ + +} + + diff -r 4bc8d9609447 -r 9953f16c304d Samples/Qt/Scene2DInteractor.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Qt/Scene2DInteractor.h Fri Jul 05 15:33:02 2019 +0200 @@ -0,0 +1,33 @@ +#pragma once + +#include "../../Framework/Scene2DViewport/ViewportController.h" +#include "../../Applications/Generic/GuiAdapter.h" + + +namespace OrthancStone +{ + + class Scene2DInteractor + { + protected: + boost::shared_ptr viewportController_; + + public: + Scene2DInteractor(boost::shared_ptr viewportController) : + viewportController_(viewportController) + {} + + virtual void OnMouseEvent(const GuiAdapterMouseEvent& event) = 0; + }; +} + +class BasicScene2DInteractor : public OrthancStone::Scene2DInteractor +{ +public: + BasicScene2DInteractor(boost::shared_ptr viewportController) : + Scene2DInteractor(viewportController) + {} + + virtual void OnMouseEvent(const OrthancStone::GuiAdapterMouseEvent& event) override; +}; +