# HG changeset patch # User am@osimis.io # Date 1543500679 -3600 # Node ID b85f635f1eb5f6acb2c8f3dbca8b4fdd989f69fb # Parent c7fb700a7d129ec7533e8c5ffe0cc4e54fa15d06# Parent 3f9017db17381b12f2442c7ae819a49ca62a2e39 added serialization for RadiographyScene diff -r c7fb700a7d12 -r b85f635f1eb5 Applications/Samples/SingleFrameEditorApplication.h --- a/Applications/Samples/SingleFrameEditorApplication.h Wed Nov 28 10:46:32 2018 +0100 +++ b/Applications/Samples/SingleFrameEditorApplication.h Thu Nov 29 15:11:19 2018 +0100 @@ -31,6 +31,8 @@ #include "../../Framework/Radiography/RadiographySceneCommand.h" #include "../../Framework/Radiography/RadiographyWidget.h" #include "../../Framework/Radiography/RadiographyWindowingTracker.h" +#include "../../Framework/Radiography/RadiographySceneWriter.h" +#include "../../Framework/Radiography/RadiographySceneReader.h" #include #include @@ -48,8 +50,8 @@ namespace Samples { class RadiographyEditorInteractor : - public IWorldSceneInteractor, - public IObserver + public IWorldSceneInteractor, + public IObserver { private: enum Tool @@ -60,7 +62,7 @@ Tool_Resize, Tool_Windowing }; - + StoneApplicationContext* context_; UndoRedoStack undoRedoStack_; @@ -71,8 +73,8 @@ { return 10.0; } - - + + public: RadiographyEditorInteractor(MessageBroker& broker) : IObserver(broker), @@ -85,7 +87,7 @@ { context_ = &context; } - + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& worldWidget, const ViewportGeometry& view, MouseButton button, @@ -101,16 +103,16 @@ if (button == MouseButton_Left) { size_t selected; - + if (tool_ == Tool_Windowing) { return new RadiographyWindowingTracker( - undoRedoStack_, widget.GetScene(), - viewportX, viewportY, - RadiographyWindowingTracker::Action_DecreaseWidth, - RadiographyWindowingTracker::Action_IncreaseWidth, - RadiographyWindowingTracker::Action_DecreaseCenter, - RadiographyWindowingTracker::Action_IncreaseCenter); + undoRedoStack_, widget.GetScene(), + viewportX, viewportY, + RadiographyWindowingTracker::Action_DecreaseWidth, + RadiographyWindowingTracker::Action_IncreaseWidth, + RadiographyWindowingTracker::Action_DecreaseCenter, + RadiographyWindowingTracker::Action_IncreaseCenter); } else if (!widget.LookupSelectedLayer(selected)) { @@ -133,23 +135,23 @@ { switch (tool_) { - case Tool_Crop: - return new RadiographyLayerCropTracker + case Tool_Crop: + return new RadiographyLayerCropTracker (undoRedoStack_, widget.GetScene(), view, selected, x, y, corner); - case Tool_Resize: - return new RadiographyLayerResizeTracker + case Tool_Resize: + return new RadiographyLayerResizeTracker (undoRedoStack_, widget.GetScene(), selected, x, y, corner, (modifiers & KeyboardModifiers_Shift)); - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } } else { size_t layer; - + if (widget.GetScene().LookupLayer(layer, x, y)) { widget.Select(layer); @@ -158,7 +160,7 @@ { widget.Unselect(); } - + return NULL; } } @@ -172,18 +174,18 @@ { switch (tool_) { - case Tool_Move: - return new RadiographyLayerMoveTracker + case Tool_Move: + return new RadiographyLayerMoveTracker (undoRedoStack_, widget.GetScene(), layer, x, y, (modifiers & KeyboardModifiers_Shift)); - case Tool_Rotate: - return new RadiographyLayerRotateTracker + case Tool_Rotate: + return new RadiographyLayerRotateTracker (undoRedoStack_, widget.GetScene(), view, layer, x, y, (modifiers & KeyboardModifiers_Shift)); - - default: - break; + + default: + break; } return NULL; @@ -232,14 +234,14 @@ tool_ == Tool_Resize)) { RadiographyScene::LayerAccessor accessor(widget.GetScene(), selected); - + Corner corner; if (accessor.GetLayer().LookupCorner(corner, x, y, view.GetZoom(), GetHandleSize())) { accessor.GetLayer().GetCorner(x, y, corner); - + double z = 1.0 / view.GetZoom(); - + context.SetSourceColor(255, 0, 0); cairo_t* cr = context.GetObject(); cairo_set_line_width(cr, 2.0 * z); @@ -270,118 +272,141 @@ switch (keyChar) { - case 'a': - widget.FitContent(); - break; + case 'a': + widget.FitContent(); + break; + + case 'c': + tool_ = Tool_Crop; + break; - case 'c': - tool_ = Tool_Crop; - break; + case 'd': + { + // dump to json and reload + Json::Value snapshot; + RadiographySceneWriter writer; + writer.Write(snapshot, widget.GetScene()); - case 'e': - { - Orthanc::DicomMap tags; + LOG(INFO) << "JSON export was successful: " + << snapshot.toStyledString(); + + boost::shared_ptr scene(new RadiographyScene(GetBroker())); + RadiographySceneReader reader(*scene, context_->GetOrthancApiClient()); + + Orthanc::FontRegistry fontRegistry; + fontRegistry.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); + + reader.SetFontRegistry(fontRegistry); + reader.Read(snapshot); - // Minimal set of tags to generate a valid CR image - tags.SetValue(Orthanc::DICOM_TAG_ACCESSION_NUMBER, "NOPE", false); - tags.SetValue(Orthanc::DICOM_TAG_BODY_PART_EXAMINED, "PELVIS", false); - tags.SetValue(Orthanc::DICOM_TAG_INSTANCE_NUMBER, "1", false); - //tags.SetValue(Orthanc::DICOM_TAG_LATERALITY, "", false); - tags.SetValue(Orthanc::DICOM_TAG_MANUFACTURER, "OSIMIS", false); - tags.SetValue(Orthanc::DICOM_TAG_MODALITY, "CR", false); - tags.SetValue(Orthanc::DICOM_TAG_PATIENT_BIRTH_DATE, "20000101", false); - tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ID, "hello", false); - tags.SetValue(Orthanc::DICOM_TAG_PATIENT_NAME, "HELLO^WORLD", false); - tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ORIENTATION, "", false); - tags.SetValue(Orthanc::DICOM_TAG_PATIENT_SEX, "M", false); - tags.SetValue(Orthanc::DICOM_TAG_REFERRING_PHYSICIAN_NAME, "HOUSE^MD", false); - tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false); - tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false); - tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false); - tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false); + widget.SetScene(scene); + };break; + + case 'e': + { + Orthanc::DicomMap tags; - if (context_ != NULL) - { - widget.GetScene().ExportDicom(context_->GetOrthancApiClient(), - tags, std::string(), 0.1, 0.1, widget.IsInverted(), - widget.GetInterpolation(), EXPORT_USING_PAM); - } - - break; + // Minimal set of tags to generate a valid CR image + tags.SetValue(Orthanc::DICOM_TAG_ACCESSION_NUMBER, "NOPE", false); + tags.SetValue(Orthanc::DICOM_TAG_BODY_PART_EXAMINED, "PELVIS", false); + tags.SetValue(Orthanc::DICOM_TAG_INSTANCE_NUMBER, "1", false); + //tags.SetValue(Orthanc::DICOM_TAG_LATERALITY, "", false); + tags.SetValue(Orthanc::DICOM_TAG_MANUFACTURER, "OSIMIS", false); + tags.SetValue(Orthanc::DICOM_TAG_MODALITY, "CR", false); + tags.SetValue(Orthanc::DICOM_TAG_PATIENT_BIRTH_DATE, "20000101", false); + tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ID, "hello", false); + tags.SetValue(Orthanc::DICOM_TAG_PATIENT_NAME, "HELLO^WORLD", false); + tags.SetValue(Orthanc::DICOM_TAG_PATIENT_ORIENTATION, "", false); + tags.SetValue(Orthanc::DICOM_TAG_PATIENT_SEX, "M", false); + tags.SetValue(Orthanc::DICOM_TAG_REFERRING_PHYSICIAN_NAME, "HOUSE^MD", false); + tags.SetValue(Orthanc::DICOM_TAG_SERIES_NUMBER, "1", false); + tags.SetValue(Orthanc::DICOM_TAG_SOP_CLASS_UID, "1.2.840.10008.5.1.4.1.1.1", false); + tags.SetValue(Orthanc::DICOM_TAG_STUDY_ID, "STUDY", false); + tags.SetValue(Orthanc::DICOM_TAG_VIEW_POSITION, "", false); + + if (context_ != NULL) + { + widget.GetScene().ExportDicom(context_->GetOrthancApiClient(), + tags, std::string(), 0.1, 0.1, widget.IsInverted(), + widget.GetInterpolation(), EXPORT_USING_PAM); } - case 'i': - widget.SwitchInvert(); - break; - - case 'm': - tool_ = Tool_Move; + break; + } + + case 'i': + widget.SwitchInvert(); + break; + + case 'm': + tool_ = Tool_Move; + break; + + case 'n': + { + switch (widget.GetInterpolation()) + { + case ImageInterpolation_Nearest: + LOG(INFO) << "Switching to bilinear interpolation"; + widget.SetInterpolation(ImageInterpolation_Bilinear); break; - case 'n': - { - switch (widget.GetInterpolation()) - { - case ImageInterpolation_Nearest: - LOG(INFO) << "Switching to bilinear interpolation"; - widget.SetInterpolation(ImageInterpolation_Bilinear); - break; - - case ImageInterpolation_Bilinear: - LOG(INFO) << "Switching to nearest neighbor interpolation"; - widget.SetInterpolation(ImageInterpolation_Nearest); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - break; - } - - case 'r': - tool_ = Tool_Rotate; + case ImageInterpolation_Bilinear: + LOG(INFO) << "Switching to nearest neighbor interpolation"; + widget.SetInterpolation(ImageInterpolation_Nearest); break; - case 's': - tool_ = Tool_Resize; - break; + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + break; + } - case 'w': - tool_ = Tool_Windowing; - break; + case 'r': + tool_ = Tool_Rotate; + break; + + case 's': + tool_ = Tool_Resize; + break; + + case 'w': + tool_ = Tool_Windowing; + break; - case 'y': - if (modifiers & KeyboardModifiers_Control) - { - undoRedoStack_.Redo(); - widget.NotifyContentChanged(); - } - break; + case 'y': + if (modifiers & KeyboardModifiers_Control) + { + undoRedoStack_.Redo(); + widget.NotifyContentChanged(); + } + break; - case 'z': - if (modifiers & KeyboardModifiers_Control) - { - undoRedoStack_.Undo(); - widget.NotifyContentChanged(); - } - break; - - default: - break; + case 'z': + if (modifiers & KeyboardModifiers_Control) + { + undoRedoStack_.Undo(); + widget.NotifyContentChanged(); + } + break; + + default: + break; } } }; - - + + class SingleFrameEditorApplication : - public SampleSingleCanvasApplicationBase, - public IObserver + public SampleSingleCanvasApplicationBase, + public IObserver { private: - boost::shared_ptr scene_; - RadiographyEditorInteractor interactor_; + boost::shared_ptr scene_; + RadiographyEditorInteractor interactor_; + Orthanc::FontRegistry fontRegistry_; public: SingleFrameEditorApplication(MessageBroker& broker) : @@ -399,11 +424,11 @@ { boost::program_options::options_description generic("Sample options"); generic.add_options() - ("instance", boost::program_options::value(), - "Orthanc ID of the instance") - ("frame", boost::program_options::value()->default_value(0), - "Number of the frame, for multi-frame DICOM instances") - ; + ("instance", boost::program_options::value(), + "Orthanc ID of the instance") + ("frame", boost::program_options::value()->default_value(0), + "Number of the frame, for multi-frame DICOM instances") + ; options.add(generic); } @@ -440,12 +465,11 @@ std::string instance = parameters["instance"].as(); int frame = parameters["frame"].as(); - Orthanc::FontRegistry fonts; - fonts.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); + fontRegistry_.AddFromResource(Orthanc::EmbeddedResources::FONT_UBUNTU_MONO_BOLD_16); scene_.reset(new RadiographyScene(GetBroker())); //scene_->LoadDicomFrame(instance, frame, false); //.SetPan(200, 0); - scene_->LoadDicomFrame(context->GetOrthancApiClient(), "61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false); + scene_->LoadDicomFrame(context->GetOrthancApiClient(), "61f3143e-96f34791-ad6bbb8d-62559e75-45943e1b", 0, false, NULL); #if !defined(ORTHANC_ENABLE_WASM) || ORTHANC_ENABLE_WASM != 1 Orthanc::HttpClient::ConfigureSsl(true, "/etc/ssl/certs/ca-certificates.crt"); @@ -454,12 +478,12 @@ //scene_->LoadDicomWebFrame(context->GetWebService()); { - RadiographyLayer& layer = scene_->LoadText(fonts.GetFont(0), "Hello\nworld"); + RadiographyLayer& layer = scene_->LoadText(fontRegistry_.GetFont(0), "Hello\nworld", NULL); layer.SetResizeable(true); } { - RadiographyLayer& layer = scene_->LoadTestBlock(100, 50); + RadiographyLayer& layer = scene_->LoadTestBlock(100, 50, NULL); layer.SetResizeable(true); layer.SetPan(0, 200); } diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyAlphaLayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Radiography/RadiographyAlphaLayer.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -0,0 +1,132 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "RadiographyAlphaLayer.h" + +#include "RadiographyScene.h" + +#include +#include + +namespace OrthancStone +{ + + void RadiographyAlphaLayer::SetAlpha(Orthanc::ImageAccessor* image) + { + std::auto_ptr raii(image); + + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + if (image->GetFormat() != Orthanc::PixelFormat_Grayscale8) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + SetSize(image->GetWidth(), image->GetHeight()); + alpha_ = raii; + } + + void RadiographyAlphaLayer::Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation) const + { + if (alpha_.get() == NULL) + { + return; + } + + if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + unsigned int cropX, cropY, cropWidth, cropHeight; + GetCrop(cropX, cropY, cropWidth, cropHeight); + + const AffineTransform2D t = AffineTransform2D::Combine( + viewTransform, GetTransform(), + AffineTransform2D::CreateOffset(cropX, cropY)); + + Orthanc::ImageAccessor cropped; + alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); + + Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); + + t.Apply(tmp, cropped, interpolation, true /* clear */); + + // Blit + const unsigned int width = buffer.GetWidth(); + const unsigned int height = buffer.GetHeight(); + + float value = foreground_; + + if (useWindowing_) + { + float center, width; + if (scene_.GetWindowing(center, width)) + { + value = center + width / 2.0f; // TODO: shouldn't it be center alone ? + } + } + + for (unsigned int y = 0; y < height; y++) + { + float *q = reinterpret_cast(buffer.GetRow(y)); + const uint8_t *p = reinterpret_cast(tmp.GetRow(y)); + + for (unsigned int x = 0; x < width; x++, p++, q++) + { + float a = static_cast(*p) / 255.0f; + + *q = (a * value + (1.0f - a) * (*q)); + } + } + } + + bool RadiographyAlphaLayer::GetRange(float& minValue, + float& maxValue) const + { + if (useWindowing_) + { + return false; + } + else + { + minValue = 0; + maxValue = 0; + + if (foreground_ < 0) + { + minValue = foreground_; + } + + if (foreground_ > 0) + { + maxValue = foreground_; + } + + return true; + } + } +} diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyAlphaLayer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Radiography/RadiographyAlphaLayer.h Thu Nov 29 15:11:19 2018 +0100 @@ -0,0 +1,89 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "RadiographyLayer.h" + +namespace OrthancStone +{ + class RadiographyScene; + + // creates a transparent layer whose alpha channel is provided as a UINT8 image to SetAlpha. + // The color of the "mask" is either defined by a ForegroundValue or by the center value of the + // windowing from the scene. + class RadiographyAlphaLayer : public RadiographyLayer + { + private: + const RadiographyScene& scene_; + std::auto_ptr alpha_; // Grayscale8 + bool useWindowing_; + float foreground_; + + public: + RadiographyAlphaLayer(const RadiographyScene& scene) : + scene_(scene), + useWindowing_(true), + foreground_(0) + { + } + + + void SetForegroundValue(float foreground) + { + useWindowing_ = false; + foreground_ = foreground; + } + + float GetForegroundValue() const + { + return foreground_; + } + + bool IsUsingWindowing() const + { + return useWindowing_; + } + + void SetAlpha(Orthanc::ImageAccessor* image); + + virtual bool GetDefaultWindowing(float& center, + float& width) const + { + return false; + } + + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation) const; + + virtual bool GetRange(float& minValue, + float& maxValue) const; + + const Orthanc::ImageAccessor& GetAlpha() const + { + return *(alpha_.get()); + } + + + }; +} diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyDicomLayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Radiography/RadiographyDicomLayer.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -0,0 +1,159 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "RadiographyDicomLayer.h" + +#include "RadiographyScene.h" +#include "../Toolbox/DicomFrameConverter.h" + +#include +#include +#include +#include + +static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) +{ + return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); +} + +namespace OrthancStone +{ + + void RadiographyDicomLayer::ApplyConverter() + { + if (source_.get() != NULL && + converter_.get() != NULL) + { + converted_.reset(converter_->ConvertFrame(*source_)); + } + } + + + void RadiographyDicomLayer::SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) + { + converter_.reset(new DicomFrameConverter); + converter_->ReadParameters(dataset); + ApplyConverter(); + + std::string tmp; + Vector pixelSpacing; + + if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && + LinearAlgebra::ParseVector(pixelSpacing, tmp) && + pixelSpacing.size() == 2) + { + SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); + } + + //SetPan(-0.5 * GetPixelSpacingX(), -0.5 * GetPixelSpacingY()); + + OrthancPlugins::DicomDatasetReader reader(dataset); + + unsigned int width, height; + if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) || + !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS))) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + SetSize(width, height); + } + } + + void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership + { + std::auto_ptr raii(image); + + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + SetSize(image->GetWidth(), image->GetHeight()); + + source_ = raii; + ApplyConverter(); + } + + void RadiographyDicomLayer::Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation) const + { + if (converted_.get() != NULL) + { + if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + unsigned int cropX, cropY, cropWidth, cropHeight; + GetCrop(cropX, cropY, cropWidth, cropHeight); + + AffineTransform2D t = AffineTransform2D::Combine( + viewTransform, GetTransform(), + AffineTransform2D::CreateOffset(cropX, cropY)); + + Orthanc::ImageAccessor cropped; + converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); + + t.Apply(buffer, cropped, interpolation, false); + } + } + + + bool RadiographyDicomLayer::GetDefaultWindowing(float& center, + float& width) const + { + if (converter_.get() != NULL && + converter_->HasDefaultWindow()) + { + center = static_cast(converter_->GetDefaultWindowCenter()); + width = static_cast(converter_->GetDefaultWindowWidth()); + return true; + } + else + { + return false; + } + } + + + bool RadiographyDicomLayer::GetRange(float& minValue, + float& maxValue) const + { + if (converted_.get() != NULL) + { + if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_); + return true; + } + else + { + return false; + } + } + +} diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyDicomLayer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Radiography/RadiographyDicomLayer.h Thu Nov 29 15:11:19 2018 +0100 @@ -0,0 +1,74 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "RadiographyLayer.h" +#include + +namespace OrthancStone +{ + class RadiographyScene; + class DicomFrameConverter; + + class RadiographyDicomLayer : public RadiographyLayer + { + private: + std::auto_ptr source_; // Content of PixelData + std::auto_ptr converter_; + std::auto_ptr converted_; // Float32 + std::string instanceId_; + unsigned int frame_; + + void ApplyConverter(); + + public: + void SetInstance(const std::string& instanceId, unsigned int frame) + { + instanceId_ = instanceId; + frame_ = frame; + } + + std::string GetInstanceId() const + { + return instanceId_; + } + + unsigned int GetFrame() const + { + return frame_; + } + + void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset); + + void SetSourceImage(Orthanc::ImageAccessor* image); // Takes ownership + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation) const; + + virtual bool GetDefaultWindowing(float& center, + float& width) const; + + virtual bool GetRange(float& minValue, + float& maxValue) const; + }; +} diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyLayer.cpp --- a/Framework/Radiography/RadiographyLayer.cpp Wed Nov 28 10:46:32 2018 +0100 +++ b/Framework/Radiography/RadiographyLayer.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -32,18 +32,41 @@ } + RadiographyLayer::Geometry::Geometry() : + hasCrop_(false), + panX_(0), + panY_(0), + angle_(0), + resizeable_(false), + pixelSpacingX_(1), + pixelSpacingY_(1) + { + + } + + void RadiographyLayer::Geometry::GetCrop(unsigned int &x, unsigned int &y, unsigned int &width, unsigned int &height) const + { + if (!hasCrop_) + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); // you should probably use Radiography::GetCrop() or at least call HasCrop() before + + x = cropX_; + y = cropY_; + width = cropWidth_; + height = cropHeight_; + } + void RadiographyLayer::UpdateTransform() { - transform_ = AffineTransform2D::CreateScaling(pixelSpacingX_, pixelSpacingY_); + transform_ = AffineTransform2D::CreateScaling(geometry_.GetPixelSpacingX(), geometry_.GetPixelSpacingY()); double centerX, centerY; GetCenter(centerX, centerY); transform_ = AffineTransform2D::Combine( - AffineTransform2D::CreateOffset(panX_ + centerX, panY_ + centerY), - AffineTransform2D::CreateRotation(angle_), - AffineTransform2D::CreateOffset(-centerX, -centerY), - transform_); + AffineTransform2D::CreateOffset(geometry_.GetPanX() + centerX, geometry_.GetPanY() + centerY), + AffineTransform2D::CreateRotation(geometry_.GetAngle()), + AffineTransform2D::CreateOffset(-centerX, -centerY), + transform_); transformInverse_ = AffineTransform2D::Invert(transform_); } @@ -73,28 +96,28 @@ switch (corner) { - case Corner_TopLeft: - x = dx; - y = dy; - break; + case Corner_TopLeft: + x = dx; + y = dy; + break; - case Corner_TopRight: - x = dx + dwidth; - y = dy; - break; + case Corner_TopRight: + x = dx + dwidth; + y = dy; + break; - case Corner_BottomLeft: - x = dx; - y = dy + dheight; - break; + case Corner_BottomLeft: + x = dx; + y = dy + dheight; + break; - case Corner_BottomRight: - x = dx + dwidth; - y = dy + dheight; - break; + case Corner_BottomRight: + x = dx + dwidth; + y = dy + dheight; + break; - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } transform_.Apply(x, y); @@ -105,7 +128,7 @@ double y) const { transformInverse_.Apply(x, y); - + unsigned int cropX, cropY, cropWidth, cropHeight; GetCrop(cropX, cropY, cropWidth, cropHeight); @@ -127,7 +150,7 @@ cairo_t* cr = context.GetObject(); cairo_set_line_width(cr, 2.0 / zoom); - + double x, y; x = dx; y = dy; @@ -162,18 +185,16 @@ index_(0), hasSize_(false), width_(0), - height_(0), - hasCrop_(false), - pixelSpacingX_(1), - pixelSpacingY_(1), - panX_(0), - panY_(0), - angle_(0), - resizeable_(false) + height_(0) { UpdateTransform(); } + void RadiographyLayer::ResetCrop() + { + geometry_.ResetCrop(); + UpdateTransform(); + } void RadiographyLayer::SetCrop(unsigned int x, unsigned int y, @@ -184,34 +205,36 @@ { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - + if (x + width > width_ || y + height > height_) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - - hasCrop_ = true; - cropX_ = x; - cropY_ = y; - cropWidth_ = width; - cropHeight_ = height; + geometry_.SetCrop(x, y, width, height); UpdateTransform(); } - + void RadiographyLayer::SetGeometry(const Geometry& geometry) + { + geometry_ = geometry; + + if (hasSize_) + { + UpdateTransform(); + } + } + + void RadiographyLayer::GetCrop(unsigned int& x, unsigned int& y, unsigned int& width, unsigned int& height) const { - if (hasCrop_) + if (GetGeometry().HasCrop()) { - x = cropX_; - y = cropY_; - width = cropWidth_; - height = cropHeight_; + GetGeometry().GetCrop(x, y, width, height); } else { @@ -222,10 +245,10 @@ } } - + void RadiographyLayer::SetAngle(double angle) { - angle_ = angle; + geometry_.SetAngle(angle); UpdateTransform(); } @@ -239,7 +262,7 @@ { throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageSize); } - + hasSize_ = true; width_ = width; height_ = height; @@ -251,7 +274,7 @@ Extent2D RadiographyLayer::GetExtent() const { Extent2D extent; - + unsigned int x, y, width, height; GetCrop(x, y, width, height); @@ -264,7 +287,7 @@ AddToExtent(extent, dx + dwidth, dy); AddToExtent(extent, dx, dy + dheight); AddToExtent(extent, dx + dwidth, dy + dheight); - + return extent; } @@ -282,7 +305,7 @@ else { transformInverse_.Apply(sceneX, sceneY); - + int x = static_cast(std::floor(sceneX)); int y = static_cast(std::floor(sceneY)); @@ -320,8 +343,7 @@ void RadiographyLayer::SetPan(double x, double y) { - panX_ = x; - panY_ = y; + geometry_.SetPan(x, y); UpdateTransform(); } @@ -329,8 +351,7 @@ void RadiographyLayer::SetPixelSpacing(double x, double y) { - pixelSpacingX_ = x; - pixelSpacingY_ = y; + geometry_.SetPixelSpacing(x, y); UpdateTransform(); } @@ -352,8 +373,8 @@ GetCrop(cropX, cropY, cropWidth, cropHeight); GetCornerInternal(x, y, corner, cropX, cropY, cropWidth, cropHeight); } - - + + bool RadiographyLayer::LookupCorner(Corner& corner /* out */, double x, double y, @@ -366,26 +387,26 @@ Corner_BottomLeft, Corner_BottomRight }; - + unsigned int cropX, cropY, cropWidth, cropHeight; GetCrop(cropX, cropY, cropWidth, cropHeight); double threshold = Square(viewportDistance / zoom); - + for (size_t i = 0; i < 4; i++) { double cx, cy; GetCornerInternal(cx, cy, CORNERS[i], cropX, cropY, cropWidth, cropHeight); double d = Square(cx - x) + Square(cy - y); - + if (d <= threshold) { corner = CORNERS[i]; return true; } } - + return false; } } diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyLayer.h --- a/Framework/Radiography/RadiographyLayer.h Wed Nov 28 10:46:32 2018 +0100 +++ b/Framework/Radiography/RadiographyLayer.h Thu Nov 29 15:11:19 2018 +0100 @@ -31,24 +31,114 @@ { friend class RadiographyScene; + public: + class Geometry + { + bool hasCrop_; + unsigned int cropX_; + unsigned int cropY_; + unsigned int cropWidth_; + unsigned int cropHeight_; + double panX_; + double panY_; + double angle_; + bool resizeable_; + double pixelSpacingX_; + double pixelSpacingY_; + + public: + Geometry(); + + void ResetCrop() + { + hasCrop_ = false; + } + + void SetCrop(unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height) + { + cropX_ = x; + cropY_ = y; + cropWidth_ = width; + cropHeight_ = height; + } + + bool HasCrop() const + { + return hasCrop_; + } + + void GetCrop(unsigned int& x, + unsigned int& y, + unsigned int& width, + unsigned int& height) const; + + void SetAngle(double angle) + { + angle_ = angle; + } + + double GetAngle() const + { + return angle_; + } + + void SetPan(double x, + double y) + { + panX_ = x; + panY_ = y; + } + + double GetPanX() const + { + return panX_; + } + + double GetPanY() const + { + return panY_; + } + + bool IsResizeable() const + { + return resizeable_; + } + + void SetResizeable(bool resizeable) + { + resizeable_ = resizeable; + } + + void SetPixelSpacing(double x, + double y) + { + pixelSpacingX_ = x; + pixelSpacingY_ = y; + } + + double GetPixelSpacingX() const + { + return pixelSpacingX_; + } + + double GetPixelSpacingY() const + { + return pixelSpacingY_; + } + + }; + private: size_t index_; bool hasSize_; unsigned int width_; unsigned int height_; - bool hasCrop_; - unsigned int cropX_; - unsigned int cropY_; - unsigned int cropWidth_; - unsigned int cropHeight_; AffineTransform2D transform_; AffineTransform2D transformInverse_; - double pixelSpacingX_; - double pixelSpacingY_; - double panX_; - double panY_; - double angle_; - bool resizeable_; + Geometry geometry_; protected: const AffineTransform2D& GetTransform() const @@ -94,11 +184,15 @@ return index_; } - void ResetCrop() + const Geometry& GetGeometry() const { - hasCrop_ = false; + return geometry_; } + void SetGeometry(const Geometry& geometry); + + void ResetCrop(); + void SetCrop(unsigned int x, unsigned int y, unsigned int width, @@ -111,14 +205,22 @@ void SetAngle(double angle); - double GetAngle() const + void SetPan(double x, + double y); + + void SetResizeable(bool resizeable) { - return angle_; + geometry_.SetResizeable(resizeable); } void SetSize(unsigned int width, unsigned int height); + bool HasSize() const + { + return hasSize_; + } + unsigned int GetWidth() const { return width_; @@ -136,32 +238,9 @@ double sceneX, double sceneY) const; - void SetPan(double x, - double y); - void SetPixelSpacing(double x, double y); - double GetPixelSpacingX() const - { - return pixelSpacingX_; - } - - double GetPixelSpacingY() const - { - return pixelSpacingY_; - } - - double GetPanX() const - { - return panX_; - } - - double GetPanY() const - { - return panY_; - } - void GetCenter(double& centerX, double& centerY) const; @@ -175,16 +254,6 @@ double zoom, double viewportDistance) const; - bool IsResizeable() const - { - return resizeable_; - } - - void SetResizeable(bool resizeable) - { - resizeable_ = resizeable; - } - virtual bool GetDefaultWindowing(float& center, float& width) const = 0; diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyLayerCropTracker.cpp --- a/Framework/Radiography/RadiographyLayerCropTracker.cpp Wed Nov 28 10:46:32 2018 +0100 +++ b/Framework/Radiography/RadiographyLayerCropTracker.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -58,7 +58,7 @@ sourceCropWidth_(tracker.cropWidth_), sourceCropHeight_(tracker.cropHeight_) { - tracker.accessor_.GetLayer().GetCrop(targetCropX_, targetCropY_, + tracker.accessor_.GetLayer().GetGeometry().GetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_); } }; @@ -77,7 +77,7 @@ { if (accessor_.IsValid()) { - accessor_.GetLayer().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_); + accessor_.GetLayer().GetGeometry().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_); } } diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyLayerMoveTracker.cpp --- a/Framework/Radiography/RadiographyLayerMoveTracker.cpp Wed Nov 28 10:46:32 2018 +0100 +++ b/Framework/Radiography/RadiographyLayerMoveTracker.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -51,8 +51,8 @@ RadiographySceneCommand(tracker.accessor_), sourceX_(tracker.panX_), sourceY_(tracker.panY_), - targetX_(tracker.accessor_.GetLayer().GetPanX()), - targetY_(tracker.accessor_.GetLayer().GetPanY()) + targetX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()), + targetY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY()) { } }; @@ -72,8 +72,8 @@ { if (accessor_.IsValid()) { - panX_ = accessor_.GetLayer().GetPanX(); - panY_ = accessor_.GetLayer().GetPanY(); + panX_ = accessor_.GetLayer().GetGeometry().GetPanX(); + panY_ = accessor_.GetLayer().GetGeometry().GetPanY(); } } diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyLayerResizeTracker.cpp --- a/Framework/Radiography/RadiographyLayerResizeTracker.cpp Wed Nov 28 10:46:32 2018 +0100 +++ b/Framework/Radiography/RadiographyLayerResizeTracker.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -73,10 +73,10 @@ sourceSpacingY_(tracker.originalSpacingY_), sourcePanX_(tracker.originalPanX_), sourcePanY_(tracker.originalPanY_), - targetSpacingX_(tracker.accessor_.GetLayer().GetPixelSpacingX()), - targetSpacingY_(tracker.accessor_.GetLayer().GetPixelSpacingY()), - targetPanX_(tracker.accessor_.GetLayer().GetPanX()), - targetPanY_(tracker.accessor_.GetLayer().GetPanY()) + targetSpacingX_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingX()), + targetSpacingY_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingY()), + targetPanX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()), + targetPanY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY()) { } }; @@ -94,12 +94,12 @@ roundScaling_(roundScaling) { if (accessor_.IsValid() && - accessor_.GetLayer().IsResizeable()) + accessor_.GetLayer().GetGeometry().IsResizeable()) { - originalSpacingX_ = accessor_.GetLayer().GetPixelSpacingX(); - originalSpacingY_ = accessor_.GetLayer().GetPixelSpacingY(); - originalPanX_ = accessor_.GetLayer().GetPanX(); - originalPanY_ = accessor_.GetLayer().GetPanY(); + originalSpacingX_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingX(); + originalSpacingY_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingY(); + originalPanX_ = accessor_.GetLayer().GetGeometry().GetPanX(); + originalPanY_ = accessor_.GetLayer().GetGeometry().GetPanY(); switch (corner) { @@ -149,7 +149,7 @@ void RadiographyLayerResizeTracker::MouseUp() { if (accessor_.IsValid() && - accessor_.GetLayer().IsResizeable()) + accessor_.GetLayer().GetGeometry().IsResizeable()) { undoRedoStack_.Add(new UndoRedoCommand(*this)); } @@ -164,7 +164,7 @@ static const double ROUND_SCALING = 0.1; if (accessor_.IsValid() && - accessor_.GetLayer().IsResizeable()) + accessor_.GetLayer().GetGeometry().IsResizeable()) { double scaling = ComputeDistance(oppositeX_, oppositeY_, sceneX, sceneY) * baseScaling_; @@ -180,8 +180,8 @@ // Keep the opposite corner at a fixed location double ox, oy; layer.GetCorner(ox, oy, oppositeCorner_); - layer.SetPan(layer.GetPanX() + oppositeX_ - ox, - layer.GetPanY() + oppositeY_ - oy); + layer.SetPan(layer.GetGeometry().GetPanX() + oppositeX_ - ox, + layer.GetGeometry().GetPanY() + oppositeY_ - oy); } } } diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyLayerRotateTracker.cpp --- a/Framework/Radiography/RadiographyLayerRotateTracker.cpp Wed Nov 28 10:46:32 2018 +0100 +++ b/Framework/Radiography/RadiographyLayerRotateTracker.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -58,7 +58,7 @@ UndoRedoCommand(const RadiographyLayerRotateTracker& tracker) : RadiographySceneCommand(tracker.accessor_), sourceAngle_(tracker.originalAngle_), - targetAngle_(tracker.accessor_.GetLayer().GetAngle()) + targetAngle_(tracker.accessor_.GetLayer().GetGeometry().GetAngle()) { } }; @@ -100,7 +100,7 @@ if (accessor_.IsValid()) { accessor_.GetLayer().GetCenter(centerX_, centerY_); - originalAngle_ = accessor_.GetLayer().GetAngle(); + originalAngle_ = accessor_.GetLayer().GetGeometry().GetAngle(); double sceneX, sceneY; view.MapDisplayToScene(sceneX, sceneY, x, y); diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyScene.cpp --- a/Framework/Radiography/RadiographyScene.cpp Wed Nov 28 10:46:32 2018 +0100 +++ b/Framework/Radiography/RadiographyScene.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -21,6 +21,9 @@ #include "RadiographyScene.h" +#include "RadiographyAlphaLayer.h" +#include "RadiographyDicomLayer.h" +#include "RadiographyTextLayer.h" #include "../Toolbox/DicomFrameConverter.h" #include @@ -121,288 +124,6 @@ } } - - - class RadiographyScene::AlphaLayer : public RadiographyLayer - { - private: - const RadiographyScene& scene_; - std::auto_ptr alpha_; // Grayscale8 - bool useWindowing_; - float foreground_; - - public: - AlphaLayer(const RadiographyScene& scene) : - scene_(scene), - useWindowing_(true), - foreground_(0) - { - } - - - void SetForegroundValue(float foreground) - { - useWindowing_ = false; - foreground_ = foreground; - } - - - void SetAlpha(Orthanc::ImageAccessor* image) - { - std::auto_ptr raii(image); - - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - if (image->GetFormat() != Orthanc::PixelFormat_Grayscale8) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - SetSize(image->GetWidth(), image->GetHeight()); - alpha_ = raii; - } - - - void LoadText(const Orthanc::Font& font, - const std::string& utf8) - { - SetAlpha(font.RenderAlpha(utf8)); - } - - - virtual bool GetDefaultWindowing(float& center, - float& width) const - { - return false; - } - - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation) const - { - if (alpha_.get() == NULL) - { - return; - } - - if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - unsigned int cropX, cropY, cropWidth, cropHeight; - GetCrop(cropX, cropY, cropWidth, cropHeight); - - const AffineTransform2D t = AffineTransform2D::Combine( - viewTransform, GetTransform(), - AffineTransform2D::CreateOffset(cropX, cropY)); - - Orthanc::ImageAccessor cropped; - alpha_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); - - Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); - - t.Apply(tmp, cropped, interpolation, true /* clear */); - - // Blit - const unsigned int width = buffer.GetWidth(); - const unsigned int height = buffer.GetHeight(); - - float value = foreground_; - - if (useWindowing_) - { - float center, width; - if (scene_.GetWindowing(center, width)) - { - value = center + width / 2.0f; - } - } - - for (unsigned int y = 0; y < height; y++) - { - float *q = reinterpret_cast(buffer.GetRow(y)); - const uint8_t *p = reinterpret_cast(tmp.GetRow(y)); - - for (unsigned int x = 0; x < width; x++, p++, q++) - { - float a = static_cast(*p) / 255.0f; - - *q = (a * value + (1.0f - a) * (*q)); - } - } - } - - - virtual bool GetRange(float& minValue, - float& maxValue) const - { - if (useWindowing_) - { - return false; - } - else - { - minValue = 0; - maxValue = 0; - - if (foreground_ < 0) - { - minValue = foreground_; - } - - if (foreground_ > 0) - { - maxValue = foreground_; - } - - return true; - } - } - }; - - - - class RadiographyScene::DicomLayer : public RadiographyLayer - { - private: - std::auto_ptr source_; // Content of PixelData - std::auto_ptr converter_; - std::auto_ptr converted_; // Float32 - - static OrthancPlugins::DicomTag ConvertTag(const Orthanc::DicomTag& tag) - { - return OrthancPlugins::DicomTag(tag.GetGroup(), tag.GetElement()); - } - - - void ApplyConverter() - { - if (source_.get() != NULL && - converter_.get() != NULL) - { - converted_.reset(converter_->ConvertFrame(*source_)); - } - } - - public: - void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) - { - converter_.reset(new DicomFrameConverter); - converter_->ReadParameters(dataset); - ApplyConverter(); - - std::string tmp; - Vector pixelSpacing; - - if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PIXEL_SPACING)) && - LinearAlgebra::ParseVector(pixelSpacing, tmp) && - pixelSpacing.size() == 2) - { - SetPixelSpacing(pixelSpacing[0], pixelSpacing[1]); - } - - //SetPan(-0.5 * GetPixelSpacingX(), -0.5 * GetPixelSpacingY()); - - OrthancPlugins::DicomDatasetReader reader(dataset); - - unsigned int width, height; - if (!reader.GetUnsignedIntegerValue(width, ConvertTag(Orthanc::DICOM_TAG_COLUMNS)) || - !reader.GetUnsignedIntegerValue(height, ConvertTag(Orthanc::DICOM_TAG_ROWS))) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); - } - else - { - SetSize(width, height); - } - } - - - void SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership - { - std::auto_ptr raii(image); - - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - SetSize(image->GetWidth(), image->GetHeight()); - - source_ = raii; - ApplyConverter(); - } - - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation) const - { - if (converted_.get() != NULL) - { - if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - unsigned int cropX, cropY, cropWidth, cropHeight; - GetCrop(cropX, cropY, cropWidth, cropHeight); - - AffineTransform2D t = AffineTransform2D::Combine( - viewTransform, GetTransform(), - AffineTransform2D::CreateOffset(cropX, cropY)); - - Orthanc::ImageAccessor cropped; - converted_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); - - t.Apply(buffer, cropped, interpolation, false); - } - } - - - virtual bool GetDefaultWindowing(float& center, - float& width) const - { - if (converter_.get() != NULL && - converter_->HasDefaultWindow()) - { - center = static_cast(converter_->GetDefaultWindowCenter()); - width = static_cast(converter_->GetDefaultWindowWidth()); - return true; - } - else - { - return false; - } - } - - - virtual bool GetRange(float& minValue, - float& maxValue) const - { - if (converted_.get() != NULL) - { - if (converted_->GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - Orthanc::ImageProcessing::GetMinMaxFloatValue(minValue, maxValue, *converted_); - return true; - } - else - { - return false; - } - } - }; - - RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer) { if (layer == NULL) @@ -418,8 +139,8 @@ raii->SetIndex(index); layers_[index] = raii.release(); - EmitMessage(GeometryChangedMessage(*this)); - EmitMessage(ContentChangedMessage(*this)); + EmitMessage(GeometryChangedMessage(*this, *layer)); + EmitMessage(ContentChangedMessage(*this, *layer)); return *layer; } @@ -445,6 +166,14 @@ } } + void RadiographyScene::GetLayersIndexes(std::vector& output) const + { + for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) + { + output.push_back(it->first); + } + } + void RadiographyScene::RemoveLayer(size_t layerIndex) { LOG(INFO) << "Removing layer: " << layerIndex; @@ -459,13 +188,14 @@ LOG(INFO) << "Removing layer, there are now : " << countLayers_ << " layers"; } - RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) + const RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) const { if (layerIndex > countLayers_) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - return *(layers_[layerIndex]); + + return *(layers_.at(layerIndex)); } bool RadiographyScene::GetWindowing(float& center, @@ -505,17 +235,23 @@ RadiographyLayer& RadiographyScene::LoadText(const Orthanc::Font& font, - const std::string& utf8) + const std::string& utf8, + RadiographyLayer::Geometry* geometry) { - std::auto_ptr alpha(new AlphaLayer(*this)); + std::auto_ptr alpha(new RadiographyTextLayer(*this)); alpha->LoadText(font, utf8); + if (geometry != NULL) + { + alpha->SetGeometry(*geometry); + } return RegisterLayer(alpha.release()); } RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, - unsigned int height) + unsigned int height, + RadiographyLayer::Geometry* geometry) { std::auto_ptr block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); @@ -538,19 +274,34 @@ Orthanc::ImageProcessing::Set(region, color); } - std::auto_ptr alpha(new AlphaLayer(*this)); - alpha->SetAlpha(block.release()); + return LoadAlphaBitmap(block.release(), geometry); + } + + RadiographyLayer& RadiographyScene::LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, RadiographyLayer::Geometry *geometry) + { + std::auto_ptr alpha(new RadiographyAlphaLayer(*this)); + alpha->SetAlpha(bitmap); + if (geometry != NULL) + { + alpha->SetGeometry(*geometry); + } return RegisterLayer(alpha.release()); } - RadiographyLayer& RadiographyScene::LoadDicomFrame(OrthancApiClient& orthanc, const std::string& instance, unsigned int frame, - bool httpCompression) + bool httpCompression, + RadiographyLayer::Geometry* geometry) { - RadiographyLayer& layer = RegisterLayer(new DicomLayer); + RadiographyDicomLayer& layer = dynamic_cast(RegisterLayer(new RadiographyDicomLayer)); + layer.SetInstance(instance, frame); + + if (geometry != NULL) + { + layer.SetGeometry(*geometry); + } { IWebService::HttpHeaders headers; @@ -588,7 +339,7 @@ RadiographyLayer& RadiographyScene::LoadDicomWebFrame(IWebService& web) { - RadiographyLayer& layer = RegisterLayer(new DicomLayer); + RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer); return layer; @@ -610,7 +361,7 @@ assert(layer->second != NULL); OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); - dynamic_cast(layer->second)->SetDicomTags(dicom); + dynamic_cast(layer->second)->SetDicomTags(dicom); float c, w; if (!hasWindowing_ && @@ -621,7 +372,7 @@ windowingWidth_ = w; } - EmitMessage(GeometryChangedMessage(*this)); + EmitMessage(GeometryChangedMessage(*this, *(layer->second))); } } @@ -646,9 +397,9 @@ std::auto_ptr reader(new Orthanc::PamReader); reader->ReadFromMemory(content); - dynamic_cast(layer->second)->SetSourceImage(reader.release()); + dynamic_cast(layer->second)->SetSourceImage(reader.release()); - EmitMessage(ContentChangedMessage(*this)); + EmitMessage(ContentChangedMessage(*this, *(layer->second))); } } @@ -672,7 +423,7 @@ const AffineTransform2D& viewTransform, ImageInterpolation interpolation) const { - Orthanc::ImageProcessing::Set(buffer, 0); + Orthanc::ImageProcessing::Set(buffer, 0); // TODO: get background color (depending on inverted state) // Render layers in the background-to-foreground order for (size_t index = 0; index < countLayers_; index++) @@ -770,7 +521,7 @@ { Json::Value createDicomRequestContent; - Export(createDicomRequestContent, dicom, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam); + ExportToCreateDicomRequest(createDicomRequestContent, dicom, pixelSpacingX, pixelSpacingY, invert, interpolation, usePam); if (!parentOrthancId.empty()) { @@ -786,7 +537,7 @@ // Export using PAM is faster than using PNG, but requires Orthanc // core >= 1.4.3 - void RadiographyScene::Export(Json::Value& createDicomRequestContent, + void RadiographyScene::ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, const Orthanc::DicomMap& dicom, double pixelSpacingX, double pixelSpacingY, diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyScene.h --- a/Framework/Radiography/RadiographyScene.h Wed Nov 28 10:46:32 2018 +0100 +++ b/Framework/Radiography/RadiographyScene.h Thu Nov 29 15:11:19 2018 +0100 @@ -32,8 +32,46 @@ public IObservable { public: - typedef OriginMessage GeometryChangedMessage; - typedef OriginMessage ContentChangedMessage; + class GeometryChangedMessage : + public OriginMessage + { + private: + RadiographyLayer& layer_; + + public: + GeometryChangedMessage(const RadiographyScene& origin, + RadiographyLayer& layer) : + OriginMessage(origin), + layer_(layer) + { + } + + RadiographyLayer& GetLayer() const + { + return layer_; + } + }; + + class ContentChangedMessage : + public OriginMessage + { + private: + RadiographyLayer& layer_; + + public: + ContentChangedMessage(const RadiographyScene& origin, + RadiographyLayer& layer) : + OriginMessage(origin), + layer_(layer) + { + } + + RadiographyLayer& GetLayer() const + { + return layer_; + } + }; + class LayerAccessor : public boost::noncopyable { @@ -69,9 +107,6 @@ private: - class AlphaLayer; - class DicomLayer; - typedef std::map Layers; size_t countLayers_; @@ -105,26 +140,29 @@ float width); RadiographyLayer& LoadText(const Orthanc::Font& font, - const std::string& utf8); + const std::string& utf8, + RadiographyLayer::Geometry* geometry); RadiographyLayer& LoadTestBlock(unsigned int width, - unsigned int height); - + unsigned int height, + RadiographyLayer::Geometry* geometry); + + RadiographyLayer& LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, // takes ownership + RadiographyLayer::Geometry* geometry); + RadiographyLayer& LoadDicomFrame(OrthancApiClient& orthanc, const std::string& instance, unsigned int frame, - bool httpCompression); + bool httpCompression, + RadiographyLayer::Geometry* geometry); // pass NULL if you want default geometry RadiographyLayer& LoadDicomWebFrame(IWebService& web); void RemoveLayer(size_t layerIndex); - size_t GetLayerCount() - { - return countLayers_; - } + const RadiographyLayer& GetLayer(size_t layerIndex) const; - RadiographyLayer& GetLayer(size_t layerIndex); + void GetLayersIndexes(std::vector& output) const; Extent2D GetSceneExtent() const; @@ -155,7 +193,7 @@ bool usePam); // temporary version used by VSOL because we need to send the same request at another url - void Export(Json::Value& createDicomRequestContent, + void ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, const Orthanc::DicomMap& dicom, double pixelSpacingX, double pixelSpacingY, diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographySceneReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Radiography/RadiographySceneReader.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -0,0 +1,112 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "RadiographySceneReader.h" + +#include +#include +#include +#include + +namespace OrthancStone +{ + void RadiographySceneReader::Read(const Json::Value& input) + { + unsigned int version = input["version"].asUInt(); + + if (version != 1) + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + + for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) + { + const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; + RadiographyLayer::Geometry geometry; + + if (jsonLayer["type"].asString() == "dicom") + { + ReadLayerGeometry(geometry, jsonLayer); + scene_.LoadDicomFrame(orthancApiClient_, jsonLayer["instanceId"].asString(), jsonLayer["frame"].asUInt(), true, &geometry); + } + else if (jsonLayer["type"].asString() == "text") + { + if (fontRegistry_ == NULL || fontRegistry_->GetSize() == 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); // you must provide a FontRegistry if you need to re-create text layers. + } + + ReadLayerGeometry(geometry, jsonLayer); + const Orthanc::Font* font = fontRegistry_->FindFont(jsonLayer["fontName"].asString()); + if (font == NULL) // if not found, take the first font in the registry + { + font = &(fontRegistry_->GetFont(0)); + } + scene_.LoadText(*font, jsonLayer["text"].asString(), &geometry); + } + else if (jsonLayer["type"].asString() == "alpha") + { + ReadLayerGeometry(geometry, jsonLayer); + + const std::string& pngContentBase64 = jsonLayer["content"].asString(); + std::string pngContent; + std::string mimeType; + Orthanc::Toolbox::DecodeDataUriScheme(mimeType, pngContent, pngContentBase64); + + std::auto_ptr image; + if (mimeType == "image/png") + { + image.reset(new Orthanc::PngReader()); + dynamic_cast(image.get())->ReadFromMemory(pngContent); + } + else + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + + RadiographyAlphaLayer& layer = dynamic_cast(scene_.LoadAlphaBitmap(image.release(), &geometry)); + + if (!jsonLayer["isUsingWindowing"].asBool()) + { + layer.SetForegroundValue((float)(jsonLayer["foreground"].asDouble())); + } + } + else + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + + void RadiographySceneReader::ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& jsonLayer) + { + {// crop + unsigned int x, y, width, height; + if (jsonLayer["crop"]["hasCrop"].asBool()) + { + x = jsonLayer["crop"]["x"].asUInt(); + y = jsonLayer["crop"]["y"].asUInt(); + width = jsonLayer["crop"]["width"].asUInt(); + height = jsonLayer["crop"]["height"].asUInt(); + geometry.SetCrop(x, y, width, height); + } + } + + geometry.SetAngle(jsonLayer["angle"].asDouble()); + geometry.SetResizeable(jsonLayer["isResizable"].asBool()); + geometry.SetPan(jsonLayer["pan"]["x"].asDouble(), jsonLayer["pan"]["y"].asDouble()); + geometry.SetPixelSpacing(jsonLayer["pixelSpacing"]["x"].asDouble(), jsonLayer["pixelSpacing"]["y"].asDouble()); + } +} diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographySceneReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Radiography/RadiographySceneReader.h Thu Nov 29 15:11:19 2018 +0100 @@ -0,0 +1,59 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "RadiographyScene.h" +#include "RadiographyAlphaLayer.h" +#include "RadiographyDicomLayer.h" +#include "RadiographyTextLayer.h" +#include +#include + +namespace OrthancStone +{ + class OrthancApiClient; + + class RadiographySceneReader : public boost::noncopyable + { + RadiographyScene& scene_; + OrthancApiClient& orthancApiClient_; + const Orthanc::FontRegistry* fontRegistry_; + + public: + RadiographySceneReader(RadiographyScene& scene, OrthancApiClient& orthancApiClient) : + scene_(scene), + orthancApiClient_(orthancApiClient), + fontRegistry_(NULL) + { + } + + void Read(const Json::Value& output); + + void SetFontRegistry(const Orthanc::FontRegistry& fontRegistry) + { + fontRegistry_ = &fontRegistry; + } + + private: + void ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& output); + }; +} diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographySceneWriter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Radiography/RadiographySceneWriter.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -0,0 +1,136 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "RadiographySceneWriter.h" + +#include +#include +#include + +namespace OrthancStone +{ + void RadiographySceneWriter::Write(Json::Value& output, const RadiographyScene& scene) + { + output["version"] = 1; + output["layers"] = Json::arrayValue; + + std::vector layersIndexes; + scene.GetLayersIndexes(layersIndexes); + + for (std::vector::iterator itLayerIndex = layersIndexes.begin(); itLayerIndex < layersIndexes.end(); itLayerIndex++) + { + Json::Value layer; + WriteLayer(layer, scene.GetLayer(*itLayerIndex)); + output["layers"].append(layer); + } + } + + void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyDicomLayer& layer) + { + output["type"] = "dicom"; + output["instanceId"] = layer.GetInstanceId(); + output["frame"] = layer.GetFrame(); + } + + void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyTextLayer& layer) + { + output["type"] = "text"; + output["text"] = layer.GetText(); + output["fontName"] = layer.GetFontName(); + } + + void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyAlphaLayer& layer) + { + output["type"] = "alpha"; + + //output["bitmap"] = + const Orthanc::ImageAccessor& alpha = layer.GetAlpha(); + + Orthanc::PngWriter pngWriter; + std::string pngContent; + std::string pngContentBase64; + pngWriter.WriteToMemory(pngContent, alpha); + + Orthanc::Toolbox::EncodeDataUriScheme(pngContentBase64, "image/png", pngContent); + output["content"] = pngContentBase64; + output["foreground"] = layer.GetForegroundValue(); + output["isUsingWindowing"] = layer.IsUsingWindowing(); + } + + void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyLayer& layer) + { + const RadiographyLayer::Geometry& geometry = layer.GetGeometry(); + + {// crop + Json::Value crop; + if (geometry.HasCrop()) + { + unsigned int x, y, width, height; + geometry.GetCrop(x, y, width, height); + crop["hasCrop"] = true; + crop["x"] = x; + crop["y"] = y; + crop["width"] = width; + crop["height"] = height; + } + else + { + crop["hasCrop"] = false; + } + + output["crop"] = crop; + } + + output["angle"] = geometry.GetAngle(); + output["isResizable"] = geometry.IsResizeable(); + + {// pan + Json::Value pan; + pan["x"] = geometry.GetPanX(); + pan["y"] = geometry.GetPanY(); + output["pan"] = pan; + } + + {// pixelSpacing + Json::Value pan; + pan["x"] = geometry.GetPixelSpacingX(); + pan["y"] = geometry.GetPixelSpacingY(); + output["pixelSpacing"] = pan; + } + + if (dynamic_cast(&layer) != NULL) + { + WriteLayer(output, dynamic_cast(layer)); + } + else if (dynamic_cast(&layer) != NULL) + { + WriteLayer(output, dynamic_cast(layer)); + } + else if (dynamic_cast(&layer) != NULL) + { + WriteLayer(output, dynamic_cast(layer)); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } +} diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographySceneWriter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Radiography/RadiographySceneWriter.h Thu Nov 29 15:11:19 2018 +0100 @@ -0,0 +1,50 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "RadiographyScene.h" +#include "RadiographyAlphaLayer.h" +#include "RadiographyDicomLayer.h" +#include "RadiographyTextLayer.h" +#include + +namespace OrthancStone +{ + class RadiographyScene; + + class RadiographySceneWriter : public boost::noncopyable + { + + public: + RadiographySceneWriter() + { + } + + void Write(Json::Value& output, const RadiographyScene& scene); + + private: + void WriteLayer(Json::Value& output, const RadiographyLayer& layer); + void WriteLayer(Json::Value& output, const RadiographyDicomLayer& layer); + void WriteLayer(Json::Value& output, const RadiographyTextLayer& layer); + void WriteLayer(Json::Value& output, const RadiographyAlphaLayer& layer); + }; +} diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyTextLayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Radiography/RadiographyTextLayer.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -0,0 +1,36 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#include "RadiographyTextLayer.h" + +#include "RadiographyScene.h" + +namespace OrthancStone +{ + void RadiographyTextLayer::LoadText(const Orthanc::Font& font, + const std::string& utf8) + { + text_ = utf8; + fontName_ = font.GetName(); + + SetAlpha(font.RenderAlpha(utf8)); + } +} diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Radiography/RadiographyTextLayer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Radiography/RadiographyTextLayer.h Thu Nov 29 15:11:19 2018 +0100 @@ -0,0 +1,55 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2018 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include "RadiographyAlphaLayer.h" + +namespace OrthancStone +{ + class RadiographyScene; + + class RadiographyTextLayer : public RadiographyAlphaLayer + { + private: + std::string text_; + std::string fontName_; + + public: + RadiographyTextLayer(const RadiographyScene& scene) : + RadiographyAlphaLayer(scene) + { + } + + void LoadText(const Orthanc::Font& font, + const std::string& utf8); + + const std::string& GetText() const + { + return text_; + } + + const std::string& GetFontName() const + { + return fontName_; + } + }; +} diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/StoneEnumerations.h --- a/Framework/StoneEnumerations.h Wed Nov 28 10:46:32 2018 +0100 +++ b/Framework/StoneEnumerations.h Thu Nov 29 15:11:19 2018 +0100 @@ -157,6 +157,9 @@ MessageType_OrthancApi_GenericHttpError_Ready, MessageType_OrthancApi_GenericEmptyResponse_Ready, + MessageType_Scene_GeometryChanged, + MessageType_Scene_ContentChanged, + MessageType_ViewportChanged, // used in unit tests only diff -r c7fb700a7d12 -r b85f635f1eb5 Framework/Toolbox/OrthancApiClient.cpp --- a/Framework/Toolbox/OrthancApiClient.cpp Wed Nov 28 10:46:32 2018 +0100 +++ b/Framework/Toolbox/OrthancApiClient.cpp Thu Nov 29 15:11:19 2018 +0100 @@ -170,7 +170,7 @@ class OrthancApiClient::CachedHttpRequestSuccessMessage { protected: - const std::string& uri_; + std::string uri_; void* answer_; size_t answerSize_; IWebService::HttpHeaders answerHeaders_; diff -r c7fb700a7d12 -r b85f635f1eb5 Resources/CMake/OrthancStoneConfiguration.cmake --- a/Resources/CMake/OrthancStoneConfiguration.cmake Wed Nov 28 10:46:32 2018 +0100 +++ b/Resources/CMake/OrthancStoneConfiguration.cmake Thu Nov 29 15:11:19 2018 +0100 @@ -246,6 +246,8 @@ ${ORTHANC_STONE_ROOT}/Framework/Layers/LineMeasureTracker.cpp ${ORTHANC_STONE_ROOT}/Framework/Layers/RenderStyle.cpp ${ORTHANC_STONE_ROOT}/Framework/Layers/SliceOutlineRenderer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyAlphaLayer.cpp + ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyDicomLayer.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayer.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerCropTracker.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerMoveTracker.cpp @@ -253,6 +255,9 @@ ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyLayerRotateTracker.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyScene.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographySceneCommand.cpp + ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographySceneReader.cpp + ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographySceneWriter.cpp + ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyTextLayer.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyWidget.cpp ${ORTHANC_STONE_ROOT}/Framework/Radiography/RadiographyWindowingTracker.cpp ${ORTHANC_STONE_ROOT}/Framework/SmartLoader.cpp