Mercurial > hg > orthanc-stone
changeset 2208:b36f7b6eec39 dicom-sr
integration mainline->dicom-sr
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 22 Apr 2025 14:29:53 +0200 (6 weeks ago) |
parents | 02cee9f4c147 (current diff) dcfabb36dc21 (diff) |
children | 9b82533263f1 |
files | Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp |
diffstat | 8 files changed, 535 insertions(+), 367 deletions(-) [+] |
line wrap: on
line diff
--- a/Applications/Samples/Sdl/CMakeLists.txt Tue Apr 22 14:03:14 2025 +0200 +++ b/Applications/Samples/Sdl/CMakeLists.txt Tue Apr 22 14:29:53 2025 +0200 @@ -90,12 +90,8 @@ add_executable(RtViewerSdl RtViewer/RtViewerSdl.cpp - SdlHelpers.h ../Common/RtViewerApp.cpp - ../Common/RtViewerApp.h ../Common/RtViewerView.cpp - ../Common/RtViewerView.h - ../Common/SampleHelpers.h ) if (COMMAND DefineSourceBasenameForTarget) @@ -108,9 +104,6 @@ project(SdlSimpleViewer) add_executable(SdlSimpleViewer - SdlHelpers.h - ../Common/SampleHelpers.h - SingleFrameViewer/SdlSimpleViewerApplication.h SingleFrameViewer/SdlSimpleViewer.cpp )
--- a/Applications/Samples/WebAssembly/CMakeLists.txt Tue Apr 22 14:03:14 2025 +0200 +++ b/Applications/Samples/WebAssembly/CMakeLists.txt Tue Apr 22 14:29:53 2025 +0200 @@ -104,9 +104,7 @@ add_executable(RtViewerWasm RtViewer/RtViewerWasm.cpp ../Common/RtViewerApp.cpp - ../Common/RtViewerApp.h ../Common/RtViewerView.cpp - ../Common/RtViewerView.h ) if (COMMAND DefineSourceBasenameForTarget)
--- a/Applications/StoneWebViewer/WebApplication/app.js Tue Apr 22 14:03:14 2025 +0200 +++ b/Applications/StoneWebViewer/WebApplication/app.js Tue Apr 22 14:29:53 2025 +0200 @@ -1778,6 +1778,23 @@ document.onselectstart = new Function ('return false'); + +function CallLoadOsirixAnnotations(xml, clear) { + /** + * XML files from OsiriX can be very large, so it is not possible to + * send them to WebAssembly as a "string" parameter, otherwise it + * may result in a stack overflow. We thus have to copy the + * JavaScript string into the WebAssembly heap. + * https://thewasmfrontier.hashnode.dev/working-with-string-in-webassembly + **/ + var length = Module.lengthBytesUTF8(xml); + var pointer = stone.Allocate(length + 1); + Module.stringToUTF8(xml, pointer, length + 1); + stone.LoadOsiriXAnnotations(pointer, length + 1, clear); + stone.Deallocate(pointer); +} + + window.addEventListener('message', function(e) { if ('type' in e.data) { if (e.data.type == 'show-osirix-annotations') { @@ -1797,12 +1814,21 @@ if ('clear' in e.data) { clear = e.data.clear; } - - app.LoadOsiriXAnnotations(e.data.xml, clear); + + if ('xml' in e.data) { + CallLoadOsirixAnnotations(e.data.xml, clear); + } else if ('url' in e.data) { + axios.get(e.data.url) + .then(function(response) { + CallLoadOsirixAnnotations(response.data, clear); + }); + } else { + console.error('Neither "xml", nor "url" was provided to load OsiriX annotations from'); + } } } else { - console.log('Unknown type of dynamic action in the Stone Web viewer: ' + e.data.type); + console.error('Unknown type of dynamic action in the Stone Web viewer: ' + e.data.type); } } });
--- a/Applications/StoneWebViewer/WebAssembly/CMakeLists.txt Tue Apr 22 14:03:14 2025 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/CMakeLists.txt Tue Apr 22 14:29:53 2025 +0200 @@ -36,7 +36,7 @@ set(WASM_FLAGS "${WASM_FLAGS} -s SAFE_HEAP=1") endif() -set(WASM_LINKER_FLAGS "${WASM_LINKER_FLAGS} -s EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\"]'") +set(WASM_LINKER_FLAGS "${WASM_LINKER_FLAGS} -s EXPORTED_RUNTIME_METHODS='[\"ccall\", \"cwrap\", \"lengthBytesUTF8\", \"stringToUTF8\"]'") set(WASM_LINKER_FLAGS "${WASM_LINKER_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") set(WASM_LINKER_FLAGS "${WASM_LINKER_FLAGS} -s ALLOW_MEMORY_GROWTH=1 -s TOTAL_MEMORY=268435456") # 256MB + resize set(WASM_LINKER_FLAGS "${WASM_LINKER_FLAGS} -s DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR=1")
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Applications/StoneWebViewer/WebAssembly/IStoneWebViewerContext.h Tue Apr 22 14:29:53 2025 +0200 @@ -0,0 +1,106 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2025 Sebastien Jodogne, ICTEAM UCLouvain, 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 "../../../OrthancStone/Sources/Scene2D/ISceneLayer.h" +#include "../../../OrthancStone/Sources/Toolbox/DicomInstanceParameters.h" + +#include <Images/ImageAccessor.h> + + +#define DISPATCH_JAVASCRIPT_EVENT(name) \ + EM_ASM( \ + const customEvent = document.createEvent("CustomEvent"); \ + customEvent.initCustomEvent(name, false, false, undefined); \ + window.dispatchEvent(customEvent); \ + ); + +#define EXTERN_CATCH_EXCEPTIONS \ + catch (Orthanc::OrthancException& e) \ + { \ + LOG(ERROR) << "OrthancException: " << e.What(); \ + DISPATCH_JAVASCRIPT_EVENT("StoneException"); \ + } \ + catch (OrthancStone::StoneException& e) \ + { \ + LOG(ERROR) << "StoneException: " << e.What(); \ + DISPATCH_JAVASCRIPT_EVENT("StoneException"); \ + } \ + catch (std::exception& e) \ + { \ + LOG(ERROR) << "Runtime error: " << e.what(); \ + DISPATCH_JAVASCRIPT_EVENT("StoneException"); \ + } \ + catch (...) \ + { \ + LOG(ERROR) << "Native exception"; \ + DISPATCH_JAVASCRIPT_EVENT("StoneException"); \ + } + + +// WARNING: This class can be shared by multiple viewports +class ILayerSource : public boost::noncopyable +{ +public: + virtual ~ILayerSource() + { + } + + virtual int GetDepth() const = 0; + + virtual OrthancStone::ISceneLayer* Create(const Orthanc::ImageAccessor& frame, + const OrthancStone::DicomInstanceParameters& instance, + unsigned int frameNumber, + double pixelSpacingX, + double pixelSpacingY, + const OrthancStone::CoordinateSystem3D& plane) = 0; +}; + + +class IStoneWebViewerContext : public boost::noncopyable +{ +public: + virtual ~IStoneWebViewerContext() + { + } + + virtual void RedrawAllViewports() = 0; + + // WARNING: The ImageAccessor will become invalid once leaving the + // JavaScript callback, do not keep a reference! + virtual bool GetSelectedFrame(Orthanc::ImageAccessor& target /* out */, + std::string& sopInstanceUid /* out */, + unsigned int& frameNumber /* out */, + const std::string& canvas /* in */) = 0; +}; + + +class IStoneWebViewerPlugin : public boost::noncopyable +{ +public: + virtual ~IStoneWebViewerPlugin() + { + } + + virtual ILayerSource& GetLayerSource() = 0; +};
--- a/Applications/StoneWebViewer/WebAssembly/ParseWebAssemblyExports.py Tue Apr 22 14:03:14 2025 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/ParseWebAssemblyExports.py Tue Apr 22 14:29:53 2025 +0200 @@ -45,8 +45,8 @@ parser.add_argument('--libclang', default = '', help = 'manually provides the path to the libclang shared library') -parser.add_argument('source', - help = 'Input C++ file') +parser.add_argument('sources', nargs='+', + help = 'Input C++ files') args = parser.parse_args() @@ -57,13 +57,6 @@ index = clang.cindex.Index.create() -# PARSE_SKIP_FUNCTION_BODIES prevents clang from failing because of -# undefined types, which prevents compilation of functions -tu = index.parse(args.source, - [ '-DEMSCRIPTEN_KEEPALIVE=__attribute__((annotate("WebAssembly")))', - '-DSTONE_WEB_VIEWER_EXPORT=__attribute__((annotate("WebAssembly")))'], - options = clang.cindex.TranslationUnit.PARSE_SKIP_FUNCTION_BODIES) - TEMPLATE = ''' @@ -160,6 +153,9 @@ elif returnType in [ 'int', 'unsigned int' ]: f['hasReturn'] = True f['returnType'] = "'int'" + elif returnType == 'void *': + f['hasReturn'] = True + f['returnType'] = "'int'" else: raise Exception('Unknown return type in function "%s()": %s' % (node.spelling, returnType)) @@ -176,6 +172,10 @@ arg['type'] = "'string'" elif argType == 'double': arg['type'] = "'double'" + elif argType == 'size_t': + arg['type'] = "'int'" + elif argType in [ 'const void *', 'void *' ]: + arg['type'] = "'int'" else: raise Exception('Unknown type for argument "%s" in function "%s()": %s' % (child.displayname, node.spelling, argType)) @@ -190,8 +190,15 @@ for child in node.get_children(): Explore(child) -Explore(tu.cursor) +for source in args.sources: + # PARSE_SKIP_FUNCTION_BODIES prevents clang from failing because of + # undefined types, which prevents compilation of functions + tu = index.parse(source, + [ '-DEMSCRIPTEN_KEEPALIVE=__attribute__((annotate("WebAssembly")))', + '-DSTONE_WEB_VIEWER_EXPORT=__attribute__((annotate("WebAssembly")))'], + options = clang.cindex.TranslationUnit.PARSE_SKIP_FUNCTION_BODIES) + Explore(tu.cursor) print(pystache.render(TEMPLATE, {
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Tue Apr 22 14:03:14 2025 +0200 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Tue Apr 22 14:29:53 2025 +0200 @@ -20,41 +20,12 @@ **/ +#include "IStoneWebViewerContext.h" + #include <EmbeddedResources.h> #include <emscripten.h> -#define DISPATCH_JAVASCRIPT_EVENT(name) \ - EM_ASM( \ - const customEvent = document.createEvent("CustomEvent"); \ - customEvent.initCustomEvent(name, false, false, undefined); \ - window.dispatchEvent(customEvent); \ - ); - - -#define EXTERN_CATCH_EXCEPTIONS \ - catch (Orthanc::OrthancException& e) \ - { \ - LOG(ERROR) << "OrthancException: " << e.What(); \ - DISPATCH_JAVASCRIPT_EVENT("StoneException"); \ - } \ - catch (OrthancStone::StoneException& e) \ - { \ - LOG(ERROR) << "StoneException: " << e.What(); \ - DISPATCH_JAVASCRIPT_EVENT("StoneException"); \ - } \ - catch (std::exception& e) \ - { \ - LOG(ERROR) << "Runtime error: " << e.what(); \ - DISPATCH_JAVASCRIPT_EVENT("StoneException"); \ - } \ - catch (...) \ - { \ - LOG(ERROR) << "Native exception"; \ - DISPATCH_JAVASCRIPT_EVENT("StoneException"); \ - } - - // Orthanc framework includes #include <Cache/MemoryObjectCache.h> #include <DicomFormat/DicomArray.h> @@ -84,10 +55,7 @@ #include "../../../OrthancStone/Sources/Toolbox/DicomInstanceParameters.h" #include "../../../OrthancStone/Sources/Toolbox/DicomStructuredReport.h" #include "../../../OrthancStone/Sources/Toolbox/GeometryToolbox.h" -#include "../../../OrthancStone/Sources/Toolbox/OsiriX/AngleAnnotation.h" #include "../../../OrthancStone/Sources/Toolbox/OsiriX/CollectionOfAnnotations.h" -#include "../../../OrthancStone/Sources/Toolbox/OsiriX/LineAnnotation.h" -#include "../../../OrthancStone/Sources/Toolbox/OsiriX/TextAnnotation.h" #include "../../../OrthancStone/Sources/Toolbox/SortedFrames.h" #include "../../../OrthancStone/Sources/Viewport/DefaultViewportInteractor.h" @@ -105,6 +73,15 @@ static const double PI = boost::math::constants::pi<double>(); +static const int LAYER_TEXTURE = 0; +static const int LAYER_OVERLAY = 1; +static const int LAYER_ORIENTATION_MARKERS = 2; +static const int LAYER_REFERENCE_LINES = 3; +static const int LAYER_ANNOTATIONS_STONE = 5; +static const int LAYER_ANNOTATIONS_OSIRIX = 4; +static const int LAYER_STRUCTURED_REPORT = 6; + + #if !defined(STONE_WEB_VIEWER_EXPORT) // We are not running ParseWebAssemblyExports.py, but we're compiling the wasm # define STONE_WEB_VIEWER_EXPORT @@ -434,7 +411,7 @@ return *frames_[index]; } } - + public: DicomStructuredReportFrames(const OrthancStone::DicomStructuredReport& sr, const OrthancStone::LoadedDicomResources& instances) : @@ -518,7 +495,7 @@ frameIndex = i; } } - + return found; } @@ -1304,7 +1281,7 @@ return NULL; } } - + std::unique_ptr<OrthancStone::SortedFrames> target(new OrthancStone::SortedFrames); target->Clear(); @@ -1469,7 +1446,7 @@ static std::string GetKey(const std::string& sopInstanceUid, - size_t frameNumber) + unsigned int frameNumber) { return sopInstanceUid + "|" + boost::lexical_cast<std::string>(frameNumber); } @@ -1499,7 +1476,7 @@ * previously cached one, or if no cache was previously available. **/ bool Acquire(const std::string& sopInstanceUid, - size_t frameNumber, + unsigned int frameNumber, Orthanc::ImageAccessor* image /* transfer ownership */, unsigned int quality) { @@ -1577,7 +1554,7 @@ public: Accessor(FramesCache& that, const std::string& sopInstanceUid, - size_t frameNumber) : + unsigned int frameNumber) : accessor_(that.cache_, GetKey(sopInstanceUid, frameNumber), false /* shared lock */) { } @@ -2188,6 +2165,65 @@ +class LayersHolder : public boost::noncopyable +{ +private: + typedef std::map<int, OrthancStone::ISceneLayer*> Layers; + + Layers layers_; + +public: + ~LayersHolder() + { + for (Layers::iterator it = layers_.begin(); it != layers_.end(); ++it) + { + if (it->second != NULL) + { + delete it->second; + } + } + } + + void AddLayer(int depth, + OrthancStone::ISceneLayer* layer) + { + std::unique_ptr<OrthancStone::ISceneLayer> protection(layer); + + if (layers_.find(depth) != layers_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "Two layer sources are writing to the same depth: " + + boost::lexical_cast<std::string>(depth)); + } + else if (layer == NULL) + { + layers_[depth] = NULL; + } + else + { + layers_[depth] = protection.release(); + } + } + + void Commit(OrthancStone::Scene2D& scene) + { + for (Layers::iterator it = layers_.begin(); it != layers_.end(); ++it) + { + if (it->second == NULL) + { + scene.DeleteLayer(it->first); + } + else + { + std::unique_ptr<OrthancStone::ISceneLayer> layer(it->second); + it->second = NULL; + scene.SetLayer(it->first, layer.release()); + } + } + } +}; + + + class ViewerViewport : public OrthancStone::ObserverBase<ViewerViewport> { public: @@ -2233,15 +2269,6 @@ }; private: - static const int LAYER_TEXTURE = 0; - static const int LAYER_OVERLAY = 1; - static const int LAYER_ORIENTATION_MARKERS = 2; - static const int LAYER_REFERENCE_LINES = 3; - static const int LAYER_ANNOTATIONS_OSIRIX = 4; - static const int LAYER_ANNOTATIONS_STONE = 5; - static const int LAYER_STRUCTURED_REPORT = 6; - - class ICommand : public Orthanc::IDynamicObject { private: @@ -2304,22 +2331,24 @@ OrthancStone::DicomInstanceParameters params(dicom); params.EnrichUsingDicomWeb(message.GetResources()->GetSourceJson(0)); - GetViewport().centralPixelSpacingX_ = params.GetPixelSpacingX(); - GetViewport().centralPixelSpacingY_ = params.GetPixelSpacingY(); if (params.HasPixelSpacing()) { + GetViewport().centralPixelSpacingX_ = params.GetPixelSpacingX(); + GetViewport().centralPixelSpacingY_ = params.GetPixelSpacingY(); GetViewport().stoneAnnotations_->SetUnits(OrthancStone::Units_Millimeters); } else { + GetViewport().centralPixelSpacingX_ = 1; + GetViewport().centralPixelSpacingY_ = 1; GetViewport().stoneAnnotations_->SetUnits(OrthancStone::Units_Pixels); } - if (params.GetPixelSpacingX() != 0 && - params.GetPixelSpacingY() != 0 && + if (!OrthancStone::LinearAlgebra::IsNear(params.GetPixelSpacingX(), 0) && + !OrthancStone::LinearAlgebra::IsNear(params.GetPixelSpacingY(), 0) && params.GetWidth() != 0 && - params.GetHeight()) + params.GetHeight() != 0) { GetViewport().centralPhysicalWidth_ = (params.GetPixelSpacingX() * static_cast<double>(params.GetWidth())); @@ -2633,20 +2662,22 @@ bool hasFocusOnInstance_; std::string focusSopInstanceUid_; - size_t focusFrameNumber_; - - // The coordinates of OsiriX annotations are expressed in 3D world coordinates - boost::shared_ptr<OrthancStone::OsiriX::CollectionOfAnnotations> osiriXAnnotations_; + unsigned int focusFrameNumber_; // The coordinates of Stone annotations are expressed in 2D // coordinates of the current texture, with (0,0) corresponding to // the center of the top-left pixel boost::shared_ptr<OrthancStone::AnnotationsSceneLayer> stoneAnnotations_; + bool linearInterpolation_; std::string pendingSeriesInstanceUid_; + // WARNING: The ownership is not transferred + std::list<ILayerSource*> layerSources_; + + void UpdateWindowing(WindowingState state, const OrthancStone::Windowing& windowing) { @@ -2910,81 +2941,16 @@ layer->SetPixelSpacing(pixelSpacingX, pixelSpacingY); } - std::unique_ptr<OrthancStone::LookupTableTextureSceneLayer> overlay; - - { - OverlaysRegistry::Accessor accessor(OverlaysRegistry::GetInstance(), instance.GetSopInstanceUid()); - if (accessor.IsValid()) - { - overlay.reset(accessor.CreateTexture()); - overlay->SetLinearInterpolation(false); - } - } - - std::unique_ptr<OrthancStone::MacroSceneLayer> annotationsOsiriX; - - if (osiriXAnnotations_) - { - std::set<size_t> a; - osiriXAnnotations_->LookupSopInstanceUid(a, instance.GetSopInstanceUid()); - if (plane.IsValid() && - !a.empty()) - { - annotationsOsiriX.reset(new OrthancStone::MacroSceneLayer); - // annotationsOsiriX->Reserve(a.size()); - - OrthancStone::OsiriXLayerFactory factory; - factory.SetColor(0, 255, 0); - - for (std::set<size_t>::const_iterator it = a.begin(); it != a.end(); ++it) - { - const OrthancStone::OsiriX::Annotation& annotation = osiriXAnnotations_->GetAnnotation(*it); - annotationsOsiriX->AddLayer(factory.Create(annotation, plane)); - } - } - } - StoneAnnotationsRegistry::GetInstance().Load(*stoneAnnotations_, instance.GetSopInstanceUid(), frameIndex); - // Orientation markers, new in Stone Web viewer 2.4 - std::unique_ptr<OrthancStone::MacroSceneLayer> orientationMarkers; - - if (instance.GetGeometry().IsValid()) - { - orientationMarkers.reset(new OrthancStone::MacroSceneLayer); - - std::string top, bottom, left, right; - instance.GetGeometry().GetOrientationMarkers(top, bottom, left, right); - - std::unique_ptr<OrthancStone::TextSceneLayer> text; - - text.reset(new OrthancStone::TextSceneLayer); - text->SetText(top); - text->SetPosition(pixelSpacingX * static_cast<double>(frame.GetWidth()) / 2.0, 0); - text->SetAnchor(OrthancStone::BitmapAnchor_TopCenter); - orientationMarkers->AddLayer(text.release()); - - text.reset(new OrthancStone::TextSceneLayer); - text->SetText(bottom); - text->SetPosition(pixelSpacingX * static_cast<double>(frame.GetWidth()) / 2.0, - pixelSpacingY * static_cast<double>(frame.GetHeight())); - text->SetAnchor(OrthancStone::BitmapAnchor_BottomCenter); - orientationMarkers->AddLayer(text.release()); - - text.reset(new OrthancStone::TextSceneLayer); - text->SetText(left); - text->SetPosition(0, pixelSpacingY * static_cast<double>(frame.GetHeight()) / 2.0); - text->SetAnchor(OrthancStone::BitmapAnchor_CenterLeft); - orientationMarkers->AddLayer(text.release()); - - text.reset(new OrthancStone::TextSceneLayer); - text->SetText(right); - text->SetPosition(pixelSpacingX * static_cast<double>(frame.GetWidth()), - pixelSpacingY * static_cast<double>(frame.GetHeight()) / 2.0); - text->SetAnchor(OrthancStone::BitmapAnchor_CenterRight); - orientationMarkers->AddLayer(text.release()); - } - + LayersHolder holder; + + for (std::list<ILayerSource*>::const_iterator it = layerSources_.begin(); it != layerSources_.end(); ++it) + { + assert(*it != NULL); + holder.AddLayer((*it)->GetDepth(), (*it)->Create(frame, instance, frameIndex, + layer->GetPixelSpacingX(), layer->GetPixelSpacingY(), plane)); + } std::unique_ptr<OrthancStone::ISceneLayer> structuredReportAnnotations; @@ -2992,63 +2958,19 @@ frames_->GetType() == FramesCollectionType_DicomSR) { const DicomStructuredReportFrames& sr = dynamic_cast<const DicomStructuredReportFrames&>(*frames_); - structuredReportAnnotations.reset(sr.ExtractAnnotations(instance.GetSopInstanceUid(), frameIndex, - layer->GetOriginX(), layer->GetOriginY(), - layer->GetPixelSpacingX(), layer->GetPixelSpacingY())); - } - + holder.AddLayer(LAYER_STRUCTURED_REPORT, sr.ExtractAnnotations(instance.GetSopInstanceUid(), frameIndex, + layer->GetOriginX(), layer->GetOriginY(), + layer->GetPixelSpacingX(), layer->GetPixelSpacingY())); + } + + holder.AddLayer(LAYER_TEXTURE, layer.release()); { std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); OrthancStone::Scene2D& scene = lock->GetController().GetScene(); - scene.SetLayer(LAYER_TEXTURE, layer.release()); - - if (overlay.get() != NULL) - { - scene.SetLayer(LAYER_OVERLAY, overlay.release()); - } - else - { - scene.DeleteLayer(LAYER_OVERLAY); - } - - if (annotationsOsiriX.get() != NULL) - { - scene.SetLayer(LAYER_ANNOTATIONS_OSIRIX, annotationsOsiriX.release()); - } - else - { - scene.DeleteLayer(LAYER_ANNOTATIONS_OSIRIX); - } - - if (orientationMarkers.get() != NULL) - { - scene.SetLayer(LAYER_ORIENTATION_MARKERS, orientationMarkers.release()); - } - else - { - scene.DeleteLayer(LAYER_ORIENTATION_MARKERS); - } - - if (orientationMarkers.get() != NULL) - { - scene.SetLayer(LAYER_ORIENTATION_MARKERS, orientationMarkers.release()); - } - else - { - scene.DeleteLayer(LAYER_ORIENTATION_MARKERS); - } - - if (structuredReportAnnotations.get() != NULL) - { - scene.SetLayer(LAYER_STRUCTURED_REPORT, structuredReportAnnotations.release()); - } - else - { - scene.DeleteLayer(LAYER_STRUCTURED_REPORT); - } + holder.Commit(scene); stoneAnnotations_->Render(scene); // Necessary for "FitContent()" to work @@ -3076,7 +2998,7 @@ { const size_t cursorIndex = cursor_->GetCurrentIndex(); const OrthancStone::DicomInstanceParameters& instance = frames_->GetInstanceOfFrame(cursorIndex); - const size_t frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); + const unsigned int frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); // Only change the scene if the loaded frame still corresponds to the current cursor if (instance.GetSopInstanceUid() == loadedSopInstanceUid && @@ -3377,7 +3299,7 @@ { const size_t cursorIndex = cursor_->GetCurrentIndex(); const OrthancStone::DicomInstanceParameters& instance = frames_->GetInstanceOfFrame(cursorIndex); - const size_t frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); + const unsigned int frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); StoneAnnotationsRegistry::GetInstance().Save(instance.GetSopInstanceUid(), frameNumber, *stoneAnnotations_); @@ -3482,6 +3404,13 @@ return viewport; } + + void AddLayerSource(ILayerSource& source) // Ownership is not transfered + { + layerSources_.push_back(&source); + } + + void SetFrames(IFramesCollection* frames) { if (frames == NULL) @@ -3609,7 +3538,7 @@ const size_t cursorIndex = cursor_->GetCurrentIndex(); const OrthancStone::DicomInstanceParameters& instance = frames_->GetInstanceOfFrame(cursorIndex); - const size_t frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); + const unsigned int frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); FramesCache::Accessor accessor(*framesCache_, instance.GetSopInstanceUid(), frameNumber); if (accessor.IsValid()) @@ -4057,11 +3986,6 @@ viewport_->FitForPrint(); } - void SetOsiriXAnnotations(boost::shared_ptr<OrthancStone::OsiriX::CollectionOfAnnotations> annotations) - { - osiriXAnnotations_ = annotations; - } - void ScheduleFrameFocus(const std::string& sopInstanceUid, unsigned int frameNumber) { @@ -4156,7 +4080,7 @@ { const size_t cursorIndex = cursor_->GetCurrentIndex(); const OrthancStone::DicomInstanceParameters& instance = frames_->GetInstanceOfFrame(cursorIndex); - const size_t frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); + const unsigned int frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); if (instance.GetSopInstanceUid() == sopInstanceUid && frameNumber == frame) @@ -4190,6 +4114,25 @@ } + bool GetCurrentFrame(std::string& sopInstanceUid /* out */, + unsigned int& frameNumber /* out */) const + { + if (cursor_.get() != NULL && + frames_.get() != NULL) + { + const size_t cursorIndex = cursor_->GetCurrentIndex(); + const OrthancStone::DicomInstanceParameters& instance = frames_->GetInstanceOfFrame(cursorIndex); + sopInstanceUid = instance.GetSopInstanceUid(); + frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); + return true; + } + else + { + return false; + } + } + + void SignalSynchronizedBrowsing() { if (synchronizationEnabled_ && @@ -4234,11 +4177,156 @@ +class OrientationMarkersSource : public ILayerSource +{ +public: + virtual int GetDepth() const ORTHANC_OVERRIDE + { + return LAYER_ORIENTATION_MARKERS; + } + + virtual OrthancStone::ISceneLayer* Create(const Orthanc::ImageAccessor& frame, + const OrthancStone::DicomInstanceParameters& instance, + unsigned int frameNumber, + double pixelSpacingX, + double pixelSpacingY, + const OrthancStone::CoordinateSystem3D& plane) ORTHANC_OVERRIDE + { + if (instance.GetGeometry().IsValid()) + { + std::unique_ptr<OrthancStone::MacroSceneLayer> layer(new OrthancStone::MacroSceneLayer); + + std::string top, bottom, left, right; + instance.GetGeometry().GetOrientationMarkers(top, bottom, left, right); + + std::unique_ptr<OrthancStone::TextSceneLayer> text; + + text.reset(new OrthancStone::TextSceneLayer); + text->SetText(top); + text->SetPosition(pixelSpacingX * static_cast<double>(frame.GetWidth()) / 2.0, 0); + text->SetAnchor(OrthancStone::BitmapAnchor_TopCenter); + layer->AddLayer(text.release()); + + text.reset(new OrthancStone::TextSceneLayer); + text->SetText(bottom); + text->SetPosition(pixelSpacingX * static_cast<double>(frame.GetWidth()) / 2.0, + pixelSpacingY * static_cast<double>(frame.GetHeight())); + text->SetAnchor(OrthancStone::BitmapAnchor_BottomCenter); + layer->AddLayer(text.release()); + + text.reset(new OrthancStone::TextSceneLayer); + text->SetText(left); + text->SetPosition(0, pixelSpacingY * static_cast<double>(frame.GetHeight()) / 2.0); + text->SetAnchor(OrthancStone::BitmapAnchor_CenterLeft); + layer->AddLayer(text.release()); + + text.reset(new OrthancStone::TextSceneLayer); + text->SetText(right); + text->SetPosition(pixelSpacingX * static_cast<double>(frame.GetWidth()), + pixelSpacingY * static_cast<double>(frame.GetHeight()) / 2.0); + text->SetAnchor(OrthancStone::BitmapAnchor_CenterRight); + layer->AddLayer(text.release()); + + return layer.release(); + } + else + { + return NULL; + } + } +}; + + +class OsiriXLayerSource : public ILayerSource +{ +private: + // The coordinates of OsiriX annotations are expressed in 3D world coordinates + OrthancStone::OsiriX::CollectionOfAnnotations annotations_; + +public: + OrthancStone::OsiriX::CollectionOfAnnotations& GetAnnotations() + { + return annotations_; + } + + virtual int GetDepth() const ORTHANC_OVERRIDE + { + return LAYER_ANNOTATIONS_OSIRIX; + } + + virtual OrthancStone::ISceneLayer* Create(const Orthanc::ImageAccessor& frame, + const OrthancStone::DicomInstanceParameters& instance, + unsigned int frameNumber, + double pixelSpacingX, + double pixelSpacingY, + const OrthancStone::CoordinateSystem3D& plane) ORTHANC_OVERRIDE + { + std::set<size_t> a; + annotations_.LookupSopInstanceUid(a, instance.GetSopInstanceUid()); + if (plane.IsValid() && + !a.empty()) + { + std::unique_ptr<OrthancStone::MacroSceneLayer> layer(new OrthancStone::MacroSceneLayer); + // layer->Reserve(a.size()); + + OrthancStone::OsiriXLayerFactory factory; + factory.SetColor(0, 255, 0); + + for (std::set<size_t>::const_iterator it = a.begin(); it != a.end(); ++it) + { + const OrthancStone::OsiriX::Annotation& annotation = annotations_.GetAnnotation(*it); + layer->AddLayer(factory.Create(annotation, plane)); + } + + return layer.release(); + } + + return NULL; + } +}; + + + +class OverlayLayerSource : public ILayerSource +{ +public: + virtual int GetDepth() const ORTHANC_OVERRIDE + { + return LAYER_OVERLAY; + } + + virtual OrthancStone::ISceneLayer* Create(const Orthanc::ImageAccessor& frame, + const OrthancStone::DicomInstanceParameters& instance, + unsigned int frameNumber, + double pixelSpacingX, + double pixelSpacingY, + const OrthancStone::CoordinateSystem3D& plane) ORTHANC_OVERRIDE + { + OverlaysRegistry::Accessor accessor(OverlaysRegistry::GetInstance(), instance.GetSopInstanceUid()); + if (accessor.IsValid()) + { + std::unique_ptr<OrthancStone::LookupTableTextureSceneLayer> layer(accessor.CreateTexture()); + layer->SetLinearInterpolation(false); + return layer.release(); + } + else + { + return NULL; + } + } +}; + + + typedef std::map<std::string, boost::shared_ptr<ViewerViewport> > Viewports; static Viewports allViewports_; static bool showReferenceLines_ = true; -static boost::shared_ptr<OrthancStone::OsiriX::CollectionOfAnnotations> osiriXAnnotations_; +static std::unique_ptr<OverlayLayerSource> overlayLayerSource_; +static std::unique_ptr<OsiriXLayerSource> osiriXLayerSource_; + +// Orientation markers, new in Stone Web viewer 2.4 +static std::unique_ptr<OrientationMarkersSource> orientationMarkersSource_; static void UpdateReferenceLines() @@ -4563,6 +4651,15 @@ } +typedef IStoneWebViewerPlugin* (*PluginInitializer) (IStoneWebViewerContext&); + +static const PluginInitializer pluginsInitializers_[] = { + NULL +}; + +static std::list< boost::shared_ptr<IStoneWebViewerPlugin> > plugins_; + + static boost::shared_ptr<ViewerViewport> GetViewport(const std::string& canvas) { Viewports::iterator found = allViewports_.find(canvas); @@ -4572,7 +4669,15 @@ ViewerViewport::Create(*context_, source_, canvas, framesCache_, instancesCache_, softwareRendering_, linearInterpolation_)); viewport->SetMouseButtonActions(leftButtonAction_, middleButtonAction_, rightButtonAction_); viewport->AcquireObserver(new WebAssemblyObserver); - viewport->SetOsiriXAnnotations(osiriXAnnotations_); + viewport->AddLayerSource(*overlayLayerSource_); + viewport->AddLayerSource(*osiriXLayerSource_); + viewport->AddLayerSource(*orientationMarkersSource_); + + for (std::list< boost::shared_ptr<IStoneWebViewerPlugin> >::iterator it = plugins_.begin(); it != plugins_.end(); ++it) + { + viewport->AddLayerSource((*it)->GetLayerSource()); + } + allViewports_[canvas] = viewport; return viewport; } @@ -4604,6 +4709,53 @@ } +class StoneWebViewerContext : public IStoneWebViewerContext +{ +public: + static StoneWebViewerContext& GetInstance() + { + static StoneWebViewerContext instance; + return instance; + } + + virtual void RedrawAllViewports() ORTHANC_OVERRIDE + { + for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it) + { + assert(it->second != NULL); + it->second->Redraw(); + } + } + + virtual bool GetSelectedFrame(Orthanc::ImageAccessor& target /* out */, + std::string& sopInstanceUid /* out */, + unsigned int& frameNumber /* out */, + const std::string& canvas /* in */) ORTHANC_OVERRIDE + { + boost::shared_ptr<ViewerViewport> viewport = GetViewport(canvas); + + if (viewport->GetCurrentFrame(sopInstanceUid, frameNumber)) + { + FramesCache::Accessor accessor(*framesCache_, sopInstanceUid, frameNumber); + if (accessor.IsValid()) + { + accessor.GetImage().GetReadOnlyAccessor(target); + return true; + } + else + { + return false; + } + } + else + { + LOG(WARNING) << "No active frame"; + return false; + } + } +}; + + extern "C" { int main(int argc, char const *argv[]) @@ -4620,7 +4772,18 @@ framesCache_.reset(new FramesCache); instancesCache_.reset(new InstancesCache); - osiriXAnnotations_.reset(new OrthancStone::OsiriX::CollectionOfAnnotations); + overlayLayerSource_.reset(new OverlayLayerSource); + osiriXLayerSource_.reset(new OsiriXLayerSource); + orientationMarkersSource_.reset(new OrientationMarkersSource); + + for (size_t i = 0; pluginsInitializers_[i] != NULL; i++) + { + std::unique_ptr<IStoneWebViewerPlugin> plugin(pluginsInitializers_[i] (StoneWebViewerContext::GetInstance())); + if (plugin.get() != NULL) + { + plugins_.push_back(boost::shared_ptr<IStoneWebViewerPlugin>(plugin.release())); + } + } DISPATCH_JAVASCRIPT_EVENT("StoneInitialized"); } @@ -5221,17 +5384,18 @@ // Side-effect: "GetStringBuffer()" is filled with the "Series // Instance UID" of the first loaded annotation EMSCRIPTEN_KEEPALIVE - int LoadOsiriXAnnotations(const char* xml, + int LoadOsiriXAnnotations(const void* xmlPointer, + int xmlSize, int clearPreviousAnnotations) { try { if (clearPreviousAnnotations) { - osiriXAnnotations_->Clear(); + osiriXLayerSource_->GetAnnotations().Clear(); } - - osiriXAnnotations_->LoadXml(xml); + + osiriXLayerSource_->GetAnnotations().LoadXml(reinterpret_cast<const char*>(xmlPointer), xmlSize); // Force redraw, as the annotations might have changed for (Viewports::iterator it = allViewports_.begin(); it != allViewports_.end(); ++it) @@ -5240,16 +5404,16 @@ it->second->Redraw(); } - if (osiriXAnnotations_->GetSize() == 0) + if (osiriXLayerSource_->GetAnnotations().GetSize() == 0) { stringBuffer_.clear(); } else { - stringBuffer_ = osiriXAnnotations_->GetAnnotation(0).GetSeriesInstanceUid(); + stringBuffer_ = osiriXLayerSource_->GetAnnotations().GetAnnotation(0).GetSeriesInstanceUid(); } - LOG(WARNING) << "Loaded " << osiriXAnnotations_->GetSize() << " annotations from OsiriX"; + LOG(WARNING) << "Loaded " << osiriXLayerSource_->GetAnnotations().GetSize() << " annotations from OsiriX"; return 1; } EXTERN_CATCH_EXCEPTIONS; @@ -5262,9 +5426,9 @@ { try { - if (osiriXAnnotations_->GetSize() != 0) + if (osiriXLayerSource_->GetAnnotations().GetSize() != 0) { - const OrthancStone::OsiriX::Annotation& annotation = osiriXAnnotations_->GetAnnotation(0); + const OrthancStone::OsiriX::Annotation& annotation = osiriXLayerSource_->GetAnnotations().GetAnnotation(0); boost::shared_ptr<ViewerViewport> viewport = GetViewport(canvas); viewport->ScheduleFrameFocus(annotation.GetSopInstanceUid(), 0 /* focus on first frame */); @@ -5389,4 +5553,18 @@ } EXTERN_CATCH_EXCEPTIONS; } + + + EMSCRIPTEN_KEEPALIVE + void *Allocate(size_t size) + { + return malloc(size); + } + + + EMSCRIPTEN_KEEPALIVE + void Deallocate(void* ptr) + { + free(ptr); + } }
--- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Tue Apr 22 14:03:14 2025 +0200 +++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake Tue Apr 22 14:29:53 2025 +0200 @@ -200,7 +200,6 @@ if (NOT ORTHANC_SANDBOXED AND ENABLE_THREADS AND ENABLE_WEB_CLIENT) list(APPEND ORTHANC_STONE_SOURCES ${ORTHANC_STONE_ROOT}/Loaders/GenericLoadersContext.cpp - ${ORTHANC_STONE_ROOT}/Loaders/GenericLoadersContext.h ${ORTHANC_STONE_ROOT}/Oracle/GenericOracleRunner.cpp ${ORTHANC_STONE_ROOT}/Oracle/ThreadedOracle.cpp ) @@ -210,7 +209,6 @@ if (ENABLE_PUGIXML) list(APPEND ORTHANC_STONE_SOURCES ${ORTHANC_STONE_ROOT}/Scene2D/OsiriXLayerFactory.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/OsiriXLayerFactory.h ${ORTHANC_STONE_ROOT}/Toolbox/OsiriX/AngleAnnotation.cpp ${ORTHANC_STONE_ROOT}/Toolbox/OsiriX/Annotation.cpp ${ORTHANC_STONE_ROOT}/Toolbox/OsiriX/ArrayValue.cpp @@ -239,36 +237,22 @@ ${ORTHANC_STONE_ROOT}/Fonts/TextBoundingBox.cpp ${ORTHANC_STONE_ROOT}/Loaders/BasicFetchingItemsSorter.cpp - ${ORTHANC_STONE_ROOT}/Loaders/BasicFetchingItemsSorter.h ${ORTHANC_STONE_ROOT}/Loaders/BasicFetchingStrategy.cpp - ${ORTHANC_STONE_ROOT}/Loaders/BasicFetchingStrategy.h ${ORTHANC_STONE_ROOT}/Loaders/DicomResourcesLoader.cpp ${ORTHANC_STONE_ROOT}/Loaders/DicomSource.cpp ${ORTHANC_STONE_ROOT}/Loaders/DicomStructureSetLoader.cpp - ${ORTHANC_STONE_ROOT}/Loaders/DicomStructureSetLoader.h ${ORTHANC_STONE_ROOT}/Loaders/DicomVolumeLoader.cpp - ${ORTHANC_STONE_ROOT}/Loaders/IFetchingItemsSorter.h - ${ORTHANC_STONE_ROOT}/Loaders/IFetchingStrategy.h ${ORTHANC_STONE_ROOT}/Loaders/LoadedDicomResources.cpp ${ORTHANC_STONE_ROOT}/Loaders/LoaderStateMachine.cpp - ${ORTHANC_STONE_ROOT}/Loaders/LoaderStateMachine.h ${ORTHANC_STONE_ROOT}/Loaders/OrthancMultiframeVolumeLoader.cpp - ${ORTHANC_STONE_ROOT}/Loaders/OrthancMultiframeVolumeLoader.h ${ORTHANC_STONE_ROOT}/Loaders/OracleScheduler.cpp ${ORTHANC_STONE_ROOT}/Loaders/OrthancSeriesVolumeProgressiveLoader.cpp - ${ORTHANC_STONE_ROOT}/Loaders/OrthancSeriesVolumeProgressiveLoader.h ${ORTHANC_STONE_ROOT}/Loaders/SeriesFramesLoader.cpp ${ORTHANC_STONE_ROOT}/Loaders/SeriesMetadataLoader.cpp ${ORTHANC_STONE_ROOT}/Loaders/SeriesOrderedFrames.cpp ${ORTHANC_STONE_ROOT}/Loaders/SeriesThumbnailsLoader.cpp - ${ORTHANC_STONE_ROOT}/Messages/ICallable.h - ${ORTHANC_STONE_ROOT}/Messages/IMessage.h - ${ORTHANC_STONE_ROOT}/Messages/IMessageEmitter.h ${ORTHANC_STONE_ROOT}/Messages/IObservable.cpp - ${ORTHANC_STONE_ROOT}/Messages/IObservable.h - ${ORTHANC_STONE_ROOT}/Messages/IObserver.h - ${ORTHANC_STONE_ROOT}/Messages/ObserverBase.h ${ORTHANC_STONE_ROOT}/Oracle/GetOrthancImageCommand.cpp ${ORTHANC_STONE_ROOT}/Oracle/GetOrthancWebViewerJpegCommand.cpp @@ -279,208 +263,103 @@ ${ORTHANC_STONE_ROOT}/Oracle/ParseDicomFromWadoCommand.cpp ${ORTHANC_STONE_ROOT}/Scene2D/AnnotationsSceneLayer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/AnnotationsSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/ArrowSceneLayer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/ArrowSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/CairoCompositor.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/CairoCompositor.h - ${ORTHANC_STONE_ROOT}/Scene2D/Color.h - ${ORTHANC_STONE_ROOT}/Scene2D/ColorSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/ColorTextureSceneLayer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/ColorTextureSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/CopyStyleConfigurator.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/CopyStyleConfigurator.h ${ORTHANC_STONE_ROOT}/Scene2D/FloatTextureSceneLayer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/FloatTextureSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/GrayscaleStyleConfigurator.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/GrayscaleStyleConfigurator.h ${ORTHANC_STONE_ROOT}/Scene2D/GrayscaleWindowingSceneTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/GrayscaleWindowingSceneTracker.h - ${ORTHANC_STONE_ROOT}/Scene2D/ICompositor.h - ${ORTHANC_STONE_ROOT}/Scene2D/ILayerStyleConfigurator.h - ${ORTHANC_STONE_ROOT}/Scene2D/ISceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/InfoPanelSceneLayer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/InfoPanelSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/LookupTableStyleConfigurator.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/LookupTableStyleConfigurator.h ${ORTHANC_STONE_ROOT}/Scene2D/LookupTableTextureSceneLayer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/LookupTableTextureSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/MacroSceneLayer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/MacroSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/MagnifyingGlassTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/MagnifyingGlassTracker.h - ${ORTHANC_STONE_ROOT}/Scene2D/NullLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/PanSceneTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/PanSceneTracker.h ${ORTHANC_STONE_ROOT}/Scene2D/PointerEvent.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/PointerEvent.h ${ORTHANC_STONE_ROOT}/Scene2D/PolylineSceneLayer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/PolylineSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/RotateSceneTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/RotateSceneTracker.h ${ORTHANC_STONE_ROOT}/Scene2D/Scene2D.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Scene2D.h ${ORTHANC_STONE_ROOT}/Scene2D/ScenePoint2D.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/ScenePoint2D.h ${ORTHANC_STONE_ROOT}/Scene2D/TextSceneLayer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/TextSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/TextureBaseSceneLayer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/TextureBaseSceneLayer.h ${ORTHANC_STONE_ROOT}/Scene2D/ZoomSceneTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/ZoomSceneTracker.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoArrowRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoArrowRenderer.h - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoBaseRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoColorTextureRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoColorTextureRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoFloatTextureRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoFloatTextureRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoInfoPanelRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoInfoPanelRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoLookupTableTextureRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoLookupTableTextureRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoPolylineRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoPolylineRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoTextRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CairoTextRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CompositorHelper.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/CompositorHelper.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/FixedPointAligner.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/FixedPointAligner.h - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/ICairoContextProvider.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/MacroLayerRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/MacroLayerRenderer.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/AngleMeasureTool.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/AngleMeasureTool.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateAngleMeasureCommand.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateAngleMeasureCommand.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateAngleMeasureTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateAngleMeasureTracker.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateCircleMeasureTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateCircleMeasureTracker.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateLineMeasureCommand.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateLineMeasureCommand.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateLineMeasureTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateLineMeasureTracker.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateMeasureTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/CreateMeasureTracker.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/EditAngleMeasureCommand.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/EditAngleMeasureCommand.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/EditAngleMeasureTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/EditAngleMeasureTracker.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/EditLineMeasureCommand.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/EditLineMeasureCommand.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/EditLineMeasureTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/EditLineMeasureTracker.h - ${ORTHANC_STONE_ROOT}/Scene2DViewport/IFlexiblePointerTracker.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/LayerHolder.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/LayerHolder.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/LineMeasureTool.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/LineMeasureTool.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/MeasureCommands.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/MeasureCommands.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/MeasureTool.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/MeasureTool.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/MeasureToolsToolbox.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/MeasureToolsToolbox.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/MeasureTrackers.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/MeasureTrackers.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/OneGesturePointerTracker.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/OneGesturePointerTracker.h - ${ORTHANC_STONE_ROOT}/Scene2DViewport/PredeclaredTypes.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/UndoStack.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/UndoStack.h ${ORTHANC_STONE_ROOT}/Scene2DViewport/ViewportController.cpp - ${ORTHANC_STONE_ROOT}/Scene2DViewport/ViewportController.h ${ORTHANC_STONE_ROOT}/StoneEnumerations.cpp - ${ORTHANC_STONE_ROOT}/StoneException.h ${ORTHANC_STONE_ROOT}/StoneInitialization.cpp ${ORTHANC_STONE_ROOT}/Toolbox/AffineTransform2D.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/AffineTransform2D.h ${ORTHANC_STONE_ROOT}/Toolbox/BucketAccumulator1D.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/BucketAccumulator1D.h ${ORTHANC_STONE_ROOT}/Toolbox/BucketAccumulator2D.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/BucketAccumulator2D.h ${ORTHANC_STONE_ROOT}/Toolbox/CoordinateSystem3D.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/CoordinateSystem3D.h ${ORTHANC_STONE_ROOT}/Toolbox/DebugDrawing2D.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/DebugDrawing2D.h ${ORTHANC_STONE_ROOT}/Toolbox/DicomInstanceParameters.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/DicomInstanceParameters.h ${ORTHANC_STONE_ROOT}/Toolbox/DicomStructureSet.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/DicomStructureSet.h ${ORTHANC_STONE_ROOT}/Toolbox/DynamicBitmap.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/DynamicBitmap.h ${ORTHANC_STONE_ROOT}/Toolbox/Extent2D.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/Extent2D.h ${ORTHANC_STONE_ROOT}/Toolbox/FiniteProjectiveCamera.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/FiniteProjectiveCamera.h ${ORTHANC_STONE_ROOT}/Toolbox/GenericToolbox.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/GenericToolbox.h ${ORTHANC_STONE_ROOT}/Toolbox/GeometryToolbox.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/GeometryToolbox.h ${ORTHANC_STONE_ROOT}/Toolbox/ImageGeometry.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/ImageGeometry.h ${ORTHANC_STONE_ROOT}/Toolbox/ImageToolbox.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/ImageToolbox.h ${ORTHANC_STONE_ROOT}/Toolbox/Internals/BucketMapper.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/Internals/BucketMapper.h ${ORTHANC_STONE_ROOT}/Toolbox/Internals/OrientedIntegerLine2D.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/Internals/OrientedIntegerLine2D.h ${ORTHANC_STONE_ROOT}/Toolbox/Internals/RectanglesIntegerProjection.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/Internals/RectanglesIntegerProjection.h ${ORTHANC_STONE_ROOT}/Toolbox/LinearAlgebra.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/LinearAlgebra.h - ${ORTHANC_STONE_ROOT}/Toolbox/PixelTestPatterns.h ${ORTHANC_STONE_ROOT}/Toolbox/SegmentTree.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/SegmentTree.h ${ORTHANC_STONE_ROOT}/Toolbox/ShearWarpProjectiveTransform.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/ShearWarpProjectiveTransform.h ${ORTHANC_STONE_ROOT}/Toolbox/SlicesSorter.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/SlicesSorter.h ${ORTHANC_STONE_ROOT}/Toolbox/SortedFrames.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/SortedFrames.h ${ORTHANC_STONE_ROOT}/Toolbox/StoneToolbox.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/StoneToolbox.h - ${ORTHANC_STONE_ROOT}/Toolbox/SubpixelReader.h - ${ORTHANC_STONE_ROOT}/Toolbox/SubvoxelReader.h ${ORTHANC_STONE_ROOT}/Toolbox/TextRenderer.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/TextRenderer.h ${ORTHANC_STONE_ROOT}/Toolbox/UndoRedoStack.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/UndoRedoStack.h ${ORTHANC_STONE_ROOT}/Toolbox/UnionOfRectangles.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/UnionOfRectangles.h ${ORTHANC_STONE_ROOT}/Toolbox/Windowing.cpp - ${ORTHANC_STONE_ROOT}/Toolbox/Windowing.h ${ORTHANC_STONE_ROOT}/Viewport/DefaultViewportInteractor.cpp - ${ORTHANC_STONE_ROOT}/Viewport/IViewport.h ${ORTHANC_STONE_ROOT}/Viewport/ViewportLocker.cpp - ${ORTHANC_STONE_ROOT}/Volumes/IGeometryProvider.h ${ORTHANC_STONE_ROOT}/Volumes/IVolumeSlicer.cpp - ${ORTHANC_STONE_ROOT}/Volumes/IVolumeSlicer.h ${ORTHANC_STONE_ROOT}/Volumes/OrientedVolumeBoundingBox.cpp - ${ORTHANC_STONE_ROOT}/Volumes/OrientedVolumeBoundingBox.h ${ORTHANC_STONE_ROOT}/Volumes/VolumeImageGeometry.cpp - ${ORTHANC_STONE_ROOT}/Volumes/VolumeImageGeometry.h ${ORTHANC_STONE_ROOT}/Volumes/VolumeReslicer.cpp - ${ORTHANC_STONE_ROOT}/Volumes/VolumeReslicer.h ${ORTHANC_STONE_ROOT}/Volumes/VolumeSceneLayerSource.cpp - ${ORTHANC_STONE_ROOT}/Volumes/VolumeSceneLayerSource.h - ${ORTHANC_STONE_ROOT}/Volumes/DicomVolumeImage.h ${ORTHANC_STONE_ROOT}/Volumes/DicomVolumeImage.cpp - ${ORTHANC_STONE_ROOT}/Volumes/DicomVolumeImage.h ${ORTHANC_STONE_ROOT}/Volumes/DicomVolumeImageMPRSlicer.cpp - ${ORTHANC_STONE_ROOT}/Volumes/DicomVolumeImageMPRSlicer.h ${ORTHANC_STONE_ROOT}/Volumes/DicomVolumeImageReslicer.cpp - ${ORTHANC_STONE_ROOT}/Volumes/DicomVolumeImageReslicer.h ${ORTHANC_STONE_ROOT}/Volumes/ImageBuffer3D.cpp - ${ORTHANC_STONE_ROOT}/Volumes/ImageBuffer3D.h ${ORTHANC_STONE_ROOT}/Wrappers/CairoContext.cpp ${ORTHANC_STONE_ROOT}/Wrappers/CairoSurface.cpp @@ -502,44 +381,25 @@ if (ENABLE_OPENGL) list(APPEND ORTHANC_STONE_SOURCES - ${ORTHANC_STONE_ROOT}/Fonts/OpenGLTextCoordinates.h ${ORTHANC_STONE_ROOT}/Fonts/OpenGLTextCoordinates.cpp - ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLProgram.h ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLProgram.cpp - ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLShader.h ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLShader.cpp - ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLTexture.h ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLTexture.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/OpenGLCompositor.h ${ORTHANC_STONE_ROOT}/Scene2D/OpenGLCompositor.cpp ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLAdvancedPolylineRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLArrowRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLArrowRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLBasicPolylineRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLBasicPolylineRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLColorTextureProgram.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLColorTextureProgram.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLColorTextureRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLColorTextureRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLFloatTextureProgram.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLFloatTextureProgram.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLFloatTextureRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLFloatTextureRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLInfoPanelRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLInfoPanelRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLLinesProgram.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLLinesProgram.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLLookupTableTextureRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLLookupTableTextureRenderer.h - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLShaderVersionDirective.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLTextProgram.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLTextProgram.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLTextRenderer.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLTextRenderer.h ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLTextureProgram.cpp - ${ORTHANC_STONE_ROOT}/Scene2D/Internals/OpenGLTextureProgram.h ) endif() @@ -577,4 +437,4 @@ endmacro() -AutodetectHeaderFiles(ORTHANC_STONE_SOURCES) +# AutodetectHeaderFiles(ORTHANC_STONE_SOURCES)