Mercurial > hg > orthanc-stone
changeset 605:7a7e36c52d62
Merged am-dev into default
author | Alain Mazy <am@osimis.io> |
---|---|
date | Mon, 29 Apr 2019 15:24:59 +0200 |
parents | 86dfde451f4c (diff) 8432926e9db9 (current diff) |
children | d9c0a66304cb |
files | Framework/Radiography/RadiographyScene.cpp |
diffstat | 125 files changed, 9416 insertions(+), 132 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Mon Apr 29 12:01:55 2019 +0200 +++ b/.hgignore Mon Apr 29 15:24:59 2019 +0200 @@ -1,28 +1,31 @@ -CMakeLists.txt.user -Platforms/Generic/ThirdPartyDownloads/ -Applications/build-* +syntax: glob +*~ +*.cpp.orig +*.h.orig +.vs/ +.vscode/ Applications/Qt/archive/ Applications/Samples/ThirdPartyDownloads/ +Applications/Samples/build-wasm/ +Applications/Samples/build-web/ +Applications/Samples/node_modules/ +Applications/Samples/rt-viewer-demo/ThirdPartyDownloads/ Applications/Samples/rt-viewer-demo/build-sdl-msvc15/ Applications/Samples/rt-viewer-demo/build-tsc-output/ Applications/Samples/rt-viewer-demo/build-wasm/ Applications/Samples/rt-viewer-demo/build-web/ -Applications/Samples/rt-viewer-demo/ThirdPartyDownloads/ -Applications/Samples/build-wasm/ -Applications/Samples/build-web/ -Applications/Samples/node_modules/ -Resources/CommandTool/protoc-tests/node_modules/ -Resources/CommandTool/protoc-tests/generated_js/ -Resources/CommandTool/protoc-tests/generated_ts/ -Resources/CommandTool/flatc-tests/basic/build/ -.vscode/ +Applications/build-* +CMakeLists.txt.user +Platforms/Generic/ThirdPartyDownloads/ Resources/CodeGeneration/__pycache__ Resources/CodeGeneration/build/ Resources/CodeGeneration/build_browser/ Resources/CodeGeneration/testCppHandler/build/ Resources/CodeGeneration/testCppHandler/build_msbuild/ -syntax: glob -Resources/CodeGeneration/testWasmIntegrated/build-wasm/ +Resources/CodeGeneration/testWasmIntegrated/build-final/ Resources/CodeGeneration/testWasmIntegrated/build-tsc/ -Resources/CodeGeneration/testWasmIntegrated/build-final/ - +Resources/CodeGeneration/testWasmIntegrated/build-wasm/ +Resources/CommandTool/flatc-tests/basic/build/ +Resources/CommandTool/protoc-tests/generated_js/ +Resources/CommandTool/protoc-tests/generated_ts/ +Resources/CommandTool/protoc-tests/node_modules/
--- a/Applications/Generic/NativeStoneApplicationRunner.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Applications/Generic/NativeStoneApplicationRunner.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -81,11 +81,13 @@ generic.add_options() ("help", "Display this help and exit") ("verbose", "Be verbose in logs") - ("orthanc", boost::program_options::value<std::string>()->default_value("http://localhost:8042/"), + ("orthanc", boost::program_options::value<std::string>()-> + 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<bool>()->default_value(true), "Check HTTPS certificates") + ("https-verify", boost::program_options::value<bool>()-> + default_value(true), "Check HTTPS certificates") ; options.add(generic); @@ -102,13 +104,15 @@ try { - boost::program_options::store(boost::program_options::command_line_parser(argc, argv). - options(options).run(), parameters); + 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(); + LOG(ERROR) << + "Error while parsing the command-line arguments: " << e.what(); error = true; } @@ -120,12 +124,11 @@ 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 in native environment." - << 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 in native " + << "environment." << std::endl; std::cout << options << "\n"; return error ? -1 : 0; @@ -138,21 +141,24 @@ Orthanc::HttpClient::ConfigureSsl(false, ""); } + LOG(ERROR) << "???????? if (parameters.count(\"verbose\"))"; if (parameters.count("verbose")) { + LOG(ERROR) << "parameters.count(\"verbose\") != 0"; Orthanc::Logging::EnableInfoLevel(true); LOG(INFO) << "Verbose logs are enabled"; } + LOG(ERROR) << "???????? if (parameters.count(\"trace\"))"; if (parameters.count("trace")) { + LOG(ERROR) << "parameters.count(\"trace\") != 0"; Orthanc::Logging::EnableTraceLevel(true); VLOG(1) << "Trace logs are enabled"; } ParseCommandLineOptions(parameters); - bool success = true; try { @@ -169,17 +175,20 @@ if (parameters.count("username") && parameters.count("password")) { - webServiceParameters.SetCredentials(parameters["username"].as<std::string>(), - parameters["password"].as<std::string>()); + webServiceParameters.SetCredentials(parameters["username"]. + as<std::string>(), + parameters["password"].as<std::string>()); } - LOG(WARNING) << "URL to the Orthanc REST API: " << webServiceParameters.GetUrl(); + LOG(WARNING) << "URL to the Orthanc REST API: " << + webServiceParameters.GetUrl(); { OrthancPlugins::OrthancHttpConnection orthanc(webServiceParameters); if (!MessagingToolbox::CheckOrthancVersion(orthanc)) { - LOG(ERROR) << "Your version of Orthanc is incompatible with Stone of Orthanc, please upgrade"; + LOG(ERROR) << "Your version of Orthanc is incompatible with Stone of " + << "Orthanc, please upgrade"; throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); } } @@ -196,11 +205,15 @@ NativeStoneApplicationContext context(broker_); { - Oracle oracle(6); // use multiple threads to execute asynchronous tasks like download content + // use multiple threads to execute asynchronous tasks like + // download content + Oracle oracle(6); oracle.Start(); { - OracleWebService webService(broker_, oracle, webServiceParameters, context); + OracleWebService webService( + broker_, oracle, webServiceParameters, context); + context.SetWebService(webService); context.SetOrthancBaseUrl(webServiceParameters.GetUrl()); @@ -255,5 +268,4 @@ return (success ? 0 : -1); } - }
--- a/Applications/Samples/CMakeLists.txt Mon Apr 29 12:01:55 2019 +0200 +++ b/Applications/Samples/CMakeLists.txt Mon Apr 29 15:24:59 2019 +0200 @@ -216,7 +216,6 @@ ${SIMPLE_VIEWER_APPLICATION_SOURCES} ) target_link_libraries(OrthancStoneSimpleViewer OrthancStone) - endif() #####################################################################
--- a/Applications/Samples/SampleMainNative.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Applications/Samples/SampleMainNative.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -42,3 +42,4 @@ return qtAppRunner.Execute(argc, argv); #endif } +
--- a/Applications/Samples/rt-viewer-demo/main.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Applications/Samples/rt-viewer-demo/main.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -546,6 +546,24 @@ void ParseParameters(const boost::program_options::variables_map& parameters) { + // Generic + { + if (parameters.count("verbose")) + { + Orthanc::Logging::EnableInfoLevel(true); + LOG(INFO) << "Verbose logs (info) are enabled"; + } + } + + { + if (parameters.count("trace")) + { + LOG(INFO) << "parameters.count(\"trace\") != 0"; + Orthanc::Logging::EnableTraceLevel(true); + VLOG(1) << "Trace logs (debug) are enabled"; + } + } + // CT series { @@ -570,12 +588,14 @@ if (parameters.count("dose-series") != 1) { LOG(ERROR) << "the RTDOSE series is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + throw Orthanc::OrthancException( + Orthanc::ErrorCode_ParameterOutOfRange); } doseSeries_ = parameters["ct"].as<std::string>(); #endif LOG(ERROR) << "the RTSTRUCT instance is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + throw Orthanc::OrthancException( + Orthanc::ErrorCode_ParameterOutOfRange); } } @@ -592,26 +612,40 @@ if (parameters.count("struct-series") != 1) { LOG(ERROR) << "the RTSTRUCT series is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + throw Orthanc::OrthancException( + Orthanc::ErrorCode_ParameterOutOfRange); } - structSeries_ = parametersstruct - series"].as<std::string>(); + structSeries_ = parameters["struct-series"].as<std::string>(); #endif LOG(ERROR) << "the RTSTRUCT instance is missing"; - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + throw Orthanc::OrthancException( + Orthanc::ErrorCode_ParameterOutOfRange); } } } - virtual void DeclareStartupOptions(boost::program_options::options_description& options) + virtual void DeclareStartupOptions( + boost::program_options::options_description& options) { - boost::program_options::options_description generic("RtViewerDemo options. Please note that some of these options are mutually exclusive"); + boost::program_options::options_description generic( + "RtViewerDemo options. Please note that some of these options " + "are mutually exclusive"); generic.add_options() - ("ct-series", boost::program_options::value<std::string>(),"Orthanc ID of the CT series") - ("dose-instance", boost::program_options::value<std::string>(), "Orthanc ID of the RTDOSE instance (incompatible with dose-series)") - ("dose-series", boost::program_options::value<std::string>(), "NOT IMPLEMENTED YET. Orthanc ID of the RTDOSE series (incompatible with dose-instance)") - ("struct-instance", boost::program_options::value<std::string>(), "Orthanc ID of the RTSTRUCT instance (incompatible with struct-series)") - ("struct-series", boost::program_options::value<std::string>(), "NOT IMPLEMENTED YET. Orthanc ID of the RTSTRUCT (incompatible with struct-instance)") - ("smooth", boost::program_options::value<bool>()->default_value(true),"Enable bilinear image smoothing") + ("ct-series", boost::program_options::value<std::string>(), + "Orthanc ID of the CT series") + ("dose-instance", boost::program_options::value<std::string>(), + "Orthanc ID of the RTDOSE instance (incompatible with dose-series)") + ("dose-series", boost::program_options::value<std::string>(), + "NOT IMPLEMENTED YET. Orthanc ID of the RTDOSE series (incompatible" + " with dose-instance)") + ("struct-instance", boost::program_options::value<std::string>(), + "Orthanc ID of the RTSTRUCT instance (incompatible with struct-" + "series)") + ("struct-series", boost::program_options::value<std::string>(), + "NOT IMPLEMENTED YET. Orthanc ID of the RTSTRUCT (incompatible with" + " struct-instance)") + ("smooth", boost::program_options::value<bool>()->default_value(true), + "Enable bilinear image smoothing") ; options.add(generic); @@ -637,8 +671,10 @@ ct_.reset(new OrthancStone::OrthancVolumeImage( IObserver::GetBroker(), context->GetOrthancApiClient(), false)); ct_->ScheduleLoadSeries(ctSeries_); - //ct_->ScheduleLoadSeries("a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // IBA - //ct_->ScheduleLoadSeries("03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0"); // 0522c0001 TCIA + //ct_->ScheduleLoadSeries( + // "a04ecf01-79b2fc33-58239f7e-ad9db983-28e81afa"); // IBA + //ct_->ScheduleLoadSeries( + // "03677739-1d8bca40-db1daf59-d74ff548-7f6fc9c0"); // 0522c0001 TCIA } if (!doseSeries_.empty() || @@ -665,8 +701,10 @@ dose_->ScheduleLoadInstance(doseInstance_); } - //dose_->ScheduleLoadInstance("830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // IBA 1 - //dose_->ScheduleLoadInstance("269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c"); // 0522c0001 TCIA + //dose_->ScheduleLoadInstance( + //"830a69ff-8e4b5ee3-b7f966c8-bccc20fb-d322dceb"); // IBA 1 + //dose_->ScheduleLoadInstance( + //"269f26f4-0c83eeeb-2e67abbd-5467a40f-f1bec90c"); //0522c0001 TCIA } if (!structInstance_.empty()) @@ -676,8 +714,10 @@ struct_->ScheduleLoadInstance(structInstance_); - //struct_->ScheduleLoadInstance("54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // IBA - //struct_->ScheduleLoadInstance("17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20"); // 0522c0001 TCIA + //struct_->ScheduleLoadInstance( + //"54460695-ba3885ee-ddf61ac0-f028e31d-a6e474d9"); // IBA + //struct_->ScheduleLoadInstance( + //"17cd032b-ad92a438-ca05f06a-f9e96668-7e3e9e20"); // 0522c0001 TCIA } mainWidget_ = new LayoutWidget("main-layout");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Sdl/SdlOpenGLWindow.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,87 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "SdlOpenGLWindow.h" + +#if ORTHANC_ENABLE_SDL == 1 + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + SdlOpenGLWindow::SdlOpenGLWindow(const char* title, + unsigned int width, + unsigned int height) : + window_(title, width, height, true /* enable OpenGL */) + { + context_ = SDL_GL_CreateContext(window_.GetObject()); + + if (context_ == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Cannot initialize OpenGL"); + } + } + + + SdlOpenGLWindow::~SdlOpenGLWindow() + { + SDL_GL_DeleteContext(context_); + } + + + void SdlOpenGLWindow::MakeCurrent() + { + if (SDL_GL_MakeCurrent(window_.GetObject(), context_) != 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Cannot set current OpenGL context"); + } + + // This makes our buffer swap syncronized with the monitor's vertical refresh + SDL_GL_SetSwapInterval(1); + } + + + void SdlOpenGLWindow::SwapBuffer() + { + // Swap our buffer to display the current contents of buffer on screen + SDL_GL_SwapWindow(window_.GetObject()); + } + + + unsigned int SdlOpenGLWindow::GetCanvasWidth() + { + int w = 0; + SDL_GL_GetDrawableSize(window_.GetObject(), &w, NULL); + return static_cast<unsigned int>(w); + } + + + unsigned int SdlOpenGLWindow::GetCanvasHeight() + { + int h = 0; + SDL_GL_GetDrawableSize(window_.GetObject(), NULL, &h); + return static_cast<unsigned int>(h); + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/Sdl/SdlOpenGLWindow.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if ORTHANC_ENABLE_SDL == 1 + +#include "../../Framework/OpenGL/IOpenGLContext.h" +#include "SdlWindow.h" + +namespace OrthancStone +{ + class SdlOpenGLWindow : public OpenGL::IOpenGLContext + { + private: + SdlWindow window_; + SDL_GLContext context_; + + public: + SdlOpenGLWindow(const char* title, + unsigned int width, + unsigned int height); + + ~SdlOpenGLWindow(); + + SdlWindow& GetWindow() + { + return window_; + } + + virtual void MakeCurrent(); + + virtual void SwapBuffer(); + + virtual unsigned int GetCanvasWidth(); + + virtual unsigned int GetCanvasHeight(); + }; +} + +#endif
--- a/Applications/Wasm/StartupParametersBuilder.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Applications/Wasm/StartupParametersBuilder.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -1,43 +1,66 @@ #include "StartupParametersBuilder.h" +#include <iostream> namespace OrthancStone { - void StartupParametersBuilder::Clear() { - startupParameters_.clear(); - } + void StartupParametersBuilder::Clear() + { + startupParameters_.clear(); + } + + void StartupParametersBuilder::SetStartupParameter( + const char* name, + const char* value) + { + startupParameters_.push_back(std::make_tuple(name, value)); + } - void StartupParametersBuilder::SetStartupParameter(const char* name, const char* value) { - startupParameters_.push_back(std::make_tuple(name, value)); + void StartupParametersBuilder::GetStartupParameters( + boost::program_options::variables_map& parameters, + const boost::program_options::options_description& options) + { + std::vector<std::string> argvStrings(startupParameters_.size() + 1); + // argv mirrors pointers to the internal argvStrings buffers. + // ****************************************************** + // THIS IS HIGHLY DANGEROUS SO BEWARE!!!!!!!!!!!!!! + // ****************************************************** + std::vector<const char*> argv(startupParameters_.size() + 1); + + int argCounter = 0; + argvStrings[argCounter] = "Toto.exe"; + argv[argCounter] = argvStrings[argCounter].c_str(); + + argCounter++; + + std::string cmdLine = ""; + for ( StartupParameters::const_iterator it = startupParameters_.begin(); + it != startupParameters_.end(); + it++) + { + std::stringstream argSs; + + argSs << "--" << std::get<0>(*it); + if(std::get<1>(*it).length() > 0) + argSs << "=" << std::get<1>(*it); + + argvStrings[argCounter] = argSs.str(); + cmdLine = cmdLine + " " + argvStrings[argCounter]; + argv[argCounter] = argvStrings[argCounter].c_str(); + argCounter++; } - void StartupParametersBuilder::GetStartupParameters(boost::program_options::variables_map& parameters, const boost::program_options::options_description& options) { - - const char* argv[startupParameters_.size() + 1]; - int argCounter = 0; - argv[0] = "Toto.exe"; - argCounter++; - - std::string cmdLine = ""; - for (StartupParameters::const_iterator it = startupParameters_.begin(); it != startupParameters_.end(); it++) { - char* arg = new char[128]; - snprintf(arg, 128, "--%s=%s", std::get<0>(*it).c_str(), std::get<1>(*it).c_str()); - argv[argCounter] = arg; - cmdLine = cmdLine + " --" + std::get<0>(*it) + "=" + std::get<1>(*it); - argCounter++; - } + std::cout << "simulated cmdLine = \"" << cmdLine.c_str() << "\"\n"; - printf("simulated cmdLine = %s\n", cmdLine.c_str()); - - try - { - boost::program_options::store(boost::program_options::command_line_parser(argCounter, argv). - options(options).run(), parameters); - boost::program_options::notify(parameters); - } - catch (boost::program_options::error& e) - { - printf("Error while parsing the command-line arguments: %s\n", e.what()); - } - + try + { + boost::program_options::store( + boost::program_options::command_line_parser(argCounter, argv.data()). + options(options).run(), parameters); + boost::program_options::notify(parameters); } -} \ No newline at end of file + catch (boost::program_options::error& e) + { + std::cerr << "Error while parsing the command-line arguments: " << + e.what() << std::endl; } + } +}
--- a/Applications/Wasm/StartupParametersBuilder.h Mon Apr 29 12:01:55 2019 +0200 +++ b/Applications/Wasm/StartupParametersBuilder.h Mon Apr 29 15:24:59 2019 +0200 @@ -43,8 +43,12 @@ public: void Clear(); + // Please note that if a parameter is a flag-style one, the value that + // is passed should be an empty string void SetStartupParameter(const char* name, const char* value); - void GetStartupParameters(boost::program_options::variables_map& parameters_, const boost::program_options::options_description& options); + void GetStartupParameters( + boost::program_options::variables_map& parameters_, + const boost::program_options::options_description& options); }; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/FontRenderer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,185 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "FontRenderer.h" + +#include "../Toolbox/DynamicBitmap.h" + +#include <Core/OrthancException.h> + + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_GLYPH_H + + +// https://stackoverflow.com/questions/31161284/how-can-i-get-the-corresponding-error-string-from-an-ft-error-code +static std::string GetErrorMessage(FT_Error err) +{ +#undef __FTERRORS_H__ +#define FT_ERRORDEF( e, v, s ) case e: return s; +#define FT_ERROR_START_LIST switch (err) { +#define FT_ERROR_END_LIST } +#include FT_ERRORS_H + return "(Unknown error)"; +} + + +static void CheckError(FT_Error err) +{ + if (err != 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Error in FreeType: " + GetErrorMessage(err)); + } +} + + +namespace OrthancStone +{ + class FontRenderer::PImpl : public boost::noncopyable + { + private: + std::string fontContent_; + FT_Library library_; + FT_Face face_; + + void Clear() + { + if (face_ != NULL) + { + FT_Done_Face(face_); + face_ = NULL; + } + + fontContent_.clear(); + } + + public: + PImpl() : + library_(NULL), + face_(NULL) + { + CheckError(FT_Init_FreeType(&library_)); + } + + + ~PImpl() + { + Clear(); + FT_Done_FreeType(library_); + } + + + void LoadFont(const std::string& fontContent, + unsigned int fontSize) + { + Clear(); + + // It is necessary to make a private copy of the font, as + // Freetype makes the assumption that the buffer containing the + // font is never deleted + fontContent_.assign(fontContent); + + const FT_Byte* data = reinterpret_cast<const FT_Byte*>(fontContent_.c_str()); + + CheckError(FT_New_Memory_Face(library_, data, fontContent_.size(), 0, &face_)); + CheckError(FT_Set_Char_Size(face_, // handle to face object + 0, // char_width in 1/64th of points + fontSize * 64, // char_height in 1/64th of points + 72, // horizontal device resolution + 72)); // vertical device resolution + + CheckError(FT_Select_Charmap(face_, FT_ENCODING_UNICODE)); + } + + + Glyph* Render(uint32_t unicode) + { + if (face_ == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, + "First call LoadFont()"); + } + else if (FT_Load_Char(face_, unicode, FT_LOAD_RENDER) != 0) + { + // This character is not available + return NULL; + } + else + { + if (face_->glyph->format != FT_GLYPH_FORMAT_BITMAP) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + //CheckError(FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1)); + } + + Orthanc::ImageAccessor bitmap; + bitmap.AssignReadOnly(Orthanc::PixelFormat_Grayscale8, + face_->glyph->bitmap.width, + face_->glyph->bitmap.rows, + face_->glyph->bitmap.pitch, + face_->glyph->bitmap.buffer); + + std::auto_ptr<Glyph> glyph( + new Glyph(bitmap.GetWidth(), + bitmap.GetHeight(), + face_->glyph->bitmap_left, + -face_->glyph->bitmap_top, // Positive for an upwards vertical distance + face_->glyph->advance.x >> 6, + face_->glyph->metrics.vertAdvance >> 6)); + + glyph->SetPayload(new DynamicBitmap(bitmap)); + + return glyph.release(); + } + } + }; + + + + FontRenderer::FontRenderer() : + pimpl_(new PImpl) + { + } + + + void FontRenderer::LoadFont(const std::string& fontContent, + unsigned int fontSize) + { + pimpl_->LoadFont(fontContent, fontSize); + } + + + void FontRenderer::LoadFont(Orthanc::EmbeddedResources::FileResourceId resource, + unsigned int fontSize) + { + std::string content; + Orthanc::EmbeddedResources::GetFileResource(content, resource); + LoadFont(content, fontSize); + } + + + Glyph* FontRenderer::Render(uint32_t unicode) + { + return pimpl_->Render(unicode); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/FontRenderer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,51 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "Glyph.h" + +#include <EmbeddedResources.h> + +#include <stdint.h> +#include <boost/shared_ptr.hpp> + + +namespace OrthancStone +{ + class FontRenderer : public boost::noncopyable + { + private: + class PImpl; + boost::shared_ptr<PImpl> pimpl_; + + public: + FontRenderer(); + + void LoadFont(const std::string& fontContent, + unsigned int fontSize); + + void LoadFont(Orthanc::EmbeddedResources::FileResourceId resource, + unsigned int fontSize); + + Glyph* Render(uint32_t unicode); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/Glyph.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,93 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "Glyph.h" + +#include <Core/OrthancException.h> + + +namespace OrthancStone +{ + Glyph::Glyph(const Glyph& other) : + width_(other.width_), + height_(other.height_), + offsetLeft_(other.offsetLeft_), + offsetTop_(other.offsetTop_), + advanceX_(other.advanceX_), + lineHeight_(other.lineHeight_) + { + } + + + Glyph::Glyph(unsigned int width, + unsigned int height, + int offsetLeft, + int offsetTop, + int advanceX, + unsigned int lineHeight) : + width_(width), + height_(height), + offsetLeft_(offsetLeft), + offsetTop_(offsetTop), + advanceX_(advanceX), + lineHeight_(lineHeight) + { + } + + + void Glyph::SetPayload(Orthanc::IDynamicObject* payload) // Takes ownership + { + if (payload == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + payload_.reset(payload); + } + } + + + const Orthanc::IDynamicObject& Glyph::GetPayload() const + { + if (payload_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return *payload_; + } + } + + + Orthanc::IDynamicObject* Glyph::ReleasePayload() + { + if (payload_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return payload_.release(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/Glyph.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,95 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <Core/IDynamicObject.h> + +#include <memory> + + +namespace OrthancStone +{ + class Glyph : public boost::noncopyable + { + private: + unsigned int width_; + unsigned int height_; + int offsetLeft_; + int offsetTop_; + int advanceX_; + unsigned int lineHeight_; + + std::auto_ptr<Orthanc::IDynamicObject> payload_; + + public: + // WARNING: This does not copy the payload + Glyph(const Glyph& other); + + Glyph(unsigned int width, + unsigned int height, + int offsetLeft, + int offsetTop, + int advanceX, + unsigned int lineHeight); + + void SetPayload(Orthanc::IDynamicObject* payload); + + int GetOffsetLeft() const + { + return offsetLeft_; + } + + int GetOffsetTop() const + { + return offsetTop_; + } + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + unsigned int GetAdvanceX() const + { + return advanceX_; + } + + unsigned int GetLineHeight() const + { + return lineHeight_; + } + + bool HasPayload() const + { + return payload_.get() != NULL; + } + + const Orthanc::IDynamicObject& GetPayload() const; + + Orthanc::IDynamicObject* ReleasePayload(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/GlyphAlphabet.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,170 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "GlyphAlphabet.h" + +#include <Core/OrthancException.h> +#include <Core/Toolbox.h> + + +namespace OrthancStone +{ + void GlyphAlphabet::Clear() + { + for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it) + { + assert(it->second != NULL); + delete it->second; + } + + content_.clear(); + lineHeight_ = 0; + } + + + void GlyphAlphabet::Register(uint32_t unicode, + const Glyph& glyph, + Orthanc::IDynamicObject* payload) + { + std::auto_ptr<Orthanc::IDynamicObject> protection(payload); + + // Don't add twice the same character + if (content_.find(unicode) == content_.end()) + { + std::auto_ptr<Glyph> raii(new Glyph(glyph)); + + if (payload != NULL) + { + raii->SetPayload(protection.release()); + } + + content_[unicode] = raii.release(); + + lineHeight_ = std::max(lineHeight_, glyph.GetLineHeight()); + } + } + + + void GlyphAlphabet::Register(FontRenderer& renderer, + uint32_t unicode) + { + std::auto_ptr<Glyph> glyph(renderer.Render(unicode)); + + if (glyph.get() != NULL) + { + Register(unicode, *glyph, glyph->ReleasePayload()); + } + } + + +#if ORTHANC_ENABLE_LOCALE == 1 + bool GlyphAlphabet::GetUnicodeFromCodepage(uint32_t& unicode, + unsigned int index, + Orthanc::Encoding encoding) + { + if (index > 255) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string character; + character.resize(1); + character[0] = static_cast<unsigned char>(index); + + std::string utf8 = Orthanc::Toolbox::ConvertToUtf8(character, encoding, false /* no code extensions */); + + if (utf8.empty()) + { + // This character is not available in this codepage + return false; + } + else + { + size_t length; + Orthanc::Toolbox::Utf8ToUnicodeCharacter(unicode, length, utf8, 0); + assert(length != 0); + return true; + } + } +#endif + + + void GlyphAlphabet::Apply(IGlyphVisitor& visitor) const + { + for (Content::const_iterator it = content_.begin(); it != content_.end(); ++it) + { + assert(it->second != NULL); + visitor.Visit(it->first, *it->second); + } + } + + + void GlyphAlphabet::Apply(ITextVisitor& visitor, + const std::string& utf8) const + { + size_t pos = 0; + int x = 0; + int y = 0; + + while (pos < utf8.size()) + { + if (utf8[pos] == '\r') + { + // Ignore carriage return + pos++; + } + else if (utf8[pos] == '\n') + { + // This is a newline character + x = 0; + y += static_cast<int>(lineHeight_); + + pos++; + } + else + { + uint32_t unicode; + size_t length; + Orthanc::Toolbox::Utf8ToUnicodeCharacter(unicode, length, utf8, pos); + + Content::const_iterator glyph = content_.find(unicode); + + if (glyph != content_.end()) + { + assert(glyph->second != NULL); + const Orthanc::IDynamicObject* payload = + (glyph->second->HasPayload() ? &glyph->second->GetPayload() : NULL); + + visitor.Visit(unicode, + x + glyph->second->GetOffsetLeft(), + y + glyph->second->GetOffsetTop(), + glyph->second->GetWidth(), + glyph->second->GetHeight(), + payload); + x += glyph->second->GetAdvanceX(); + } + + assert(length != 0); + pos += length; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/GlyphAlphabet.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,105 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "FontRenderer.h" + +#include <Core/Enumerations.h> + +#include <map> + +namespace OrthancStone +{ + class GlyphAlphabet : public boost::noncopyable + { + public: + class ITextVisitor : public boost::noncopyable + { + public: + virtual ~ITextVisitor() + { + } + + virtual void Visit(uint32_t unicode, + int x, + int y, + unsigned int width, + unsigned int height, + const Orthanc::IDynamicObject* payload /* can be NULL */) = 0; + }; + + + class IGlyphVisitor : public boost::noncopyable + { + public: + virtual ~IGlyphVisitor() + { + } + + virtual void Visit(uint32_t unicode, + const Glyph& glyph) = 0; + }; + + + private: + typedef std::map<uint32_t, Glyph*> Content; + + Content content_; + unsigned int lineHeight_; + + public: + GlyphAlphabet() : + lineHeight_(0) + { + } + + ~GlyphAlphabet() + { + Clear(); + } + + void Clear(); + + void Register(uint32_t unicode, + const Glyph& glyph, + Orthanc::IDynamicObject* payload); + + void Register(FontRenderer& renderer, + uint32_t unicode); + +#if ORTHANC_ENABLE_LOCALE == 1 + static bool GetUnicodeFromCodepage(uint32_t& unicode, + unsigned int index, + Orthanc::Encoding encoding); +#endif + + size_t GetSize() const + { + return content_.size(); + } + + void Apply(IGlyphVisitor& visitor) const; + + void Apply(ITextVisitor& visitor, + const std::string& utf8) const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/GlyphBitmapAlphabet.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,113 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "GlyphBitmapAlphabet.h" + +#include "TextBoundingBox.h" +#include "../Toolbox/DynamicBitmap.h" + +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> + +namespace OrthancStone +{ + class GlyphBitmapAlphabet::RenderTextVisitor : public GlyphAlphabet::ITextVisitor + { + private: + Orthanc::ImageAccessor& target_; + const GlyphBitmapAlphabet& that_; + int offsetX_; + int offsetY_; + + public: + RenderTextVisitor(Orthanc::ImageAccessor& target, + const GlyphBitmapAlphabet& that, + int offsetX, + int offsetY) : + target_(target), + that_(that), + offsetX_(offsetX), + offsetY_(offsetY) + { + } + + virtual void Visit(uint32_t unicode, + int x, + int y, + unsigned int width, + unsigned int height, + const Orthanc::IDynamicObject* payload) + { + int left = x + offsetX_; + int top = y + offsetY_; + + assert(payload != NULL); + const DynamicBitmap& glyph = *dynamic_cast<const DynamicBitmap*>(payload); + + assert(left >= 0 && + top >= 0 && + static_cast<unsigned int>(left) + width <= target_.GetWidth() && + static_cast<unsigned int>(top) + height <= target_.GetHeight() && + width == glyph.GetBitmap().GetWidth() && + height == glyph.GetBitmap().GetHeight()); + + { + Orthanc::ImageAccessor region; + target_.GetRegion(region, left, top, width, height); + Orthanc::ImageProcessing::Copy(region, glyph.GetBitmap()); + } + } + }; + + +#if ORTHANC_ENABLE_LOCALE == 1 + void GlyphBitmapAlphabet::LoadCodepage(FontRenderer& renderer, + Orthanc::Encoding codepage) + { + for (unsigned int i = 0; i < 256; i++) + { + uint32_t unicode; + if (GlyphAlphabet::GetUnicodeFromCodepage(unicode, i, codepage)) + { + AddUnicodeCharacter(renderer, unicode); + } + } + } +#endif + + + Orthanc::ImageAccessor* GlyphBitmapAlphabet::RenderText(const std::string& utf8) const + { + TextBoundingBox box(alphabet_, utf8); + + std::auto_ptr<Orthanc::ImageAccessor> bitmap( + new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, + box.GetWidth(), box.GetHeight(), + true /* force minimal pitch */)); + + Orthanc::ImageProcessing::Set(*bitmap, 0); + + RenderTextVisitor visitor(*bitmap, *this, -box.GetLeft(), -box.GetTop()); + alphabet_.Apply(visitor, utf8); + + return bitmap.release(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/GlyphBitmapAlphabet.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,58 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "GlyphAlphabet.h" + +#include <Core/Images/ImageAccessor.h> + +namespace OrthancStone +{ + class GlyphBitmapAlphabet : public boost::noncopyable + { + private: + class RenderTextVisitor; + + GlyphAlphabet alphabet_; + + public: + const GlyphAlphabet& GetAlphabet() const + { + return alphabet_; + } + + void AddUnicodeCharacter(FontRenderer& renderer, + uint32_t unicode) + { + alphabet_.Register(renderer, unicode); + } + + +#if ORTHANC_ENABLE_LOCALE == 1 + void LoadCodepage(FontRenderer& renderer, + Orthanc::Encoding codepage); +#endif + + + Orthanc::ImageAccessor* RenderText(const std::string& utf8) const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/GlyphTextureAlphabet.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,295 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "GlyphTextureAlphabet.h" + +#include "TextBoundingBox.h" +#include "../Toolbox/DynamicBitmap.h" + +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/OrthancException.h> + +#include <boost/math/special_functions/round.hpp> + + +namespace OrthancStone +{ + class GlyphTextureAlphabet::GlyphSizeVisitor : public GlyphAlphabet::IGlyphVisitor + { + private: + unsigned int maxWidth_; + unsigned int maxHeight_; + + public: + GlyphSizeVisitor() : + maxWidth_(0), + maxHeight_(0) + { + } + + virtual void Visit(uint32_t unicode, + const Glyph& glyph) + { + maxWidth_ = std::max(maxWidth_, glyph.GetWidth()); + maxHeight_ = std::max(maxHeight_, glyph.GetHeight()); + } + + unsigned int GetMaxWidth() const + { + return maxWidth_; + } + + unsigned int GetMaxHeight() const + { + return maxHeight_; + } + }; + + + class GlyphTextureAlphabet::TextureGenerator : public GlyphAlphabet::IGlyphVisitor + { + private: + std::auto_ptr<Orthanc::ImageAccessor> texture_; + + unsigned int countColumns_; + unsigned int countRows_; + GlyphAlphabet& targetAlphabet_; + unsigned int glyphMaxWidth_; + unsigned int glyphMaxHeight_; + unsigned int column_; + unsigned int row_; + + public: + TextureGenerator(GlyphAlphabet& targetAlphabet, + unsigned int countGlyphs, + unsigned int glyphMaxWidth, + unsigned int glyphMaxHeight) : + targetAlphabet_(targetAlphabet), + glyphMaxWidth_(glyphMaxWidth), + glyphMaxHeight_(glyphMaxHeight), + column_(0), + row_(0) + { + int c = boost::math::iround<int>(sqrt(static_cast<float>(countGlyphs))); + + if (c <= 0) + { + countColumns_ = 1; + } + else + { + countColumns_ = static_cast<unsigned int>(c); + } + + countRows_ = countGlyphs / countColumns_; + if (countGlyphs % countColumns_ != 0) + { + countRows_++; + } + + texture_.reset(new Orthanc::Image(Orthanc::PixelFormat_RGBA32, + countColumns_ * glyphMaxWidth_, + countRows_ * glyphMaxHeight_, + true /* force minimal pitch */)); + + Orthanc::ImageProcessing::Set(*texture_, 0, 0, 0, 0); + } + + + virtual void Visit(uint32_t unicode, + const Glyph& glyph) + { + if (!glyph.HasPayload()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + if (column_ >= countColumns_ || + row_ >= countRows_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + unsigned int x = column_ * glyphMaxWidth_; + unsigned int y = row_ * glyphMaxHeight_; + + const Orthanc::ImageAccessor& source = dynamic_cast<const DynamicBitmap&>(glyph.GetPayload()).GetBitmap(); + + if (source.GetFormat() != Orthanc::PixelFormat_Grayscale8) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + targetAlphabet_.Register(unicode, glyph, new TextureLocation(x, y)); + + Orthanc::ImageAccessor target; + texture_->GetRegion(target, x, y, source.GetWidth(), source.GetHeight()); + + //Orthanc::ImageProcessing::Copy(target, bitmap->GetBitmap()); + + for (unsigned int y = 0; y < source.GetHeight(); y++) + { + const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y)); + uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); + + for (unsigned int x = 0; x < source.GetWidth(); x++) + { + // Premultiplied alpha + q[0] = 0; + q[1] = 0; + q[2] = 0; + q[3] = *p; + + p++; + q += 4; + } + } + + column_++; + if (column_ == countColumns_) + { + column_ = 0; + row_++; + } + } + + + Orthanc::ImageAccessor* ReleaseTexture() + { + return texture_.release(); + } + }; + + + class GlyphTextureAlphabet::RenderTextVisitor : public GlyphAlphabet::ITextVisitor + { + private: + Orthanc::ImageAccessor& target_; + const Orthanc::ImageAccessor& texture_; + int offsetX_; + int offsetY_; + + public: + RenderTextVisitor(Orthanc::ImageAccessor& target, + const GlyphTextureAlphabet& that, + int offsetX, + int offsetY) : + target_(target), + texture_(that.GetTexture()), + offsetX_(offsetX), + offsetY_(offsetY) + { + } + + virtual void Visit(uint32_t unicode, + int x, + int y, + unsigned int width, + unsigned int height, + const Orthanc::IDynamicObject* payload) + { + int left = x + offsetX_; + int top = y + offsetY_; + + assert(payload != NULL); + const TextureLocation& location = *dynamic_cast<const TextureLocation*>(payload); + + assert(left >= 0 && + top >= 0 && + static_cast<unsigned int>(left) + width <= target_.GetWidth() && + static_cast<unsigned int>(top) + height <= target_.GetHeight()); + + { + Orthanc::ImageAccessor to; + target_.GetRegion(to, left, top, width, height); + + Orthanc::ImageAccessor from; + texture_.GetRegion(from, location.GetX(), location.GetY(), width, height); + + Orthanc::ImageProcessing::Copy(to, from); + } + } + }; + + + GlyphTextureAlphabet::GlyphTextureAlphabet(const GlyphBitmapAlphabet& sourceAlphabet) : + textureWidth_(0), + textureHeight_(0) + { + GlyphSizeVisitor size; + sourceAlphabet.GetAlphabet().Apply(size); + + TextureGenerator generator(alphabet_, + sourceAlphabet.GetAlphabet().GetSize(), + size.GetMaxWidth(), + size.GetMaxHeight()); + sourceAlphabet.GetAlphabet().Apply(generator); + + texture_.reset(generator.ReleaseTexture()); + textureWidth_ = texture_->GetWidth(); + textureHeight_ = texture_->GetHeight(); + } + + + const Orthanc::ImageAccessor& GlyphTextureAlphabet::GetTexture() const + { + if (texture_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return *texture_; + } + } + + + Orthanc::ImageAccessor* GlyphTextureAlphabet::ReleaseTexture() + { + if (texture_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return texture_.release(); + } + } + + + Orthanc::ImageAccessor* GlyphTextureAlphabet::RenderText(const std::string& utf8) + { + TextBoundingBox box(alphabet_, utf8); + + std::auto_ptr<Orthanc::ImageAccessor> bitmap( + new Orthanc::Image(Orthanc::PixelFormat_RGBA32, + box.GetWidth(), box.GetHeight(), + true /* force minimal pitch */)); + + Orthanc::ImageProcessing::Set(*bitmap, 0, 0, 0, 0); + + RenderTextVisitor visitor(*bitmap, *this, -box.GetLeft(), -box.GetTop()); + alphabet_.Apply(visitor, utf8); + + return bitmap.release(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/GlyphTextureAlphabet.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,92 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "GlyphBitmapAlphabet.h" + +#include <Core/Images/ImageAccessor.h> + +namespace OrthancStone +{ + class GlyphTextureAlphabet : public boost::noncopyable + { + public: + class TextureLocation : public Orthanc::IDynamicObject + { + private: + unsigned int x_; + unsigned int y_; + + public: + TextureLocation(unsigned int x, + unsigned int y) : + x_(x), + y_(y) + { + } + + unsigned int GetX() const + { + return x_; + } + + unsigned int GetY() const + { + return y_; + } + }; + + private: + class GlyphSizeVisitor; + class TextureGenerator; + class RenderTextVisitor; + + GlyphAlphabet alphabet_; + std::auto_ptr<Orthanc::ImageAccessor> texture_; + unsigned int textureWidth_; + unsigned int textureHeight_; + + public: + GlyphTextureAlphabet(const GlyphBitmapAlphabet& sourceAlphabet); + + const Orthanc::ImageAccessor& GetTexture() const; + + Orthanc::ImageAccessor* ReleaseTexture(); + + Orthanc::ImageAccessor* RenderText(const std::string& utf8); + + const GlyphAlphabet& GetAlphabet() const + { + return alphabet_; + } + + unsigned int GetTextureWidth() const + { + return textureWidth_; + } + + unsigned int GetTextureHeight() const + { + return textureHeight_; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/OpenGLTextCoordinates.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,117 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLTextCoordinates.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + namespace OpenGL + { + void OpenGLTextCoordinates::Visit(uint32_t unicode, + int x, + int y, + unsigned int width, + unsigned int height, + const Orthanc::IDynamicObject* payload) + { + // Rendering coordinates + float rx1 = x - box_.GetLeft(); + float ry1 = y - box_.GetTop(); + float rx2 = rx1 + static_cast<float>(width); + float ry2 = ry1 + static_cast<float>(height); + + // Texture coordinates + assert(payload != NULL); + const GlyphTextureAlphabet::TextureLocation& location = + *dynamic_cast<const GlyphTextureAlphabet::TextureLocation*>(payload); + + float tx1 = location.GetX() / textureWidth_; + float ty1 = location.GetY() / textureHeight_; + float tx2 = tx1 + (static_cast<float>(width) / textureWidth_); + float ty2 = ty1 + (static_cast<float>(height) / textureHeight_); + + const float rpos[6][2] = { + { rx1, ry1 }, + { rx1, ry2 }, + { rx2, ry1 }, + { rx2, ry1 }, + { rx1, ry2 }, + { rx2, ry2 } + }; + + const float tpos[6][2] = { + { tx1, ty1 }, + { tx1, ty2 }, + { tx2, ty1 }, + { tx2, ty1 }, + { tx1, ty2 }, + { tx2, ty2 } + }; + + for (unsigned int i = 0; i < 6; i++) + { + renderingCoords_.push_back(rpos[i][0]); + renderingCoords_.push_back(rpos[i][1]); + textureCoords_.push_back(tpos[i][0]); + textureCoords_.push_back(tpos[i][1]); + } + } + + + OpenGLTextCoordinates::OpenGLTextCoordinates(const GlyphTextureAlphabet& alphabet, + const std::string& utf8) : + box_(alphabet.GetAlphabet(), utf8), + textureWidth_(alphabet.GetTextureWidth()), + textureHeight_(alphabet.GetTextureHeight()) + { + if (textureWidth_ <= 0 || + textureHeight_ <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + width_ = static_cast<float>(box_.GetWidth()); + height_ = static_cast<float>(box_.GetHeight()); + + // Each character is made of two 2D triangles (= 2 * 3 * 2 = 12) + renderingCoords_.reserve(box_.GetCharactersCount() * 12); + textureCoords_.reserve(box_.GetCharactersCount() * 12); + + alphabet.GetAlphabet().Apply(*this, utf8); + } + + + const std::vector<float>& OpenGLTextCoordinates::GetRenderingCoords() const + { + assert(renderingCoords_.size() == textureCoords_.size()); + return renderingCoords_; + } + + + const std::vector<float>& OpenGLTextCoordinates::GetTextureCoords() const + { + assert(renderingCoords_.size() == textureCoords_.size()); + return textureCoords_; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/OpenGLTextCoordinates.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "GlyphTextureAlphabet.h" +#include "TextBoundingBox.h" + +#include <vector> + +namespace OrthancStone +{ + namespace OpenGL + { + class OpenGLTextCoordinates : protected GlyphAlphabet::ITextVisitor + { + private: + TextBoundingBox box_; + float width_; + float height_; + std::vector<float> renderingCoords_; + std::vector<float> textureCoords_; + float textureWidth_; + float textureHeight_; + + protected: + virtual void Visit(uint32_t unicode, + int x, + int y, + unsigned int width, + unsigned int height, + const Orthanc::IDynamicObject* payload); + + public: + OpenGLTextCoordinates(const GlyphTextureAlphabet& alphabet, + const std::string& utf8); + + unsigned int GetTextWidth() const + { + return box_.GetWidth(); + } + + unsigned int GetTextHeight() const + { + return box_.GetHeight(); + } + + bool IsEmpty() const + { + return renderingCoords_.empty(); + } + + const std::vector<float>& GetRenderingCoords() const; + + const std::vector<float>& GetTextureCoords() const; + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/TextBoundingBox.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,80 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "TextBoundingBox.h" + +namespace OrthancStone +{ + void TextBoundingBox::AddPoint(int x, + int y) + { + left_ = std::min(left_, x); + right_ = std::max(right_, x); + top_ = std::min(top_, y); + bottom_ = std::max(bottom_, y); + } + + + void TextBoundingBox::Clear() + { + left_ = 0; + top_ = 0; + right_ = 0; + bottom_ = 0; + countCharacters_ = 0; + } + + + void TextBoundingBox::Visit(uint32_t unicode, + int x, + int y, + unsigned int width, + unsigned int height, + const Orthanc::IDynamicObject* payload /* ignored */) + { + AddPoint(x, y); + AddPoint(x + static_cast<int>(width), + y + static_cast<int>(height)); + countCharacters_++; + } + + + TextBoundingBox::TextBoundingBox(const GlyphAlphabet& alphabet, + const std::string& utf8) + { + Clear(); + alphabet.Apply(*this, utf8); + } + + + unsigned int TextBoundingBox::GetWidth() const + { + assert(left_ <= right_); + return static_cast<unsigned int>(right_ - left_ + 1); + } + + + unsigned int TextBoundingBox::GetHeight() const + { + assert(top_ <= bottom_); + return static_cast<unsigned int>(bottom_ - top_ + 1); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Fonts/TextBoundingBox.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,73 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "GlyphAlphabet.h" + +namespace OrthancStone +{ + class TextBoundingBox : protected GlyphAlphabet::ITextVisitor + { + private: + int left_; + int top_; + int right_; + int bottom_; + unsigned int countCharacters_; + + void AddPoint(int x, + int y); + + void Clear(); + + protected: + virtual void Visit(uint32_t unicode, + int x, + int y, + unsigned int width, + unsigned int height, + const Orthanc::IDynamicObject* payload /* ignored */); + + public: + TextBoundingBox(const GlyphAlphabet& alphabet, + const std::string& utf8); + + int GetLeft() const + { + return left_; + } + + int GetTop() const + { + return top_; + } + + unsigned int GetWidth() const; + + unsigned int GetHeight() const; + + unsigned int GetCharactersCount() const + { + return countCharacters_; + } + }; +}
--- a/Framework/Layers/RenderStyle.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Framework/Layers/RenderStyle.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -29,7 +29,7 @@ { visible_ = true; reverse_ = false; - windowing_ = ImageWindowing_Default; + windowing_ = ImageWindowing_Custom; alpha_ = 1; applyLut_ = false; lut_ = Orthanc::EmbeddedResources::COLORMAP_HOT;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/OpenGL/IOpenGLContext.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <boost/noncopyable.hpp> + +namespace OrthancStone +{ + namespace OpenGL + { + class IOpenGLContext : public boost::noncopyable + { + public: + virtual ~IOpenGLContext() + { + } + + virtual void MakeCurrent() = 0; + + virtual void SwapBuffer() = 0; + + virtual unsigned int GetCanvasWidth() = 0; + + virtual unsigned int GetCanvasHeight() = 0; + }; + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/OpenGL/OpenGLIncludes.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,45 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if !defined(ORTHANC_ENABLE_OPENGL) +# error The macro ORTHANC_ENABLE_OPENGL must be defined +#endif + +#if ORTHANC_ENABLE_OPENGL != 1 +# error Support for OpenGL is disabled +#endif + +#if defined(__APPLE__) +# include <OpenGL/gl.h> +# include <OpenGL/glext.h> +#elif defined(_WIN32) +// On Windows, use the compatibility headers provided by SDL +# if ORTHANC_ENABLE_SDL == 1 +# include <SDL_opengl.h> +# else +# error Stone cannot be compiled on Windows without SDL +# endif +#else +# include <GL/gl.h> +# include <GL/glext.h> +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/OpenGL/OpenGLProgram.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,103 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLProgram.h" + +#include "OpenGLShader.h" + +#include <Core/OrthancException.h> + + +namespace OrthancStone +{ + namespace OpenGL + { + OpenGLProgram::OpenGLProgram() + { + program_ = glCreateProgram(); + if (program_ == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Cannot create an OpenGL program"); + } + } + + + OpenGLProgram::~OpenGLProgram() + { + assert(program_ != 0); + glDeleteProgram(program_); + } + + + void OpenGLProgram::Use() + { + glUseProgram(program_); + } + + + void OpenGLProgram::CompileShaders(const std::string& vertexCode, + const std::string& fragmentCode) + { + assert(program_ != 0); + + OpenGLShader vertexShader(GL_VERTEX_SHADER, vertexCode); + OpenGLShader fragmentShader(GL_FRAGMENT_SHADER, fragmentCode); + + glAttachShader(program_, vertexShader.Release()); + glAttachShader(program_, fragmentShader.Release()); + glLinkProgram(program_); + glValidateProgram(program_); + } + + + GLint OpenGLProgram::GetUniformLocation(const std::string& name) + { + GLint location = glGetUniformLocation(program_, name.c_str()); + + if (location == -1) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem, + "Inexistent uniform variable in shader: " + name); + } + else + { + return location; + } + } + + + GLint OpenGLProgram::GetAttributeLocation(const std::string& name) + { + GLint location = glGetAttribLocation(program_, name.c_str()); + + if (location == -1) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem, + "Inexistent attribute in shader: " + name); + } + else + { + return location; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/OpenGL/OpenGLProgram.h Mon Apr 29 15:24:59 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "OpenGLIncludes.h" + +#include <string> +#include <boost/noncopyable.hpp> + +namespace OrthancStone +{ + namespace OpenGL + { + class OpenGLProgram : public boost::noncopyable + { + private: + GLuint program_; + + public: + // WARNING: A global OpenGL context must be active to create this object! + OpenGLProgram(); + + ~OpenGLProgram(); + + void Use(); + + // WARNING: A global OpenGL context must be active to run this method! + void CompileShaders(const std::string& vertexCode, + const std::string& fragmentCode); + + GLint GetUniformLocation(const std::string& name); + + GLint GetAttributeLocation(const std::string& name); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/OpenGL/OpenGLShader.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,104 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLShader.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + namespace OpenGL + { + static GLuint CompileShader(GLenum type, + const std::string& source) + { + // Create shader object + const GLchar* sourceString[1]; + GLint sourceStringLengths[1]; + + sourceString[0] = source.c_str(); + sourceStringLengths[0] = source.length(); + GLuint shader = glCreateShader(type); + + if (shader == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Cannot create an OpenGL shader"); + } + else + { + // Assign and compile the source to the shader object + glShaderSource(shader, 1, sourceString, sourceStringLengths); + glCompileShader(shader); + + // Check if there were errors + int infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + + if (infoLen > 1) // Might be equal to 1, which amounts to no error + { + std::string infoLog; + infoLog.resize(infoLen + 1); + glGetShaderInfoLog(shader, infoLen, NULL, &infoLog[0]); + glDeleteShader(shader); + + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Error while creating an OpenGL shader: " + infoLog); + } + else + { + return shader; + } + } + } + + + OpenGLShader::OpenGLShader(GLenum type, + const std::string& source) + { + shader_ = CompileShader(type, source); + isValid_ = true; + } + + + OpenGLShader::~OpenGLShader() + { + if (isValid_) + { + glDeleteShader(shader_); + } + } + + + GLuint OpenGLShader::Release() + { + if (isValid_) + { + isValid_ = false; + return shader_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/OpenGL/OpenGLShader.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,53 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "OpenGLIncludes.h" + +#include <string> +#include <boost/noncopyable.hpp> + +namespace OrthancStone +{ + namespace OpenGL + { + class OpenGLShader : public boost::noncopyable + { + private: + bool isValid_; + GLuint shader_; + + public: + OpenGLShader(GLenum type, + const std::string& source); + + ~OpenGLShader(); + + bool IsValid() const + { + return isValid_; + } + + GLuint Release(); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/OpenGL/OpenGLTexture.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,113 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLTexture.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + namespace OpenGL + { + OpenGLTexture::OpenGLTexture() : + width_(0), + height_(0) + { + // Generate a texture object + glGenTextures(1, &texture_); + if (texture_ == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, + "Cannot create an OpenGL program"); + } + } + + + OpenGLTexture::~OpenGLTexture() + { + assert(texture_ != 0); + glDeleteTextures(1, &texture_); + } + + + void OpenGLTexture::Load(const Orthanc::ImageAccessor& image, + bool isLinearInterpolation) + { + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Disable byte-alignment restriction + + if (image.GetPitch() != image.GetBytesPerPixel() * image.GetWidth()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, + "Unsupported non-zero padding"); + } + + // Bind it + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_); + + GLenum sourceFormat, internalFormat; + + switch (image.GetFormat()) + { + case Orthanc::PixelFormat_Grayscale8: + sourceFormat = GL_RED; + internalFormat = GL_RED; + break; + + case Orthanc::PixelFormat_RGB24: + sourceFormat = GL_RGB; + internalFormat = GL_RGBA; + break; + + case Orthanc::PixelFormat_RGBA32: + sourceFormat = GL_RGBA; + internalFormat = GL_RGBA; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented, + "No support for this format in OpenGL textures: " + + std::string(EnumerationToString(image.GetFormat()))); + } + + width_ = image.GetWidth(); + height_ = image.GetHeight(); + + GLint interpolation = (isLinearInterpolation ? GL_LINEAR : GL_NEAREST); + + // Load the texture from the image buffer + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, image.GetWidth(), image.GetHeight(), + 0, sourceFormat, GL_UNSIGNED_BYTE, image.GetBuffer()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, interpolation); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, interpolation); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + + + void OpenGLTexture::Bind(GLint location) + { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_); + glUniform1i(location, 0 /* texture unit */); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/OpenGL/OpenGLTexture.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "OpenGLIncludes.h" + +#include <Core/Images/ImageAccessor.h> + +#include <boost/noncopyable.hpp> + + +namespace OrthancStone +{ + namespace OpenGL + { + class OpenGLTexture : public boost::noncopyable + { + private: + GLuint texture_; + unsigned int width_; + unsigned int height_; + + public: + OpenGLTexture(); + + ~OpenGLTexture(); + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + void Load(const Orthanc::ImageAccessor& image, + bool isLinearInterpolation); + + void Bind(GLint location); + }; + } +}
--- a/Framework/Radiography/RadiographyScene.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Framework/Radiography/RadiographyScene.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -199,24 +199,37 @@ { LOG(INFO) << "Removing layer: " << layerIndex; - if (layerIndex > countLayers_) + Layers::iterator found = layers_.find(layerIndex); + + if (found == layers_.end()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - delete layers_[layerIndex]; - layers_.erase(layerIndex); - countLayers_--; - LOG(INFO) << "Removing layer, there are now : " << countLayers_ << " layers"; + else + { + assert(found->second != NULL); + delete found->second; + + layers_.erase(found); + countLayers_--; + + LOG(INFO) << "Removing layer, there are now : " << countLayers_ << " layers"; + } } const RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) const { - if (layerIndex > countLayers_) + Layers::const_iterator found = layers_.find(layerIndex); + + if (found == layers_.end()) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - - return *(layers_.at(layerIndex)); + else + { + assert(found->second != NULL); + return *found->second; + } } bool RadiographyScene::GetWindowing(float& center,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/CairoCompositor.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,177 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "CairoCompositor.h" + +#include "Internals/CairoColorTextureRenderer.h" +#include "Internals/CairoFloatTextureRenderer.h" +#include "Internals/CairoInfoPanelRenderer.h" +#include "Internals/CairoPolylineRenderer.h" +#include "Internals/CairoTextRenderer.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + cairo_t* CairoCompositor::GetCairoContext() + { + if (context_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return context_->GetObject(); + } + } + + + Internals::CompositorHelper::ILayerRenderer* CairoCompositor::Create(const ISceneLayer& layer) + { + switch (layer.GetType()) + { + case ISceneLayer::Type_Polyline: + return new Internals::CairoPolylineRenderer(*this, layer); + + case ISceneLayer::Type_InfoPanel: + return new Internals::CairoInfoPanelRenderer(*this, layer); + + case ISceneLayer::Type_ColorTexture: + return new Internals::CairoColorTextureRenderer(*this, layer); + + case ISceneLayer::Type_FloatTexture: + return new Internals::CairoFloatTextureRenderer(*this, layer); + + case ISceneLayer::Type_Text: + { + const TextSceneLayer& l = dynamic_cast<const TextSceneLayer&>(layer); + + Fonts::const_iterator found = fonts_.find(l.GetFontIndex()); + if (found == fonts_.end()) + { + return NULL; + } + else + { + assert(found->second != NULL); + return new Internals::CairoTextRenderer(*this, *found->second, l); + } + } + + default: + return NULL; + } + } + + + CairoCompositor::CairoCompositor(const Scene2D& scene, + unsigned int canvasWidth, + unsigned int canvasHeight) : + helper_(scene, *this) + { + canvas_.SetSize(canvasWidth, canvasHeight, false); + } + + + CairoCompositor::~CairoCompositor() + { + for (Fonts::iterator it = fonts_.begin(); it != fonts_.end(); ++it) + { + assert(it->second != NULL); + delete it->second; + } + } + + + void CairoCompositor::SetFont(size_t index, + GlyphBitmapAlphabet* dict) // Takes ownership + { + if (dict == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + std::auto_ptr<GlyphBitmapAlphabet> protection(dict); + + Fonts::iterator found = fonts_.find(index); + + if (found == fonts_.end()) + { + fonts_[index] = protection.release(); + } + else + { + assert(found->second != NULL); + delete found->second; + + found->second = protection.release(); + } + } + } + + +#if ORTHANC_ENABLE_LOCALE == 1 + void CairoCompositor::SetFont(size_t index, + Orthanc::EmbeddedResources::FileResourceId resource, + unsigned int fontSize, + Orthanc::Encoding codepage) + { + FontRenderer renderer; + renderer.LoadFont(resource, fontSize); + + std::auto_ptr<GlyphBitmapAlphabet> alphabet(new GlyphBitmapAlphabet); + alphabet->LoadCodepage(renderer, codepage); + + SetFont(index, alphabet.release()); + } +#endif + + + void CairoCompositor::Refresh() + { + context_.reset(new CairoContext(canvas_)); + + // https://www.cairographics.org/FAQ/#clear_a_surface + cairo_set_source_rgba(context_->GetObject(), 0, 0, 0, 255); + cairo_paint(context_->GetObject()); + + helper_.Refresh(canvas_.GetWidth(), canvas_.GetHeight()); + context_.reset(); + } + + + Orthanc::ImageAccessor* CairoCompositor::RenderText(size_t fontIndex, + const std::string& utf8) const + { + Fonts::const_iterator found = fonts_.find(fontIndex); + + if (found == fonts_.end()) + { + return NULL; + } + else + { + assert(found->second != NULL); + return found->second->RenderText(utf8); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/CairoCompositor.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,86 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Fonts/GlyphBitmapAlphabet.h" +#include "../Viewport/CairoContext.h" +#include "Internals/CompositorHelper.h" +#include "Internals/ICairoContextProvider.h" + +namespace OrthancStone +{ + class CairoCompositor : + private Internals::CompositorHelper::IRendererFactory, + private Internals::ICairoContextProvider + { + private: + typedef std::map<size_t, GlyphBitmapAlphabet*> Fonts; + + Internals::CompositorHelper helper_; + CairoSurface canvas_; + Fonts fonts_; + + // Only valid during a call to "Refresh()" + std::auto_ptr<CairoContext> context_; + + virtual cairo_t* GetCairoContext(); + + virtual unsigned int GetCairoWidth() + { + return canvas_.GetWidth(); + } + + virtual unsigned int GetCairoHeight() + { + return canvas_.GetHeight(); + } + + virtual Internals::CompositorHelper::ILayerRenderer* Create(const ISceneLayer& layer); + + public: + CairoCompositor(const Scene2D& scene, + unsigned int canvasWidth, + unsigned int canvasHeight); + + ~CairoCompositor(); + + const CairoSurface& GetCanvas() const + { + return canvas_; + } + + void SetFont(size_t index, + GlyphBitmapAlphabet* dict); // Takes ownership + +#if ORTHANC_ENABLE_LOCALE == 1 + void SetFont(size_t index, + Orthanc::EmbeddedResources::FileResourceId resource, + unsigned int fontSize, + Orthanc::Encoding codepage); +#endif + + void Refresh(); + + Orthanc::ImageAccessor* RenderText(size_t fontIndex, + const std::string& utf8) const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/ColorSceneLayer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,84 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ISceneLayer.h" + +#include <stdint.h> + +namespace OrthancStone +{ + class ColorSceneLayer : public ISceneLayer + { + private: + uint8_t red_; + uint8_t green_; + uint8_t blue_; + + public: + ColorSceneLayer() : + red_(255), + green_(255), + blue_(255) + { + } + + void SetColor(uint8_t red, + uint8_t green, + uint8_t blue) + { + red_ = red; + green_ = green; + blue_ = blue; + } + + uint8_t GetRed() const + { + return red_; + } + + uint8_t GetGreen() const + { + return green_; + } + + uint8_t GetBlue() const + { + return blue_; + } + + float GetRedAsFloat() const + { + return static_cast<float>(red_) / 255.0f; + } + + float GetGreenAsFloat() const + { + return static_cast<float>(green_) / 255.0f; + } + + float GetBlueAsFloat() const + { + return static_cast<float>(blue_) / 255.0f; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/ColorTextureSceneLayer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,49 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "ColorTextureSceneLayer.h" + +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> + + +namespace OrthancStone +{ + ColorTextureSceneLayer::ColorTextureSceneLayer(const Orthanc::ImageAccessor& texture) + { + if (texture.GetFormat() != Orthanc::PixelFormat_Grayscale8 && + texture.GetFormat() != Orthanc::PixelFormat_RGBA32 && + texture.GetFormat() != Orthanc::PixelFormat_RGB24) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + SetTexture(Orthanc::Image::Clone(texture)); + } + + + ISceneLayer* ColorTextureSceneLayer::Clone() const + { + std::auto_ptr<ColorTextureSceneLayer> cloned(new ColorTextureSceneLayer(GetTexture())); + cloned->CopyParameters(*this); + return cloned.release(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/ColorTextureSceneLayer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,40 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "TextureBaseSceneLayer.h" + +namespace OrthancStone +{ + class ColorTextureSceneLayer : public TextureBaseSceneLayer + { + public: + ColorTextureSceneLayer(const Orthanc::ImageAccessor& texture); + + virtual ISceneLayer* Clone() const; + + virtual Type GetType() const + { + return Type_ColorTexture; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/FloatTextureSceneLayer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,122 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "FloatTextureSceneLayer.h" + +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + FloatTextureSceneLayer::FloatTextureSceneLayer(const Orthanc::ImageAccessor& texture) + { + { + std::auto_ptr<Orthanc::ImageAccessor> t( + new Orthanc::Image(Orthanc::PixelFormat_Float32, + texture.GetWidth(), + texture.GetHeight(), + false)); + + Orthanc::ImageProcessing::Convert(*t, texture); + SetTexture(t.release()); + } + + SetCustomWindowing(128, 256); + } + + + void FloatTextureSceneLayer::SetWindowing(ImageWindowing windowing) + { + if (windowing_ != windowing) + { + if (windowing == ImageWindowing_Custom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + windowing_ = windowing; + IncrementRevision(); + } + } + } + + + void FloatTextureSceneLayer::SetCustomWindowing(float customCenter, + float customWidth) + { + if (customWidth <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + windowing_ = ImageWindowing_Custom; + customCenter_ = customCenter; + customWidth_ = customWidth; + IncrementRevision(); + } + } + + + void FloatTextureSceneLayer::GetWindowing(float& targetCenter, + float& targetWidth) const + { + ::OrthancStone::ComputeWindowing(targetCenter, targetWidth, + windowing_, customCenter_, customWidth_); + } + + + void FloatTextureSceneLayer::FitRange() + { + float minValue, maxValue; + Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, GetTexture()); + + float width; + + assert(minValue <= maxValue); + if (LinearAlgebra::IsCloseToZero(maxValue - minValue)) + { + width = 1; + } + else + { + width = maxValue - minValue; + } + + SetCustomWindowing((minValue + maxValue) / 2.0f, width); + } + + + ISceneLayer* FloatTextureSceneLayer::Clone() const + { + std::auto_ptr<FloatTextureSceneLayer> cloned + (new FloatTextureSceneLayer(GetTexture())); + + cloned->CopyParameters(*this); + cloned->windowing_ = windowing_; + cloned->customCenter_ = customCenter_; + cloned->customWidth_ = customWidth_; + + return cloned.release(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/FloatTextureSceneLayer.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "TextureBaseSceneLayer.h" + +namespace OrthancStone +{ + class FloatTextureSceneLayer : public TextureBaseSceneLayer + { + private: + ImageWindowing windowing_; + float customCenter_; + float customWidth_; + + public: + FloatTextureSceneLayer(const Orthanc::ImageAccessor& texture); + + void SetWindowing(ImageWindowing windowing); + + void SetCustomWindowing(float customCenter, + float customWidth); + + void GetWindowing(float& targetCenter, + float& targetWidth) const; + + ImageWindowing GetWindowingType() const + { + return windowing_; + } + + void FitRange(); + + virtual ISceneLayer* Clone() const; + + virtual Type GetType() const + { + return Type_FloatTexture; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/IPointerTracker.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,39 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "PointerEvent.h" + +namespace OrthancStone +{ + class IPointerTracker : public boost::noncopyable + { + public: + virtual ~IPointerTracker() + { + } + + virtual void Update(const PointerEvent& event) = 0; + + virtual void Release() = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/ISceneLayer.h Mon Apr 29 15:24:59 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Toolbox/Extent2D.h" + +#include <boost/noncopyable.hpp> +#include <stdint.h> + +namespace OrthancStone +{ + class ISceneLayer : public boost::noncopyable + { + public: + enum Type + { + Type_InfoPanel, + Type_ColorTexture, + Type_Polyline, + Type_Text, + Type_FloatTexture + }; + + virtual ~ISceneLayer() + { + } + + virtual ISceneLayer* Clone() const = 0; + + virtual Type GetType() const = 0; + + virtual bool GetBoundingBox(Extent2D& target) const = 0; + + virtual uint64_t GetRevision() const = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/InfoPanelSceneLayer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,105 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "InfoPanelSceneLayer.h" + +#include <Core/Images/Image.h> +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + InfoPanelSceneLayer::InfoPanelSceneLayer(const Orthanc::ImageAccessor& texture, + BitmapAnchor anchor, + bool isLinearInterpolation) : + texture_(Orthanc::Image::Clone(texture)), + anchor_(anchor), + isLinearInterpolation_(isLinearInterpolation) + { + if (texture_->GetFormat() != Orthanc::PixelFormat_RGBA32 && + texture_->GetFormat() != Orthanc::PixelFormat_RGB24) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + } + + + void InfoPanelSceneLayer::ComputeAnchorLocation(int& x, + int& y, + BitmapAnchor anchor, + unsigned int textureWidth, + unsigned int textureHeight, + unsigned int canvasWidth, + unsigned int canvasHeight) + { + int tw = static_cast<int>(textureWidth); + int th = static_cast<int>(textureHeight); + int cw = static_cast<int>(canvasWidth); + int ch = static_cast<int>(canvasHeight); + + switch (anchor) + { + case BitmapAnchor_TopLeft: + case BitmapAnchor_CenterLeft: + case BitmapAnchor_BottomLeft: + x = 0; + break; + + case BitmapAnchor_TopCenter: + case BitmapAnchor_Center: + case BitmapAnchor_BottomCenter: + x = (cw - tw) / 2; + break; + + case BitmapAnchor_TopRight: + case BitmapAnchor_CenterRight: + case BitmapAnchor_BottomRight: + x = cw - tw; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + switch (anchor) + { + case BitmapAnchor_TopLeft: + case BitmapAnchor_TopCenter: + case BitmapAnchor_TopRight: + y = 0; + break; + + case BitmapAnchor_CenterLeft: + case BitmapAnchor_Center: + case BitmapAnchor_CenterRight: + y = (ch - th) / 2; + break; + + case BitmapAnchor_BottomLeft: + case BitmapAnchor_BottomCenter: + case BitmapAnchor_BottomRight: + y = ch - th; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/InfoPanelSceneLayer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,88 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ISceneLayer.h" +#include "../StoneEnumerations.h" + +#include <Core/Images/ImageAccessor.h> + +#include <memory> + +namespace OrthancStone +{ + class InfoPanelSceneLayer : public ISceneLayer + { + private: + std::auto_ptr<Orthanc::ImageAccessor> texture_; + BitmapAnchor anchor_; + bool isLinearInterpolation_; + + public: + InfoPanelSceneLayer(const Orthanc::ImageAccessor& texture, + BitmapAnchor anchor, + bool isLinearInterpolation); + + virtual ISceneLayer* Clone() const + { + return new InfoPanelSceneLayer(*texture_, anchor_, isLinearInterpolation_); + } + + const Orthanc::ImageAccessor& GetTexture() const + { + return *texture_; + } + + BitmapAnchor GetAnchor() const + { + return anchor_; + } + + bool IsLinearInterpolation() const + { + return isLinearInterpolation_; + } + + virtual Type GetType() const + { + return Type_InfoPanel; + } + + virtual bool GetBoundingBox(Extent2D& target) const + { + return false; + } + + virtual uint64_t GetRevision() const + { + return 0; + } + + static void ComputeAnchorLocation(int& x, + int& y, + BitmapAnchor anchor, + unsigned int textureWidth, + unsigned int textureHeight, + unsigned int canvasWidth, + unsigned int canvasHeight); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoBaseRenderer.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ICairoContextProvider.h" +#include "CompositorHelper.h" + +namespace OrthancStone +{ + namespace Internals + { + class CairoBaseRenderer : public CompositorHelper::ILayerRenderer + { + private: + ICairoContextProvider& target_; + std::auto_ptr<ISceneLayer> layer_; + + protected: + template<typename T> + const T& GetLayer() const + { + return dynamic_cast<T&>(*layer_); + } + + cairo_t* GetCairoContext() const + { + return target_.GetCairoContext(); + } + + public: + CairoBaseRenderer(ICairoContextProvider& target, + const ISceneLayer& layer) : + target_(target) + { + Update(layer); + } + + virtual void Update(const ISceneLayer& layer) + { + layer_.reset(layer.Clone()); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoColorTextureRenderer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,79 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "CairoColorTextureRenderer.h" + +#include "../ColorTextureSceneLayer.h" + +namespace OrthancStone +{ + namespace Internals + { + CairoColorTextureRenderer::CairoColorTextureRenderer(ICairoContextProvider& target, + const ISceneLayer& layer) : + target_(target) + { + Update(layer); + } + + + void CairoColorTextureRenderer::Update(const ISceneLayer& layer) + { + const ColorTextureSceneLayer& l = dynamic_cast<const ColorTextureSceneLayer&>(layer); + + texture_.Copy(l.GetTexture(), true); + textureTransform_ = l.GetTransform(); + isLinearInterpolation_ = l.IsLinearInterpolation(); + } + + + void CairoColorTextureRenderer::Render(const AffineTransform2D& transform) + { + cairo_t* cr = target_.GetCairoContext(); + + AffineTransform2D t = + AffineTransform2D::Combine(transform, textureTransform_); + Matrix h = t.GetHomogeneousMatrix(); + + cairo_save(cr); + + cairo_matrix_t m; + cairo_matrix_init(&m, h(0, 0), h(1, 0), h(0, 1), h(1, 1), h(0, 2), h(1, 2)); + cairo_transform(cr, &m); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(cr, texture_.GetObject(), 0, 0); + + if (isLinearInterpolation_) + { + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR); + } + else + { + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); + } + + cairo_paint(cr); + + cairo_restore(cr); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoColorTextureRenderer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,49 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../Viewport/CairoSurface.h" +#include "CompositorHelper.h" +#include "ICairoContextProvider.h" + +namespace OrthancStone +{ + namespace Internals + { + class CairoColorTextureRenderer : public CompositorHelper::ILayerRenderer + { + private: + ICairoContextProvider& target_; + CairoSurface texture_; + AffineTransform2D textureTransform_; + bool isLinearInterpolation_; + + public: + CairoColorTextureRenderer(ICairoContextProvider& target, + const ISceneLayer& layer); + + virtual void Update(const ISceneLayer& layer); + + virtual void Render(const AffineTransform2D& transform); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoFloatTextureRenderer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,116 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "CairoFloatTextureRenderer.h" + +#include "../FloatTextureSceneLayer.h" + +namespace OrthancStone +{ + namespace Internals + { + void CairoFloatTextureRenderer::Update(const ISceneLayer& layer) + { + const FloatTextureSceneLayer& l = dynamic_cast<const FloatTextureSceneLayer&>(layer); + + textureTransform_ = l.GetTransform(); + isLinearInterpolation_ = l.IsLinearInterpolation(); + + float windowCenter, windowWidth; + l.GetWindowing(windowCenter, windowWidth); + + const float a = windowCenter - windowWidth; + const float slope = 256.0f / (2.0f * windowWidth); + + const Orthanc::ImageAccessor& source = l.GetTexture(); + const unsigned int width = source.GetWidth(); + const unsigned int height = source.GetHeight(); + texture_.SetSize(width, height, false); + + Orthanc::ImageAccessor target; + texture_.GetWriteableAccessor(target); + + assert(source.GetFormat() == Orthanc::PixelFormat_Float32 && + target.GetFormat() == Orthanc::PixelFormat_BGRA32 && + sizeof(float) == 4); + + for (unsigned int y = 0; y < height; y++) + { + const float* p = reinterpret_cast<const float*>(source.GetConstRow(y)); + uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); + + for (unsigned int x = 0; x < width; x++) + { + float v = (*p - a) * slope; + if (v <= 0) + { + v = 0; + } + else if (v >= 255) + { + v = 255; + } + + uint8_t vv = static_cast<uint8_t>(v); + + q[0] = vv; + q[1] = vv; + q[2] = vv; + + p++; + q += 4; + } + } + } + + + void CairoFloatTextureRenderer::Render(const AffineTransform2D& transform) + { + cairo_t* cr = target_.GetCairoContext(); + + AffineTransform2D t = + AffineTransform2D::Combine(transform, textureTransform_); + Matrix h = t.GetHomogeneousMatrix(); + + cairo_save(cr); + + cairo_matrix_t m; + cairo_matrix_init(&m, h(0, 0), h(1, 0), h(0, 1), h(1, 1), h(0, 2), h(1, 2)); + cairo_transform(cr, &m); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(cr, texture_.GetObject(), 0, 0); + + if (isLinearInterpolation_) + { + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR); + } + else + { + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); + } + + cairo_paint(cr); + + cairo_restore(cr); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoFloatTextureRenderer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,53 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../Viewport/CairoSurface.h" +#include "CompositorHelper.h" +#include "ICairoContextProvider.h" + +namespace OrthancStone +{ + namespace Internals + { + class CairoFloatTextureRenderer : public CompositorHelper::ILayerRenderer + { + private: + ICairoContextProvider& target_; + CairoSurface texture_; + AffineTransform2D textureTransform_; + bool isLinearInterpolation_; + + public: + CairoFloatTextureRenderer(ICairoContextProvider& target, + const ISceneLayer& layer) : + target_(target) + { + Update(layer); + } + + virtual void Update(const ISceneLayer& layer); + + virtual void Render(const AffineTransform2D& transform); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoInfoPanelRenderer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,73 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "CairoInfoPanelRenderer.h" + +#include "../InfoPanelSceneLayer.h" + +namespace OrthancStone +{ + namespace Internals + { + void CairoInfoPanelRenderer::Update(const ISceneLayer& layer) + { + const InfoPanelSceneLayer& l = dynamic_cast<const InfoPanelSceneLayer&>(layer); + + texture_.Copy(l.GetTexture(), true); + anchor_ = l.GetAnchor(); + isLinearInterpolation_ = l.IsLinearInterpolation(); + } + + + void CairoInfoPanelRenderer::Render(const AffineTransform2D& transform) + { + int dx, dy; + InfoPanelSceneLayer::ComputeAnchorLocation( + dx, dy, anchor_, texture_.GetWidth(), texture_.GetHeight(), + target_.GetCairoWidth(), target_.GetCairoHeight()); + + cairo_t* cr = target_.GetCairoContext(); + + cairo_save(cr); + + cairo_matrix_t t; + cairo_matrix_init_identity(&t); + cairo_matrix_translate(&t, dx, dy); + cairo_transform(cr, &t); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(cr, texture_.GetObject(), 0, 0); + + if (isLinearInterpolation_) + { + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR); + } + else + { + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); + } + + cairo_paint(cr); + + cairo_restore(cr); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoInfoPanelRenderer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,53 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../Viewport/CairoSurface.h" +#include "CompositorHelper.h" +#include "ICairoContextProvider.h" + +namespace OrthancStone +{ + namespace Internals + { + class CairoInfoPanelRenderer : public CompositorHelper::ILayerRenderer + { + private: + ICairoContextProvider& target_; + CairoSurface texture_; + BitmapAnchor anchor_; + bool isLinearInterpolation_; + + public: + CairoInfoPanelRenderer(ICairoContextProvider& target, + const ISceneLayer& layer) : + target_(target) + { + Update(layer); + } + + virtual void Update(const ISceneLayer& layer); + + virtual void Render(const AffineTransform2D& transform); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoPolylineRenderer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,70 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "CairoPolylineRenderer.h" + +#include "../PolylineSceneLayer.h" + +namespace OrthancStone +{ + namespace Internals + { + void CairoPolylineRenderer::Render(const AffineTransform2D& transform) + { + const PolylineSceneLayer& layer = GetLayer<PolylineSceneLayer>(); + + cairo_t* cr = GetCairoContext(); + + cairo_set_source_rgb(cr, layer.GetRedAsFloat(), layer.GetGreenAsFloat(), layer.GetBlueAsFloat()); + cairo_set_line_width(cr, layer.GetThickness()); + + for (size_t i = 0; i < layer.GetChainsCount(); i++) + { + const PolylineSceneLayer::Chain& chain = layer.GetChain(i); + + if (!chain.empty()) + { + for (size_t j = 0; j < chain.size(); j++) + { + ScenePoint2D p = chain[j].Apply(transform); + + if (j == 0) + { + cairo_move_to(cr, p.GetX(), p.GetY()); + } + else + { + cairo_line_to(cr, p.GetX(), p.GetY()); + } + } + + if (layer.IsClosedChain(i)) + { + ScenePoint2D p = chain[0].Apply(transform); + cairo_line_to(cr, p.GetX(), p.GetY()); + } + } + } + + cairo_stroke(cr); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoPolylineRenderer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,42 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "CairoBaseRenderer.h" + +namespace OrthancStone +{ + namespace Internals + { + class CairoPolylineRenderer : public CairoBaseRenderer + { + public: + CairoPolylineRenderer(ICairoContextProvider& target, + const ISceneLayer& layer) : + CairoBaseRenderer(target, layer) + { + } + + virtual void Render(const AffineTransform2D& transform); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoTextRenderer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,114 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "CairoTextRenderer.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + namespace Internals + { + CairoTextRenderer::CairoTextRenderer(ICairoContextProvider& target, + const GlyphBitmapAlphabet& alphabet, + const TextSceneLayer& layer) : + CairoBaseRenderer(target, layer) + { + std::auto_ptr<Orthanc::ImageAccessor> source(alphabet.RenderText(layer.GetText())); + + if (source.get() != NULL) + { + text_.SetSize(source->GetWidth(), source->GetHeight(), true); + + Orthanc::ImageAccessor target; + text_.GetWriteableAccessor(target); + + if (source->GetFormat() != Orthanc::PixelFormat_Grayscale8 || + target.GetFormat() != Orthanc::PixelFormat_BGRA32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + const unsigned int width = source->GetWidth(); + const unsigned int red = layer.GetRed(); + const unsigned int green = layer.GetGreen(); + const unsigned int blue = layer.GetBlue(); + + for (unsigned int y = 0; y < source->GetHeight(); y++) + { + const uint8_t* p = reinterpret_cast<const uint8_t*>(source->GetConstRow(y)); + uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); + + for (unsigned int x = 0; x < width; x++) + { + unsigned int alpha = *p; + + // Premultiplied alpha + q[0] = static_cast<uint8_t>((blue * alpha) / 255); + q[1] = static_cast<uint8_t>((green * alpha) / 255); + q[2] = static_cast<uint8_t>((red * alpha) / 255); + q[3] = *p; + + p++; + q += 4; + } + } + + cairo_surface_mark_dirty(text_.GetObject()); + } + } + + + void CairoTextRenderer::Render(const AffineTransform2D& transform) + { + if (text_.GetWidth() != 0 && + text_.GetHeight() != 0) + { + const TextSceneLayer& layer = GetLayer<TextSceneLayer>(); + + cairo_t* cr = GetCairoContext(); + cairo_set_source_rgb(cr, layer.GetRedAsFloat(), layer.GetGreenAsFloat(), layer.GetBlueAsFloat()); + + double dx, dy; // In pixels + ComputeAnchorTranslation(dx, dy, layer.GetAnchor(), text_.GetWidth(), + text_.GetHeight(), layer.GetBorder()); + + double x = layer.GetX(); + double y = layer.GetY(); + transform.Apply(x, y); + + cairo_save(cr); + + cairo_matrix_t t; + cairo_matrix_init_identity(&t); + cairo_matrix_translate(&t, x + dx, y + dy); + cairo_transform(cr, &t); + + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(cr, text_.GetObject(), 0, 0); + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR); + cairo_paint(cr); + + cairo_restore(cr); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CairoTextRenderer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,46 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../Fonts/GlyphBitmapAlphabet.h" +#include "../../Viewport/CairoSurface.h" +#include "../TextSceneLayer.h" +#include "CairoBaseRenderer.h" + +namespace OrthancStone +{ + namespace Internals + { + class CairoTextRenderer : public CairoBaseRenderer + { + private: + CairoSurface text_; + + public: + CairoTextRenderer(ICairoContextProvider& target, + const GlyphBitmapAlphabet& alphabet, + const TextSceneLayer& layer); + + virtual void Render(const AffineTransform2D& transform); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CompositorHelper.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,156 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "CompositorHelper.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + namespace Internals + { + class CompositorHelper::Item : public boost::noncopyable + { + private: + std::auto_ptr<ILayerRenderer> renderer_; + const ISceneLayer& layer_; + uint64_t layerIdentifier_; + uint64_t lastRevision_; + + public: + Item(ILayerRenderer* renderer, // Takes ownership + const ISceneLayer& layer, + uint64_t layerIdentifier) : + renderer_(renderer), + layer_(layer), + layerIdentifier_(layerIdentifier), + lastRevision_(layer.GetRevision()) + { + if (renderer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + ILayerRenderer& GetRenderer() const + { + assert(renderer_.get() != NULL); + return *renderer_; + } + + const ISceneLayer& GetLayer() const + { + return layer_; + } + + uint64_t GetLayerIdentifier() const + { + return layerIdentifier_; + } + + uint64_t GetLastRevision() const + { + return lastRevision_; + } + + void UpdateRenderer() + { + assert(renderer_.get() != NULL); + renderer_->Update(layer_); + lastRevision_ = layer_.GetRevision(); + } + }; + + + void CompositorHelper::Visit(const ISceneLayer& layer, + uint64_t layerIdentifier, + int depth) + { + // "Visit()" is only applied to layers existing in the scene + assert(scene_.HasLayer(depth)); + + Content::iterator found = content_.find(depth); + + assert(found == content_.end() || + found->second != NULL); + + if (found == content_.end() || + found->second->GetLayerIdentifier() != layerIdentifier) + { + // This is the first time this layer is rendered, or the layer + // is not the same as before + if (found != content_.end()) + { + delete found->second; + content_.erase(found); + } + + std::auto_ptr<ILayerRenderer> renderer(factory_.Create(layer)); + + if (renderer.get() != NULL) + { + renderer->Render(sceneTransform_); + content_[depth] = new Item(renderer.release(), layer, layerIdentifier); + } + } + else + { + // This layer has already been rendered + assert(found->second->GetLastRevision() <= layer.GetRevision()); + + if (found->second->GetLastRevision() < layer.GetRevision()) + { + found->second->UpdateRenderer(); + } + + found->second->GetRenderer().Render(sceneTransform_); + } + + // Check invariants + assert(content_.find(depth) == content_.end() || + (content_[depth]->GetLayerIdentifier() == layerIdentifier && + content_[depth]->GetLastRevision() == layer.GetRevision())); + } + + + CompositorHelper::~CompositorHelper() + { + for (Content::iterator it = content_.begin(); it != content_.end(); ++it) + { + assert(it->second != NULL); + delete it->second; + } + } + + + void CompositorHelper::Refresh(unsigned int canvasWidth, + unsigned int canvasHeight) + { + // Bring coordinate (0,0) to the center of the canvas + AffineTransform2D offset = AffineTransform2D::CreateOffset( + static_cast<double>(canvasWidth) / 2.0, + static_cast<double>(canvasHeight) / 2.0); + + sceneTransform_ = AffineTransform2D::Combine(offset, scene_.GetSceneToCanvasTransform()); + scene_.Apply(*this); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/CompositorHelper.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Scene2D.h" + +namespace OrthancStone +{ + namespace Internals + { + class CompositorHelper : protected Scene2D::IVisitor + { + public: + class ILayerRenderer : public boost::noncopyable + { + public: + virtual ~ILayerRenderer() + { + } + + virtual void Render(const AffineTransform2D& transform) = 0; + + // "Update()" is only called if the type of the layer has not changed + virtual void Update(const ISceneLayer& layer) = 0; + }; + + class IRendererFactory : public boost::noncopyable + { + public: + virtual ~IRendererFactory() + { + } + + virtual ILayerRenderer* Create(const ISceneLayer& layer) = 0; + }; + + private: + class Item; + + typedef std::map<int, Item*> Content; + + const Scene2D& scene_; + IRendererFactory& factory_; + Content content_; + AffineTransform2D sceneTransform_; + + protected: + virtual void Visit(const ISceneLayer& layer, + uint64_t layerIdentifier, + int depth); + + public: + CompositorHelper(const Scene2D& scene, + IRendererFactory& factory) : + scene_(scene), + factory_(factory) + { + } + + ~CompositorHelper(); + + void Refresh(unsigned int canvasWidth, + unsigned int canvasHeight); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/FixedPointAligner.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,51 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "FixedPointAligner.h" + +namespace OrthancStone +{ + namespace Internals + { + FixedPointAligner::FixedPointAligner(Scene2D& scene, + const ScenePoint2D& p, + unsigned int canvasWidth, + unsigned int canvasHeight) : + scene_(scene) + { + canvas_ = ScenePoint2D(p.GetX() - static_cast<double>(canvasWidth) / 2.0, + p.GetY() - static_cast<double>(canvasHeight) / 2.0); + pivot_ = canvas_.Apply(scene_.GetCanvasToSceneTransform()); + } + + + void FixedPointAligner::Apply() + { + ScenePoint2D p = canvas_.Apply(scene_.GetCanvasToSceneTransform()); + + scene_.SetSceneToCanvasTransform( + AffineTransform2D::Combine( + scene_.GetSceneToCanvasTransform(), + AffineTransform2D::CreateOffset(p.GetX() - pivot_.GetX(), + p.GetY() - pivot_.GetY()))); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/FixedPointAligner.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,49 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Scene2D.h" +#include "../ScenePoint2D.h" + +namespace OrthancStone +{ + namespace Internals + { + // During a mouse event that modifies the view of a scene, keeps + // one point (the pivot) at the same position on the canvas + class FixedPointAligner : public boost::noncopyable + { + private: + Scene2D& scene_; + ScenePoint2D pivot_; + ScenePoint2D canvas_; + + public: + FixedPointAligner(Scene2D& scene, + const ScenePoint2D& p, + unsigned int canvasWidth, + unsigned int canvasHeight); + + void Apply(); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/ICairoContextProvider.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,46 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <boost/noncopyable.hpp> +#include <cairo.h> +#include <stdint.h> + +namespace OrthancStone +{ + namespace Internals + { + class ICairoContextProvider : public boost::noncopyable + { + public: + virtual ~ICairoContextProvider() + { + } + + virtual cairo_t* GetCairoContext() = 0; + + virtual unsigned int GetCairoWidth() = 0; + + virtual unsigned int GetCairoHeight() = 0; + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,51 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLAdvancedPolylineRenderer.h" + +#include <Core/OrthancException.h> + + +namespace OrthancStone +{ + namespace Internals + { + void OpenGLAdvancedPolylineRenderer::LoadLayer(const PolylineSceneLayer& layer) + { + data_.reset(new OpenGLLinesProgram::Data(context_, layer)); + + if (data_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + + OpenGLAdvancedPolylineRenderer::OpenGLAdvancedPolylineRenderer(OpenGL::IOpenGLContext& context, + OpenGLLinesProgram& program, + const PolylineSceneLayer& layer) : + context_(context), + program_(program) + { + LoadLayer(layer); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "CompositorHelper.h" +#include "OpenGLLinesProgram.h" + + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLAdvancedPolylineRenderer : public CompositorHelper::ILayerRenderer + { + private: + OpenGL::IOpenGLContext& context_; + OpenGLLinesProgram& program_; + std::auto_ptr<OpenGLLinesProgram::Data> data_; + + void LoadLayer(const PolylineSceneLayer& layer); + + public: + OpenGLAdvancedPolylineRenderer(OpenGL::IOpenGLContext& context, + OpenGLLinesProgram& program, + const PolylineSceneLayer& layer); + + virtual void Render(const AffineTransform2D& transform) + { + program_.Apply(*data_, transform, true, true); + } + + virtual void Update(const ISceneLayer& layer) + { + LoadLayer(dynamic_cast<const PolylineSceneLayer&>(layer)); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,86 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLBasicPolylineRenderer.h" + +#include "../../OpenGL/OpenGLIncludes.h" + +namespace OrthancStone +{ + namespace Internals + { + OpenGLBasicPolylineRenderer::OpenGLBasicPolylineRenderer(OpenGL::IOpenGLContext& context, + const PolylineSceneLayer& layer) : + context_(context) + { + layer_.Copy(layer); + } + + + void OpenGLBasicPolylineRenderer::Render(const AffineTransform2D& transform) + { + AffineTransform2D t = AffineTransform2D::Combine( + AffineTransform2D::CreateOpenGLClipspace(context_.GetCanvasWidth(), context_.GetCanvasHeight()), + transform); + + glUseProgram(0); + glColor3ub(layer_.GetRed(), layer_.GetGreen(), layer_.GetBlue()); + + glBegin(GL_LINES); + + for (size_t i = 0; i < layer_.GetChainsCount(); i++) + { + const PolylineSceneLayer::Chain& chain = layer_.GetChain(i); + + if (chain.size() > 1) + { + ScenePoint2D previous = chain[0].Apply(t); + + for (size_t j = 1; j < chain.size(); j++) + { + ScenePoint2D p = chain[j].Apply(t); + + glVertex2f(previous.GetX(), previous.GetY()); + glVertex2f(p.GetX(), p.GetY()); + + previous = p; + } + + if (layer_.IsClosedChain(i)) + { + ScenePoint2D p = chain[0].Apply(t); + + glVertex2f(previous.GetX(), previous.GetY()); + glVertex2f(p.GetX(), p.GetY()); + } + } + } + + glEnd(); + } + + + void OpenGLBasicPolylineRenderer::Update(const ISceneLayer& layer) + { + layer_.Copy(dynamic_cast<const PolylineSceneLayer&>(layer)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../OpenGL/IOpenGLContext.h" +#include "../PolylineSceneLayer.h" +#include "CompositorHelper.h" + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLBasicPolylineRenderer : public CompositorHelper::ILayerRenderer + { + private: + OpenGL::IOpenGLContext& context_; + PolylineSceneLayer layer_; + + public: + OpenGLBasicPolylineRenderer(OpenGL::IOpenGLContext& context, + const PolylineSceneLayer& layer); + + virtual void Render(const AffineTransform2D& transform); + + virtual void Update(const ISceneLayer& layer); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLColorTextureProgram.cpp Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLColorTextureProgram.h" + + +static const char* FRAGMENT_SHADER = + "uniform sampler2D u_texture; \n" + "varying vec2 v_texcoord; \n" + "void main() \n" + "{ \n" + " gl_FragColor = texture2D(u_texture, v_texcoord); \n" + "}"; + + +namespace OrthancStone +{ + namespace Internals + { + OpenGLColorTextureProgram::OpenGLColorTextureProgram(OpenGL::IOpenGLContext& context) : + program_(context, FRAGMENT_SHADER) + { + } + + + void OpenGLColorTextureProgram::Apply(OpenGL::OpenGLTexture& texture, + const AffineTransform2D& transform, + bool useAlpha) + { + OpenGLTextureProgram::Execution execution(program_, texture, transform); + + if (useAlpha) + { + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + execution.DrawTriangles(); + glDisable(GL_BLEND); + } + else + { + execution.DrawTriangles(); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLColorTextureProgram.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "OpenGLTextureProgram.h" + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLColorTextureProgram : public boost::noncopyable + { + private: + OpenGLTextureProgram program_; + + public: + OpenGLColorTextureProgram(OpenGL::IOpenGLContext& context); + + void Apply(OpenGL::OpenGLTexture& texture, + const AffineTransform2D& transform, + bool useAlpha); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLColorTextureRenderer.cpp Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLColorTextureRenderer.h" + +namespace OrthancStone +{ + namespace Internals + { + void OpenGLColorTextureRenderer::LoadTexture(const ColorTextureSceneLayer& layer) + { + context_.MakeCurrent(); + texture_.reset(new OpenGL::OpenGLTexture); + texture_->Load(layer.GetTexture(), layer.IsLinearInterpolation()); + layerTransform_ = layer.GetTransform(); + } + + + OpenGLColorTextureRenderer::OpenGLColorTextureRenderer(OpenGL::IOpenGLContext& context, + OpenGLColorTextureProgram& program, + const ColorTextureSceneLayer& layer) : + context_(context), + program_(program) + { + LoadTexture(layer); + } + + + void OpenGLColorTextureRenderer::Render(const AffineTransform2D& transform) + { + if (texture_.get() != NULL) + { + program_.Apply(*texture_, AffineTransform2D::Combine(transform, layerTransform_), true); + } + } + + + void OpenGLColorTextureRenderer::Update(const ISceneLayer& layer) + { + // Should never happen (no revisions in color textures) + LoadTexture(dynamic_cast<const ColorTextureSceneLayer&>(layer)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLColorTextureRenderer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,52 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "OpenGLColorTextureProgram.h" +#include "CompositorHelper.h" +#include "../ColorTextureSceneLayer.h" + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLColorTextureRenderer : public CompositorHelper::ILayerRenderer + { + private: + OpenGL::IOpenGLContext& context_; + OpenGLColorTextureProgram& program_; + std::auto_ptr<OpenGL::OpenGLTexture> texture_; + AffineTransform2D layerTransform_; + + void LoadTexture(const ColorTextureSceneLayer& layer); + + public: + OpenGLColorTextureRenderer(OpenGL::IOpenGLContext& context, + OpenGLColorTextureProgram& program, + const ColorTextureSceneLayer& layer); + + virtual void Render(const AffineTransform2D& transform); + + virtual void Update(const ISceneLayer& layer); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLFloatTextureProgram.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,146 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLFloatTextureProgram.h" + +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> + + +static const char* FRAGMENT_SHADER = + "uniform float u_offset; \n" + "uniform float u_slope; \n" + "uniform float u_windowCenter; \n" + "uniform float u_windowWidth; \n" + "uniform sampler2D u_texture; \n" + "varying vec2 v_texcoord; \n" + "void main() \n" + "{ \n" + " vec4 t = texture2D(u_texture, v_texcoord); \n" + " float v = (t.r * 256.0 + t.g) * 256.0; \n" + " v = v * u_slope + u_offset; \n" // (*) + " float a = u_windowCenter - u_windowWidth; \n" + " float dy = 1.0 / (2.0 * u_windowWidth); \n" + " if (v <= a) \n" + " v = 0.0; \n" + " else \n" + " { \n" + " v = (v - a) * dy; \n" + " if (v >= 1.0) \n" + " v = 1.0; \n" + " } \n" + " gl_FragColor = vec4(v, v, v, 1); \n" + "}"; + + +namespace OrthancStone +{ + namespace Internals + { + OpenGLFloatTextureProgram::Data::Data(const Orthanc::ImageAccessor& texture, + bool isLinearInterpolation) + { + if (texture.GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + float minValue, maxValue; + Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, texture); + + offset_ = minValue; + + if (LinearAlgebra::IsCloseToZero(maxValue - minValue)) + { + slope_ = 1; + } + else + { + slope_ = (maxValue - minValue) / 65536.0f; + assert(!LinearAlgebra::IsCloseToZero(slope_)); + } + + const unsigned int width = texture.GetWidth(); + const unsigned int height = texture.GetHeight(); + + Orthanc::Image converted(Orthanc::PixelFormat_RGB24, width, height, true); + + for (unsigned int y = 0; y < height; y++) + { + const float *p = reinterpret_cast<const float*>(texture.GetConstRow(y)); + uint8_t *q = reinterpret_cast<uint8_t*>(converted.GetRow(y)); + + for (unsigned int x = 0; x < width; x++) + { + /** + * At (*), the floating-point "value" is reconstructed as + * "value = texture * slope + offset". + * <=> texture = (value - offset) / slope + **/ + + float texture = (*p - offset_) / slope_; + if (texture < 0) + { + texture = 0; + } + else if (texture >= 65535.0f) + { + texture = 65535.0f; + } + + uint16_t t = static_cast<uint16_t>(texture); + + q[0] = t / 256; // red + q[1] = t % 256; // green + q[2] = 0; // blue is unused + + p++; + q += 3; + } + } + + texture_.Load(converted, isLinearInterpolation); + } + + + OpenGLFloatTextureProgram::OpenGLFloatTextureProgram(OpenGL::IOpenGLContext& context) : + program_(context, FRAGMENT_SHADER) + { + } + + + void OpenGLFloatTextureProgram::Apply(Data& data, + const AffineTransform2D& transform, + float windowCenter, + float windowWidth) + { + OpenGLTextureProgram::Execution execution(program_, data.GetTexture(), transform); + + glUniform1f(execution.GetUniformLocation("u_slope"), data.GetSlope()); + glUniform1f(execution.GetUniformLocation("u_offset"), data.GetOffset()); + glUniform1f(execution.GetUniformLocation("u_windowCenter"), windowCenter); + glUniform1f(execution.GetUniformLocation("u_windowWidth"), windowWidth); + + execution.DrawTriangles(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLFloatTextureProgram.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,72 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "OpenGLTextureProgram.h" + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLFloatTextureProgram : public boost::noncopyable + { + public: + class Data : public boost::noncopyable + { + private: + OpenGL::OpenGLTexture texture_; + float offset_; + float slope_; + + public: + Data(const Orthanc::ImageAccessor& texture, + bool isLinearInterpolation); + + float GetOffset() const + { + return offset_; + } + + float GetSlope() const + { + return slope_; + } + + OpenGL::OpenGLTexture& GetTexture() + { + return texture_; + } + }; + + private: + OpenGLTextureProgram program_; + + public: + OpenGLFloatTextureProgram(OpenGL::IOpenGLContext& context); + + void Apply(Data& data, + const AffineTransform2D& transform, + float windowCenter, + float windowWidth); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,67 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLFloatTextureRenderer.h" + +namespace OrthancStone +{ + namespace Internals + { + void OpenGLFloatTextureRenderer::UpdateInternal(const FloatTextureSceneLayer& layer, + bool loadTexture) + { + if (loadTexture) + { + context_.MakeCurrent(); + texture_.reset(new OpenGLFloatTextureProgram::Data(layer.GetTexture(), layer.IsLinearInterpolation())); + } + + layerTransform_ = layer.GetTransform(); + layer.GetWindowing(windowCenter_, windowWidth_); + } + + + OpenGLFloatTextureRenderer::OpenGLFloatTextureRenderer(OpenGL::IOpenGLContext& context, + OpenGLFloatTextureProgram& program, + const FloatTextureSceneLayer& layer) : + context_(context), + program_(program) + { + UpdateInternal(layer, true); + } + + + void OpenGLFloatTextureRenderer::Render(const AffineTransform2D& transform) + { + if (texture_.get() != NULL) + { + program_.Apply(*texture_, AffineTransform2D::Combine(transform, layerTransform_), + windowCenter_, windowWidth_); + } + } + + + void OpenGLFloatTextureRenderer::Update(const ISceneLayer& layer) + { + UpdateInternal(dynamic_cast<const FloatTextureSceneLayer&>(layer), false); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLFloatTextureRenderer.h Mon Apr 29 15:24:59 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "CompositorHelper.h" +#include "OpenGLFloatTextureProgram.h" +#include "../FloatTextureSceneLayer.h" + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLFloatTextureRenderer : public CompositorHelper::ILayerRenderer + { + private: + OpenGL::IOpenGLContext& context_; + OpenGLFloatTextureProgram& program_; + std::auto_ptr<OpenGLFloatTextureProgram::Data> texture_; + AffineTransform2D layerTransform_; + float windowCenter_; + float windowWidth_; + + void UpdateInternal(const FloatTextureSceneLayer& layer, + bool loadTexture); + + public: + OpenGLFloatTextureRenderer(OpenGL::IOpenGLContext& context, + OpenGLFloatTextureProgram& program, + const FloatTextureSceneLayer& layer); + + virtual void Render(const AffineTransform2D& transform); + + virtual void Update(const ISceneLayer& layer); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLInfoPanelRenderer.cpp Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLInfoPanelRenderer.h" + +namespace OrthancStone +{ + namespace Internals + { + void OpenGLInfoPanelRenderer::LoadTexture(const InfoPanelSceneLayer& layer) + { + context_.MakeCurrent(); + texture_.reset(new OpenGL::OpenGLTexture); + texture_->Load(layer.GetTexture(), layer.IsLinearInterpolation()); + anchor_ = layer.GetAnchor(); + } + + + OpenGLInfoPanelRenderer::OpenGLInfoPanelRenderer(OpenGL::IOpenGLContext& context, + OpenGLColorTextureProgram& program, + const InfoPanelSceneLayer& layer) : + context_(context), + program_(program) + { + LoadTexture(layer); + } + + + void OpenGLInfoPanelRenderer::Render(const AffineTransform2D& transform) + { + if (texture_.get() != NULL) + { + int dx, dy; + InfoPanelSceneLayer::ComputeAnchorLocation( + dx, dy, anchor_, texture_->GetWidth(), texture_->GetHeight(), + context_.GetCanvasWidth(), context_.GetCanvasHeight()); + + // The position of this type of layer is layer: Ignore the + // "transform" coming from the scene + program_.Apply(*texture_, AffineTransform2D::CreateOffset(dx, dy), true); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLInfoPanelRenderer.h Mon Apr 29 15:24:59 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "CompositorHelper.h" +#include "OpenGLColorTextureProgram.h" +#include "../InfoPanelSceneLayer.h" + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLInfoPanelRenderer : public CompositorHelper::ILayerRenderer + { + private: + OpenGL::IOpenGLContext& context_; + OpenGLColorTextureProgram& program_; + std::auto_ptr<OpenGL::OpenGLTexture> texture_; + BitmapAnchor anchor_; + + void LoadTexture(const InfoPanelSceneLayer& layer); + + public: + OpenGLInfoPanelRenderer(OpenGL::IOpenGLContext& context, + OpenGLColorTextureProgram& program, + const InfoPanelSceneLayer& layer); + + virtual void Render(const AffineTransform2D& transform); + + virtual void Update(const ISceneLayer& layer) + { + LoadTexture(dynamic_cast<const InfoPanelSceneLayer&>(layer)); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLLinesProgram.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,459 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLLinesProgram.h" + +#include <Core/OrthancException.h> + + +static const unsigned int COMPONENTS_POSITION = 3; +static const unsigned int COMPONENTS_MITER = 2; + + +static const char* VERTEX_SHADER = + "attribute vec2 a_miter_direction; \n" + "attribute vec4 a_position; \n" + "uniform float u_thickness; \n" + "uniform mat4 u_matrix; \n" + "varying float v_distance; \n" + "void main() \n" + "{ \n" + " v_distance = a_position.z; \n" + " gl_Position = u_matrix * vec4(a_position.xy + a_position.z * a_miter_direction * u_thickness, 0, 1); \n" + "}"; + + +static const char* FRAGMENT_SHADER = + "uniform bool u_antialiasing; \n" + "uniform float u_antialiasing_start; \n" + "uniform vec3 u_color; \n" + "varying float v_distance; \n" // Distance of the point to the segment + "void main() \n" + "{ \n" + " float d = abs(v_distance); \n" + " if (!u_antialiasing || \n" + " d <= u_antialiasing_start) \n" + " gl_FragColor = vec4(u_color, 1); \n" + " else if (d >= 1.0) \n" + " gl_FragColor = vec4(0, 0, 0, 0); \n" + " else \n" + " { \n" + " float alpha = 1.0 - smoothstep(u_antialiasing_start, 1.0, d); \n" + " gl_FragColor = vec4(u_color * alpha, alpha); \n" + " } \n" + "}"; + + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLLinesProgram::Data::Segment + { + private: + bool isEmpty_; + double x1_; + double y1_; + double x2_; + double y2_; + double miterX1_; + double miterY1_; + double miterX2_; + double miterY2_; + + Vector lineAbove_; // In homogeneous coordinates (size = 3) + Vector lineBelow_; + + public: + Segment(const PolylineSceneLayer::Chain& chain, + size_t index1, + size_t index2) : + isEmpty_(false) + { + if (index1 >= chain.size() || + index2 >= chain.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + const ScenePoint2D& p = chain[index1]; + const ScenePoint2D& q = chain[index2]; + + x1_ = p.GetX(); + y1_ = p.GetY(); + x2_ = q.GetX(); + y2_ = q.GetY(); + + const double dx = x2_ - x1_; + const double dy = y2_ - y1_; + const double norm = sqrt(dx * dx + dy * dy); + + if (LinearAlgebra::IsCloseToZero(norm)) + { + isEmpty_ = true; + } + else + { + isEmpty_ = false; + const double normalX = -dy / norm; + const double normalY = dx / norm; + + miterX1_ = normalX; + miterY1_ = normalY; + miterX2_ = normalX; + miterY2_ = normalY; + + Vector a = LinearAlgebra::CreateVector(x1_ + normalX, y1_ + normalY, 1); + Vector b = LinearAlgebra::CreateVector(x2_ + normalX, y2_ + normalY, 1); + LinearAlgebra::CrossProduct(lineAbove_, a, b); + + a = LinearAlgebra::CreateVector(x1_ - normalX, y1_ - normalY, 1); + b = LinearAlgebra::CreateVector(x2_ - normalX, y2_ - normalY, 1); + LinearAlgebra::CrossProduct(lineBelow_, a, b); + } + } + } + + bool IsEmpty() const + { + return isEmpty_; + } + + static double ComputeSignedArea(double x1, + double y1, + double x2, + double y2, + double x3, + double y3) + { + // This computes the signed area of a 2D triangle. This + // formula is e.g. used in the sorting algorithm of Graham's + // scan to compute the convex hull. + // https://en.wikipedia.org/wiki/Graham_scan + return (x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1); + } + + static void CreateMiter(Segment& left, + Segment& right) + { + if (!left.IsEmpty() && + !right.IsEmpty()) + { + Vector above, below; + LinearAlgebra::CrossProduct(above, left.lineAbove_, right.lineAbove_); + LinearAlgebra::CrossProduct(below, left.lineBelow_, right.lineBelow_); + + if (!LinearAlgebra::IsCloseToZero(above[2]) && + !LinearAlgebra::IsCloseToZero(below[2])) + { + // Back to inhomogeneous 2D coordinates + above /= above[2]; + below /= below[2]; + + // Check whether "above" and "below" intersection points + // are on the half-plane defined by the endpoints of the + // two segments. This is an indicator of whether the angle + // is too acute. + double s1 = ComputeSignedArea(left.x1_, left.y1_, + above[0], above[1], + right.x2_, right.y2_); + double s2 = ComputeSignedArea(left.x1_, left.y1_, + below[0], below[1], + right.x2_, right.y2_); + + // The two signed areas must have the same sign + if (s1 * s2 >= 0) + { + left.miterX2_ = above[0] - left.x2_; + left.miterY2_ = above[1] - left.y2_; + + right.miterX1_ = left.miterX2_; + right.miterY1_ = left.miterY2_; + } + } + } + } + + void AddTriangles(std::vector<float>& coords, + std::vector<float>& miterDirections) + { + if (isEmpty_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + // First triangle + coords.push_back(x1_); + coords.push_back(y1_); + coords.push_back(1); + coords.push_back(x2_); + coords.push_back(y2_); + coords.push_back(-1); + coords.push_back(x2_); + coords.push_back(y2_); + coords.push_back(1); + + miterDirections.push_back(miterX1_); + miterDirections.push_back(miterY1_); + miterDirections.push_back(miterX2_); + miterDirections.push_back(miterY2_); + miterDirections.push_back(miterX2_); + miterDirections.push_back(miterY2_); + + // Second triangle + coords.push_back(x1_); + coords.push_back(y1_); + coords.push_back(1); + coords.push_back(x1_); + coords.push_back(y1_); + coords.push_back(-1); + coords.push_back(x2_); + coords.push_back(y2_); + coords.push_back(-1); + + miterDirections.push_back(miterX1_); + miterDirections.push_back(miterY1_); + miterDirections.push_back(miterX1_); + miterDirections.push_back(miterY1_); + miterDirections.push_back(miterX2_); + miterDirections.push_back(miterY2_); + } + }; + + + OpenGLLinesProgram::Data::Data(OpenGL::IOpenGLContext& context, + const PolylineSceneLayer& layer) : + context_(context), + verticesCount_(0), + thickness_(layer.GetThickness()), + red_(layer.GetRedAsFloat()), + green_(layer.GetGreenAsFloat()), + blue_(layer.GetBlueAsFloat()) + { + // High-level reference: + // https://mattdesl.svbtle.com/drawing-lines-is-hard + // https://forum.libcinder.org/topic/smooth-thick-lines-using-geometry-shader + + size_t countVertices = 0; + for (size_t i = 0; i < layer.GetChainsCount(); i++) + { + size_t countSegments = layer.GetChain(i).size() - 1; + + if (layer.IsClosedChain(i)) + { + countSegments++; + } + + // Each segment is made of 2 triangles. One triangle is + // defined by 3 points in 2D => 6 vertices per segment. + countVertices += countSegments * 2 * 3; + } + + std::vector<float> coords, miterDirections; + coords.reserve(countVertices * COMPONENTS_POSITION); + miterDirections.reserve(countVertices * COMPONENTS_MITER); + + for (size_t i = 0; i < layer.GetChainsCount(); i++) + { + const PolylineSceneLayer::Chain& chain = layer.GetChain(i); + + if (chain.size() > 1) + { + std::vector<Segment> segments; + for (size_t j = 1; j < chain.size(); j++) + { + segments.push_back(Segment(chain, j - 1, j)); + } + + if (layer.IsClosedChain(i)) + { + segments.push_back(Segment(chain, chain.size() - 1, 0)); + } + + // Try and create nice miters + for (size_t j = 1; j < segments.size(); j++) + { + Segment::CreateMiter(segments[j - 1], segments[j]); + } + + if (layer.IsClosedChain(i)) + { + Segment::CreateMiter(segments.back(), segments.front()); + } + + for (size_t j = 0; j < segments.size(); j++) + { + if (!segments[j].IsEmpty()) + { + segments[j].AddTriangles(coords, miterDirections); + } + } + } + } + + if (!coords.empty()) + { + verticesCount_ = coords.size() / COMPONENTS_POSITION; + + context_.MakeCurrent(); + glGenBuffers(2, buffers_); + + glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * coords.size(), &coords[0], GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * miterDirections.size(), &miterDirections[0], GL_STATIC_DRAW); + } + } + + + OpenGLLinesProgram::Data::~Data() + { + if (!IsEmpty()) + { + context_.MakeCurrent(); + glDeleteBuffers(2, buffers_); + } + } + + + GLuint OpenGLLinesProgram::Data::GetVerticesBuffer() const + { + if (IsEmpty()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return buffers_[0]; + } + } + + + GLuint OpenGLLinesProgram::Data::GetMiterDirectionsBuffer() const + { + if (IsEmpty()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return buffers_[1]; + } + } + + + OpenGLLinesProgram::OpenGLLinesProgram(OpenGL::IOpenGLContext& context) : + context_(context) + { + + context_.MakeCurrent(); + + program_.reset(new OpenGL::OpenGLProgram); + program_->CompileShaders(VERTEX_SHADER, FRAGMENT_SHADER); + } + + + void OpenGLLinesProgram::Apply(const Data& data, + const AffineTransform2D& transform, + bool antialiasing, + bool scaleIndependantThickness) + { + if (!data.IsEmpty()) + { + context_.MakeCurrent(); + program_->Use(); + + GLint locationPosition = program_->GetAttributeLocation("a_position"); + GLint locationMiterDirection = program_->GetAttributeLocation("a_miter_direction"); + + float m[16]; + transform.ConvertToOpenGLMatrix(m, context_.GetCanvasWidth(), context_.GetCanvasHeight()); + + glUniformMatrix4fv(program_->GetUniformLocation("u_matrix"), 1, GL_FALSE, m); + glUniform3f(program_->GetUniformLocation("u_color"), + data.GetRed(), data.GetGreen(), data.GetBlue()); + + glBindBuffer(GL_ARRAY_BUFFER, data.GetVerticesBuffer()); + glEnableVertexAttribArray(locationPosition); + glVertexAttribPointer(locationPosition, COMPONENTS_POSITION, GL_FLOAT, GL_FALSE, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, data.GetMiterDirectionsBuffer()); + glEnableVertexAttribArray(locationMiterDirection); + glVertexAttribPointer(locationMiterDirection, COMPONENTS_MITER, GL_FLOAT, GL_FALSE, 0, 0); + + glUniform1i(program_->GetUniformLocation("u_antialiasing"), (antialiasing ? 1 : 0)); + + const double zoom = transform.ComputeZoom(); + const double thickness = data.GetThickness() / 2.0; + const double aliasingBorder = 2.0; // Border for antialiasing ramp, in pixels + assert(aliasingBorder > 0); // Prevent division by zero with "t1" + + if (scaleIndependantThickness) + { + if (antialiasing) + { + double t1 = std::max(thickness, aliasingBorder); + double t0 = std::max(0.0, thickness - aliasingBorder); + + glUniform1f(program_->GetUniformLocation("u_thickness"), t1 / zoom); + glUniform1f(program_->GetUniformLocation("u_antialiasing_start"), t0 / t1); + } + else + { + glUniform1f(program_->GetUniformLocation("u_thickness"), thickness / zoom); + } + } + else + { + if (antialiasing) + { + double t1 = std::max(thickness, aliasingBorder / zoom); + double t0 = std::max(0.0, thickness - aliasingBorder / zoom); + + glUniform1f(program_->GetUniformLocation("u_thickness"), t1); + glUniform1f(program_->GetUniformLocation("u_antialiasing_start"), t0 / t1); + } + else + { + glUniform1f(program_->GetUniformLocation("u_thickness"), thickness); + } + } + + if (antialiasing) + { + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDrawArrays(GL_TRIANGLES, 0, data.GetVerticesCount()); + glDisable(GL_BLEND); + } + else + { + glDrawArrays(GL_TRIANGLES, 0, data.GetVerticesCount()); + } + + glDisableVertexAttribArray(locationPosition); + glDisableVertexAttribArray(locationMiterDirection); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLLinesProgram.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,103 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../OpenGL/IOpenGLContext.h" +#include "../../OpenGL/OpenGLProgram.h" +#include "../../Toolbox/AffineTransform2D.h" +#include "../PolylineSceneLayer.h" + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLLinesProgram : public boost::noncopyable + { + public: + class Data : public boost::noncopyable + { + private: + class Segment; + + OpenGL::IOpenGLContext& context_; + GLuint buffers_[2]; + size_t verticesCount_; + float thickness_; + float red_; + float green_; + float blue_; + + public: + Data(OpenGL::IOpenGLContext& context, + const PolylineSceneLayer& layer); + + ~Data(); + + bool IsEmpty() const + { + return verticesCount_ == 0; + } + + const size_t GetVerticesCount() const + { + return verticesCount_; + } + + GLuint GetVerticesBuffer() const; + + GLuint GetMiterDirectionsBuffer() const; + + float GetThickness() const + { + return thickness_; + } + + float GetRed() const + { + return red_; + } + + float GetGreen() const + { + return green_; + } + + float GetBlue() const + { + return blue_; + } + }; + + private: + OpenGL::IOpenGLContext& context_; + std::auto_ptr<OpenGL::OpenGLProgram> program_; + + public: + OpenGLLinesProgram(OpenGL::IOpenGLContext& context); + + void Apply(const Data& data, + const AffineTransform2D& transform, + bool antialiasing, + bool scaleIndependantThickness); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLTextProgram.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,190 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLTextProgram.h" + +#include "../../Fonts/OpenGLTextCoordinates.h" + +#include <Core/OrthancException.h> + + +static const unsigned int COMPONENTS = 2; + +static const char* VERTEX_SHADER = + "attribute vec2 a_texcoord; \n" + "attribute vec4 a_position; \n" + "uniform mat4 u_matrix; \n" + "varying vec2 v_texcoord; \n" + "void main() \n" + "{ \n" + " gl_Position = u_matrix * a_position; \n" + " v_texcoord = a_texcoord; \n" + "}"; + +static const char* FRAGMENT_SHADER = + "uniform sampler2D u_texture; \n" + "uniform vec3 u_color; \n" + "varying vec2 v_texcoord; \n" + "void main() \n" + "{ \n" + " vec4 v = texture2D(u_texture, v_texcoord); \n" + " gl_FragColor = vec4(u_color * v.w, v.w); \n" // Premultiplied alpha + "}"; + + +namespace OrthancStone +{ + namespace Internals + { + OpenGLTextProgram::OpenGLTextProgram(OpenGL::IOpenGLContext& context) : + context_(context) + { + + context_.MakeCurrent(); + + program_.reset(new OpenGL::OpenGLProgram); + program_->CompileShaders(VERTEX_SHADER, FRAGMENT_SHADER); + + positionLocation_ = program_->GetAttributeLocation("a_position"); + textureLocation_ = program_->GetAttributeLocation("a_texcoord"); + } + + + OpenGLTextProgram::Data::Data(OpenGL::IOpenGLContext& context, + const GlyphTextureAlphabet& alphabet, + const TextSceneLayer& layer) : + context_(context), + red_(layer.GetRedAsFloat()), + green_(layer.GetGreenAsFloat()), + blue_(layer.GetBlueAsFloat()), + x_(layer.GetX()), + y_(layer.GetY()), + border_(layer.GetBorder()), + anchor_(layer.GetAnchor()) + { + OpenGL::OpenGLTextCoordinates coordinates(alphabet, layer.GetText()); + textWidth_ = coordinates.GetTextWidth(); + textHeight_ = coordinates.GetTextHeight(); + + if (coordinates.IsEmpty()) + { + coordinatesCount_ = 0; + } + else + { + coordinatesCount_ = coordinates.GetRenderingCoords().size(); + + context_.MakeCurrent(); + glGenBuffers(2, buffers_); + + glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * coordinatesCount_, + &coordinates.GetRenderingCoords() [0], GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * coordinatesCount_, + &coordinates.GetTextureCoords() [0], GL_STATIC_DRAW); + } + } + + + OpenGLTextProgram::Data::~Data() + { + if (!IsEmpty()) + { + context_.MakeCurrent(); + glDeleteBuffers(2, buffers_); + } + } + + + GLuint OpenGLTextProgram::Data::GetSceneLocationsBuffer() const + { + if (IsEmpty()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return buffers_[0]; + } + } + + + GLuint OpenGLTextProgram::Data::GetTextureLocationsBuffer() const + { + if (IsEmpty()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return buffers_[1]; + } + } + + + void OpenGLTextProgram::Apply(OpenGL::OpenGLTexture& fontTexture, + const Data& data, + const AffineTransform2D& transform) + { + if (!data.IsEmpty()) + { + context_.MakeCurrent(); + program_->Use(); + + double dx, dy; // In pixels + ComputeAnchorTranslation(dx, dy, data.GetAnchor(), + data.GetTextWidth(), data.GetTextHeight(), data.GetBorder()); + + double x = data.GetX(); + double y = data.GetY(); + transform.Apply(x, y); + + const AffineTransform2D t = AffineTransform2D::CreateOffset(x + dx, y + dy); + + float m[16]; + t.ConvertToOpenGLMatrix(m, context_.GetCanvasWidth(), context_.GetCanvasHeight()); + + fontTexture.Bind(program_->GetUniformLocation("u_texture")); + glUniformMatrix4fv(program_->GetUniformLocation("u_matrix"), 1, GL_FALSE, m); + glUniform3f(program_->GetUniformLocation("u_color"), + data.GetRed(), data.GetGreen(), data.GetBlue()); + + glBindBuffer(GL_ARRAY_BUFFER, data.GetSceneLocationsBuffer()); + glEnableVertexAttribArray(positionLocation_); + glVertexAttribPointer(positionLocation_, COMPONENTS, GL_FLOAT, GL_FALSE, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, data.GetTextureLocationsBuffer()); + glEnableVertexAttribArray(textureLocation_); + glVertexAttribPointer(textureLocation_, COMPONENTS, GL_FLOAT, GL_FALSE, 0, 0); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + glDrawArrays(GL_TRIANGLES, 0, data.GetCoordinatesCount() / COMPONENTS); + glDisable(GL_BLEND); + + glDisableVertexAttribArray(positionLocation_); + glDisableVertexAttribArray(textureLocation_); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLTextProgram.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../Fonts/GlyphTextureAlphabet.h" +#include "../../OpenGL/IOpenGLContext.h" +#include "../../OpenGL/OpenGLProgram.h" +#include "../../OpenGL/OpenGLTexture.h" +#include "../../Toolbox/AffineTransform2D.h" +#include "../TextSceneLayer.h" + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLTextProgram : public boost::noncopyable + { + public: + class Data : public boost::noncopyable + { + private: + OpenGL::IOpenGLContext& context_; + size_t coordinatesCount_; + GLuint buffers_[2]; + float red_; + float green_; + float blue_; + double x_; + double y_; + double border_; + unsigned int textWidth_; + unsigned int textHeight_; + BitmapAnchor anchor_; + + public: + Data(OpenGL::IOpenGLContext& context, + const GlyphTextureAlphabet& alphabet, + const TextSceneLayer& layer); + + ~Data(); + + bool IsEmpty() const + { + return coordinatesCount_ == 0; + } + + size_t GetCoordinatesCount() const + { + return coordinatesCount_; + } + + GLuint GetSceneLocationsBuffer() const; + + GLuint GetTextureLocationsBuffer() const; + + float GetRed() const + { + return red_; + } + + float GetGreen() const + { + return green_; + } + + float GetBlue() const + { + return blue_; + } + + double GetX() const + { + return x_; + } + + double GetY() const + { + return y_; + } + + double GetBorder() const + { + return border_; + } + + unsigned int GetTextWidth() const + { + return textWidth_; + } + + unsigned int GetTextHeight() const + { + return textHeight_; + } + + BitmapAnchor GetAnchor() const + { + return anchor_; + } + }; + + private: + OpenGL::IOpenGLContext& context_; + std::auto_ptr<OpenGL::OpenGLProgram> program_; + GLint positionLocation_; + GLint textureLocation_; + + public: + OpenGLTextProgram(OpenGL::IOpenGLContext& context); + + void Apply(OpenGL::OpenGLTexture& fontTexture, + const Data& data, + const AffineTransform2D& transform); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLTextRenderer.cpp Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLTextRenderer.h" + +namespace OrthancStone +{ + namespace Internals + { + void OpenGLTextRenderer::LoadLayer(const TextSceneLayer& layer) + { + data_.reset(new OpenGLTextProgram::Data(context_, alphabet_, layer)); + } + + + OpenGLTextRenderer::OpenGLTextRenderer(OpenGL::IOpenGLContext& context, + OpenGLTextProgram& program, + const GlyphTextureAlphabet& alphabet, + OpenGL::OpenGLTexture& texture, + const TextSceneLayer& layer) : + context_(context), + program_(program), + alphabet_(alphabet), + texture_(texture) + { + LoadLayer(layer); + } + + + void OpenGLTextRenderer::Render(const AffineTransform2D& transform) + { + if (data_.get() != NULL) + { + program_.Apply(texture_, *data_, transform); + } + } + + + void OpenGLTextRenderer::Update(const ISceneLayer& layer) + { + LoadLayer(dynamic_cast<const TextSceneLayer&>(layer)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLTextRenderer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,54 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "CompositorHelper.h" +#include "OpenGLTextProgram.h" + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLTextRenderer : public CompositorHelper::ILayerRenderer + { + private: + OpenGL::IOpenGLContext& context_; + OpenGLTextProgram& program_; + const GlyphTextureAlphabet& alphabet_; + OpenGL::OpenGLTexture& texture_; + std::auto_ptr<OpenGLTextProgram::Data> data_; + + void LoadLayer(const TextSceneLayer& layer); + + public: + OpenGLTextRenderer(OpenGL::IOpenGLContext& context, + OpenGLTextProgram& program, + const GlyphTextureAlphabet& alphabet, + OpenGL::OpenGLTexture& texture, + const TextSceneLayer& layer); + + virtual void Render(const AffineTransform2D& transform); + + virtual void Update(const ISceneLayer& layer); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLTextureProgram.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,120 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLTextureProgram.h" + +static const unsigned int COMPONENTS = 2; +static const unsigned int COUNT = 6; // 2 triangles in 2D + +static const char* VERTEX_SHADER = + "attribute vec2 a_texcoord; \n" + "attribute vec4 a_position; \n" + "uniform mat4 u_matrix; \n" + "varying vec2 v_texcoord; \n" + "void main() \n" + "{ \n" + " gl_Position = u_matrix * a_position; \n" + " v_texcoord = a_texcoord; \n" + "}"; + + +namespace OrthancStone +{ + namespace Internals + { + void OpenGLTextureProgram::InitializeExecution(OpenGL::OpenGLTexture& texture, + const AffineTransform2D& transform) + { + context_.MakeCurrent(); + program_->Use(); + + AffineTransform2D scale = AffineTransform2D::CreateScaling + (texture.GetWidth(), texture.GetHeight()); + + AffineTransform2D t = AffineTransform2D::Combine(transform, scale); + + float m[16]; + t.ConvertToOpenGLMatrix(m, context_.GetCanvasWidth(), context_.GetCanvasHeight()); + + texture.Bind(program_->GetUniformLocation("u_texture")); + glUniformMatrix4fv(program_->GetUniformLocation("u_matrix"), 1, GL_FALSE, m); + + glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); + glEnableVertexAttribArray(positionLocation_); + glVertexAttribPointer(positionLocation_, COMPONENTS, GL_FLOAT, GL_FALSE, 0, 0); + + glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); + glEnableVertexAttribArray(textureLocation_); + glVertexAttribPointer(textureLocation_, COMPONENTS, GL_FLOAT, GL_FALSE, 0, 0); + } + + + void OpenGLTextureProgram::FinalizeExecution() + { + glDisableVertexAttribArray(positionLocation_); + glDisableVertexAttribArray(textureLocation_); + } + + + OpenGLTextureProgram::OpenGLTextureProgram(OpenGL::IOpenGLContext& context, + const char* fragmentShader) : + context_(context) + { + static const float POSITIONS[COMPONENTS * COUNT] = { + 0, 0, + 0, 1, + 1, 0, + 1, 0, + 0, 1, + 1, 1 + }; + + context_.MakeCurrent(); + + program_.reset(new OpenGL::OpenGLProgram); + program_->CompileShaders(VERTEX_SHADER, fragmentShader); + + positionLocation_ = program_->GetAttributeLocation("a_position"); + textureLocation_ = program_->GetAttributeLocation("a_texcoord"); + + glGenBuffers(2, buffers_); + + glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * COMPONENTS * COUNT, POSITIONS, GL_STATIC_DRAW); + + glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * COMPONENTS * COUNT, POSITIONS, GL_STATIC_DRAW); + } + + + OpenGLTextureProgram::~OpenGLTextureProgram() + { + context_.MakeCurrent(); + glDeleteBuffers(2, buffers_); + } + + + void OpenGLTextureProgram::Execution::DrawTriangles() + { + glDrawArrays(GL_TRIANGLES, 0, COUNT); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Internals/OpenGLTextureProgram.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,81 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../../OpenGL/IOpenGLContext.h" +#include "../../OpenGL/OpenGLProgram.h" +#include "../../OpenGL/OpenGLTexture.h" +#include "../../Toolbox/AffineTransform2D.h" + +namespace OrthancStone +{ + namespace Internals + { + class OpenGLTextureProgram : public boost::noncopyable + { + private: + OpenGL::IOpenGLContext& context_; + std::auto_ptr<OpenGL::OpenGLProgram> program_; + GLint positionLocation_; + GLint textureLocation_; + GLuint buffers_[2]; + + void InitializeExecution(OpenGL::OpenGLTexture& texture, + const AffineTransform2D& transform); + + void FinalizeExecution(); + + public: + OpenGLTextureProgram(OpenGL::IOpenGLContext& context, + const char* fragmentShader); + + ~OpenGLTextureProgram(); + + class Execution : public boost::noncopyable + { + private: + OpenGLTextureProgram& that_; + + public: + Execution(OpenGLTextureProgram& that, + OpenGL::OpenGLTexture& texture, + const AffineTransform2D& transform) : + that_(that) + { + that_.InitializeExecution(texture, transform); + } + + ~Execution() + { + that_.FinalizeExecution(); + } + + void DrawTriangles(); + + GLint GetUniformLocation(const std::string& name) + { + return that_.program_->GetUniformLocation(name); + } + }; + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/OpenGLCompositor.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,205 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "OpenGLCompositor.h" + +#include "Internals/OpenGLAdvancedPolylineRenderer.h" +#include "Internals/OpenGLBasicPolylineRenderer.h" +#include "Internals/OpenGLColorTextureRenderer.h" +#include "Internals/OpenGLFloatTextureRenderer.h" +#include "Internals/OpenGLInfoPanelRenderer.h" +#include "Internals/OpenGLTextRenderer.h" + +namespace OrthancStone +{ + class OpenGLCompositor::Font : public boost::noncopyable + { + private: + std::auto_ptr<GlyphTextureAlphabet> alphabet_; + std::auto_ptr<OpenGL::OpenGLTexture> texture_; + + public: + Font(const GlyphBitmapAlphabet& dict) + { + alphabet_.reset(new GlyphTextureAlphabet(dict)); + texture_.reset(new OpenGL::OpenGLTexture); + + std::auto_ptr<Orthanc::ImageAccessor> bitmap(alphabet_->ReleaseTexture()); + texture_->Load(*bitmap, true /* enable linear interpolation */); + } + + OpenGL::OpenGLTexture& GetTexture() const + { + assert(texture_.get() != NULL); + return *texture_; + } + + const GlyphTextureAlphabet& GetAlphabet() const + { + assert(alphabet_.get() != NULL); + return *alphabet_; + } + }; + + + const OpenGLCompositor::Font* OpenGLCompositor::GetFont(size_t fontIndex) const + { + Fonts::const_iterator found = fonts_.find(fontIndex); + + if (found == fonts_.end()) + { + return NULL; // Unknown font, nothing should be drawn + } + else + { + assert(found->second != NULL); + return found->second; + } + } + + + Internals::CompositorHelper::ILayerRenderer* OpenGLCompositor::Create(const ISceneLayer& layer) + { + switch (layer.GetType()) + { + case ISceneLayer::Type_InfoPanel: + return new Internals::OpenGLInfoPanelRenderer + (context_, colorTextureProgram_, dynamic_cast<const InfoPanelSceneLayer&>(layer)); + + case ISceneLayer::Type_ColorTexture: + return new Internals::OpenGLColorTextureRenderer + (context_, colorTextureProgram_, dynamic_cast<const ColorTextureSceneLayer&>(layer)); + + case ISceneLayer::Type_FloatTexture: + return new Internals::OpenGLFloatTextureRenderer + (context_, floatTextureProgram_, dynamic_cast<const FloatTextureSceneLayer&>(layer)); + + case ISceneLayer::Type_Polyline: + return new Internals::OpenGLAdvancedPolylineRenderer + (context_, linesProgram_, dynamic_cast<const PolylineSceneLayer&>(layer)); + //return new Internals::OpenGLBasicPolylineRenderer(context_, dynamic_cast<const PolylineSceneLayer&>(layer)); + + case ISceneLayer::Type_Text: + { + const TextSceneLayer& l = dynamic_cast<const TextSceneLayer&>(layer); + const Font* font = GetFont(l.GetFontIndex()); + if (font == NULL) + { + return NULL; + } + else + { + return new Internals::OpenGLTextRenderer + (context_, textProgram_, font->GetAlphabet(), font->GetTexture(), l); + } + } + + default: + return NULL; + } + } + + + OpenGLCompositor::OpenGLCompositor(OpenGL::IOpenGLContext& context, + const Scene2D& scene) : + context_(context), + helper_(scene, *this), + colorTextureProgram_(context), + floatTextureProgram_(context), + linesProgram_(context), + textProgram_(context), + canvasWidth_(0), + canvasHeight_(0) + { + UpdateSize(); + } + + + OpenGLCompositor::~OpenGLCompositor() + { + for (Fonts::iterator it = fonts_.begin(); it != fonts_.end(); ++it) + { + assert(it->second != NULL); + delete it->second; + } + } + + + void OpenGLCompositor::UpdateSize() + { + canvasWidth_ = context_.GetCanvasWidth(); + canvasHeight_ = context_.GetCanvasHeight(); + + context_.MakeCurrent(); + glViewport(0, 0, canvasWidth_, canvasHeight_); + } + + + void OpenGLCompositor::Refresh() + { + context_.MakeCurrent(); + + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + helper_.Refresh(canvasWidth_, canvasHeight_); + + context_.SwapBuffer(); + } + + + void OpenGLCompositor::SetFont(size_t index, + const GlyphBitmapAlphabet& dict) + { + std::auto_ptr<Font> font(new Font(dict)); + + Fonts::iterator found = fonts_.find(index); + + if (found == fonts_.end()) + { + fonts_[index] = font.release(); + } + else + { + assert(found->second != NULL); + delete found->second; + + found->second = font.release(); + } + } + + +#if ORTHANC_ENABLE_LOCALE == 1 + void OpenGLCompositor::SetFont(size_t index, + Orthanc::EmbeddedResources::FileResourceId resource, + unsigned int fontSize, + Orthanc::Encoding codepage) + { + FontRenderer renderer; + renderer.LoadFont(resource, fontSize); + + GlyphBitmapAlphabet dict; + dict.LoadCodepage(renderer, codepage); + + SetFont(index, dict); + } +#endif +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/OpenGLCompositor.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,73 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "Internals/CompositorHelper.h" +#include "Internals/OpenGLColorTextureProgram.h" +#include "Internals/OpenGLFloatTextureProgram.h" +#include "Internals/OpenGLLinesProgram.h" +#include "Internals/OpenGLTextProgram.h" + +namespace OrthancStone +{ + class OpenGLCompositor : private Internals::CompositorHelper::IRendererFactory + { + private: + class Font; + + typedef std::map<size_t, Font*> Fonts; + + OpenGL::IOpenGLContext& context_; + Fonts fonts_; + Internals::CompositorHelper helper_; + Internals::OpenGLColorTextureProgram colorTextureProgram_; + Internals::OpenGLFloatTextureProgram floatTextureProgram_; + Internals::OpenGLLinesProgram linesProgram_; + Internals::OpenGLTextProgram textProgram_; + unsigned int canvasWidth_; + unsigned int canvasHeight_; + + const Font* GetFont(size_t fontIndex) const; + + virtual Internals::CompositorHelper::ILayerRenderer* Create(const ISceneLayer& layer); + + public: + OpenGLCompositor(OpenGL::IOpenGLContext& context, + const Scene2D& scene); + + ~OpenGLCompositor(); + + void UpdateSize(); + + void Refresh(); + + void SetFont(size_t index, + const GlyphBitmapAlphabet& dict); + +#if ORTHANC_ENABLE_LOCALE == 1 + void SetFont(size_t index, + Orthanc::EmbeddedResources::FileResourceId resource, + unsigned int fontSize, + Orthanc::Encoding codepage); +#endif + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/PanSceneTracker.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,46 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "PanSceneTracker.h" + +namespace OrthancStone +{ + PanSceneTracker::PanSceneTracker(Scene2D& scene, + const PointerEvent& event) : + scene_(scene), + originalSceneToCanvas_(scene_.GetSceneToCanvasTransform()), + originalCanvasToScene_(scene_.GetCanvasToSceneTransform()) + { + pivot_ = event.GetMainPosition().Apply(originalCanvasToScene_); + } + + + void PanSceneTracker::Update(const PointerEvent& event) + { + ScenePoint2D p = event.GetMainPosition().Apply(originalCanvasToScene_); + + scene_.SetSceneToCanvasTransform( + AffineTransform2D::Combine( + originalSceneToCanvas_, + AffineTransform2D::CreateOffset(p.GetX() - pivot_.GetX(), + p.GetY() - pivot_.GetY()))); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/PanSceneTracker.h Mon Apr 29 15:24:59 2019 +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-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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IPointerTracker.h" +#include "Scene2D.h" + +namespace OrthancStone +{ + class PanSceneTracker : public IPointerTracker + { + private: + Scene2D& scene_; + ScenePoint2D pivot_; + AffineTransform2D originalSceneToCanvas_; + AffineTransform2D originalCanvasToScene_; + + public: + PanSceneTracker(Scene2D& scene, + const PointerEvent& event); + + virtual void Update(const PointerEvent& event); + + virtual void Release() + { + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/PointerEvent.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,69 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "PointerEvent.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + PointerEvent::PointerEvent() : + hasAltModifier_(false), + hasControlModifier_(false), + hasShiftModifier_(false) + { + } + + + ScenePoint2D PointerEvent::GetMainPosition() const + { + if (positions_.empty()) + { + return ScenePoint2D(0, 0); + } + else + { + return positions_[0]; + } + } + + + // Add the center of the pixel + void PointerEvent::AddIntegerPosition(int x, + int y) + { + AddPosition(static_cast<double>(x) + 0.5, + static_cast<double>(y) + 0.5); + } + + + ScenePoint2D PointerEvent::GetPosition(size_t index) const + { + if (index < positions_.size()) + { + return positions_[index]; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/PointerEvent.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,91 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ScenePoint2D.h" + +#include <boost/noncopyable.hpp> +#include <stdint.h> + +namespace OrthancStone +{ + class PointerEvent : public boost::noncopyable + { + private: + std::vector<ScenePoint2D> positions_; + bool hasAltModifier_; + bool hasControlModifier_; + bool hasShiftModifier_; + + public: + PointerEvent(); + + ScenePoint2D GetMainPosition() const; + + void AddPosition(double x, + double y) + { + positions_.push_back(ScenePoint2D(x, y)); + } + + // Add the center of the pixel + void AddIntegerPosition(int x, + int y); + + size_t GetPositionsCount() const + { + return positions_.size(); + } + + ScenePoint2D GetPosition(size_t index) const; + + void SetAltModifier(bool value) + { + hasAltModifier_ = value; + } + + bool HasAltModifier() const + { + return hasAltModifier_; + } + + void SetControlModifier(bool value) + { + hasControlModifier_ = value; + } + + bool HasControlModifier() const + { + return hasControlModifier_; + } + + void SetShiftModifier(bool value) + { + hasShiftModifier_ = value; + } + + bool HasShiftModifier() const + { + return hasShiftModifier_; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/PolylineSceneLayer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,117 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "PolylineSceneLayer.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + ISceneLayer* PolylineSceneLayer::Clone() const + { + std::auto_ptr<PolylineSceneLayer> cloned(new PolylineSceneLayer); + cloned->Copy(*this); + return cloned.release(); + } + + + void PolylineSceneLayer::SetThickness(double thickness) + { + if (thickness <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + thickness_ = thickness; + } + } + + + void PolylineSceneLayer::Copy(const PolylineSceneLayer& from) + { + SetColor(from.GetRed(), from.GetGreen(), from.GetBlue()); + chains_ = from.chains_; + closed_ = from.closed_; + thickness_ = from.thickness_; + } + + + void PolylineSceneLayer::Reserve(size_t countChains) + { + chains_.reserve(countChains); + closed_.reserve(countChains); + } + + + void PolylineSceneLayer::AddChain(const Chain& chain, + bool isClosed) + { + if (!chain.empty()) + { + chains_.push_back(chain); + closed_.push_back(isClosed); + } + } + + + const PolylineSceneLayer::Chain& PolylineSceneLayer::GetChain(size_t i) const + { + if (i < chains_.size()) + { + return chains_[i]; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + bool PolylineSceneLayer::IsClosedChain(size_t i) const + { + if (i < closed_.size()) + { + return closed_[i]; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + bool PolylineSceneLayer::GetBoundingBox(Extent2D& target) const + { + target.Reset(); + + for (size_t i = 0; i < chains_.size(); i++) + { + for (size_t j = 0; j < chains_[i].size(); j++) + { + const ScenePoint2D& p = chains_[i][j]; + target.AddPoint(p.GetX(), p.GetY()); + } + } + + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/PolylineSceneLayer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,84 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ColorSceneLayer.h" +#include "ScenePoint2D.h" + +#include <vector> + +namespace OrthancStone +{ + class PolylineSceneLayer : public ColorSceneLayer + { + public: + typedef std::vector<ScenePoint2D> Chain; + + private: + std::vector<Chain> chains_; + std::vector<bool> closed_; + double thickness_; + + public: + PolylineSceneLayer() : + thickness_(1.0) + { + } + + virtual ISceneLayer* Clone() const; + + void SetThickness(double thickness); + + double GetThickness() const + { + return thickness_; + } + + void Copy(const PolylineSceneLayer& from); + + void Reserve(size_t countChains); + + void AddChain(const Chain& chain, + bool isClosed); + + size_t GetChainsCount() const + { + return chains_.size(); + } + + const Chain& GetChain(size_t i) const; + + bool IsClosedChain(size_t i) const; + + virtual Type GetType() const + { + return Type_Polyline; + } + + virtual bool GetBoundingBox(Extent2D& target) const; + + virtual uint64_t GetRevision() const + { + return 0; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/RotateSceneTracker.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,64 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "RotateSceneTracker.h" + +namespace OrthancStone +{ + RotateSceneTracker::RotateSceneTracker(Scene2D& scene, + const PointerEvent& event, + unsigned int canvasWidth, + unsigned int canvasHeight) : + scene_(scene), + click_(event.GetMainPosition()), + aligner_(scene, click_, canvasWidth, canvasHeight), + isFirst_(true), + originalSceneToCanvas_(scene.GetSceneToCanvasTransform()) + { + } + + + void RotateSceneTracker::Update(const PointerEvent& event) + { + ScenePoint2D p = event.GetMainPosition(); + double dx = p.GetX() - click_.GetX(); + double dy = p.GetY() - click_.GetY(); + + if (std::abs(dx) > 5.0 || + std::abs(dy) > 5.0) + { + double a = atan2(dy, dx); + + if (isFirst_) + { + referenceAngle_ = a; + isFirst_ = false; + } + + scene_.SetSceneToCanvasTransform( + AffineTransform2D::Combine( + AffineTransform2D::CreateRotation(a - referenceAngle_), + originalSceneToCanvas_)); + + aligner_.Apply(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/RotateSceneTracker.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,51 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IPointerTracker.h" +#include "Internals/FixedPointAligner.h" + +namespace OrthancStone +{ + class RotateSceneTracker : public IPointerTracker + { + private: + Scene2D& scene_; + ScenePoint2D click_; + Internals::FixedPointAligner aligner_; + double referenceAngle_; + bool isFirst_; + AffineTransform2D originalSceneToCanvas_; + + public: + RotateSceneTracker(Scene2D& scene, + const PointerEvent& event, + unsigned int canvasWidth, + unsigned int canvasHeight); + + virtual void Update(const PointerEvent& event); + + virtual void Release() + { + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Scene2D.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,205 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "Scene2D.h" + +#include <Core/OrthancException.h> + + +namespace OrthancStone +{ + class Scene2D::Item + { + private: + std::auto_ptr<ISceneLayer> layer_; + uint64_t identifier_; + + public: + Item(ISceneLayer* layer, + uint64_t identifier) : + layer_(layer), + identifier_(identifier) + { + if (layer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + } + + ISceneLayer& GetLayer() const + { + assert(layer_.get() != NULL); + return *layer_; + } + + uint64_t GetIdentifier() const + { + return identifier_; + } + }; + + + Scene2D::Scene2D(const Scene2D& other) : + sceneToCanvas_(other.sceneToCanvas_), + canvasToScene_(other.canvasToScene_), + layerCounter_(0) + { + for (Content::const_iterator it = other.content_.begin(); + it != other.content_.end(); ++it) + { + content_[it->first] = new Item(it->second->GetLayer().Clone(), layerCounter_++); + } + } + + + Scene2D::~Scene2D() + { + for (Content::iterator it = content_.begin(); + it != content_.end(); ++it) + { + assert(it->second != NULL); + delete it->second; + } + } + + + void Scene2D::SetLayer(int depth, + ISceneLayer* layer) // Takes ownership + { + std::auto_ptr<Item> item(new Item(layer, layerCounter_++)); + + if (layer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + Content::iterator found = content_.find(depth); + + if (found == content_.end()) + { + content_[depth] = item.release(); + } + else + { + assert(found->second != NULL); + delete found->second; + found->second = item.release(); + } + } + + + void Scene2D::DeleteLayer(int depth) + { + Content::iterator found = content_.find(depth); + + if (found != content_.end()) + { + assert(found->second != NULL); + delete found->second; + content_.erase(found); + } + } + + + bool Scene2D::HasLayer(int depth) const + { + return (content_.find(depth) != content_.end()); + } + + + ISceneLayer& Scene2D::GetLayer(int depth) const + { + Content::const_iterator found = content_.find(depth); + + if (found == content_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + assert(found->second != NULL); + return found->second->GetLayer(); + } + } + + + void Scene2D::Apply(IVisitor& visitor) const + { + for (Content::const_iterator it = content_.begin(); + it != content_.end(); ++it) + { + assert(it->second != NULL); + visitor.Visit(it->second->GetLayer(), it->second->GetIdentifier(), it->first); + } + } + + + void Scene2D::SetSceneToCanvasTransform(const AffineTransform2D& transform) + { + // Make sure the transform is invertible before making any change + AffineTransform2D inverse = AffineTransform2D::Invert(transform); + + sceneToCanvas_ = transform; + canvasToScene_ = inverse; + } + + + void Scene2D::FitContent(unsigned int canvasWidth, + unsigned int canvasHeight) + { + Extent2D extent; + + for (Content::const_iterator it = content_.begin(); + it != content_.end(); ++it) + { + assert(it->second != NULL); + + Extent2D tmp; + if (it->second->GetLayer().GetBoundingBox(tmp)) + { + extent.Union(tmp); + } + } + + if (!extent.IsEmpty()) + { + double zoomX = static_cast<double>(canvasWidth) / extent.GetWidth(); + double zoomY = static_cast<double>(canvasHeight) / extent.GetHeight(); + + double zoom = std::min(zoomX, zoomY); + if (LinearAlgebra::IsCloseToZero(zoom)) + { + zoom = 1; + } + + double panX = extent.GetCenterX(); + double panY = extent.GetCenterY(); + + // Bring the center of the scene to (0,0) + AffineTransform2D t1 = AffineTransform2D::CreateOffset(-panX, -panY); + + // Scale the scene + AffineTransform2D t2 = AffineTransform2D::CreateScaling(zoom, zoom); + + SetSceneToCanvasTransform(AffineTransform2D::Combine(t2, t1)); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/Scene2D.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,97 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ISceneLayer.h" +#include "../Toolbox/AffineTransform2D.h" + +#include <map> + +namespace OrthancStone +{ + class Scene2D : public boost::noncopyable + { + public: + class IVisitor : public boost::noncopyable + { + public: + virtual ~IVisitor() + { + } + + virtual void Visit(const ISceneLayer& layer, + uint64_t layerIdentifier, + int depth) = 0; + }; + + private: + class Item; + + typedef std::map<int, Item*> Content; + + Content content_; + AffineTransform2D sceneToCanvas_; + AffineTransform2D canvasToScene_; + uint64_t layerCounter_; + + Scene2D(const Scene2D& other); + + public: + Scene2D() : + layerCounter_(0) + { + } + + ~Scene2D(); + + Scene2D* Clone() const + { + return new Scene2D(*this); + } + + void SetLayer(int depth, + ISceneLayer* layer); // Takes ownership + + void DeleteLayer(int depth); + + bool HasLayer(int depth) const; + + ISceneLayer& GetLayer(int depth) const; + + void Apply(IVisitor& visitor) const; + + const AffineTransform2D& GetSceneToCanvasTransform() const + { + return sceneToCanvas_; + } + + const AffineTransform2D& GetCanvasToSceneTransform() const + { + return canvasToScene_; + } + + void SetSceneToCanvasTransform(const AffineTransform2D& transform); + + void FitContent(unsigned int canvasWidth, + unsigned int canvasHeight); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/ScenePoint2D.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,67 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Toolbox/AffineTransform2D.h" + + +namespace OrthancStone +{ + class ScenePoint2D + { + private: + double x_; + double y_; + + public: + ScenePoint2D() : + x_(0), + y_(0) + { + } + + ScenePoint2D(double x, + double y) : + x_(x), + y_(y) + { + } + + double GetX() const + { + return x_; + } + + double GetY() const + { + return y_; + } + + ScenePoint2D Apply(const AffineTransform2D& t) const + { + double x = x_; + double y = y_; + t.Apply(x, y); + return ScenePoint2D(x, y); + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/TextSceneLayer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,81 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "TextSceneLayer.h" + +namespace OrthancStone +{ + TextSceneLayer::TextSceneLayer() : + x_(0), + y_(0), + fontIndex_(0), + anchor_(BitmapAnchor_Center), + border_(0), + revision_(0) + { + } + + + ISceneLayer* TextSceneLayer::Clone() const + { + std::auto_ptr<TextSceneLayer> cloned(new TextSceneLayer); + cloned->SetColor(GetRed(), GetGreen(), GetBlue()); + cloned->x_ = x_; + cloned->y_ = y_; + cloned->utf8_ = utf8_; + cloned->fontIndex_ = fontIndex_; + cloned->anchor_ = anchor_; + cloned->border_ = border_; + return cloned.release(); + } + + void TextSceneLayer::SetPosition(double x, + double y) + { + x_ = x; + y_ = y; + revision_ ++; + } + + void TextSceneLayer::SetText(const std::string& utf8) + { + utf8_ = utf8; + revision_ ++; + } + + void TextSceneLayer::SetFontIndex(size_t fontIndex) + { + fontIndex_ = fontIndex; + revision_ ++; + } + + void TextSceneLayer::SetAnchor(BitmapAnchor anchor) + { + anchor_ = anchor; + revision_ ++; + } + + void TextSceneLayer::SetBorder(unsigned int border) + { + border_ = border; + revision_ ++; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/TextSceneLayer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,104 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ColorSceneLayer.h" +#include "../StoneEnumerations.h" + +#include <memory> +#include <string> + +namespace OrthancStone +{ + class TextSceneLayer : public ColorSceneLayer + { + private: + double x_; + double y_; + std::string utf8_; + size_t fontIndex_; + BitmapAnchor anchor_; + unsigned int border_; + uint64_t revision_; + + public: + TextSceneLayer(); + + virtual ISceneLayer* Clone() const; + + void SetPosition(double x, + double y); + + void SetText(const std::string& utf8); + + void SetFontIndex(size_t fontIndex); + + void SetAnchor(BitmapAnchor anchor); + + void SetBorder(unsigned int border); + + double GetX() const + { + return x_; + } + + double GetY() const + { + return y_; + } + + unsigned int GetBorder() const + { + return border_; + } + + const std::string& GetText() const + { + return utf8_; + } + + size_t GetFontIndex() const + { + return fontIndex_; + } + + BitmapAnchor GetAnchor() const + { + return anchor_; + } + + virtual Type GetType() const + { + return Type_Text; + } + + virtual bool GetBoundingBox(Extent2D& target) const + { + return false; + } + + virtual uint64_t GetRevision() const + { + return revision_; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/TextureBaseSceneLayer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,170 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "TextureBaseSceneLayer.h" + +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + void TextureBaseSceneLayer::SetTexture(Orthanc::ImageAccessor* texture) + { + if (texture == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + else + { + texture_.reset(texture); + IncrementRevision(); + } + } + + + void TextureBaseSceneLayer::CopyParameters(const TextureBaseSceneLayer& other) + { + originX_ = other.originX_; + originY_ = other.originY_; + pixelSpacingX_ = other.pixelSpacingX_; + pixelSpacingY_ = other.pixelSpacingY_; + angle_ = other.angle_; + isLinearInterpolation_ = other.isLinearInterpolation_; + } + + + TextureBaseSceneLayer::TextureBaseSceneLayer() : + originX_(0), + originY_(0), + pixelSpacingX_(1), + pixelSpacingY_(1), + angle_(0), + isLinearInterpolation_(false), + revision_(0) + { + if (pixelSpacingX_ <= 0 || + pixelSpacingY_ <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + void TextureBaseSceneLayer::SetOrigin(double x, + double y) + { + originX_ = x; + originY_ = y; + IncrementRevision(); + } + + + void TextureBaseSceneLayer::SetPixelSpacing(double sx, + double sy) + { + if (sx <= 0 || + sy <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + pixelSpacingX_ = sx; + pixelSpacingY_ = sy; + IncrementRevision(); + } + } + + + void TextureBaseSceneLayer::SetAngle(double angle) + { + angle_ = angle; + IncrementRevision(); + } + + + void TextureBaseSceneLayer::SetLinearInterpolation(bool isLinearInterpolation) + { + isLinearInterpolation_ = isLinearInterpolation; + IncrementRevision(); + } + + + const Orthanc::ImageAccessor& TextureBaseSceneLayer::GetTexture() const + { + if (!HasTexture()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return *texture_; + } + } + + + AffineTransform2D TextureBaseSceneLayer::GetTransform() const + { + return AffineTransform2D::Combine( + AffineTransform2D::CreateOffset(originX_, originY_), + AffineTransform2D::CreateRotation(angle_), + AffineTransform2D::CreateScaling(pixelSpacingX_, pixelSpacingY_), + AffineTransform2D::CreateOffset(-0.5, -0.5)); + } + + + bool TextureBaseSceneLayer::GetBoundingBox(Extent2D& target) const + { + if (texture_.get() == NULL) + { + return false; + } + else + { + const AffineTransform2D t = GetTransform(); + + target.Reset(); + + double x, y; + + x = 0; + y = 0; + t.Apply(x, y); + target.AddPoint(x, y); + + x = static_cast<double>(texture_->GetWidth()); + y = 0; + t.Apply(x, y); + target.AddPoint(x, y); + + x = 0; + y = static_cast<double>(texture_->GetHeight()); + t.Apply(x, y); + target.AddPoint(x, y); + + x = static_cast<double>(texture_->GetWidth()); + y = static_cast<double>(texture_->GetHeight()); + t.Apply(x, y); + target.AddPoint(x, y); + + return true; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/TextureBaseSceneLayer.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,114 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ISceneLayer.h" +#include "../Toolbox/AffineTransform2D.h" + +#include <Core/Images/ImageAccessor.h> + +namespace OrthancStone +{ + class TextureBaseSceneLayer : public ISceneLayer + { + private: + std::auto_ptr<Orthanc::ImageAccessor> texture_; + double originX_; + double originY_; + double pixelSpacingX_; + double pixelSpacingY_; + double angle_; + bool isLinearInterpolation_; + uint64_t revision_; + + protected: + void SetTexture(Orthanc::ImageAccessor* texture); + + void IncrementRevision() + { + revision_++; + } + + void CopyParameters(const TextureBaseSceneLayer& other); + + public: + TextureBaseSceneLayer(); + + // Center of the top-left pixel + void SetOrigin(double x, + double y); + + void SetPixelSpacing(double sx, + double sy); + + // In radians + void SetAngle(double angle); + + void SetLinearInterpolation(bool isLinearInterpolation); + + double GetOriginX() const + { + return originX_; + } + + double GetOriginY() const + { + return originY_; + } + + double GetPixelSpacingX() const + { + return pixelSpacingX_; + } + + double GetPixelSpacingY() const + { + return pixelSpacingY_; + } + + double GetAngle() const + { + return angle_; + } + + bool IsLinearInterpolation() const + { + return isLinearInterpolation_; + } + + bool HasTexture() const + { + return (texture_.get() != NULL); + } + + const Orthanc::ImageAccessor& GetTexture() const; + + AffineTransform2D GetTransform() const; + + virtual bool GetBoundingBox(Extent2D& target) const; + + virtual uint64_t GetRevision() const + { + return revision_; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/ZoomSceneTracker.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,82 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "ZoomSceneTracker.h" + +namespace OrthancStone +{ + ZoomSceneTracker::ZoomSceneTracker(Scene2D& scene, + const PointerEvent& event, + unsigned int canvasWidth, + unsigned int canvasHeight) : + scene_(scene), + clickY_(event.GetMainPosition().GetY()), + aligner_(scene, event.GetMainPosition(), canvasWidth, canvasHeight), + originalSceneToCanvas_(scene.GetSceneToCanvasTransform()) + { + if (canvasHeight <= 3) + { + active_ = false; + } + else + { + normalization_ = 1.0 / static_cast<double>(canvasHeight - 1); + active_ = true; + } + } + + + void ZoomSceneTracker::Update(const PointerEvent& event) + { + static const double MIN_ZOOM = -4; + static const double MAX_ZOOM = 4; + + if (active_) + { + double y = event.GetMainPosition().GetY(); + double dy = static_cast<double>(y - clickY_) * normalization_; // In the range [-1,1] + double z; + + // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM] + if (dy < -1.0) + { + z = MIN_ZOOM; + } + else if (dy > 1.0) + { + z = MAX_ZOOM; + } + else + { + z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0; + } + + double zoom = pow(2.0, z); + + scene_.SetSceneToCanvasTransform( + AffineTransform2D::Combine( + AffineTransform2D::CreateScaling(zoom, zoom), + originalSceneToCanvas_)); + + aligner_.Apply(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Scene2D/ZoomSceneTracker.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,51 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IPointerTracker.h" +#include "Internals/FixedPointAligner.h" + +namespace OrthancStone +{ + class ZoomSceneTracker : public IPointerTracker + { + private: + Scene2D& scene_; + double clickY_; + bool active_; + double normalization_; + Internals::FixedPointAligner aligner_; + AffineTransform2D originalSceneToCanvas_; + + public: + ZoomSceneTracker(Scene2D& scene, + const PointerEvent& event, + unsigned int canvasWidth, + unsigned int canvasHeight); + + virtual void Update(const PointerEvent& event); + + virtual void Release() + { + } + }; +}
--- a/Framework/StoneEnumerations.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Framework/StoneEnumerations.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -48,14 +48,14 @@ void ComputeWindowing(float& targetCenter, float& targetWidth, ImageWindowing windowing, - float defaultCenter, - float defaultWidth) + float customCenter, + float customWidth) { switch (windowing) { - case ImageWindowing_Default: - targetCenter = defaultCenter; - targetWidth = defaultWidth; + case ImageWindowing_Custom: + targetCenter = customCenter; + targetWidth = customWidth; break; case ImageWindowing_Bone:
--- a/Framework/StoneEnumerations.h Mon Apr 29 12:01:55 2019 +0200 +++ b/Framework/StoneEnumerations.h Mon Apr 29 15:24:59 2019 +0200 @@ -34,7 +34,6 @@ enum ImageWindowing { - ImageWindowing_Default, ImageWindowing_Bone, ImageWindowing_Lung, ImageWindowing_Custom @@ -199,8 +198,8 @@ void ComputeWindowing(float& targetCenter, float& targetWidth, ImageWindowing windowing, - float defaultCenter, - float defaultWidth); + float customCenter, + float customWidth); void ComputeAnchorTranslation(double& deltaX /* out */, double& deltaY /* out */,
--- a/Framework/Toolbox/AffineTransform2D.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Framework/Toolbox/AffineTransform2D.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -90,6 +90,76 @@ } + void AffineTransform2D::ConvertToOpenGLMatrix(float target[16], + unsigned int canvasWidth, + unsigned int canvasHeight) const + { + const AffineTransform2D t = AffineTransform2D::Combine( + CreateOpenGLClipspace(canvasWidth, canvasHeight), *this); + + const Matrix source = t.GetHomogeneousMatrix(); + + if (source.size1() != 3 || + source.size2() != 3) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + // "z" must be in the [-1,1] range, otherwise the texture does not show up + float z = 0; + + // Embed the 3x3 affine transform of the 2D plane into a 4x4 + // matrix (3D) for OpenGL. The matrix must be transposed. + + target[0] = static_cast<float>(source(0, 0)); + target[1] = static_cast<float>(source(1, 0)); + target[2] = 0; + target[3] = static_cast<float>(source(2, 0)); + target[4] = static_cast<float>(source(0, 1)); + target[5] = static_cast<float>(source(1, 1)); + target[6] = 0; + target[7] = static_cast<float>(source(2, 1)); + target[8] = 0; + target[9] = 0; + target[10] = -1; + target[11] = 0; + target[12] = static_cast<float>(source(0, 2)); + target[13] = static_cast<float>(source(1, 2)); + target[14] = -z; + target[15] = static_cast<float>(source(2, 2)); + } + + + double AffineTransform2D::ComputeZoom() const + { + // Compute the length of the (0,0)-(1,1) diagonal (whose + // length is sqrt(2)) instead of the (0,0)-(1,0) unit segment, + // in order to cope with possible anisotropic zooming + + double x1 = 0; + double y1 = 0; + Apply(x1, y1); + + double x2 = 1; + double y2 = 1; + Apply(x2, y2); + + double dx = x2 - x1; + double dy = y2 - y1; + + double zoom = sqrt(dx * dx + dy * dy) / sqrt(2.0); + + if (LinearAlgebra::IsCloseToZero(zoom)) + { + return 1; // Default value if transform is ill-conditioned + } + else + { + return zoom; + } + } + + AffineTransform2D AffineTransform2D::Invert(const AffineTransform2D& a) { AffineTransform2D t; @@ -163,4 +233,17 @@ return t; } + + + AffineTransform2D AffineTransform2D::CreateOpenGLClipspace(unsigned int canvasWidth, + unsigned int canvasHeight) + { + AffineTransform2D t; + t.matrix_(0, 0) = 2.0 / static_cast<double>(canvasWidth); + t.matrix_(0, 2) = -1.0; + t.matrix_(1, 1) = -2.0 / static_cast<double>(canvasHeight); + t.matrix_(1, 2) = 1.0; + + return t; + } }
--- a/Framework/Toolbox/AffineTransform2D.h Mon Apr 29 12:01:55 2019 +0200 +++ b/Framework/Toolbox/AffineTransform2D.h Mon Apr 29 15:24:59 2019 +0200 @@ -56,6 +56,12 @@ const Orthanc::ImageAccessor& source, ImageInterpolation interpolation, bool clear) const; + + void ConvertToOpenGLMatrix(float target[16], + unsigned int canvasWidth, + unsigned int canvasHeight) const; + + double ComputeZoom() const; static AffineTransform2D Invert(const AffineTransform2D& a); @@ -78,5 +84,8 @@ double sy); static AffineTransform2D CreateRotation(double angle); + + static AffineTransform2D CreateOpenGLClipspace(unsigned int canvasWidth, + unsigned int canvasHeight); }; }
--- a/Framework/Toolbox/BaseWebService.h Mon Apr 29 12:01:55 2019 +0200 +++ b/Framework/Toolbox/BaseWebService.h Mon Apr 29 15:24:59 2019 +0200 @@ -81,7 +81,7 @@ class BaseWebServicePayload; bool cacheEnabled_; - std::map<std::string, boost::shared_ptr<CachedHttpRequestSuccessMessage>> cache_; // TODO: this is currently an infinite cache ! + std::map<std::string, boost::shared_ptr<CachedHttpRequestSuccessMessage> > cache_; // TODO: this is currently an infinite cache ! public:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DynamicBitmap.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,37 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#include "DynamicBitmap.h" + +#include <Core/Images/Image.h> +#include <Core/OrthancException.h> + +namespace OrthancStone +{ + DynamicBitmap::DynamicBitmap(const Orthanc::ImageAccessor& bitmap) : + bitmap_(Orthanc::Image::Clone(bitmap)) + { + if (bitmap_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DynamicBitmap.h Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,44 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <Core/IDynamicObject.h> +#include <Core/Images/ImageAccessor.h> + +#include <memory> + +namespace OrthancStone +{ + class DynamicBitmap : public Orthanc::IDynamicObject + { + private: + std::auto_ptr<Orthanc::ImageAccessor> bitmap_; + + public: + DynamicBitmap(const Orthanc::ImageAccessor& bitmap); + + const Orthanc::ImageAccessor& GetBitmap() const + { + return *bitmap_; + } + }; +}
--- a/Framework/Viewport/IMouseTracker.h Mon Apr 29 12:01:55 2019 +0200 +++ b/Framework/Viewport/IMouseTracker.h Mon Apr 29 15:24:59 2019 +0200 @@ -62,7 +62,5 @@ virtual void MouseMove(int x, int y, const std::vector<Touch>& displayTouches) = 0; - - virtual bool IsTouchTracker() const {return false;} }; }
--- a/Framework/Volumes/VolumeReslicer.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Framework/Volumes/VolumeReslicer.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -655,8 +655,7 @@ float rescaleSlope, float rescaleIntercept) { - if (windowing == ImageWindowing_Custom || - windowing == ImageWindowing_Default) + if (windowing == ImageWindowing_Custom) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); }
--- a/Platforms/Generic/DelayedCallCommand.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Platforms/Generic/DelayedCallCommand.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -36,7 +36,7 @@ callback_(callback), payload_(payload), context_(context), - expirationTimePoint_(boost::chrono::system_clock::now() + boost::chrono::milliseconds(timeoutInMs)), + expirationTimePoint_(boost::posix_time::microsec_clock::local_time() + boost::posix_time::milliseconds(timeoutInMs)), timeoutInMs_(timeoutInMs) { } @@ -44,9 +44,9 @@ void DelayedCallCommand::Execute() { - while (boost::chrono::system_clock::now() < expirationTimePoint_) + while (boost::posix_time::microsec_clock::local_time() < expirationTimePoint_) { - boost::this_thread::sleep_for(boost::chrono::milliseconds(1)); + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); } }
--- a/Platforms/Generic/DelayedCallCommand.h Mon Apr 29 12:01:55 2019 +0200 +++ b/Platforms/Generic/DelayedCallCommand.h Mon Apr 29 15:24:59 2019 +0200 @@ -27,7 +27,8 @@ #include "../../Framework/Messages/IObservable.h" #include "../../Framework/Messages/ICallable.h" #include "../../Applications/Generic/NativeStoneApplicationContext.h" -#include <boost/chrono.hpp> + +#include <boost/date_time/posix_time/posix_time.hpp> namespace OrthancStone { @@ -37,7 +38,7 @@ std::auto_ptr<MessageHandler<IDelayedCallExecutor::TimeoutMessage> > callback_; std::auto_ptr<Orthanc::IDynamicObject> payload_; NativeStoneApplicationContext& context_; - boost::chrono::system_clock::time_point expirationTimePoint_; + boost::posix_time::ptime expirationTimePoint_; unsigned int timeoutInMs_; public:
--- a/Platforms/Wasm/Defaults.cpp Mon Apr 29 12:01:55 2019 +0200 +++ b/Platforms/Wasm/Defaults.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -5,9 +5,12 @@ #include <Framework/dev.h> #include "Framework/Widgets/TestCairoWidget.h" #include <Framework/Viewport/WidgetViewport.h> +#include <Applications/Wasm/StartupParametersBuilder.h> +#include <Platforms/Wasm/WasmPlatformApplicationAdapter.h> +#include <Core/Logging.h> + #include <algorithm> -#include "Applications/Wasm/StartupParametersBuilder.h" -#include "Platforms/Wasm/WasmPlatformApplicationAdapter.h" + static unsigned int width_ = 0; static unsigned int height_ = 0; @@ -88,6 +91,10 @@ printf("StartWasmApplication\n"); + Orthanc::Logging::SetErrorWarnInfoTraceLoggingFunctions( + stone_console_error, stone_console_warning, + stone_console_info, stone_console_trace); + // recreate a command line from uri arguments and parse it boost::program_options::variables_map parameters; boost::program_options::options_description options; @@ -106,6 +113,16 @@ printf("StartWasmApplication - completed\n"); } + bool EMSCRIPTEN_KEEPALIVE WasmIsTraceLevelEnabled() + { + return Orthanc::Logging::IsTraceLevelEnabled(); + } + + bool EMSCRIPTEN_KEEPALIVE WasmIsInfoLevelEnabled() + { + return Orthanc::Logging::IsInfoLevelEnabled(); + } + void EMSCRIPTEN_KEEPALIVE WasmDoAnimation() { for (auto viewport : viewports_) {
--- a/Platforms/Wasm/Defaults.h Mon Apr 29 12:01:55 2019 +0200 +++ b/Platforms/Wasm/Defaults.h Mon Apr 29 15:24:59 2019 +0200 @@ -18,7 +18,11 @@ extern void ScheduleWebViewportRedrawFromCpp(ViewportHandle cppViewportHandle); extern void UpdateStoneApplicationStatusFromCppWithString(const char* statusUpdateMessage); extern void UpdateStoneApplicationStatusFromCppWithSerializedMessage(const char* statusUpdateMessage); - + extern void stone_console_error(const char*); + extern void stone_console_warning(const char*); + extern void stone_console_info(const char*); + extern void stone_console_trace(const char*); + // C++ methods accessible from JS extern void EMSCRIPTEN_KEEPALIVE CreateWasmApplication(ViewportHandle cppViewportHandle); extern void EMSCRIPTEN_KEEPALIVE SetStartupParameter(const char* keyc, const char* value);
--- a/Platforms/Wasm/default-library.js Mon Apr 29 12:01:55 2019 +0200 +++ b/Platforms/Wasm/default-library.js Mon Apr 29 15:24:59 2019 +0200 @@ -1,21 +1,49 @@ // this file contains the JS method you want to expose to C++ code mergeInto(LibraryManager.library, { + ScheduleWebViewportRedrawFromCpp: function(cppViewportHandle) { window.ScheduleWebViewportRedraw(cppViewportHandle); }, + CreateWasmViewportFromCpp: function(htmlCanvasId) { return window.CreateWasmViewport(htmlCanvasId); }, + // each time the StoneApplication updates its status, it may signal it // through this method. i.e, to change the status of a button in the web interface UpdateStoneApplicationStatusFromCppWithString: function(statusUpdateMessage) { var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); window.UpdateWebApplicationWithString(statusUpdateMessage_); }, + // same, but with a serialized message UpdateStoneApplicationStatusFromCppWithSerializedMessage: function(statusUpdateMessage) { var statusUpdateMessage_ = UTF8ToString(statusUpdateMessage); window.UpdateWebApplicationWithSerializedMessage(statusUpdateMessage_); + }, + + // These functions are called from C++ (through an extern declaration) + // and call the standard logger that, here, routes to the console. + + stone_console_error : function(message) { + var text = UTF8ToString(message); + window.errorFromCpp(text); + }, + + stone_console_warning : function(message) { + var text = UTF8ToString(message); + window.warningFromCpp(text); + }, + + stone_console_info: function(message) { + var text = UTF8ToString(message); + window.infoFromCpp(text); + }, + + stone_console_trace : function(message) { + var text = UTF8ToString(message); + window.debugFromCpp(text); } + });
--- a/Platforms/Wasm/logger.ts Mon Apr 29 12:01:55 2019 +0200 +++ b/Platforms/Wasm/logger.ts Mon Apr 29 15:24:59 2019 +0200 @@ -10,6 +10,10 @@ this._debug(LogSource.Typescript, ...args); } + public debugFromCpp(...args: any[]): void { + this._debug(LogSource.Cpp, ...args); + } + public info(...args: any[]): void { this._info(LogSource.Typescript, ...args); } @@ -22,6 +26,10 @@ this._warning(LogSource.Typescript, ...args); } + public warningFromCpp(message: string): void { + this._warning(LogSource.Cpp, message); + } + public error(...args: any[]): void { this._error(LogSource.Typescript, ...args); } @@ -31,13 +39,25 @@ } public _debug(source: LogSource, ...args: any[]): void { - var output = this.getOutput(source, args); - console.debug(...output); + if ((<any> window).IsTraceLevelEnabled) + { + if ((<any> window).IsTraceLevelEnabled()) + { + var output = this.getOutput(source, args); + console.debug(...output); + } + } } private _info(source: LogSource, ...args: any[]): void { - var output = this.getOutput(source, args); - console.info(...output); + if ((<any> window).IsInfoLevelEnabled) + { + if ((<any> window).IsInfoLevelEnabled()) + { + var output = this.getOutput(source, args); + console.info(...output); + } + } } public _warning(source: LogSource, ...args: any[]): void { @@ -88,3 +108,4 @@ } export var defaultLogger: StandardConsoleLogger = new TimeConsoleLogger(); +
--- a/Platforms/Wasm/stone-framework-loader.ts Mon Apr 29 12:01:55 2019 +0200 +++ b/Platforms/Wasm/stone-framework-loader.ts Mon Apr 29 15:24:59 2019 +0200 @@ -59,6 +59,11 @@ { Logger.defaultLogger.debug('Initializing WebAssembly Module'); + (<any> window).errorFromCpp = function(text:any) { Logger.defaultLogger.errorFromCpp(text); }; + (<any> window).warningFromCpp = function(text:any) { Logger.defaultLogger.warningFromCpp(text); }; + (<any> window).infoFromCpp = function(text:any) { Logger.defaultLogger.infoFromCpp(text); }; + (<any> window).debugFromCpp = function(text:any) { Logger.defaultLogger.debugFromCpp(text); }; + // (<any> window). (<any> window).StoneFrameworkModule = { preRun: [
--- a/Platforms/Wasm/wasm-application-runner.ts Mon Apr 29 12:01:55 2019 +0200 +++ b/Platforms/Wasm/wasm-application-runner.ts Mon Apr 29 15:24:59 2019 +0200 @@ -27,7 +27,8 @@ WasmDoAnimation(); } - setTimeout(DoAnimationThread, 100); // Update the viewport content every 100ms if need be + // Update the viewport content every 100ms if need be + setTimeout(DoAnimationThread, 100); } function GetUriParameters(): Map<string, string> { @@ -42,10 +43,12 @@ var tmp = tokens[i].split('='); if (tmp.length == 2) { result[tmp[0]] = decodeURIComponent(tmp[1]); + } else if(tmp.length == 1) { + // if there is no '=', we treat ot afterwards as a flag-style param + result[tmp[0]] = ""; } } - - return result; + return result; } else { return new Map<string, string>(); @@ -65,6 +68,8 @@ for (let key in parameters) { if (parameters.hasOwnProperty(key)) { + Logger.defaultLogger.debug( + `About to call SetStartupParameter("${key}","${parameters[key]}")`); SetStartupParameter(key, parameters[key]); } } @@ -92,6 +97,8 @@ CreateCppViewport = (<any> window).StoneFrameworkModule.cwrap('CreateCppViewport', 'number', []); ReleaseCppViewport = (<any> window).StoneFrameworkModule.cwrap('ReleaseCppViewport', null, ['number']); StartWasmApplication = (<any> window).StoneFrameworkModule.cwrap('StartWasmApplication', null, ['string']); + (<any> window).IsTraceLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsTraceLevelEnabled', 'boolean', null); + (<any> window).IsInfoLevelEnabled = (<any> window).StoneFrameworkModule.cwrap('WasmIsInfoLevelEnabled', 'boolean', null); (<any> window).WasmWebService_NotifyCachedSuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifyCachedSuccess', null, ['number']); (<any> window).WasmWebService_NotifySuccess = (<any> window).StoneFrameworkModule.cwrap('WasmWebService_NotifySuccess', null, ['number', 'string', 'array', 'number', 'number']);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CMake/FreetypeConfiguration.cmake Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,87 @@ +if (STATIC_BUILD OR NOT USE_SYSTEM_FREETYPE) + set(FREETYPE_SOURCES_DIR ${CMAKE_BINARY_DIR}/freetype-2.9.1) + set(FREETYPE_URL "http://orthanc.osimis.io/ThirdPartyDownloads/freetype-2.9.1.tar.gz") + set(FREETYPE_MD5 "3adb0e35d3c100c456357345ccfa8056") + + DownloadPackage(${FREETYPE_MD5} ${FREETYPE_URL} "${FREETYPE_SOURCES_DIR}") + + include_directories(BEFORE + ${FREETYPE_SOURCES_DIR}/include/ + ) + + add_definitions( + -DFT2_BUILD_LIBRARY + -DFT_CONFIG_OPTION_NO_ASSEMBLER + ) + + set(FREETYPE_SOURCES + ${FREETYPE_SOURCES_DIR}/src/autofit/autofit.c + ${FREETYPE_SOURCES_DIR}/src/base/ftbase.c + ${FREETYPE_SOURCES_DIR}/src/base/ftbbox.c + ${FREETYPE_SOURCES_DIR}/src/base/ftbdf.c + ${FREETYPE_SOURCES_DIR}/src/base/ftbitmap.c + ${FREETYPE_SOURCES_DIR}/src/base/ftcid.c + ${FREETYPE_SOURCES_DIR}/src/base/ftfstype.c + ${FREETYPE_SOURCES_DIR}/src/base/ftgasp.c + ${FREETYPE_SOURCES_DIR}/src/base/ftglyph.c + ${FREETYPE_SOURCES_DIR}/src/base/ftgxval.c + ${FREETYPE_SOURCES_DIR}/src/base/ftinit.c + ${FREETYPE_SOURCES_DIR}/src/base/ftmm.c + ${FREETYPE_SOURCES_DIR}/src/base/ftotval.c + ${FREETYPE_SOURCES_DIR}/src/base/ftpatent.c + ${FREETYPE_SOURCES_DIR}/src/base/ftpfr.c + ${FREETYPE_SOURCES_DIR}/src/base/ftstroke.c + ${FREETYPE_SOURCES_DIR}/src/base/ftsynth.c + ${FREETYPE_SOURCES_DIR}/src/base/ftsystem.c + ${FREETYPE_SOURCES_DIR}/src/base/fttype1.c + ${FREETYPE_SOURCES_DIR}/src/base/ftwinfnt.c + ${FREETYPE_SOURCES_DIR}/src/bdf/bdf.c + ${FREETYPE_SOURCES_DIR}/src/bzip2/ftbzip2.c + ${FREETYPE_SOURCES_DIR}/src/cache/ftcache.c + ${FREETYPE_SOURCES_DIR}/src/cff/cff.c + ${FREETYPE_SOURCES_DIR}/src/cid/type1cid.c + ${FREETYPE_SOURCES_DIR}/src/gzip/ftgzip.c + ${FREETYPE_SOURCES_DIR}/src/lzw/ftlzw.c + ${FREETYPE_SOURCES_DIR}/src/pcf/pcf.c + ${FREETYPE_SOURCES_DIR}/src/pfr/pfr.c + ${FREETYPE_SOURCES_DIR}/src/psaux/psaux.c + ${FREETYPE_SOURCES_DIR}/src/pshinter/pshinter.c + ${FREETYPE_SOURCES_DIR}/src/psnames/psnames.c + ${FREETYPE_SOURCES_DIR}/src/raster/raster.c + ${FREETYPE_SOURCES_DIR}/src/sfnt/sfnt.c + ${FREETYPE_SOURCES_DIR}/src/smooth/smooth.c + ${FREETYPE_SOURCES_DIR}/src/truetype/truetype.c + ${FREETYPE_SOURCES_DIR}/src/type1/type1.c + ${FREETYPE_SOURCES_DIR}/src/type42/type42.c + ${FREETYPE_SOURCES_DIR}/src/winfonts/winfnt.c + ) + + if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + list(APPEND FREETYPE_SOURCES + ${FREETYPE_SOURCES_DIR}/builds/windows/ftdebug.c + ) + endif() + + foreach(header + ${FREETYPE_SOURCES_DIR}/include/freetype/config/ftconfig.h + ${FREETYPE_SOURCES_DIR}/include/freetype/config/ftoption.h + ) + + set_source_files_properties( + ${FREETYPE_SOURCES} + PROPERTIES OBJECT_DEPENDS ${header} + ) + endforeach() + + source_group(ThirdParty\\Freetype REGULAR_EXPRESSION ${FREETYPE_SOURCES_DIR}/.*) + +else() + include(FindFreetype) + + if (NOT FREETYPE_FOUND) + message(FATAL_ERROR "Please install the libfreetype6-dev package") + endif() + + include_directories(${FREETYPE_INCLUDE_DIRS}) + link_libraries(${FREETYPE_LIBRARIES}) +endif()
--- a/Resources/CMake/OrthancStoneConfiguration.cmake Mon Apr 29 12:01:55 2019 +0200 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Mon Apr 29 15:24:59 2019 +0200 @@ -67,6 +67,7 @@ include(FindPkgConfig) include(${CMAKE_CURRENT_LIST_DIR}/BoostExtendedConfiguration.cmake) include(${CMAKE_CURRENT_LIST_DIR}/CairoConfiguration.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/FreetypeConfiguration.cmake) include(${CMAKE_CURRENT_LIST_DIR}/PixmanConfiguration.cmake) @@ -105,6 +106,23 @@ endif() +if (ENABLE_OPENGL) + include(FindOpenGL) + if (NOT OPENGL_FOUND) + message(FATAL_ERROR "Cannot find OpenGL on your system") + endif() + + link_libraries(${OPENGL_LIBRARIES}) + + add_definitions( + -DGL_GLEXT_PROTOTYPES=1 + -DORTHANC_ENABLE_OPENGL=1 + ) +else() + add_definitions(-DORTHANC_ENABLE_OPENGL=0) +endif() + + ##################################################################### ## Configuration of the C/C++ macros @@ -124,6 +142,8 @@ add_definitions(-DCHECK_OBSERVERS_MESSAGES) endif() + + ##################################################################### ## Embed the colormaps into the binaries ##################################################################### @@ -200,10 +220,11 @@ ) if (ENABLE_SDL) list(APPEND APPLICATIONS_SOURCES - ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlStoneApplicationRunner.cpp + ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlCairoSurface.cpp ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlEngine.cpp - ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlCairoSurface.cpp + ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlOpenGLWindow.cpp ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlOrthancSurface.cpp + ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlStoneApplicationRunner.cpp ${ORTHANC_STONE_ROOT}/Applications/Sdl/SdlWindow.cpp ) endif() @@ -243,6 +264,32 @@ #${ORTHANC_STONE_ROOT}/Framework/Layers/SeriesFrameRendererFactory.cpp #${ORTHANC_STONE_ROOT}/Framework/Layers/SingleFrameRendererFactory.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/CairoCompositor.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/ColorTextureSceneLayer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/FloatTextureSceneLayer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/InfoPanelSceneLayer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoColorTextureRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoFloatTextureRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoInfoPanelRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoPolylineRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CairoTextRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/CompositorHelper.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/FixedPointAligner.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/PanSceneTracker.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/PointerEvent.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/PolylineSceneLayer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/RotateSceneTracker.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Scene2D.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/TextSceneLayer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/TextureBaseSceneLayer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/ZoomSceneTracker.cpp + + ${ORTHANC_STONE_ROOT}/Framework/Fonts/FontRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Fonts/Glyph.cpp + ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphAlphabet.cpp + ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphBitmapAlphabet.cpp + ${ORTHANC_STONE_ROOT}/Framework/Fonts/GlyphTextureAlphabet.cpp + ${ORTHANC_STONE_ROOT}/Framework/Fonts/TextBoundingBox.cpp ${ORTHANC_STONE_ROOT}/Framework/Layers/CircleMeasureTracker.cpp ${ORTHANC_STONE_ROOT}/Framework/Layers/ColorFrameRenderer.cpp ${ORTHANC_STONE_ROOT}/Framework/Layers/DicomSeriesVolumeSlicer.cpp @@ -279,6 +326,7 @@ ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DicomFrameConverter.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DicomStructureSet.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DownloadStack.cpp + ${ORTHANC_STONE_ROOT}/Framework/Toolbox/DynamicBitmap.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/Extent2D.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/FiniteProjectiveCamera.cpp ${ORTHANC_STONE_ROOT}/Framework/Toolbox/GeometryToolbox.cpp @@ -320,6 +368,7 @@ ${ORTHANC_STONE_ROOT}/Framework/Widgets/WidgetBase.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/WorldSceneWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Widgets/ZoomMouseTracker.cpp + ${ORTHANC_STONE_ROOT}/Framework/dev.h ${ORTHANC_STONE_ROOT}/Framework/Messages/ICallable.h @@ -343,6 +392,7 @@ # Mandatory components ${CAIRO_SOURCES} + ${FREETYPE_SOURCES} ${PIXMAN_SOURCES} # Optional components @@ -351,6 +401,29 @@ ${BOOST_EXTENDED_SOURCES} ) + +if (ENABLE_OPENGL) + list(APPEND ORTHANC_STONE_SOURCES + ${ORTHANC_STONE_ROOT}/Framework/Fonts/OpenGLTextCoordinates.cpp + ${ORTHANC_STONE_ROOT}/Framework/OpenGL/OpenGLProgram.cpp + ${ORTHANC_STONE_ROOT}/Framework/OpenGL/OpenGLShader.cpp + ${ORTHANC_STONE_ROOT}/Framework/OpenGL/OpenGLTexture.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/OpenGLCompositor.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLBasicPolylineRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLColorTextureProgram.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLColorTextureRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLFloatTextureProgram.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLInfoPanelRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLLinesProgram.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextProgram.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Scene2D/Internals/OpenGLTextureProgram.cpp + ) +endif() + + include_directories(${ORTHANC_STONE_ROOT})
--- a/Resources/CMake/OrthancStoneParameters.cmake Mon Apr 29 12:01:55 2019 +0200 +++ b/Resources/CMake/OrthancStoneParameters.cmake Mon Apr 29 15:24:59 2019 +0200 @@ -27,9 +27,10 @@ set(ENABLE_DCMTK OFF) set(ENABLE_GOOGLE_TEST ON) +set(ENABLE_JPEG ON) +set(ENABLE_OPENSSL_ENGINES ON) +set(ENABLE_PNG ON) set(ENABLE_SQLITE OFF) -set(ENABLE_JPEG ON) -set(ENABLE_PNG ON) set(ENABLE_ZLIB ON) set(HAS_EMBEDDED_RESOURCES ON) @@ -40,6 +41,7 @@ # Advanced parameters to fine-tune linking against system libraries set(USE_SYSTEM_CAIRO ON CACHE BOOL "Use the system version of Cairo") +set(USE_SYSTEM_FREETYPE ON CACHE BOOL "Use the system version of Freetype") set(USE_SYSTEM_PIXMAN ON CACHE BOOL "Use the system version of Pixman") set(USE_SYSTEM_SDL ON CACHE BOOL "Use the system version of SDL2") @@ -49,3 +51,4 @@ ## the Stone of Orthanc ##################################################################### +set(ENABLE_OPENGL ON CACHE INTERNAL "Enable support of OpenGL")
--- a/Resources/Orthanc/DownloadOrthancFramework.cmake Mon Apr 29 12:01:55 2019 +0200 +++ b/Resources/Orthanc/DownloadOrthancFramework.cmake Mon Apr 29 15:24:59 2019 +0200 @@ -95,6 +95,16 @@ set(ORTHANC_FRAMEWORK_MD5 "4429d8d9dea4ff6648df80ec3c64d79e") elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.1") set(ORTHANC_FRAMEWORK_MD5 "099671538865e5da96208b37494d6718") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.2") + set(ORTHANC_FRAMEWORK_MD5 "8867050f3e9a1ce6157c1ea7a9433b1b") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.3") + set(ORTHANC_FRAMEWORK_MD5 "bf2f5ed1adb8b0fc5f10d278e68e1dfe") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.4") + set(ORTHANC_FRAMEWORK_MD5 "404baef5d4c43e7c5d9410edda8ef5a5") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.5") + set(ORTHANC_FRAMEWORK_MD5 "cfc437e0687ae4bd725fd93dc1f08bc4") + elseif (ORTHANC_FRAMEWORK_VERSION STREQUAL "1.5.6") + set(ORTHANC_FRAMEWORK_MD5 "3c29de1e289b5472342947168f0105c0") endif() endif() endif()
--- a/Resources/Orthanc/LinuxStandardBaseToolchain.cmake Mon Apr 29 12:01:55 2019 +0200 +++ b/Resources/Orthanc/LinuxStandardBaseToolchain.cmake Mon Apr 29 15:24:59 2019 +0200 @@ -1,4 +1,4 @@ -# LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../Resources/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON +# LSB_CC=gcc-4.8 LSB_CXX=g++-4.8 cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_TOOLCHAIN_FILE=../Resources/LinuxStandardBaseToolchain.cmake -DUSE_LEGACY_JSONCPP=ON -DUSE_LEGACY_LIBICU=ON -DBOOST_LOCALE_BACKEND=icu INCLUDE(CMakeForceCompiler)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Sdl/BasicScene.cpp Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,371 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + **/ + + +// From Stone +#include "../../Applications/Sdl/SdlOpenGLWindow.h" +#include "../../Framework/Scene2D/CairoCompositor.h" +#include "../../Framework/Scene2D/OpenGLCompositor.h" +#include "../../Framework/Scene2D/Scene2D.h" +#include "../../Framework/Scene2D/PanSceneTracker.h" +#include "../../Framework/Scene2D/RotateSceneTracker.h" +#include "../../Framework/Scene2D/ZoomSceneTracker.h" +#include "../../Framework/Scene2D/ColorTextureSceneLayer.h" + +// From Orthanc framework +#include <Core/Logging.h> +#include <Core/OrthancException.h> +#include <Core/Images/Image.h> +#include <Core/Images/ImageProcessing.h> +#include <Core/Images/PngWriter.h> + +#include <SDL.h> +#include <stdio.h> + +static const unsigned int FONT_SIZE = 32; +static const int LAYER_POSITION = 150; + + +void PrepareScene(OrthancStone::Scene2D& scene) +{ + using namespace OrthancStone; + + // Texture of 2x2 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 2, 2, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + p[3] = 0; + p[4] = 255; + p[5] = 0; + + p = reinterpret_cast<uint8_t*>(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<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-3, 2); + l->SetPixelSpacing(1.5, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene.SetLayer(14, l.release()); + } + + // Texture of 1x1 size + { + Orthanc::Image i(Orthanc::PixelFormat_RGB24, 1, 1, false); + + uint8_t *p = reinterpret_cast<uint8_t*>(i.GetRow(0)); + p[0] = 255; + p[1] = 0; + p[2] = 0; + + std::auto_ptr<ColorTextureSceneLayer> l(new ColorTextureSceneLayer(i)); + l->SetOrigin(-2, 1); + l->SetAngle(20.0 / 180.0 * M_PI); + scene.SetLayer(13, l.release()); + } + + // Some lines + { + std::auto_ptr<PolylineSceneLayer> 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); + + 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); + + 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); + + layer->SetColor(0,255, 255); + scene.SetLayer(50, layer.release()); + } + + // Some text + { + std::auto_ptr<TextSceneLayer> layer(new TextSceneLayer); + layer->SetText("Hello"); + scene.SetLayer(100, layer.release()); + } +} + + +void TakeScreenshot(const std::string& target, + const OrthancStone::Scene2D& scene, + unsigned int canvasWidth, + unsigned int canvasHeight) +{ + // Take a screenshot, then save it as PNG file + OrthancStone::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(OrthancStone::Scene2D& scene, + const SDL_Event& event, + std::auto_ptr<OrthancStone::IPointerTracker>& activeTracker, + unsigned int windowWidth, + unsigned int windowHeight) +{ + bool hasPositionLayer = false; + + 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 + + OrthancStone::PointerEvent e; + e.AddIntegerPosition(event.button.x - static_cast<int>(windowWidth) / 2, + event.button.y - static_cast<int>(windowHeight) / 2); + OrthancStone::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)) + { + OrthancStone::TextSceneLayer& layer = + dynamic_cast<OrthancStone::TextSceneLayer&>(scene.GetLayer(LAYER_POSITION)); + layer.SetText(buf); + layer.SetPosition(p.GetX(), p.GetY()); + } + else + { + std::auto_ptr<OrthancStone::TextSceneLayer> layer(new OrthancStone::TextSceneLayer); + layer->SetColor(0, 255, 0); + layer->SetText(buf); + layer->SetBorder(20); + layer->SetAnchor(OrthancStone::BitmapAnchor_BottomCenter); + layer->SetPosition(p.GetX(), p.GetY()); + scene.SetLayer(LAYER_POSITION, layer.release()); + } + + hasPositionLayer = true; + } + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + OrthancStone::PointerEvent e; + e.AddIntegerPosition(event.button.x, event.button.y); + + switch (event.button.button) + { + case SDL_BUTTON_MIDDLE: + activeTracker.reset(new OrthancStone::PanSceneTracker(scene, e)); + break; + + case SDL_BUTTON_RIGHT: + activeTracker.reset(new OrthancStone::ZoomSceneTracker + (scene, e, windowWidth, windowHeight)); + break; + + case SDL_BUTTON_LEFT: + activeTracker.reset(new OrthancStone::RotateSceneTracker + (scene, e, windowWidth, windowHeight)); + break; + + default: + break; + } + } + else if (event.type == SDL_KEYDOWN && + event.key.repeat == 0 /* Ignore key bounce */) + { + switch (event.key.keysym.sym) + { + case SDLK_s: + scene.FitContent(windowWidth, windowHeight); + break; + + case SDLK_c: + TakeScreenshot("screenshot.png", scene, windowWidth, windowHeight); + break; + + default: + break; + } + } + + if (!hasPositionLayer) + { + scene.DeleteLayer(LAYER_POSITION); + } +} + + +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(OrthancStone::Scene2D& scene) +{ + OrthancStone::SdlOpenGLWindow window("Hello", 1024, 768); + scene.FitContent(window.GetCanvasWidth(), window.GetCanvasHeight()); + + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(OpenGLMessageCallback, 0 ); + + OrthancStone::OpenGLCompositor compositor(window, scene); + compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT, + FONT_SIZE, Orthanc::Encoding_Latin1); + + std::auto_ptr<OrthancStone::IPointerTracker> 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.get() != NULL) + { + OrthancStone::PointerEvent e; + e.AddIntegerPosition(event.button.x, event.button.y); + tracker->Update(e); + } + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + if (tracker.get() != NULL) + { + tracker->Release(); + tracker.reset(NULL); + } + } + else if (event.type == SDL_WINDOWEVENT && + event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + { + tracker.reset(NULL); + 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(scene, event, tracker, window.GetCanvasWidth(), window.GetCanvasHeight()); + } + + SDL_Delay(1); + } +} + + +int main() +{ + Orthanc::Logging::Initialize(); + OrthancStone::SdlWindow::GlobalInitialize(); + + try + { + OrthancStone::Scene2D scene; + PrepareScene(scene); + Run(scene); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + } + + OrthancStone::SdlWindow::GlobalFinalize(); + Orthanc::Logging::Finalize(); + + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/Sdl/CMakeLists.txt Mon Apr 29 15:24:59 2019 +0200 @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 2.8.3) + +if(MSVC) + add_definitions(/MP) + if (CMAKE_BUILD_TYPE MATCHES DEBUG) + add_definitions(/JMC) + endif() +endif() + +##################################################################### +## 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_SDL ON) +SET(ENABLE_WEB_CLIENT ON) +SET(ORTHANC_SANDBOXED OFF) +LIST(APPEND ORTHANC_BOOST_COMPONENTS program_options) + +include(${CMAKE_SOURCE_DIR}/../../Resources/CMake/OrthancStoneConfiguration.cmake) + +include_directories(${ORTHANC_STONE_ROOT}) + + +##################################################################### +## Build the samples +##################################################################### + +add_library(OrthancStone STATIC + ${ORTHANC_STONE_SOURCES} + ) + +add_executable(BasicScene + BasicScene.cpp + ) + +target_link_libraries(BasicScene OrthancStone)