changeset 2068:22a83fb9dd23 deep-learning

added AlignedMatrix and TimerLogger
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 17 May 2023 17:30:52 +0200
parents 20222330cdf6
children 5956d7357098
files OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake OrthancStone/Sources/Toolbox/AlignedMatrix.cpp OrthancStone/Sources/Toolbox/AlignedMatrix.h OrthancStone/Sources/Toolbox/SimdIncludes.h OrthancStone/Sources/Toolbox/TimerLogger.cpp OrthancStone/Sources/Toolbox/TimerLogger.h
diffstat 6 files changed, 577 insertions(+), 141 deletions(-) [+]
line wrap: on
line diff
--- a/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake	Tue May 16 11:26:05 2023 +0200
+++ b/OrthancStone/Resources/CMake/OrthancStoneConfiguration.cmake	Wed May 17 17:30:52 2023 +0200
@@ -199,7 +199,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
     )
@@ -209,7 +208,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
@@ -236,36 +234,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
@@ -276,202 +260,102 @@
   ${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/AlignedMatrix.cpp
   ${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/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/SubpixelReader.h
-  ${ORTHANC_STONE_ROOT}/Toolbox/SubvoxelReader.h
   ${ORTHANC_STONE_ROOT}/Toolbox/TextRenderer.cpp
-  ${ORTHANC_STONE_ROOT}/Toolbox/TextRenderer.h
+  ${ORTHANC_STONE_ROOT}/Toolbox/TimerLogger.cpp
   ${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}/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
@@ -493,52 +377,29 @@
 
 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}/OpenGL/OpenGLTextureArray.h
     ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLTextureArray.cpp
-    ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLTextureVolume.h
     ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLTextureVolume.cpp
-    ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLFramebuffer.h
     ${ORTHANC_STONE_ROOT}/OpenGL/OpenGLFramebuffer.cpp
-    ${ORTHANC_STONE_ROOT}/OpenGL/ImageProcessingProgram.h
     ${ORTHANC_STONE_ROOT}/OpenGL/ImageProcessingProgram.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()
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/Sources/Toolbox/AlignedMatrix.cpp	Wed May 17 17:30:52 2023 +0200
@@ -0,0 +1,276 @@
+/**
+ * Stone of Orthanc
+ * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ * Copyright (C) 2017-2022 Osimis S.A., Belgium
+ * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "AlignedMatrix.h"
+
+#include <OrthancException.h>
+
+#include <string.h>
+
+namespace OrthancStone
+{
+  static unsigned int Ceiling(unsigned int a,
+                              unsigned int b)
+  {
+    if (a % b == 0)
+    {
+      return a / b;
+    }
+    else
+    {
+      return a / b + 1;
+    }
+  }
+
+
+  void AlignedMatrix::Setup(unsigned int rows,
+                            unsigned int cols)
+  {
+    assert(sizeof(float) == 4);
+    
+    if (rows == 0 ||
+        cols == 0)
+    {
+      rows_ = 0;
+      cols_ = 0;
+      pitch_ = 0;
+      pitchFloatPointer_ = 0;
+      content_ = NULL;
+    }
+    else
+    {
+      rows_ = rows;
+      cols_ = cols;
+      pitch_ = Ceiling(cols * sizeof(float), ORTHANC_MEMORY_ALIGNMENT) * ORTHANC_MEMORY_ALIGNMENT;
+      pitchFloatPointer_ = pitch_ / sizeof(float);
+      
+      void* tmp = NULL;
+      if (posix_memalign(&tmp, ORTHANC_MEMORY_ALIGNMENT, rows_ * pitch_) != 0)
+      {
+        throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory);
+      }
+
+      assert(reinterpret_cast<intptr_t>(tmp) % ORTHANC_MEMORY_ALIGNMENT == 0);
+      assert(pitch_ % ORTHANC_MEMORY_ALIGNMENT == 0);
+      assert(pitch_ % sizeof(float) == 0);
+      assert((rows_ * pitch_) % ORTHANC_MEMORY_ALIGNMENT == 0);
+      
+      content_ = static_cast<float*>(tmp);
+    }
+  }
+
+
+  AlignedMatrix::~AlignedMatrix()
+  {
+    if (content_ != NULL)
+    {
+      free(content_);
+    }
+  }
+
+  
+  void AlignedMatrix::FillZeros()
+  {
+    memset(content_, 0, rows_ * pitch_);
+  }
+
+
+  void AlignedMatrix::ProductPlain(AlignedMatrix& c,
+                                   const AlignedMatrix& a,
+                                   const AlignedMatrix& b)
+  {
+    if (c.GetRows() != a.GetRows() ||
+        c.GetColumns() != b.GetColumns() ||
+        a.GetColumns() != b.GetRows())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
+    }
+  
+    const unsigned int M = c.GetRows();
+    const unsigned int N = c.GetColumns();
+    const unsigned int K = a.GetColumns();
+
+    c.FillZeros();
+  
+    for (unsigned int i = 0; i < M; i++)
+    {
+      // Loop over "k" to be more cache-friendly
+      // https://sahnimanas.github.io/post/anatomy-of-a-high-performance-convolution/
+      for (unsigned int k = 0; k < K; k++)
+      {
+        for (unsigned int j = 0; j < N; j++)
+        {
+          c.AddValue(i, j, a.GetValue(i, k) * b.GetValue(k, j));
+        }
+      }
+    }
+  }
+
+
+#if ORTHANC_HAS_MATRIX_PRODUCT_TRANSPOSED_VECTORIZED == 1
+  // Computes "C = A*B^T"
+  class AlignedMatrix::ProductTransposedVectorizedContext : public boost::noncopyable
+  {
+  private:
+    unsigned int vectorizedSteps_;
+    uint8_t      finalSteps_;
+  
+  public:
+    ORTHANC_FORCE_INLINE
+    ProductTransposedVectorizedContext(const AlignedMatrix& a)
+    {
+#if ORTHANC_HAS_AVX2 == 1
+      const unsigned int blockSize = 8;
+#elif ORTHANC_HAS_SSE2 == 1 || ORTHANC_HAS_WASM_SIMD == 1
+      const unsigned int blockSize = 4;
+#else
+#       error No supported SIMD instruction set
+#endif
+        
+      vectorizedSteps_ = a.GetColumns() / blockSize;
+      finalSteps_ = a.GetColumns() - vectorizedSteps_ * blockSize;
+    }
+
+    ORTHANC_FORCE_INLINE
+    float Apply(const float* ap,
+                const float* btp) const noexcept
+    {
+      float result;
+        
+#if ORTHANC_HAS_AVX2 == 1
+      __m256 accumulator = _mm256_set1_ps(0);
+
+      for (unsigned int k = 0; k < vectorizedSteps_; k++)
+      {
+        __m256 a = _mm256_load_ps(ap);
+        __m256 b = _mm256_load_ps(btp);
+        //accumulator = _mm256_add_ps(accumulator, _mm256_mul_ps(a, b));
+        accumulator = _mm256_fmadd_ps(a, b, accumulator);  // Requires the "-mfma" compiler flag
+
+        ap += 8;
+        btp += 8;
+      }
+        
+      float tmp[8] __attribute__ ((aligned (ORTHANC_MEMORY_ALIGNMENT)));
+      _mm256_store_ps(tmp, accumulator);
+      result = tmp[0] + tmp[1] + tmp[2] + tmp[3] + tmp[4] + tmp[5] + tmp[6] + tmp[7];
+
+#elif ORTHANC_HAS_SSE2 == 1
+      __m128 accumulator = _mm_set1_ps(0);
+
+      for (unsigned int k = 0; k < vectorizedSteps_; k++)
+      {
+        __m128 a = _mm_load_ps(ap);
+        __m128 b = _mm_load_ps(btp);
+        accumulator = _mm_add_ps(accumulator, _mm_mul_ps(a, b));
+        ap += 4;
+        btp += 4;
+      }
+
+#if 1
+      float tmp[4] __attribute__ ((aligned (ORTHANC_MEMORY_ALIGNMENT)));
+      _mm_storeu_ps(tmp, accumulator);
+      result = tmp[0] + tmp[1] + tmp[2] + tmp[3];
+#else
+      // This trickier version is theoretically faster, but no much difference in practice
+      const __m128 sum2 = _mm_add_ps(accumulator, _mm_shuffle_ps(accumulator, accumulator, _MM_SHUFFLE(2, 3, 0, 1)));
+      const __m128 sum1 = _mm_add_ps(sum2, _mm_shuffle_ps(sum2, sum2, _MM_SHUFFLE(0, 1, 2, 3)));
+      result = _mm_cvtss_f32(sum1);
+#endif
+
+#elif ORTHANC_HAS_WASM_SIMD == 1
+      v128_t accumulator = wasm_f32x4_splat(0);
+
+      for (unsigned int k = 0; k < vectorizedSteps_; k++)
+      {
+        v128_t a = wasm_v128_load(ap);
+        v128_t b = wasm_v128_load(btp);
+        accumulator = wasm_f32x4_add(accumulator, wasm_f32x4_mul(a, b));
+        ap += 4;
+        btp += 4;
+      }        
+        
+#if 1
+      float tmp[4];
+      wasm_v128_store(tmp, accumulator);
+      result = tmp[0] + tmp[1] + tmp[2] + tmp[3];
+#else
+      const v128_t sum2 = wasm_f32x4_add(accumulator, wasm_i32x4_shuffle(accumulator, accumulator, 2, 3, 0, 0));
+      const v128_t sum1 = wasm_f32x4_add(sum2, wasm_i32x4_shuffle(sum2, sum2, 1, 0, 0, 0));
+      result = wasm_f32x4_extract_lane(sum1, 0);
+#endif
+
+#else
+#       error No supported SIMD instruction set
+#endif
+        
+      for (uint8_t k = 0; k < finalSteps_; k++)
+      {
+        result += (*ap) * (*btp);
+        ap++;
+        btp++;
+      }
+        
+      return result;
+    }
+  };
+#endif
+    
+
+#if ORTHANC_HAS_MATRIX_PRODUCT_TRANSPOSED_VECTORIZED == 1
+  void AlignedMatrix::ProductTransposedVectorized(AlignedMatrix& c,
+                                                  const AlignedMatrix& a,
+                                                  const AlignedMatrix& bt)
+  {
+    if (c.GetRows() != a.GetRows() ||
+        c.GetColumns() != bt.GetRows() ||
+        a.GetColumns() != bt.GetColumns())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize);
+    }
+
+    AlignedMatrix::ProductTransposedVectorizedContext context(a);
+
+    const unsigned int M = a.GetRows();
+    const unsigned int N = bt.GetRows();
+
+    const size_t rowSizeA = a.GetPitch() / sizeof(float);
+    const size_t rowSizeB = bt.GetPitch() / sizeof(float);
+  
+    const float* ap = a.GetRowPointer(0);
+    for (unsigned int i = 0; i < M; i++)
+    {
+      float* cp = c.GetRowPointer(i);
+    
+      const float* btp = bt.GetRowPointer(0);
+      for (unsigned int j = 0; j < N; j++, cp++)
+      {
+        *cp = context.Apply(ap, btp);
+        btp += rowSizeB;
+      }
+
+      ap += rowSizeA;
+    }
+  }
+#endif
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/Sources/Toolbox/AlignedMatrix.h	Wed May 17 17:30:52 2023 +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-2022 Osimis S.A., Belgium
+ * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "SimdIncludes.h"
+
+#if (ORTHANC_HAS_AVX2 == 1 || ORTHANC_HAS_SSE2 == 1 || ORTHANC_HAS_WASM_SIMD == 1)
+#  define ORTHANC_HAS_MATRIX_PRODUCT_TRANSPOSED_VECTORIZED  1
+#else
+#  define ORTHANC_HAS_MATRIX_PRODUCT_TRANSPOSED_VECTORIZED  0
+#endif
+
+
+#include <boost/noncopyable.hpp>
+#include <cassert>
+
+namespace OrthancStone
+{
+  /**
+   * 2D matrix whose rows are aligned for the largest SIMD
+   * instructions that are available.
+   **/
+  class AlignedMatrix : public boost::noncopyable
+  {
+  private:
+    class ProductTransposedVectorizedContext;
+      
+    unsigned int  rows_;
+    unsigned int  cols_;
+    size_t        pitch_;
+    size_t        pitchFloatPointer_;
+    float*        content_;
+
+    void Setup(unsigned int rows,
+               unsigned int cols);
+
+  public:
+    AlignedMatrix(unsigned int rows,
+                  unsigned int cols)
+    {
+      Setup(rows, cols);
+    }
+
+    ~AlignedMatrix();
+
+    unsigned int GetRows() const
+    {
+      return rows_;
+    }
+
+    unsigned int GetColumns() const
+    {
+      return cols_;
+    }
+
+    unsigned int GetPitch() const
+    {
+      return pitch_;
+    }
+
+    float* GetRowPointer(unsigned int row)
+    {
+      assert(row < rows_);
+      return content_ + row * pitchFloatPointer_;
+    }
+
+    const float* GetRowPointer(unsigned int row) const
+    {
+      assert(row < rows_);
+      return content_ + row * pitchFloatPointer_;
+    }
+
+    size_t GetIndex(unsigned int row,
+                    unsigned int col) const
+    {
+      assert(row < rows_ && col < cols_);
+      return row * pitchFloatPointer_ + col;
+    }
+
+    float GetValue(unsigned int row,
+                   unsigned int col) const
+    {
+      return content_[GetIndex(row, col)];
+    }
+
+    void SetValue(unsigned int row,
+                  unsigned int col,
+                  float value) const
+    {
+      content_[GetIndex(row, col)] = value;
+    }
+
+    void AddValue(unsigned int row,
+                  unsigned int col,
+                  float value)
+    {
+      content_[GetIndex(row, col)] += value;
+    }
+
+    void FillZeros();
+
+    // Computes "C = A * B" without SIMD operations
+    static void ProductPlain(AlignedMatrix& c,
+                             const AlignedMatrix& a,
+                             const AlignedMatrix& b);
+
+#if ORTHANC_HAS_MATRIX_PRODUCT_TRANSPOSED_VECTORIZED == 1
+    // Computes "C = A * B^T" using SIMD operations
+    static void ProductTransposedVectorized(AlignedMatrix& c,
+                                            const AlignedMatrix& a,
+                                            const AlignedMatrix& bt);
+#endif
+  };
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/Sources/Toolbox/SimdIncludes.h	Wed May 17 17:30:52 2023 +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-2022 Osimis S.A., Belgium
+ * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#if defined(__EMSCRIPTEN__)
+#  include <emscripten.h>
+#  include <wasm_simd128.h>
+#else
+#  include <immintrin.h>  // portable to all x86 compilers
+#endif
+
+#if __AVX2__ == 1
+#  define ORTHANC_HAS_AVX2          1
+#  define ORTHANC_HAS_SSE2          1
+#  define ORTHANC_HAS_WASM_SIMD     0
+#  define ORTHANC_MEMORY_ALIGNMENT  32
+#elif __SSE2__ == 1
+#  define ORTHANC_HAS_AVX2          0
+#  define ORTHANC_HAS_SSE2          1
+#  define ORTHANC_HAS_WASM_SIMD     0
+#  define ORTHANC_MEMORY_ALIGNMENT  16
+#elif defined(__EMSCRIPTEN__)
+#  define ORTHANC_HAS_AVX2          0
+#  define ORTHANC_HAS_SSE2          0
+#  define ORTHANC_HAS_WASM_SIMD     1
+#  define ORTHANC_MEMORY_ALIGNMENT  16
+#elif defined(_MSC_VER)
+#  if _M_IX86_FP >= 2   // https://stackoverflow.com/a/18563988
+#    define ORTHANC_HAS_AVX2          0
+#    define ORTHANC_HAS_SSE2          0
+#    define ORTHANC_HAS_WASM_SIMD     1
+#    define ORTHANC_MEMORY_ALIGNMENT  16
+#  endif
+#else
+#  define ORTHANC_MEMORY_ALIGNMENT  8
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/Sources/Toolbox/TimerLogger.cpp	Wed May 17 17:30:52 2023 +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-2022 Osimis S.A., Belgium
+ * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "TimerLogger.h"
+
+#include <Logging.h>
+
+
+namespace OrthancStone
+{
+  TimerLogger::TimerLogger(const std::string& name) :
+    name_(name)
+  {
+#if defined(__EMSCRIPTEN__)
+    start_ = emscripten_get_now();
+#else
+    start_ = boost::posix_time::microsec_clock::universal_time();
+#endif
+  }
+
+  
+  TimerLogger::~TimerLogger()
+  {
+#if defined(__EMSCRIPTEN__)
+    int elapsed = static_cast<int>(round(emscripten_get_now() - start_));
+#else
+    const boost::posix_time::ptime end = boost::posix_time::microsec_clock::universal_time();
+    int elapsed = (end - start_).total_milliseconds();
+#endif
+
+    LOG(WARNING) << name_ << " - Elapsed time: " << elapsed << "ms";
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OrthancStone/Sources/Toolbox/TimerLogger.h	Wed May 17 17:30:52 2023 +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-2022 Osimis S.A., Belgium
+ * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program. If not, see
+ * <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include <boost/noncopyable.hpp>
+#include <string>
+
+#if defined(__EMSCRIPTEN__)
+#  include <emscripten.h>
+#else
+#  include <boost/date_time/posix_time/posix_time_types.hpp>
+#endif
+
+
+namespace OrthancStone
+{
+  class TimerLogger : public boost::noncopyable
+  {
+  private:
+    std::string name_;
+
+#if defined(__EMSCRIPTEN__)
+    double start_;
+#else
+    boost::posix_time::ptime start_;
+#endif
+
+  public:
+    TimerLogger(const std::string& name);
+
+    ~TimerLogger();
+  };
+}