# HG changeset patch # User Alain Mazy # Date 1588185789 -7200 # Node ID c5403d52078cd9171c835e405899781083b1b0cb # Parent 1c2d065ba3722fc1bcf14ac7016a957e2526a342 moved Radiography into Deprecated diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyAlphaLayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyAlphaLayer.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,152 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 +#include + +#include "../Toolbox/ImageGeometry.h" + +namespace OrthancStone +{ + + void RadiographyAlphaLayer::SetAlpha(Orthanc::ImageAccessor* image) + { + std::unique_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()); + +#if __cplusplus < 201103L + alpha_.reset(raii.release()); +#else + alpha_ = std::move(raii); +#endif + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyAlphaLayer::Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) 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); + + unsigned int x1, y1, x2, y2; + + if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, + t.GetHomogeneousMatrix(), + cropped.GetWidth(), + cropped.GetHeight(), + buffer.GetWidth(), + buffer.GetHeight())) + { + return; // layer is outside the buffer + } + + t.Apply(tmp, cropped, interpolation, true /* clear */); + + float value = foreground_; + + if (!applyWindowing) // if applying the windowing, it means we are ie rendering the image for a realtime visualization -> the foreground_ value is the value we want to see on the screen -> don't change it + { + // if not applying the windowing, it means ie that we are saving a dicom image to file and the windowing will be applied by a viewer later on -> we want the "foreground" value to be correct once the windowing will be applied + value = windowCenter - windowWidth/2 + (foreground_ / 65535.0f) * windowWidth; + + if (value < 0.0f) + { + value = 0.0f; + } + if (value > 65535.0f) + { + value = 65535.0f; + } + } + + for (unsigned int y = y1; y <= y2; y++) + { + float *q = reinterpret_cast(buffer.GetRow(y)) + x1; + const uint8_t *p = reinterpret_cast(tmp.GetRow(y)) + x1; + + for (unsigned int x = x1; x <= x2; 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 + { + minValue = 0; + maxValue = 0; + + if (foreground_ < 0) + { + minValue = foreground_; + } + + if (foreground_ > 0) + { + maxValue = foreground_; + } + + return true; + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyAlphaLayer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyAlphaLayer.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,85 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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; + + // 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: + std::unique_ptr alpha_; // Grayscale8 in the range [0, 255] 0 = transparent, 255 = opaque -> the foreground value will be displayed + float foreground_; // in the range [0.0, 65535.0] + + public: + RadiographyAlphaLayer(const RadiographyScene& scene) : + RadiographyLayer(scene), + foreground_(0) + { + } + + + void SetForegroundValue(float foreground) + { + foreground_ = foreground; + } + + float GetForegroundValue() const + { + return foreground_; + } + + 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, + float windowCenter, + float windowWidth, + bool applyWindowing) const; + + virtual bool GetRange(float& minValue, + float& maxValue) const; + + const Orthanc::ImageAccessor& GetAlpha() const + { + return *(alpha_.get()); + } + + + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyDicomLayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyDicomLayer.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,264 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Deprecated/Toolbox/DicomFrameConverter.h" + +#include +#include +#include +#include +#include "../Toolbox/ImageGeometry.h" + +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_)); + } + } + + + RadiographyDicomLayer::RadiographyDicomLayer(const RadiographyScene& scene) : + RadiographyLayer(scene) + { + + } + + void RadiographyDicomLayer::SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) + { + converter_.reset(new Deprecated::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]); + } + + 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); + } + + if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION))) + { + if (tmp == "MONOCHROME1") + { + SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome1); + } + else if (tmp == "MONOCHROME2") + { + SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome2); + } + } + } + + void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership + { + std::unique_ptr raii(image); + + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + SetSize(image->GetWidth(), image->GetHeight()); + +#if __cplusplus < 201103L + source_.reset(raii.release()); +#else + source_ = std::move(raii); +#endif + + ApplyConverter(); + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent) // Takes ownership + { + std::unique_ptr raii(image); + + if (image == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + SetSize(image->GetWidth(), image->GetHeight(), false); + +#if __cplusplus < 201103L + source_.reset(raii.release()); +#else + source_ = std::move(raii); +#endif + + ApplyConverter(); + + SetPixelSpacing(newPixelSpacingX, newPixelSpacingY, false); + + if (emitLayerEditedEvent) + { + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + } + + + void RadiographyDicomLayer::SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter) + { + converter_.reset(converter); + } + + void RadiographyDicomLayer::Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) 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); + + unsigned int x1, y1, x2, y2; + if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, + t.GetHomogeneousMatrix(), + cropped.GetWidth(), + cropped.GetHeight(), + buffer.GetWidth(), + buffer.GetHeight())) + { + return; // layer is outside the buffer + } + + t.Apply(buffer, cropped, interpolation, false); + + if (applyWindowing) + { + // apply windowing but stay in the range [0.0, 65535.0] + float w0 = windowCenter - windowWidth / 2.0f; + float w1 = windowCenter + windowWidth / 2.0f; + + if (windowWidth >= 0.001f) // Avoid division by zero at (*) + { + float scaling = 1.0f / (w1 - w0) * 65535.0f; + for (unsigned int y = y1; y <= y2; y++) + { + float* p = reinterpret_cast(buffer.GetRow(y)) + x1; + + for (unsigned int x = x1; x <= x2; x++, p++) + { + if (*p >= w1) + { + *p = 65535.0; + } + else if (*p <= w0) + { + *p = 0; + } + else + { + // https://en.wikipedia.org/wiki/Linear_interpolation + *p = scaling * (*p - w0); // (*) + } + } + } + } + } + + } + } + + + 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 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyDicomLayer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyDicomLayer.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,105 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Deprecated/Toolbox/DicomFrameConverter.h" +#include "RadiographyLayer.h" + +#include + +namespace OrthancStone +{ + class RadiographyScene; + + class RadiographyDicomLayer : public RadiographyLayer + { + private: + std::unique_ptr source_; // Content of PixelData + std::unique_ptr converter_; + std::unique_ptr converted_; // Float32 + std::string instanceId_; + unsigned int frame_; + + void ApplyConverter(); + + public: + RadiographyDicomLayer(const RadiographyScene& scene); + + 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_; + } + + virtual size_t GetApproximateMemoryUsage() const + { + size_t size = 0; + if (source_.get() != NULL) + { + size += source_->GetPitch() * source_->GetHeight(); + } + if (converted_.get() != NULL) + { + size += converted_->GetPitch() * converted_->GetHeight(); + } + + return size; + } + + + void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset); + + void SetSourceImage(Orthanc::ImageAccessor* image); // Takes ownership + + void SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent = true); // Takes ownership + + const Orthanc::ImageAccessor* GetSourceImage() const {return source_.get();} // currently need this access to serialize scene in plain old data to send to a WASM worker + + const Deprecated::DicomFrameConverter& GetDicomFrameConverter() const {return *converter_;} // currently need this access to serialize scene in plain old data to send to a WASM worker + + // Takes ownership + void SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter); + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const; + + virtual bool GetDefaultWindowing(float& center, + float& width) const; + + virtual bool GetRange(float& minValue, + float& maxValue) const; + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayer.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,402 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyLayer.h" + +#include + + +namespace OrthancStone +{ + static double Square(double x) + { + return x * x; + } + + + RadiographyLayer::Geometry::Geometry() : + hasCrop_(false), + flipVertical_(false), + flipHorizontal_(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 RadiographyLayer::GetCrop() or at least call HasCrop() before + + x = cropX_; + y = cropY_; + width = cropWidth_; + height = cropHeight_; + } + + void RadiographyLayer::UpdateTransform() + { + // important to update transform_ before getting the center to use the right scaling !!! + transform_ = AffineTransform2D::CreateScaling(geometry_.GetScalingX(), geometry_.GetScalingY()); + + double centerX, centerY; + GetCenter(centerX, centerY); + + transform_ = AffineTransform2D::Combine( + AffineTransform2D::CreateOffset(geometry_.GetPanX(), geometry_.GetPanY()), + AffineTransform2D::CreateRotation(geometry_.GetAngle(), centerX, centerY), + transform_); + + transformInverse_ = AffineTransform2D::Invert(transform_); + } + + + void RadiographyLayer::AddToExtent(Extent2D& extent, + double x, + double y) const + { + GetTransform().Apply(x, y); + extent.AddPoint(x, y); + } + + bool RadiographyLayer::Contains(double x, + double y) const + { + GetTransformInverse().Apply(x, y); + + unsigned int cropX, cropY, cropWidth, cropHeight; + GetCrop(cropX, cropY, cropWidth, cropHeight); + + return (x >= cropX && x <= cropX + cropWidth && + y >= cropY && y <= cropY + cropHeight); + } + + + void RadiographyLayer::DrawBorders(CairoContext& context, + double zoom) + { + if (GetControlPointCount() < 3 ) + return; + + cairo_t* cr = context.GetObject(); + cairo_set_line_width(cr, 2.0 / zoom); + + ControlPoint cp; + GetControlPoint(cp, 0); + cairo_move_to(cr, cp.x, cp.y); + + for (size_t i = 0; i < GetControlPointCount(); i++) + { + GetControlPoint(cp, i); + cairo_line_to(cr, cp.x, cp.y); + } + + cairo_close_path(cr); + cairo_stroke(cr); + } + + + RadiographyLayer::RadiographyLayer(const RadiographyScene& scene) : + index_(0), + hasSize_(false), + width_(0), + height_(0), + prefferedPhotometricDisplayMode_(RadiographyPhotometricDisplayMode_Default), + scene_(scene) + { + UpdateTransform(); + } + + void RadiographyLayer::ResetCrop() + { + geometry_.ResetCrop(); + UpdateTransform(); + } + + void RadiographyLayer::SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode) + { + prefferedPhotometricDisplayMode_ = prefferedPhotometricDisplayMode; + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetCrop(unsigned int x, + unsigned int y, + unsigned int width, + unsigned int height) + { + if (!hasSize_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + if (x + width > width_ || + y + height > height_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + geometry_.SetCrop(x, y, width, height); + UpdateTransform(); + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetGeometry(const Geometry& geometry) + { + geometry_ = geometry; + + if (hasSize_) + { + UpdateTransform(); + } + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + + void RadiographyLayer::GetCrop(unsigned int& x, + unsigned int& y, + unsigned int& width, + unsigned int& height) const + { + if (GetGeometry().HasCrop()) + { + GetGeometry().GetCrop(x, y, width, height); + } + else + { + x = 0; + y = 0; + width = width_; + height = height_; + } + } + + + void RadiographyLayer::SetAngle(double angle) + { + geometry_.SetAngle(angle); + UpdateTransform(); + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetFlipVertical(bool flip) + { + geometry_.SetFlipVertical(flip); + UpdateTransform(); + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetFlipHorizontal(bool flip) + { + geometry_.SetFlipHorizontal(flip); + UpdateTransform(); + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyLayer::SetSize(unsigned int width, + unsigned int height, + bool emitLayerEditedEvent) + { + hasSize_ = true; + width_ = width; + height_ = height; + + UpdateTransform(); + + if (emitLayerEditedEvent) + { + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + } + + Extent2D RadiographyLayer::GetSceneExtent(bool /*minimal*/) const + { + Extent2D extent; + + unsigned int x, y, width, height; + GetCrop(x, y, width, height); + + double dx = static_cast(x); + double dy = static_cast(y); + double dwidth = static_cast(width); + double dheight = static_cast(height); + + // AddToExtent transforms the coordinates from image to scene + AddToExtent(extent, dx, dy); + AddToExtent(extent, dx + dwidth, dy); + AddToExtent(extent, dx, dy + dheight); + AddToExtent(extent, dx + dwidth, dy + dheight); + + return extent; + } + + + bool RadiographyLayer::GetPixel(unsigned int& imageX, + unsigned int& imageY, + double sceneX, + double sceneY) const + { + if (width_ == 0 || + height_ == 0) + { + return false; + } + else + { + GetTransformInverse().Apply(sceneX, sceneY); + + int x = static_cast(std::floor(sceneX)); + int y = static_cast(std::floor(sceneY)); + + if (x < 0) + { + imageX = 0; + } + else if (x >= static_cast(width_)) + { + imageX = width_; + } + else + { + imageX = static_cast(x); + } + + if (y < 0) + { + imageY = 0; + } + else if (y >= static_cast(height_)) + { + imageY = height_; + } + else + { + imageY = static_cast(y); + } + + return true; + } + } + + + void RadiographyLayer::SetPan(double x, + double y) + { + geometry_.SetPan(x, y); + UpdateTransform(); + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + + void RadiographyLayer::SetPixelSpacing(double x, + double y, + bool emitLayerEditedEvent) + { + geometry_.SetPixelSpacing(x, y); + UpdateTransform(); + if (emitLayerEditedEvent) + { + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + } + + + void RadiographyLayer::GetCenter(double& centerX, + double& centerY) const + { + centerX = static_cast(width_) / 2.0; + centerY = static_cast(height_) / 2.0; + GetTransform().Apply(centerX, centerY); + } + + + + size_t RadiographyLayer::GetControlPointCount() const {return 4;} + + void RadiographyLayer::GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */, + size_t index) const + { + unsigned int cropX, cropY, cropWidth, cropHeight; + GetCrop(cropX, cropY, cropWidth, cropHeight); + + ControlPoint cp; + switch (index) + { + case RadiographyControlPointType_TopLeftCorner: + cp = ControlPoint(cropX, cropY, RadiographyControlPointType_TopLeftCorner); + break; + + case RadiographyControlPointType_TopRightCorner: + cp = ControlPoint(cropX + cropWidth, cropY, RadiographyControlPointType_TopRightCorner); + break; + + case RadiographyControlPointType_BottomLeftCorner: + cp = ControlPoint(cropX, cropY + cropHeight, RadiographyControlPointType_BottomLeftCorner); + break; + + case RadiographyControlPointType_BottomRightCorner: + cp = ControlPoint(cropX + cropWidth, cropY + cropHeight, RadiographyControlPointType_BottomRightCorner); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + // transforms image coordinates into scene coordinates + GetTransform().Apply(cp.x, cp.y); + cpScene = cp; + } + + bool RadiographyLayer::LookupControlPoint(ControlPoint& cpScene /* out */, + double x, + double y, + double zoom, + double viewportDistance) const + { + double threshold = Square(viewportDistance / zoom); + + for (size_t i = 0; i < GetControlPointCount(); i++) + { + ControlPoint cp; + GetControlPoint(cp, i); + + double d = Square(cp.x - x) + Square(cp.y - y); + + if (d <= threshold) + { + cpScene = cp; + return true; + } + } + + return false; + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayer.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,395 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 Osimis S.A., Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + **/ + + +#pragma once + +#include + +#include "../Toolbox/AffineTransform2D.h" +#include "../Toolbox/Extent2D.h" +#include "../Wrappers/CairoContext.h" +#include "../Messages/IMessage.h" +#include "../Messages/IObservable.h" + +namespace OrthancStone +{ + class RadiographyScene; + + enum RadiographyControlPointType + { + RadiographyControlPointType_TopLeftCorner = 0, + RadiographyControlPointType_TopRightCorner = 1, + RadiographyControlPointType_BottomRightCorner = 2, + RadiographyControlPointType_BottomLeftCorner = 3 + }; + + enum RadiographyPhotometricDisplayMode + { + RadiographyPhotometricDisplayMode_Default, + + RadiographyPhotometricDisplayMode_Monochrome1, + RadiographyPhotometricDisplayMode_Monochrome2 + }; + + + struct ControlPoint + { + double x; + double y; + size_t index; + + ControlPoint(double x, double y, size_t index) + : x(x), + y(y), + index(index) + {} + + ControlPoint() + : x(0), + y(0), + index(std::numeric_limits::max()) + {} + }; + + class RadiographyLayer : public IObservable + { + friend class RadiographyScene; + + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, LayerEditedMessage, RadiographyLayer); + + class Geometry + { + bool hasCrop_; + unsigned int cropX_; + unsigned int cropY_; + unsigned int cropWidth_; + unsigned int cropHeight_; + bool flipVertical_; + bool flipHorizontal_; + 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) + { + hasCrop_ = true; + 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_; + } + + void SetFlipVertical(bool flip) // mirrors image around an horizontal axis (note: flip is applied before the rotation !) + { + flipVertical_ = flip; + } + + void SetFlipHorizontal(bool flip) // mirrors image around a vertical axis (note: flip is applied before the rotation !) + { + flipHorizontal_ = flip; + } + + bool GetFlipVertical() const + { + return flipVertical_; + } + + bool GetFlipHorizontal() const + { + return flipHorizontal_; + } + + double GetScalingX() const + { + return (flipHorizontal_ ? - pixelSpacingX_: pixelSpacingX_); + } + + double GetScalingY() const + { + return (flipVertical_ ? - pixelSpacingY_: pixelSpacingY_); + } + }; + + private: + size_t index_; + bool hasSize_; + unsigned int width_; + unsigned int height_; + AffineTransform2D transform_; + AffineTransform2D transformInverse_; + Geometry geometry_; + RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode_; + const RadiographyScene& scene_; + + protected: + void SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode); + + private: + void UpdateTransform(); + + void AddToExtent(Extent2D& extent, + double x, + double y) const; + + void SetIndex(size_t index) + { + index_ = index; + } + + bool Contains(double x, + double y) const; + + void DrawBorders(CairoContext& context, + double zoom); + + public: + RadiographyLayer(const RadiographyScene& scene); + + virtual ~RadiographyLayer() + { + } + + virtual const AffineTransform2D& GetTransform() const + { + return transform_; + } + + virtual const AffineTransform2D& GetTransformInverse() const + { + return transformInverse_; + } + + size_t GetIndex() const + { + return index_; + } + + const RadiographyScene& GetScene() const + { + return scene_; + } + + const Geometry& GetGeometry() const + { + return geometry_; + } + + void SetGeometry(const Geometry& geometry); + + void ResetCrop(); + + void SetCrop(unsigned int x, // those are pixel coordinates/size + unsigned int y, + unsigned int width, + unsigned int height); + + void SetCrop(const Extent2D& sceneExtent) + { + Extent2D imageCrop; + + { + double x = sceneExtent.GetX1(); + double y = sceneExtent.GetY1(); + GetTransformInverse().Apply(x, y); + imageCrop.AddPoint(x, y); + } + + { + double x = sceneExtent.GetX2(); + double y = sceneExtent.GetY2(); + GetTransformInverse().Apply(x, y); + imageCrop.AddPoint(x, y); + } + + SetCrop(static_cast(std::max(0.0, std::floor(imageCrop.GetX1()))), + static_cast(std::max(0.0, std::floor(imageCrop.GetY1()))), + std::min(width_, static_cast(std::ceil(imageCrop.GetWidth()))), + std::min(height_, static_cast(std::ceil(imageCrop.GetHeight()))) + ); + } + + + void GetCrop(unsigned int& x, + unsigned int& y, + unsigned int& width, + unsigned int& height) const; + + void SetAngle(double angle); + + void SetPan(double x, + double y); + + void SetFlipVertical(bool flip); // mirrors image around an horizontal axis (note: flip is applied before the rotation !) + + void SetFlipHorizontal(bool flip); // mirrors image around a vertical axis (note: flip is applied before the rotation !) + + void SetResizeable(bool resizeable) + { + geometry_.SetResizeable(resizeable); + } + + void SetSize(unsigned int width, + unsigned int height, + bool emitLayerEditedEvent = true); + + bool HasSize() const + { + return hasSize_; + } + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + virtual Extent2D GetSceneExtent(bool minimal) const; + + virtual bool GetPixel(unsigned int& imageX, + unsigned int& imageY, + double sceneX, + double sceneY) const; + + void SetPixelSpacing(double x, + double y, + bool emitLayerEditedEvent = true); + + void GetCenter(double& centerX, + double& centerY) const; + + virtual void GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */, + size_t index) const; + + virtual size_t GetControlPointCount() const; + + bool LookupControlPoint(ControlPoint& cpScene /* out */, + double x, + double y, + double zoom, + double viewportDistance) const; + + virtual bool GetDefaultWindowing(float& center, + float& width) const = 0; + + RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const + { + return prefferedPhotometricDisplayMode_; + } + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const = 0; + + virtual bool GetRange(float& minValue, + float& maxValue) const = 0; + + virtual size_t GetApproximateMemoryUsage() const // this is used to limit the number of scenes loaded in RAM when resources are limited (we actually only count the size used by the images, not the C structs) + { + return 0; + } + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayerCropTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerCropTracker.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,145 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyLayerCropTracker.h" + +#include "RadiographySceneCommand.h" + +#include + +namespace OrthancStone +{ + class RadiographyLayerCropTracker::UndoRedoCommand : public RadiographySceneCommand + { + private: + unsigned int sourceCropX_; + unsigned int sourceCropY_; + unsigned int sourceCropWidth_; + unsigned int sourceCropHeight_; + unsigned int targetCropX_; + unsigned int targetCropY_; + unsigned int targetCropWidth_; + unsigned int targetCropHeight_; + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const + { + layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_); + } + + virtual void RedoInternal(RadiographyLayer& layer) const + { + layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_); + } + + public: + UndoRedoCommand(const RadiographyLayerCropTracker& tracker) : + RadiographySceneCommand(tracker.accessor_), + sourceCropX_(tracker.cropX_), + sourceCropY_(tracker.cropY_), + sourceCropWidth_(tracker.cropWidth_), + sourceCropHeight_(tracker.cropHeight_) + { + tracker.accessor_.GetLayer().GetCrop(targetCropX_, targetCropY_, + targetCropWidth_, targetCropHeight_); + } + }; + + + RadiographyLayerCropTracker::RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + const ControlPoint& startControlPoint) : + undoRedoStack_(undoRedoStack), + accessor_(scene, layer), + startControlPoint_(startControlPoint) + { + if (accessor_.IsValid()) + { + accessor_.GetLayer().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_); + } + } + + + void RadiographyLayerCropTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyLayerCropTracker::MouseUp() + { + if (accessor_.IsValid()) + { + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + } + + + void RadiographyLayerCropTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + if (accessor_.IsValid()) + { + unsigned int x, y; + + RadiographyLayer& layer = accessor_.GetLayer(); + if (layer.GetPixel(x, y, sceneX, sceneY)) + { + unsigned int targetX, targetWidth; + + if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner || + startControlPoint_.index == RadiographyControlPointType_BottomLeftCorner) + { + targetX = std::min(x, cropX_ + cropWidth_); + targetWidth = cropX_ + cropWidth_ - targetX; + } + else + { + targetX = cropX_; + targetWidth = std::max(x, cropX_) - cropX_; + } + + unsigned int targetY, targetHeight; + + if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner || + startControlPoint_.index == RadiographyControlPointType_TopRightCorner) + { + targetY = std::min(y, cropY_ + cropHeight_); + targetHeight = cropY_ + cropHeight_ - targetY; + } + else + { + targetY = cropY_; + targetHeight = std::max(y, cropY_) - cropY_; + } + + layer.SetCrop(targetX, targetY, targetWidth, targetHeight); + } + } + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayerCropTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerCropTracker.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,68 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Toolbox/ViewportGeometry.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + class RadiographyLayerCropTracker : public Deprecated::IWorldSceneMouseTracker + { + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene::LayerAccessor accessor_; + ControlPoint startControlPoint_; + unsigned int cropX_; + unsigned int cropY_; + unsigned int cropWidth_; + unsigned int cropHeight_; + + public: + RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + const ControlPoint& startControlPoint); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayerMaskTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerMaskTracker.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,140 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyLayerMaskTracker.h" +#include "RadiographyMaskLayer.h" + +#include "RadiographySceneCommand.h" + +#include + +namespace OrthancStone +{ + class RadiographyLayerMaskTracker::UndoRedoCommand : public RadiographySceneCommand + { + private: + ControlPoint sourceSceneCp_; + ControlPoint targetSceneCp_; + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const + { + RadiographyMaskLayer* maskLayer = dynamic_cast(&layer); + if (maskLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + unsigned int ix, iy; // image coordinates + if (maskLayer->GetPixel(ix, iy, sourceSceneCp_.x, sourceSceneCp_.y)) + { + maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), sourceSceneCp_.index); + } + } + + virtual void RedoInternal(RadiographyLayer& layer) const + { + RadiographyMaskLayer* maskLayer = dynamic_cast(&layer); + if (maskLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + unsigned int ix, iy; // image coordinates + if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y)) + { + maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index); + } + } + + public: + UndoRedoCommand(const RadiographyLayerMaskTracker& tracker) : + RadiographySceneCommand(tracker.accessor_), + sourceSceneCp_(tracker.startSceneCp_), + targetSceneCp_(tracker.endSceneCp_) + { + RadiographyMaskLayer* maskLayer = dynamic_cast(&(tracker.accessor_.GetLayer())); + if (maskLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + unsigned int ix, iy; // image coordinates + if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y)) + { + maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index); + } + } + }; + + + RadiographyLayerMaskTracker::RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + const ControlPoint& startSceneControlPoint) : + undoRedoStack_(undoRedoStack), + accessor_(scene, layer), + startSceneCp_(startSceneControlPoint), + endSceneCp_(startSceneControlPoint) + { + } + + + void RadiographyLayerMaskTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyLayerMaskTracker::MouseUp() + { + if (accessor_.IsValid() && startSceneCp_.x != endSceneCp_.x && startSceneCp_.y != endSceneCp_.y) + { + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + } + + + void RadiographyLayerMaskTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + if (accessor_.IsValid()) + { + unsigned int ix, iy; // image coordinates + + RadiographyLayer& layer = accessor_.GetLayer(); + if (layer.GetPixel(ix, iy, sceneX, sceneY)) + { + endSceneCp_ = ControlPoint(sceneX, sceneY, startSceneCp_.index); + + RadiographyMaskLayer* maskLayer = dynamic_cast(&layer); + if (maskLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), startSceneCp_.index); + } + } + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayerMaskTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerMaskTracker.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,65 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Toolbox/ViewportGeometry.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + class RadiographyLayerMaskTracker : public Deprecated::IWorldSceneMouseTracker + { + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene::LayerAccessor accessor_; + ControlPoint startSceneCp_; + ControlPoint endSceneCp_; + + public: + RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + const ControlPoint& startSceneControlPoint); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayerMoveTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerMoveTracker.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,126 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyLayerMoveTracker.h" + +#include "RadiographySceneCommand.h" + +#include + +namespace OrthancStone +{ + class RadiographyLayerMoveTracker::UndoRedoCommand : public RadiographySceneCommand + { + private: + double sourceX_; + double sourceY_; + double targetX_; + double targetY_; + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const + { + layer.SetPan(sourceX_, sourceY_); + } + + virtual void RedoInternal(RadiographyLayer& layer) const + { + layer.SetPan(targetX_, targetY_); + } + + public: + UndoRedoCommand(const RadiographyLayerMoveTracker& tracker) : + RadiographySceneCommand(tracker.accessor_), + sourceX_(tracker.panX_), + sourceY_(tracker.panY_), + targetX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()), + targetY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY()) + { + } + }; + + + RadiographyLayerMoveTracker::RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + size_t layer, + double x, + double y, + bool oneAxis) : + undoRedoStack_(undoRedoStack), + accessor_(scene, layer), + clickX_(x), + clickY_(y), + oneAxis_(oneAxis) + { + if (accessor_.IsValid()) + { + panX_ = accessor_.GetLayer().GetGeometry().GetPanX(); + panY_ = accessor_.GetLayer().GetGeometry().GetPanY(); + } + } + + + void RadiographyLayerMoveTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyLayerMoveTracker::MouseUp() + { + if (accessor_.IsValid()) + { + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + } + + + void RadiographyLayerMoveTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + if (accessor_.IsValid()) + { + double dx = sceneX - clickX_; + double dy = sceneY - clickY_; + + if (oneAxis_) + { + if (fabs(dx) > fabs(dy)) + { + accessor_.GetLayer().SetPan(dx + panX_, panY_); + } + else + { + accessor_.GetLayer().SetPan(panX_, dy + panY_); + } + } + else + { + accessor_.GetLayer().SetPan(dx + panX_, dy + panY_); + } + } + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayerMoveTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerMoveTracker.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,68 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + class RadiographyLayerMoveTracker : public Deprecated::IWorldSceneMouseTracker + { + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene::LayerAccessor accessor_; + double clickX_; + double clickY_; + double panX_; + double panY_; + bool oneAxis_; + + public: + RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + size_t layer, + double x, + double y, + bool oneAxis); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayerResizeTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerResizeTracker.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,188 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyLayerResizeTracker.h" + +#include "RadiographySceneCommand.h" + +#include + +#include + + +namespace OrthancStone +{ + static double ComputeDistance(double x1, + double y1, + double x2, + double y2) + { + double dx = x1 - x2; + double dy = y1 - y2; + return sqrt(dx * dx + dy * dy); + } + + + class RadiographyLayerResizeTracker::UndoRedoCommand : public RadiographySceneCommand + { + private: + double sourceSpacingX_; + double sourceSpacingY_; + double sourcePanX_; + double sourcePanY_; + double targetSpacingX_; + double targetSpacingY_; + double targetPanX_; + double targetPanY_; + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const + { + layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_); + layer.SetPan(sourcePanX_, sourcePanY_); + } + + virtual void RedoInternal(RadiographyLayer& layer) const + { + layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_); + layer.SetPan(targetPanX_, targetPanY_); + } + + public: + UndoRedoCommand(const RadiographyLayerResizeTracker& tracker) : + RadiographySceneCommand(tracker.accessor_), + sourceSpacingX_(tracker.originalSpacingX_), + sourceSpacingY_(tracker.originalSpacingY_), + sourcePanX_(tracker.originalPanX_), + sourcePanY_(tracker.originalPanY_), + targetSpacingX_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingX()), + targetSpacingY_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingY()), + targetPanX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()), + targetPanY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY()) + { + } + }; + + + RadiographyLayerResizeTracker::RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + size_t layer, + const ControlPoint& startControlPoint, + bool roundScaling) : + undoRedoStack_(undoRedoStack), + accessor_(scene, layer), + roundScaling_(roundScaling) + { + if (accessor_.IsValid() && + accessor_.GetLayer().GetGeometry().IsResizeable()) + { + originalSpacingX_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingX(); + originalSpacingY_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingY(); + originalPanX_ = accessor_.GetLayer().GetGeometry().GetPanX(); + originalPanY_ = accessor_.GetLayer().GetGeometry().GetPanY(); + + size_t oppositeControlPointType; + switch (startControlPoint.index) + { + case RadiographyControlPointType_TopLeftCorner: + oppositeControlPointType = RadiographyControlPointType_BottomRightCorner; + break; + + case RadiographyControlPointType_TopRightCorner: + oppositeControlPointType = RadiographyControlPointType_BottomLeftCorner; + break; + + case RadiographyControlPointType_BottomLeftCorner: + oppositeControlPointType = RadiographyControlPointType_TopRightCorner; + break; + + case RadiographyControlPointType_BottomRightCorner: + oppositeControlPointType = RadiographyControlPointType_TopLeftCorner; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + accessor_.GetLayer().GetControlPoint(startOppositeControlPoint_, oppositeControlPointType); + + double d = ComputeDistance(startControlPoint.x, startControlPoint.y, startOppositeControlPoint_.x, startOppositeControlPoint_.y); + if (d >= std::numeric_limits::epsilon()) + { + baseScaling_ = 1.0 / d; + } + else + { + // Avoid division by zero in extreme cases + accessor_.Invalidate(); + } + } + } + + + void RadiographyLayerResizeTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyLayerResizeTracker::MouseUp() + { + if (accessor_.IsValid() && + accessor_.GetLayer().GetGeometry().IsResizeable()) + { + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + } + + + void RadiographyLayerResizeTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + static const double ROUND_SCALING = 0.1; + + if (accessor_.IsValid() && + accessor_.GetLayer().GetGeometry().IsResizeable()) + { + double scaling = ComputeDistance(startOppositeControlPoint_.x, startOppositeControlPoint_.y, sceneX, sceneY) * baseScaling_; + + if (roundScaling_) + { + scaling = boost::math::round((scaling / ROUND_SCALING) * ROUND_SCALING); + } + + RadiographyLayer& layer = accessor_.GetLayer(); + layer.SetPixelSpacing(scaling * originalSpacingX_, + scaling * originalSpacingY_); + + // Keep the opposite corner at a fixed location + ControlPoint currentOppositeCorner; + layer.GetControlPoint(currentOppositeCorner, startOppositeControlPoint_.index); + layer.SetPan(layer.GetGeometry().GetPanX() + startOppositeControlPoint_.x - currentOppositeCorner.x, + layer.GetGeometry().GetPanY() + startOppositeControlPoint_.y - currentOppositeCorner.y); + } + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayerResizeTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerResizeTracker.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,69 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + class RadiographyLayerResizeTracker : public Deprecated::IWorldSceneMouseTracker + { + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene::LayerAccessor accessor_; + bool roundScaling_; + double originalSpacingX_; + double originalSpacingY_; + double originalPanX_; + double originalPanY_; + ControlPoint startOppositeControlPoint_; + double baseScaling_; + + public: + RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + size_t layer, + const ControlPoint& startControlPoint, + bool roundScaling); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayerRotateTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerRotateTracker.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,156 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyLayerRotateTracker.h" + +#include "RadiographySceneCommand.h" + +#include + +#include +#include + +namespace OrthancStone +{ + class RadiographyLayerRotateTracker::UndoRedoCommand : public RadiographySceneCommand + { + private: + double sourceAngle_; + double targetAngle_; + + static int ToDegrees(double angle) + { + return boost::math::iround(angle * 180.0 / boost::math::constants::pi()); + } + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const + { + LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; + layer.SetAngle(sourceAngle_); + } + + virtual void RedoInternal(RadiographyLayer& layer) const + { + LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; + layer.SetAngle(targetAngle_); + } + + public: + UndoRedoCommand(const RadiographyLayerRotateTracker& tracker) : + RadiographySceneCommand(tracker.accessor_), + sourceAngle_(tracker.originalAngle_), + targetAngle_(tracker.accessor_.GetLayer().GetGeometry().GetAngle()) + { + } + }; + + + bool RadiographyLayerRotateTracker::ComputeAngle(double& angle /* out */, + double sceneX, + double sceneY) const + { + Vector u; + LinearAlgebra::AssignVector(u, sceneX - centerX_, sceneY - centerY_); + + double nu = boost::numeric::ublas::norm_2(u); + + if (!LinearAlgebra::IsCloseToZero(nu)) + { + u /= nu; + angle = atan2(u[1], u[0]); + return true; + } + else + { + return false; + } + } + + + RadiographyLayerRotateTracker::RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + double x, + double y, + bool roundAngles) : + undoRedoStack_(undoRedoStack), + accessor_(scene, layer), + roundAngles_(roundAngles) + { + if (accessor_.IsValid()) + { + accessor_.GetLayer().GetCenter(centerX_, centerY_); + originalAngle_ = accessor_.GetLayer().GetGeometry().GetAngle(); + + double sceneX, sceneY; + view.MapDisplayToScene(sceneX, sceneY, x, y); + + if (!ComputeAngle(clickAngle_, x, y)) + { + accessor_.Invalidate(); + } + } + } + + + void RadiographyLayerRotateTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyLayerRotateTracker::MouseUp() + { + if (accessor_.IsValid()) + { + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + } + + + void RadiographyLayerRotateTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi(); + + double angle; + + if (accessor_.IsValid() && + ComputeAngle(angle, sceneX, sceneY)) + { + angle = angle - clickAngle_ + originalAngle_; + + if (roundAngles_) + { + angle = boost::math::round((angle / ROUND_ANGLE) * ROUND_ANGLE); + } + + accessor_.GetLayer().SetAngle(angle); + } + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyLayerRotateTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyLayerRotateTracker.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,75 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Toolbox/ViewportGeometry.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + + +namespace OrthancStone +{ + class RadiographyLayerRotateTracker : public Deprecated::IWorldSceneMouseTracker + { + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene::LayerAccessor accessor_; + double centerX_; + double centerY_; + double originalAngle_; + double clickAngle_; + bool roundAngles_; + + bool ComputeAngle(double& angle /* out */, + double sceneX, + double sceneY) const; + + public: + RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + const Deprecated::ViewportGeometry& view, + size_t layer, + double x, + double y, + bool roundAngles); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyMaskLayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyMaskLayer.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,199 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyMaskLayer.h" +#include "RadiographyDicomLayer.h" + +#include "RadiographyScene.h" +#include "Core/Images/Image.h" +#include "Core/Images/ImageProcessing.h" +#include +#include "../Toolbox/ImageGeometry.h" + +namespace OrthancStone +{ + const unsigned char IN_MASK_VALUE = 0x77; + const unsigned char OUT_MASK_VALUE = 0xFF; + + const AffineTransform2D& RadiographyMaskLayer::GetTransform() const + { + return dicomLayer_.GetTransform(); + } + + const AffineTransform2D& RadiographyMaskLayer::GetTransformInverse() const + { + return dicomLayer_.GetTransformInverse(); + } + + bool RadiographyMaskLayer::GetPixel(unsigned int& imageX, + unsigned int& imageY, + double sceneX, + double sceneY) const + { + return dicomLayer_.GetPixel(imageX, imageY, sceneX, sceneY); + } + + std::string RadiographyMaskLayer::GetInstanceId() const + { + return dicomLayer_.GetInstanceId(); + } + + void RadiographyMaskLayer::SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index) + { + if (index < corners_.size()) + corners_[index] = corner; + else + corners_.push_back(corner); + invalidated_ = true; + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + void RadiographyMaskLayer::SetCorners(const std::vector& corners) + { + corners_ = corners; + invalidated_ = true; + + BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); + } + + Extent2D RadiographyMaskLayer::GetSceneExtent(bool minimal) const + { + if (!minimal) + { + return RadiographyLayer::GetSceneExtent(minimal); + } + else + { // get the extent of the in-mask area + Extent2D sceneExtent; + + for (std::vector::const_iterator corner = corners_.begin(); corner != corners_.end(); ++corner) + { + double x = static_cast(corner->GetX()); + double y = static_cast(corner->GetY()); + + dicomLayer_.GetTransform().Apply(x, y); + sceneExtent.AddPoint(x, y); + } + return sceneExtent; + } + } + + + + void RadiographyMaskLayer::Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const + { + if (dicomLayer_.GetWidth() == 0 || dicomLayer_.GetSourceImage() == NULL) // nothing to do if the DICOM layer is not displayed (or not loaded) + return; + + if (invalidated_) + { + mask_.reset(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, dicomLayer_.GetWidth(), dicomLayer_.GetHeight(), false)); + + DrawMask(); + + invalidated_ = false; + } + + {// rendering + if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + unsigned int cropX, cropY, cropWidth, cropHeight; + dicomLayer_.GetCrop(cropX, cropY, cropWidth, cropHeight); + + const AffineTransform2D t = AffineTransform2D::Combine( + viewTransform, dicomLayer_.GetTransform(), + AffineTransform2D::CreateOffset(cropX, cropY)); + + Orthanc::ImageAccessor cropped; + mask_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); + + Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); + + + unsigned int x1, y1, x2, y2; + if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, + t.GetHomogeneousMatrix(), + cropped.GetWidth(), + cropped.GetHeight(), + buffer.GetWidth(), + buffer.GetHeight())) + { + return; // layer is outside the buffer + } + + t.Apply(tmp, cropped, ImageInterpolation_Nearest, true /* clear */); + + // we have observed vertical lines at the image border (probably due to bilinear filtering of the DICOM image when it is not aligned with the buffer pixels) + // -> draw the mask one line further on each side + if (x1 >= 1) + { + x1 = x1 - 1; + } + if (x2 < buffer.GetWidth() - 2) + { + x2 = x2 + 1; + } + + // Blit + for (unsigned int y = y1; y <= y2; y++) + { + float *q = reinterpret_cast(buffer.GetRow(y)) + x1; + const uint8_t *p = reinterpret_cast(tmp.GetRow(y)) + x1; + + for (unsigned int x = x1; x <= x2; x++, p++, q++) + { + if (*p != IN_MASK_VALUE) + *q = foreground_; + // else keep the underlying pixel value + } + } + + } + } + + void RadiographyMaskLayer::DrawMask() const + { + // first fill the complete image + Orthanc::ImageProcessing::Set(*mask_, OUT_MASK_VALUE); + + // clip corners + std::vector clippedCorners; + for (size_t i = 0; i < corners_.size(); i++) + { + clippedCorners.push_back(corners_[i]); + clippedCorners[i].ClipTo(0, mask_->GetWidth() - 1, 0, mask_->GetHeight() - 1); + } + + // fill mask + Orthanc::ImageProcessing::FillPolygon(*mask_, clippedCorners, IN_MASK_VALUE); + + } + +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyMaskLayer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyMaskLayer.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,146 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 +#include +#include + +namespace OrthancStone +{ + class RadiographyScene; + class RadiographyDicomLayer; + + class RadiographyMaskLayer : public RadiographyLayer + { + private: + std::vector corners_; + const RadiographyDicomLayer& dicomLayer_; + mutable bool invalidated_; + float foreground_; + + mutable std::unique_ptr mask_; + public: + RadiographyMaskLayer(const RadiographyScene& scene, const RadiographyDicomLayer& dicomLayer, + float foreground) : + RadiographyLayer(scene), + dicomLayer_(dicomLayer), + invalidated_(true), + foreground_(foreground) + { + } + + virtual size_t GetApproximateMemoryUsage() const + { + size_t size = 0; + if (mask_.get() != NULL) + { + size += mask_->GetPitch() * mask_->GetHeight(); + } + + return size; + } + + + void SetCorners(const std::vector& corners); + void SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index); + + const std::vector& GetCorners() const + { + return corners_; + } + + float GetForeground() const + { + return foreground_; + } + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + float windowCenter, + float windowWidth, + bool applyWindowing) const; + + std::string GetInstanceId() const; + + virtual size_t GetControlPointCount() const + { + return corners_.size(); + } + + virtual void GetControlPoint(ControlPoint& cpScene, + size_t index) const + { + ControlPoint cp(corners_[index].GetX(), corners_[index].GetY(), index); + + // transforms image coordinates into scene coordinates + GetTransform().Apply(cp.x, cp.y); + cpScene = cp; + } + + virtual Extent2D GetSceneExtent(bool minimal) const; + + virtual bool GetDefaultWindowing(float& center, + float& width) const + { + return false; + } + + virtual bool GetRange(float& minValue, + float& maxValue) const + { + minValue = 0; + maxValue = 0; + + if (foreground_ < 0) + { + minValue = foreground_; + } + + if (foreground_ > 0) + { + maxValue = foreground_; + } + + return true; + + } + + virtual bool GetPixel(unsigned int& imageX, + unsigned int& imageY, + double sceneX, + double sceneY) const; + + protected: + virtual const AffineTransform2D& GetTransform() const; + + virtual const AffineTransform2D& GetTransformInverse() const; + + + private: + void DrawMask() const; + + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyScene.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyScene.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,968 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyScene.h" + +#include "RadiographyAlphaLayer.h" +#include "RadiographyDicomLayer.h" +#include "RadiographyTextLayer.h" +#include "RadiographyMaskLayer.h" +#include "../Deprecated/Toolbox/DicomFrameConverter.h" +#include "../Scene2D/CairoCompositor.h" +#include "../Scene2D/FloatTextureSceneLayer.h" +#include "../Scene2D/TextSceneLayer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace OrthancStone +{ + RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, + size_t index) : + scene_(scene), + index_(index) + { + Layers::iterator layer = scene.layers_.find(index); + if (layer == scene.layers_.end()) + { + layer_ = NULL; + } + else + { + assert(layer->second != NULL); + layer_ = layer->second; + } + } + + + RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, + double x, + double y) : + scene_(scene), + index_(0) // Dummy initialization + { + if (scene.LookupLayer(index_, x, y)) + { + Layers::iterator layer = scene.layers_.find(index_); + + if (layer == scene.layers_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + else + { + assert(layer->second != NULL); + layer_ = layer->second; + } + } + else + { + layer_ = NULL; + } + } + + + RadiographyScene& RadiographyScene::LayerAccessor::GetScene() const + { + if (IsValid()) + { + return scene_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + size_t RadiographyScene::LayerAccessor::GetIndex() const + { + if (IsValid()) + { + return index_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + RadiographyLayer& RadiographyScene::LayerAccessor::GetLayer() const + { + if (IsValid()) + { + return *layer_; + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + void RadiographyScene::_RegisterLayer(RadiographyLayer* layer) + { + std::unique_ptr raii(layer); + + // LOG(INFO) << "Registering layer: " << countLayers_; + + size_t index = nextLayerIndex_++; + raii->SetIndex(index); + layers_[index] = raii.release(); + } + + RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer) + { + if (layer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); + } + + _RegisterLayer(layer); + + BroadcastMessage(GeometryChangedMessage(*this, *layer)); + BroadcastMessage(ContentChangedMessage(*this, *layer)); + Register(*layer, &RadiographyScene::OnLayerEdited); + + return *layer; + } + + size_t RadiographyScene::GetApproximateMemoryUsage() const + { + size_t size = 0; + for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) + { + size += it->second->GetApproximateMemoryUsage(); + } + return size; + } + + void RadiographyScene::OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message) + { + BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, message.GetOrigin())); + } + + + RadiographyScene::RadiographyScene() : + nextLayerIndex_(0), + hasWindowing_(false), + windowingCenter_(0), // Dummy initialization + windowingWidth_(0) // Dummy initialization + { + } + + + RadiographyScene::~RadiographyScene() + { + for (Layers::iterator it = layers_.begin(); it != layers_.end(); it++) + { + assert(it->second != NULL); + delete it->second; + } + } + + RadiographyPhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const + { + // return the mode of the first layer who "cares" about its display mode (normaly, the one and only layer that is a DicomLayer) + for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) + { + if (it->second->GetPreferredPhotomotricDisplayMode() != RadiographyPhotometricDisplayMode_Default) + { + return it->second->GetPreferredPhotomotricDisplayMode(); + } + } + + return RadiographyPhotometricDisplayMode_Default; + } + + + 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; + + Layers::iterator found = layers_.find(layerIndex); + + if (found == layers_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + assert(found->second != NULL); + delete found->second; + + layers_.erase(found); + + LOG(INFO) << "Removing layer, there are now : " << layers_.size() << " layers"; + + _OnLayerRemoved(); + + BroadcastMessage(RadiographyScene::LayerRemovedMessage(*this, layerIndex)); + } + } + + const RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) const + { + Layers::const_iterator found = layers_.find(layerIndex); + + if (found == layers_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + assert(found->second != NULL); + return *found->second; + } + } + + RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) + { + Layers::const_iterator found = layers_.find(layerIndex); + + if (found == layers_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + assert(found->second != NULL); + return *found->second; + } + } + + bool RadiographyScene::GetWindowing(float& center, + float& width) const + { + if (hasWindowing_) + { + center = windowingCenter_; + width = windowingWidth_; + return true; + } + else + { + return false; + } + } + + + void RadiographyScene::GetWindowingWithDefault(float& center, + float& width) const + { + if (!GetWindowing(center, width)) + { + center = 128; + width = 256; + } + } + + + void RadiographyScene::SetWindowing(float center, + float width) + { + hasWindowing_ = true; + windowingCenter_ = center; + windowingWidth_ = width; + + BroadcastMessage(RadiographyScene::WindowingChangedMessage(*this)); + } + + + RadiographyLayer& RadiographyScene::UpdateText(size_t layerIndex, + const std::string& utf8, + const std::string& font, + unsigned int fontSize, + uint8_t foreground) + { + RadiographyTextLayer& textLayer = dynamic_cast(GetLayer(layerIndex)); + textLayer.SetText(utf8, font, fontSize, foreground); + + BroadcastMessage(RadiographyScene::ContentChangedMessage(*this, textLayer)); + BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, textLayer)); + return textLayer; + } + + + RadiographyLayer& RadiographyScene::LoadText(const std::string& utf8, + const std::string& font, + unsigned int fontSize, + uint8_t foreground, + RadiographyLayer::Geometry* centerGeometry, + bool isCenterGeometry) + { + std::unique_ptr alpha(new RadiographyTextLayer(*this)); + alpha->SetText(utf8, font, fontSize, foreground); + if (centerGeometry != NULL) + { + if (isCenterGeometry) + { + // modify geometry to reference the top left corner + double tlx = centerGeometry->GetPanX(); + double tly = centerGeometry->GetPanY(); + Extent2D textExtent = alpha->GetSceneExtent(false); + tlx = tlx - (textExtent.GetWidth() / 2) * centerGeometry->GetPixelSpacingX(); + tly = tly - (textExtent.GetHeight() / 2) * centerGeometry->GetPixelSpacingY(); + centerGeometry->SetPan(tlx, tly); + } + alpha->SetGeometry(*centerGeometry); + } + + RadiographyLayer& registeredLayer = RegisterLayer(alpha.release()); + + BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, registeredLayer)); + return registeredLayer; + } + + + RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, + unsigned int height, + RadiographyLayer::Geometry* geometry) + { + std::unique_ptr block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); + + for (unsigned int padding = 0; + (width > 2 * padding) && (height > 2 * padding); + padding++) + { + uint8_t color; + if (255 > 10 * padding) + { + color = 255 - 10 * padding; + } + else + { + color = 0; + } + + Orthanc::ImageAccessor region; + block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding); + Orthanc::ImageProcessing::Set(region, color); + } + + return LoadAlphaBitmap(block.release(), geometry); + } + + RadiographyLayer& RadiographyScene::LoadMask(const std::vector& corners, + const RadiographyDicomLayer& dicomLayer, + float foreground, + RadiographyLayer::Geometry* geometry) + { + std::unique_ptr mask(new RadiographyMaskLayer(*this, dicomLayer, foreground)); + mask->SetCorners(corners); + if (geometry != NULL) + { + mask->SetGeometry(*geometry); + } + + return RegisterLayer(mask.release()); + } + + + RadiographyLayer& RadiographyScene::LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, RadiographyLayer::Geometry *geometry) + { + std::unique_ptr alpha(new RadiographyAlphaLayer(*this)); + alpha->SetAlpha(bitmap); + if (geometry != NULL) + { + alpha->SetGeometry(*geometry); + } + + return RegisterLayer(alpha.release()); + } + + RadiographyLayer& RadiographyScene::LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership + const std::string& instance, + unsigned int frame, + Deprecated::DicomFrameConverter* converter, // takes ownership + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode, + RadiographyLayer::Geometry* geometry) + { + RadiographyDicomLayer& layer = dynamic_cast(RegisterLayer(new RadiographyDicomLayer(*this))); + + layer.SetInstance(instance, frame); + + if (geometry != NULL) + { + layer.SetGeometry(*geometry); + } + + layer.SetDicomFrameConverter(converter); + layer.SetSourceImage(dicomImage); + layer.SetPreferredPhotomotricDisplayMode(preferredPhotometricDisplayMode); + + return layer; + } + + RadiographyLayer& RadiographyScene::LoadDicomFrame(Deprecated::OrthancApiClient& orthanc, + const std::string& instance, + unsigned int frame, + bool httpCompression, + RadiographyLayer::Geometry* geometry) + { + RadiographyDicomLayer& layer = dynamic_cast(RegisterLayer(new RadiographyDicomLayer( *this))); + layer.SetInstance(instance, frame); + + if (geometry != NULL) + { + layer.SetGeometry(*geometry); + } + + { + Deprecated::IWebService::HttpHeaders headers; + std::string uri = "/instances/" + instance + "/tags"; + + orthanc.GetBinaryAsync( + uri, headers, + new Deprecated::DeprecatedCallable + (GetSharedObserver(), &RadiographyScene::OnTagsReceived), NULL, + new Orthanc::SingleValueObject(layer.GetIndex())); + } + + { + Deprecated::IWebService::HttpHeaders headers; + headers["Accept"] = "image/x-portable-arbitrarymap"; + + if (httpCompression) + { + headers["Accept-Encoding"] = "gzip"; + } + + std::string uri = ("/instances/" + instance + "/frames/" + + boost::lexical_cast(frame) + "/image-uint16"); + + orthanc.GetBinaryAsync( + uri, headers, + new Deprecated::DeprecatedCallable + (GetSharedObserver(), &RadiographyScene::OnFrameReceived), NULL, + new Orthanc::SingleValueObject(layer.GetIndex())); + } + + return layer; + } + + + RadiographyLayer& RadiographyScene::LoadDicomWebFrame(Deprecated::IWebService& web) + { + RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer(*this)); + + + return layer; + } + + + + void RadiographyScene::OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message) + { + size_t index = dynamic_cast&> + (message.GetPayload()).GetValue(); + + VLOG(1) << "JSON received: " << message.GetUri().c_str() + << " (" << message.GetAnswerSize() << " bytes) for layer " << index; + + Layers::iterator layer = layers_.find(index); + if (layer != layers_.end()) + { + assert(layer->second != NULL); + + OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); + dynamic_cast(layer->second)->SetDicomTags(dicom); + + float c, w; + if (!hasWindowing_ && + layer->second->GetDefaultWindowing(c, w)) + { + hasWindowing_ = true; + windowingCenter_ = c; + windowingWidth_ = w; + } + + BroadcastMessage(GeometryChangedMessage(*this, *(layer->second))); + } + } + + + void RadiographyScene::OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message) + { + size_t index = dynamic_cast&>(message.GetPayload()).GetValue(); + + VLOG(1) << "DICOM frame received: " << message.GetUri().c_str() + << " (" << message.GetAnswerSize() << " bytes) for layer " << index; + + Layers::iterator layer = layers_.find(index); + if (layer != layers_.end()) + { + assert(layer->second != NULL); + + std::string content; + if (message.GetAnswerSize() > 0) + { + content.assign(reinterpret_cast(message.GetAnswer()), message.GetAnswerSize()); + } + + std::unique_ptr reader(new Orthanc::PamReader); + reader->ReadFromMemory(content); + dynamic_cast(layer->second)->SetSourceImage(reader.release()); + + BroadcastMessage(ContentChangedMessage(*this, *(layer->second))); + } + } + + + Extent2D RadiographyScene::GetSceneExtent(bool minimal) const + { + Extent2D extent; + + for (Layers::const_iterator it = layers_.begin(); + it != layers_.end(); ++it) + { + assert(it->second != NULL); + extent.Union(it->second->GetSceneExtent(minimal)); + } + + return extent; + } + + + void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + bool applyWindowing) const + { + // Render layers in the background-to-foreground order + for (size_t index = 0; index < nextLayerIndex_; index++) + { + try + { + Layers::const_iterator it = layers_.find(index); + if (it != layers_.end()) + { + assert(it->second != NULL); + it->second->Render(buffer, viewTransform, interpolation, windowingCenter_, windowingWidth_, applyWindowing); + } + } + catch (Orthanc::OrthancException& ex) + { + LOG(ERROR) << "RadiographyScene::Render: " << index << ", OrthancException: " << ex.GetDetails(); + throw ex; // rethrow because we want it to crash to see there's a problem ! + } + catch (...) + { + LOG(ERROR) << "RadiographyScene::Render: " << index << ", unkown exception: "; + throw; // rethrow because we want it to crash to see there's a problem ! + } + } + } + + + bool RadiographyScene::LookupLayer(size_t& index /* out */, + double x, + double y) const + { + // Render layers in the foreground-to-background order + for (size_t i = nextLayerIndex_; i > 0; i--) + { + index = i - 1; + Layers::const_iterator it = layers_.find(index); + if (it != layers_.end()) + { + assert(it->second != NULL); + if (it->second->Contains(x, y)) + { + return true; + } + } + } + + return false; + } + + + void RadiographyScene::DrawBorder(CairoContext& context, + unsigned int layer, + double zoom) + { + Layers::const_iterator found = layers_.find(layer); + + if (found != layers_.end()) + { + context.SetSourceColor(255, 0, 0); + found->second->DrawBorders(context, zoom); + } + } + + + void RadiographyScene::GetRange(float& minValue, + float& maxValue) const + { + bool first = true; + + for (Layers::const_iterator it = layers_.begin(); + it != layers_.end(); it++) + { + assert(it->second != NULL); + + float a, b; + if (it->second->GetRange(a, b)) + { + if (first) + { + minValue = a; + maxValue = b; + first = false; + } + else + { + minValue = std::min(a, minValue); + maxValue = std::max(b, maxValue); + } + } + } + + if (first) + { + minValue = 0; + maxValue = 0; + } + } + + void RadiographyScene::ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer, + const Orthanc::ImageAccessor& renderedScene, + size_t layerIndex, + bool isCropped, + ImageInterpolation interpolation) + { + Extent2D sceneExtent = GetSceneExtent(isCropped); + + double pixelSpacingX = sceneExtent.GetWidth() / renderedScene.GetWidth(); + double pixelSpacingY = sceneExtent.GetHeight() / renderedScene.GetHeight(); + + AffineTransform2D view = AffineTransform2D::Combine( + AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), + AffineTransform2D::CreateOffset(-sceneExtent.GetX1(), -sceneExtent.GetY1())); + + AffineTransform2D layerToSceneTransform = AffineTransform2D::Combine( + view, + GetLayer(layerIndex).GetTransform()); + + AffineTransform2D sceneToLayerTransform = AffineTransform2D::Invert(layerToSceneTransform); + sceneToLayerTransform.Apply(layer, renderedScene, interpolation, false); + } + + Orthanc::Image* RadiographyScene::ExportToImage(double pixelSpacingX, + double pixelSpacingY, + ImageInterpolation interpolation, + bool invert, + int64_t maxValue /* for inversion */, + bool autoCrop, + bool applyWindowing) + { + if (pixelSpacingX <= 0 || + pixelSpacingY <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + Extent2D extent = GetSceneExtent(autoCrop); + + int w = boost::math::iround(extent.GetWidth() / pixelSpacingX); + int h = boost::math::iround(extent.GetHeight() / pixelSpacingY); + + if (w < 0 || h < 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + Orthanc::Image layers(Orthanc::PixelFormat_Float32, + static_cast(w), + static_cast(h), false); + + AffineTransform2D view = AffineTransform2D::Combine( + AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), + AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); + + // wipe background before rendering + if (GetPreferredPhotomotricDisplayMode() == RadiographyPhotometricDisplayMode_Monochrome1) + { + Orthanc::ImageProcessing::Set(layers, 65535); + } + else + { + Orthanc::ImageProcessing::Set(layers, 0); + } + + Render(layers, view, interpolation, applyWindowing); + + std::unique_ptr rendered(new Orthanc::Image(Orthanc::PixelFormat_Grayscale16, + layers.GetWidth(), layers.GetHeight(), false)); + + Orthanc::ImageProcessing::Convert(*rendered, layers); + if (invert) + Orthanc::ImageProcessing::Invert(*rendered, maxValue); + + return rendered.release(); + } + + + Orthanc::Image* RadiographyScene::ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation) + { + LOG(INFO) << "Exporting RadiographyScene to DICOM"; + + std::unique_ptr rendered(ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, autoCrop, false)); // note: we don't invert the image in the pixels data because we'll set the PhotometricDisplayMode correctly in the DICOM tags + + createDicomRequestContent["Tags"] = dicomTags; + + RadiographyPhotometricDisplayMode photometricMode = GetPreferredPhotomotricDisplayMode(); + if ((invert && photometricMode != RadiographyPhotometricDisplayMode_Monochrome2) || + (!invert && photometricMode == RadiographyPhotometricDisplayMode_Monochrome1)) + { + createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME1"; + } + else + { + createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME2"; + } + + // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to + // avoid floating-point numbers to grow over 16 characters, + // which would be invalid according to DICOM standard + // ("dciodvfy" would complain). + char buf[32]; + sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX); + + createDicomRequestContent["Tags"]["PixelSpacing"] = buf; + + float center, width; + if (GetWindowing(center, width)) + { + createDicomRequestContent["Tags"]["WindowCenter"] = + boost::lexical_cast(boost::math::iround(center)); + + createDicomRequestContent["Tags"]["WindowWidth"] = + boost::lexical_cast(boost::math::iround(width)); + } + + if (!parentOrthancId.empty()) + { + createDicomRequestContent["Parent"] = parentOrthancId; + } + + return rendered.release(); + } + + + void RadiographyScene::ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam) + { + LOG(INFO) << "Exporting RadiographyScene to DICOM"; + VLOG(1) << "Exporting RadiographyScene to: export to image"; + + std::unique_ptr rendered(ExportToCreateDicomRequestAndImage(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation)); + + // convert the image into base64 for inclusing in the createDicomRequest + std::string base64; + + { + std::string content; + + if (usePam) + { + VLOG(1) << "Exporting RadiographyScene: convert to PAM"; + Orthanc::PamWriter writer; + writer.WriteToMemory(content, *rendered); + } + else + { + Orthanc::PngWriter writer; + writer.WriteToMemory(content, *rendered); + } + + VLOG(1) << "Exporting RadiographyScene: encoding to base64"; + Orthanc::Toolbox::EncodeBase64(base64, content); + } + + // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme + createDicomRequestContent["Content"] = ("data:" + + std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) + + ";base64," + base64); + + VLOG(1) << "Exporting RadiographyScene: create-dicom request is ready"; + } + + + void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam) + { + Json::Value createDicomRequestContent; + + ExportToCreateDicomRequest(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam); + + orthanc.PostJsonAsyncExpectJson( + "/tools/create-dicom", createDicomRequestContent, + new Deprecated::DeprecatedCallable + (GetSharedObserver(), &RadiographyScene::OnDicomExported), + NULL, NULL); + + } + + + // Export using PAM is faster than using PNG, but requires Orthanc + // core >= 1.4.3 + void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc, + const Orthanc::DicomMap& dicom, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam) + { + std::set tags; + dicom.GetTags(tags); + + Json::Value jsonTags = Json::objectValue; + + for (std::set::const_iterator + tag = tags.begin(); tag != tags.end(); ++tag) + { + const Orthanc::DicomValue& value = dicom.GetValue(*tag); + if (!value.IsNull() && + !value.IsBinary()) + { + jsonTags[tag->Format()] = value.GetContent(); + } + } + + ExportDicom(orthanc, jsonTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam); + } + + void RadiographyScene::OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) + { + LOG(INFO) << "DICOM export was successful: " + << message.GetJson().toStyledString(); + } + + + void RadiographyScene::OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message) + { + LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes"; + + const Deprecated::IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); + for (Deprecated::IWebService::HttpHeaders::const_iterator + it = h.begin(); it != h.end(); ++it) + { + printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str()); + } + } + + void RadiographyScene::ExportToScene2D(Scene2D& output) const + { + int depth = 0; + for (Layers::const_iterator it = layers_.begin(); + it != layers_.end(); ++it) + { + assert(it->second != NULL); + + std::unique_ptr layer; + if (dynamic_cast(it->second)) + { + RadiographyDicomLayer* oldLayer = dynamic_cast(it->second); + + std::unique_ptr newLayer(new FloatTextureSceneLayer(*(oldLayer->GetSourceImage()))); + + newLayer->SetOrigin(oldLayer->GetGeometry().GetPanX(), + oldLayer->GetGeometry().GetPanY() + ); + newLayer->SetAngle(oldLayer->GetGeometry().GetAngle()); + + layer.reset(newLayer.release()); + + // TODO: windowing dynamic_cast + } + else if (dynamic_cast(it->second)) + { + RadiographyTextLayer* oldLayer = dynamic_cast(it->second); + + std::unique_ptr newLayer(new TextSceneLayer()); + + newLayer->SetText(oldLayer->GetText()); + newLayer->SetColor(oldLayer->GetForegroundGreyLevel(), + oldLayer->GetForegroundGreyLevel(), + oldLayer->GetForegroundGreyLevel() + ); + newLayer->SetPosition(oldLayer->GetGeometry().GetPanX(), + oldLayer->GetGeometry().GetPanY() + ); + newLayer->SetFontIndex(1); + newLayer->SetAnchor(BitmapAnchor_TopLeft); + //newLayer->SetAngle(oldLayer->GetGeometry().GetAngle()); + + layer.reset(newLayer.release()); + } + + output.SetLayer(depth++, layer.release()); + + } + + } + +} + diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyScene.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyScene.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,370 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Messages/ObserverBase.h" +#include "../Deprecated/Toolbox/DicomFrameConverter.h" +#include "../Deprecated/Toolbox/OrthancApiClient.h" +#include "../StoneEnumerations.h" +#include "Core/Images/Image.h" +#include "Core/Images/ImageProcessing.h" + +#include "../Scene2D/Scene2D.h" + +namespace OrthancStone +{ + class RadiographyDicomLayer; + + class RadiographyScene : + public ObserverBase, + public IObservable + { + friend class RadiographySceneGeometryReader; + public: + class GeometryChangedMessage : public OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + RadiographyLayer& layer_; + + public: + GeometryChangedMessage(const RadiographyScene& origin, + RadiographyLayer& layer) : + OriginMessage(origin), + layer_(layer) + { + } + + RadiographyLayer& GetLayer() const + { + return layer_; + } + }; + + class ContentChangedMessage : public OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + RadiographyLayer& layer_; + + public: + ContentChangedMessage(const RadiographyScene& origin, + RadiographyLayer& layer) : + OriginMessage(origin), + layer_(layer) + { + } + + RadiographyLayer& GetLayer() const + { + return layer_; + } + }; + + class LayerEditedMessage : public OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + const RadiographyLayer& layer_; + + public: + LayerEditedMessage(const RadiographyScene& origin, + const RadiographyLayer& layer) : + OriginMessage(origin), + layer_(layer) + { + } + + const RadiographyLayer& GetLayer() const + { + return layer_; + } + }; + + class LayerRemovedMessage : public OriginMessage + { + ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); + + private: + size_t& layerIndex_; + + public: + LayerRemovedMessage(const RadiographyScene& origin, + size_t& layerIndex) : + OriginMessage(origin), + layerIndex_(layerIndex) + { + } + + size_t& GetLayerIndex() const + { + return layerIndex_; + } + }; + + + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, WindowingChangedMessage, RadiographyScene); + + + class LayerAccessor : public boost::noncopyable + { + private: + RadiographyScene& scene_; + size_t index_; + RadiographyLayer* layer_; + + public: + LayerAccessor(RadiographyScene& scene, + size_t index); + + LayerAccessor(RadiographyScene& scene, + double x, + double y); + + void Invalidate() + { + layer_ = NULL; + } + + bool IsValid() const + { + return layer_ != NULL; + } + + RadiographyScene& GetScene() const; + + size_t GetIndex() const; + + RadiographyLayer& GetLayer() const; + }; + + + protected: + typedef std::map Layers; + + size_t nextLayerIndex_; + bool hasWindowing_; + float windowingCenter_; + float windowingWidth_; + Layers layers_; + + public: + RadiographyLayer& RegisterLayer(RadiographyLayer* layer); + + protected: + virtual void _RegisterLayer(RadiographyLayer* layer); + virtual void _OnLayerRemoved() {} + + void SetLayerIndex(RadiographyLayer* layer, size_t index) + { + layer->SetIndex(index); + } + + virtual void OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); + + virtual void OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); + + void OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); + + void OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message); + + virtual void OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message); + + public: + RadiographyScene(); + + virtual ~RadiographyScene(); + + virtual size_t GetApproximateMemoryUsage() const; + + bool GetWindowing(float& center, + float& width) const; + + void GetWindowingWithDefault(float& center, + float& width) const; + + virtual void SetWindowing(float center, + float width); + + RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const; + + RadiographyLayer& LoadText(const std::string& utf8, + const std::string& font, + unsigned int fontSize, + uint8_t foreground, + RadiographyLayer::Geometry* geometry, + bool isCenterGeometry); + + RadiographyLayer& UpdateText(size_t layerIndex, + const std::string& font, + const std::string& utf8, + unsigned int fontSize, + uint8_t foreground); + + RadiographyLayer& LoadTestBlock(unsigned int width, + unsigned int height, + RadiographyLayer::Geometry* geometry); + + RadiographyLayer& LoadMask(const std::vector& corners, + const RadiographyDicomLayer& dicomLayer, + float foreground, + RadiographyLayer::Geometry* geometry); + + RadiographyLayer& LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, // takes ownership + RadiographyLayer::Geometry* geometry); + + virtual RadiographyLayer& LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership + const std::string& instance, + unsigned int frame, + Deprecated::DicomFrameConverter* converter, // takes ownership + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode, + RadiographyLayer::Geometry* geometry); + + virtual RadiographyLayer& LoadDicomFrame(Deprecated::OrthancApiClient& orthanc, + const std::string& instance, + unsigned int frame, + bool httpCompression, + RadiographyLayer::Geometry* geometry); // pass NULL if you want default geometry + + RadiographyLayer& LoadDicomWebFrame(Deprecated::IWebService& web); + + void RemoveLayer(size_t layerIndex); + + RadiographyLayer& GetLayer(size_t layerIndex); + + const RadiographyLayer& GetLayer(size_t layerIndex) const; + + template + TypeLayer* GetTypedLayer(size_t indexOfType = 0) + { + std::vector layerIndexes; + GetLayersIndexes(layerIndexes); + + size_t count = 0; + + for (size_t i = 0; i < layerIndexes.size(); ++i) + { + TypeLayer* typedLayer = dynamic_cast(layers_[layerIndexes[i]]); + if (typedLayer != NULL) + { + if (count == indexOfType) + { + return typedLayer; + } + count++; + } + } + + return NULL; + } + + void GetLayersIndexes(std::vector& output) const; + + virtual Extent2D GetSceneExtent(bool minimal) const; + + virtual void Render(Orthanc::ImageAccessor& buffer, + const AffineTransform2D& viewTransform, + ImageInterpolation interpolation, + bool applyWindowing) const; + + bool LookupLayer(size_t& index /* out */, + double x, + double y) const; + + void DrawBorder(CairoContext& context, + unsigned int layer, + double zoom); + + void GetRange(float& minValue, + float& maxValue) const; + + void ExportToScene2D(Scene2D& output) const; + + // Export using PAM is faster than using PNG, but requires Orthanc + // core >= 1.4.3 + void ExportDicom(Deprecated::OrthancApiClient& orthanc, + const Orthanc::DicomMap& dicom, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam); + + void ExportDicom(Deprecated::OrthancApiClient& orthanc, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam); + + void ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation, + bool usePam); + + Orthanc::Image* ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent, + const Json::Value& dicomTags, + const std::string& parentOrthancId, + double pixelSpacingX, + double pixelSpacingY, + bool invert, + bool autoCrop, + ImageInterpolation interpolation); + + Orthanc::Image* ExportToImage(double pixelSpacingX, + double pixelSpacingY, + ImageInterpolation interpolation, + bool autoCrop, + bool applyWindowing) + { + return ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, false, 0, autoCrop, applyWindowing); + } + + Orthanc::Image* ExportToImage(double pixelSpacingX, + double pixelSpacingY, + ImageInterpolation interpolation, + bool invert, + int64_t maxValue /* for inversion */, + bool autoCrop, + bool applyWindowing); + + void ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer, + const Orthanc::ImageAccessor& renderedScene, + size_t layerIndex, + bool isCropped, + ImageInterpolation interpolation); + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographySceneCommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneCommand.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,62 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographySceneCommand.h" + + +namespace OrthancStone +{ + RadiographySceneCommand::RadiographySceneCommand(RadiographyScene& scene, + size_t layer) : + scene_(scene), + layer_(layer) + { + } + + + RadiographySceneCommand::RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor) : + scene_(accessor.GetScene()), + layer_(accessor.GetIndex()) + { + } + + + void RadiographySceneCommand::Undo() const + { + RadiographyScene::LayerAccessor accessor(scene_, layer_); + + if (accessor.IsValid()) + { + UndoInternal(accessor.GetLayer()); + } + } + + + void RadiographySceneCommand::Redo() const + { + RadiographyScene::LayerAccessor accessor(scene_, layer_); + + if (accessor.IsValid()) + { + RedoInternal(accessor.GetLayer()); + } + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographySceneCommand.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneCommand.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,50 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + class RadiographySceneCommand : public UndoRedoStack::ICommand + { + private: + RadiographyScene& scene_; + size_t layer_; + + protected: + virtual void UndoInternal(RadiographyLayer& layer) const = 0; + + virtual void RedoInternal(RadiographyLayer& layer) const = 0; + + public: + RadiographySceneCommand(RadiographyScene& scene, + size_t layer); + + RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor); + + virtual void Undo() const; + + virtual void Redo() const; + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographySceneReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneReader.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,189 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Deprecated/Toolbox/DicomFrameConverter.h" + +#include +#include +#include +#include + +namespace OrthancStone +{ + + void RadiographySceneBuilder::Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage /* takes ownership */, + Deprecated::DicomFrameConverter* dicomFrameConverter /* takes ownership */, + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode + ) + { + dicomImage_.reset(dicomImage); + dicomFrameConverter_.reset(dicomFrameConverter); + preferredPhotometricDisplayMode_ = preferredPhotometricDisplayMode; + Read(input); + } + + RadiographyDicomLayer* RadiographySceneBuilder::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) + { + return dynamic_cast(&(scene_.LoadDicomImage(dicomImage_.release(), instanceId, frame, dicomFrameConverter_.release(), preferredPhotometricDisplayMode_, geometry))); + } + + + RadiographyDicomLayer* RadiographySceneReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) + { + return dynamic_cast(&(scene_.LoadDicomFrame(orthancApiClient_, instanceId, frame, false, geometry))); + } + + RadiographyDicomLayer* RadiographySceneGeometryReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) + { + std::unique_ptr layer(new RadiographyPlaceholderLayer(scene_)); + layer->SetGeometry(*geometry); + layer->SetSize(dicomImageWidth_, dicomImageHeight_); + scene_.RegisterLayer(layer.get()); + + return layer.release(); + } + + void RadiographySceneBuilder::Read(const Json::Value& input) + { + unsigned int version = input["version"].asUInt(); + + if (version != 1) + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + + if (input.isMember("hasWindowing") && input["hasWindowing"].asBool()) + { + scene_.SetWindowing(input["windowCenter"].asFloat(), input["windowWidth"].asFloat()); + } + + RadiographyDicomLayer* dicomLayer = NULL; + for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) + { + const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; + RadiographyLayer::Geometry geometry; + + if (jsonLayer["type"].asString() == "dicom") + { + ReadLayerGeometry(geometry, jsonLayer); + dicomLayer = LoadDicom(jsonLayer["instanceId"].asString(), jsonLayer["frame"].asUInt(), &geometry); + } + else if (jsonLayer["type"].asString() == "mask") + { + if (dicomLayer == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // we always assumed the dicom layer was read before the mask + } + ReadLayerGeometry(geometry, jsonLayer); + + float foreground = jsonLayer["foreground"].asFloat(); + std::vector corners; + for (size_t i = 0; i < jsonLayer["corners"].size(); i++) + { + Orthanc::ImageProcessing::ImagePoint corner(jsonLayer["corners"][(int)i]["x"].asInt(), + jsonLayer["corners"][(int)i]["y"].asInt()); + corners.push_back(corner); + } + + scene_.LoadMask(corners, *dicomLayer, foreground, &geometry); + } + else if (jsonLayer["type"].asString() == "text") + { + ReadLayerGeometry(geometry, jsonLayer); + scene_.LoadText(jsonLayer["text"].asString(), jsonLayer["font"].asString(), jsonLayer["fontSize"].asUInt(), static_cast(jsonLayer["foreground"].asUInt()), &geometry, false); + } + 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::unique_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 RadiographySceneBuilder::ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input) + { + for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) + { + const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; + if (jsonLayer["type"].asString() == "dicom") + { + ReadLayerGeometry(geometry, jsonLayer); + return; + } + } + } + + void RadiographySceneBuilder::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()); + + // these fields were introduced later -> they might not exist + if (jsonLayer.isMember("flipVertical")) + { + geometry.SetFlipVertical(jsonLayer["flipVertical"].asBool()); + } + if (jsonLayer.isMember("flipHorizontal")) + { + geometry.SetFlipHorizontal(jsonLayer["flipHorizontal"].asBool()); + } + + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographySceneReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneReader.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,113 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyMaskLayer.h" +#include "RadiographyTextLayer.h" +#include "../Deprecated/Toolbox/OrthancApiClient.h" + +#include +#include + +namespace OrthancStone +{ + // a layer containing only the geometry of a DICOM layer (bit hacky !) + class RadiographyPlaceholderLayer : public RadiographyDicomLayer + { + public: + RadiographyPlaceholderLayer(const RadiographyScene& scene) : + RadiographyDicomLayer(scene) + { + } + + }; + + + // HACK: I had to introduce this builder class in order to be able to recreate a RadiographyScene + // from a serialized scene that is passed to web-workers. + // It needs some architecturing... + class RadiographySceneBuilder : public boost::noncopyable + { + protected: + RadiographyScene& scene_; + std::unique_ptr dicomImage_; + std::unique_ptr dicomFrameConverter_; + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode_; + + public: + RadiographySceneBuilder(RadiographyScene& scene) : + scene_(scene) + { + } + + void Read(const Json::Value& input); + void Read(const Json::Value& input, + Orthanc::ImageAccessor* dicomImage, // takes ownership + Deprecated::DicomFrameConverter* dicomFrameConverter, // takes ownership + RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode + ); + + static void ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input); + static void ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input); + + protected: + virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); + + }; + + + class RadiographySceneReader : public RadiographySceneBuilder + { + Deprecated::OrthancApiClient& orthancApiClient_; + + public: + RadiographySceneReader(RadiographyScene& scene, Deprecated::OrthancApiClient& orthancApiClient) : + RadiographySceneBuilder(scene), + orthancApiClient_(orthancApiClient) + { + } + + protected: + virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); + }; + + // reads the whole scene but the DICOM image such that we have the full geometry + class RadiographySceneGeometryReader : public RadiographySceneBuilder + { + unsigned int dicomImageWidth_; + unsigned int dicomImageHeight_; + + public: + RadiographySceneGeometryReader(RadiographyScene& scene, unsigned int dicomImageWidth, unsigned int dicomImageHeight) : + RadiographySceneBuilder(scene), + dicomImageWidth_(dicomImageWidth), + dicomImageHeight_(dicomImageHeight) + { + } + + protected: + virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographySceneWriter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneWriter.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,168 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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; + float windowCenter, windowWidth; + bool hasWindowing = scene.GetWindowing(windowCenter, windowWidth); + output["hasWindowing"] = hasWindowing; + if (hasWindowing) + { + output["windowCenter"] = windowCenter; + output["windowWidth"] = windowWidth; + } + output["layers"] = Json::arrayValue; + + std::vector layersIndexes; + 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["font"] = layer.GetFont(); + output["fontSize"] = layer.GetFontSize(); + output["foreground"] = layer.GetForegroundGreyLevel(); + } + + void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer) + { + output["type"] = "mask"; + output["instanceId"] = layer.GetInstanceId(); // the dicom layer it's being linked to + output["foreground"] = layer.GetForeground(); + output["corners"] = Json::arrayValue; + const std::vector& corners = layer.GetCorners(); + for (size_t i = 0; i < corners.size(); i++) + { + Json::Value corner; + corner["x"] = corners[i].GetX(); + corner["y"] = corners[i].GetY(); + output["corners"].append(corner); + } + } + + 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(); + } + + 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; + } + + output["flipVertical"] = geometry.GetFlipVertical(); + output["flipHorizontal"] = geometry.GetFlipHorizontal(); + + 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 if (dynamic_cast(&layer) != NULL) + { + WriteLayer(output, dynamic_cast(layer)); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographySceneWriter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographySceneWriter.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,52 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyMaskLayer.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); + void WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer); + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyTextLayer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyTextLayer.cpp Wed Apr 29 20:43:09 2020 +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-2020 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 "Core/OrthancException.h" +#include "RadiographyScene.h" +#include "../Toolbox/TextRenderer.h" + +namespace OrthancStone +{ + std::map RadiographyTextLayer::fonts_; + + void RadiographyTextLayer::SetText(const std::string& utf8, + const std::string& font, + unsigned int fontSize, + uint8_t foregroundGreyLevel) + { + if (fonts_.find(font) == fonts_.end()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "The font has not been registered"); + } + + text_ = utf8; + font_ = font; + fontSize_ = fontSize; + foregroundGreyLevel_ = foregroundGreyLevel; + + SetAlpha(TextRenderer::Render(fonts_[font_], + fontSize_, + text_)); + + SetForegroundValue(foregroundGreyLevel * 256.0f); + } + +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyTextLayer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyTextLayer.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,72 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 font_; + unsigned int fontSize_; + uint8_t foregroundGreyLevel_; + + static std::map fonts_; + public: + RadiographyTextLayer(const RadiographyScene& scene) : + RadiographyAlphaLayer(scene) + { + } + + void SetText(const std::string& utf8, const std::string& font, unsigned int fontSize, uint8_t foregroundGreyLevel); + + const std::string& GetText() const + { + return text_; + } + + const std::string& GetFont() const + { + return font_; + } + + unsigned int GetFontSize() const + { + return fontSize_; + } + + uint8_t GetForegroundGreyLevel() const + { + return foregroundGreyLevel_; + } + + static void RegisterFont(const std::string& name, Orthanc::EmbeddedResources::FileResourceId fontResourceId) + { + fonts_[name] = fontResourceId; + } + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyWidget.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyWidget.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,284 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyWidget.h" + +#include +#include +#include + +#include "RadiographyMaskLayer.h" + +namespace OrthancStone +{ + + bool RadiographyWidget::IsInvertedInternal() const + { + // MONOCHROME1 images must be inverted and the user can invert the + // image, too -> XOR the two + return (scene_->GetPreferredPhotomotricDisplayMode() == + RadiographyPhotometricDisplayMode_Monochrome1) ^ invert_; + } + + void RadiographyWidget::RenderBackground( + Orthanc::ImageAccessor& image, float minValue, float maxValue) + { + // wipe background before rendering + float backgroundValue = minValue; + + switch (scene_->GetPreferredPhotomotricDisplayMode()) + { + case RadiographyPhotometricDisplayMode_Monochrome1: + case RadiographyPhotometricDisplayMode_Default: + if (IsInvertedInternal()) + backgroundValue = maxValue; + else + backgroundValue = minValue; + break; + case RadiographyPhotometricDisplayMode_Monochrome2: + if (IsInvertedInternal()) + backgroundValue = minValue; + else + backgroundValue = maxValue; + break; + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + Orthanc::ImageProcessing::Set(image, static_cast(backgroundValue)); + } + + bool RadiographyWidget::RenderInternal(unsigned int width, + unsigned int height, + ImageInterpolation interpolation) + { + if (floatBuffer_.get() == NULL || + floatBuffer_->GetWidth() != width || + floatBuffer_->GetHeight() != height) + { + floatBuffer_.reset(new Orthanc::Image( + Orthanc::PixelFormat_Float32, width, height, false)); + + if (floatBuffer_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate float buffer"); + } + } + + if (cairoBuffer_.get() == NULL || + cairoBuffer_->GetWidth() != width || + cairoBuffer_->GetHeight() != height) + { + cairoBuffer_.reset(new CairoSurface(width, height, false /* no alpha */)); + + if (cairoBuffer_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate cairo buffer"); + } + } + + RenderBackground(*floatBuffer_, 0.0, 65535.0); + + scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation, true); + + // Conversion from Float32 to BGRA32 (cairo). Very similar to + // GrayscaleFrameRenderer => TODO MERGE? + Orthanc::ImageAccessor target; + cairoBuffer_->GetWriteableAccessor(target); + + bool invert = IsInvertedInternal(); + + for (unsigned int y = 0; y < height; y++) + { + const float* p = reinterpret_cast(floatBuffer_->GetConstRow(y)); + uint8_t* q = reinterpret_cast(target.GetRow(y)); + + for (unsigned int x = 0; x < width; x++, p++, q += 4) + { + uint8_t v = 0; + if (*p >= 65535.0) + { + v = 255; + } + else if (*p <= 0.0) + { + v = 0; + } + else + { + v = static_cast(*p / 256.0); + } + + if (invert) + { + v = 255 - v; + } + + q[0] = v; + q[1] = v; + q[2] = v; + q[3] = 255; + } + } + + return true; + } + + + bool RadiographyWidget::RenderScene(CairoContext& context, + const Deprecated::ViewportGeometry& view) + { + cairo_t* cr = context.GetObject(); + + if (RenderInternal(context.GetWidth(), context.GetHeight(), interpolation_)) + { + // https://www.cairographics.org/FAQ/#paint_from_a_surface + cairo_save(cr); + cairo_identity_matrix(cr); + cairo_set_source_surface(cr, cairoBuffer_->GetObject(), 0, 0); + cairo_paint(cr); + cairo_restore(cr); + } + else + { + // https://www.cairographics.org/FAQ/#clear_a_surface + context.SetSourceColor(0, 0, 0); + cairo_paint(cr); + } + + if (hasSelection_) + { + scene_->DrawBorder( + context, static_cast(selectedLayer_), view.GetZoom()); + } + + return true; + } + + + RadiographyWidget::RadiographyWidget(boost::shared_ptr scene, + const std::string& name) : + WorldSceneWidget(name), + invert_(false), + interpolation_(ImageInterpolation_Nearest), + hasSelection_(false), + selectedLayer_(0) // Dummy initialization + { + SetScene(scene); + } + + + void RadiographyWidget::Select(size_t layer) + { + hasSelection_ = true; + selectedLayer_ = layer; + + NotifyContentChanged(); + BroadcastMessage(SelectionChangedMessage(*this)); + } + + void RadiographyWidget::Unselect() + { + hasSelection_ = false; + + NotifyContentChanged(); + BroadcastMessage(SelectionChangedMessage(*this)); + } + + bool RadiographyWidget::LookupSelectedLayer(size_t& layer) const + { + if (hasSelection_) + { + layer = selectedLayer_; + return true; + } + else + { + return false; + } + } + + + void RadiographyWidget::OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message) + { +// LOG(INFO) << "Scene geometry has changed"; + FitContent(); + } + + + void RadiographyWidget::OnContentChanged(const RadiographyScene::ContentChangedMessage& message) + { +// LOG(INFO) << "Scene content has changed"; + NotifyContentChanged(); + } + + void RadiographyWidget::OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message) + { + size_t removedLayerIndex = message.GetLayerIndex(); + if (hasSelection_ && selectedLayer_ == removedLayerIndex) + { + Unselect(); + } + NotifyContentChanged(); + } + + void RadiographyWidget::SetInvert(bool invert) + { + if (invert_ != invert) + { + invert_ = invert; + NotifyContentChanged(); + } + } + + + void RadiographyWidget::SwitchInvert() + { + invert_ = !invert_; + NotifyContentChanged(); + } + + + void RadiographyWidget::SetInterpolation(ImageInterpolation interpolation) + { + if (interpolation_ != interpolation) + { + interpolation_ = interpolation; + NotifyContentChanged(); + } + } + + void RadiographyWidget::SetScene(boost::shared_ptr scene) + { + scene_ = scene; + + Register(*scene_, &RadiographyWidget::OnGeometryChanged); + Register(*scene_, &RadiographyWidget::OnContentChanged); + Register(*scene_, &RadiographyWidget::OnLayerRemoved); + + Unselect(); + + NotifyContentChanged(); + + // force redraw + FitContent(); + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyWidget.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyWidget.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,131 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Deprecated/Widgets/WorldSceneWidget.h" +#include "../Messages/ObserverBase.h" +#include "RadiographyScene.h" + + +namespace OrthancStone +{ + class RadiographyMaskLayer; + + class RadiographyWidget : + public Deprecated::WorldSceneWidget, + public ObserverBase, + public IObservable + { + public: + ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SelectionChangedMessage, RadiographyWidget); + + private: + boost::shared_ptr scene_; + std::unique_ptr floatBuffer_; + std::unique_ptr cairoBuffer_; + bool invert_; + ImageInterpolation interpolation_; + bool hasSelection_; + size_t selectedLayer_; + + bool RenderInternal(unsigned int width, + unsigned int height, + ImageInterpolation interpolation); + + protected: + virtual Extent2D GetSceneExtent() + { + return scene_->GetSceneExtent(false); + } + + virtual bool RenderScene(CairoContext& context, + const Deprecated::ViewportGeometry& view); + + virtual void RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue); + + bool IsInvertedInternal() const; + + public: + RadiographyWidget(boost::shared_ptr scene, // TODO: check how we can avoid boost::shared_ptr here since we don't want them in the public API (app is keeping a boost::shared_ptr to this right now) + const std::string& name); + + RadiographyScene& GetScene() const + { + return *scene_; + } + + void SetScene(boost::shared_ptr scene); + + void Select(size_t layer); + + void Unselect(); + + template bool SelectLayerByType(size_t index = 0); + + bool LookupSelectedLayer(size_t& layer) const; + + void OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message); + + void OnContentChanged(const RadiographyScene::ContentChangedMessage& message); + + void OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message); + + void SetInvert(bool invert); + + void SwitchInvert(); + + bool IsInverted() const + { + return invert_; + } + + void SetInterpolation(ImageInterpolation interpolation); + + ImageInterpolation GetInterpolation() const + { + return interpolation_; + } + }; + + template bool RadiographyWidget::SelectLayerByType(size_t index) + { + std::vector layerIndexes; + size_t count = 0; + scene_->GetLayersIndexes(layerIndexes); + + for (size_t i = 0; i < layerIndexes.size(); ++i) + { + const LayerType* typedLayer = dynamic_cast(&(scene_->GetLayer(layerIndexes[i]))); + if (typedLayer != NULL) + { + if (count == index) + { + Select(layerIndexes[i]); + return true; + } + count++; + } + } + + return false; + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyWindowingTracker.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyWindowingTracker.cpp Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,192 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "RadiographyWindowingTracker.h" +#include "RadiographyWidget.h" + +#include + + +namespace OrthancStone +{ + class RadiographyWindowingTracker::UndoRedoCommand : public UndoRedoStack::ICommand + { + private: + RadiographyScene& scene_; + float sourceCenter_; + float sourceWidth_; + float targetCenter_; + float targetWidth_; + + public: + UndoRedoCommand(const RadiographyWindowingTracker& tracker) : + scene_(tracker.scene_), + sourceCenter_(tracker.sourceCenter_), + sourceWidth_(tracker.sourceWidth_) + { + scene_.GetWindowingWithDefault(targetCenter_, targetWidth_); + } + + virtual void Undo() const + { + scene_.SetWindowing(sourceCenter_, sourceWidth_); + } + + virtual void Redo() const + { + scene_.SetWindowing(targetCenter_, targetWidth_); + } + }; + + + void RadiographyWindowingTracker::ComputeAxisEffect(int& deltaCenter, + int& deltaWidth, + int delta, + Action actionNegative, + Action actionPositive) + { + if (delta < 0) + { + switch (actionNegative) + { + case Action_IncreaseWidth: + deltaWidth = -delta; + break; + + case Action_DecreaseWidth: + deltaWidth = delta; + break; + + case Action_IncreaseCenter: + deltaCenter = -delta; + break; + + case Action_DecreaseCenter: + deltaCenter = delta; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + else if (delta > 0) + { + switch (actionPositive) + { + case Action_IncreaseWidth: + deltaWidth = delta; + break; + + case Action_DecreaseWidth: + deltaWidth = -delta; + break; + + case Action_IncreaseCenter: + deltaCenter = delta; + break; + + case Action_DecreaseCenter: + deltaCenter = -delta; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + } + + + RadiographyWindowingTracker::RadiographyWindowingTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + RadiographyWidget& widget, + ImageInterpolation interpolationDuringTracking, + int x, + int y, + Action leftAction, + Action rightAction, + Action upAction, + Action downAction) : + undoRedoStack_(undoRedoStack), + scene_(scene), + widget_(widget), + initialWidgetInterpolation_(widget.GetInterpolation()), + clickX_(x), + clickY_(y), + leftAction_(leftAction), + rightAction_(rightAction), + upAction_(upAction), + downAction_(downAction) + { + scene_.GetWindowingWithDefault(sourceCenter_, sourceWidth_); + widget_.SetInterpolation(interpolationDuringTracking); + + float minValue, maxValue; + scene.GetRange(minValue, maxValue); + + assert(minValue <= maxValue); + + float delta = (maxValue - minValue); + strength_ = delta / 1000.0f; // 1px move will change the ww/wc by 0.1% + + if (strength_ < 1) + { + strength_ = 1; + } + } + + + void RadiographyWindowingTracker::Render(CairoContext& context, + double zoom) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void RadiographyWindowingTracker::MouseUp() + { + widget_.SetInterpolation(initialWidgetInterpolation_); + undoRedoStack_.Add(new UndoRedoCommand(*this)); + } + + + void RadiographyWindowingTracker::MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches) + { + // This follows the behavior of the Osimis Web viewer: + // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js + + static const float SCALE = 1.0; + + int deltaCenter = 0; + int deltaWidth = 0; + + ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_); + ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_); + + float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_); + float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_); + scene_.SetWindowing(newCenter, newWidth); + } +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Deprecated/Radiography/RadiographyWindowingTracker.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Deprecated/Radiography/RadiographyWindowingTracker.h Wed Apr 29 20:43:09 2020 +0200 @@ -0,0 +1,96 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" +#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" +#include "RadiographyScene.h" + +namespace OrthancStone +{ + + class RadiographyWidget; + + class RadiographyWindowingTracker : public Deprecated::IWorldSceneMouseTracker + { + public: + enum Action + { + Action_IncreaseWidth, + Action_DecreaseWidth, + Action_IncreaseCenter, + Action_DecreaseCenter + }; + + private: + class UndoRedoCommand; + + UndoRedoStack& undoRedoStack_; + RadiographyScene& scene_; + RadiographyWidget& widget_; + ImageInterpolation initialWidgetInterpolation_; + int clickX_; + int clickY_; + Action leftAction_; + Action rightAction_; + Action upAction_; + Action downAction_; + float strength_; + float sourceCenter_; + float sourceWidth_; + + static void ComputeAxisEffect(int& deltaCenter, + int& deltaWidth, + int delta, + Action actionNegative, + Action actionPositive); + + public: + RadiographyWindowingTracker(UndoRedoStack& undoRedoStack, + RadiographyScene& scene, + RadiographyWidget& widget, + ImageInterpolation interpolationDuringTracking, + int x, + int y, + Action leftAction, + Action rightAction, + Action upAction, + Action downAction); + + virtual bool HasRender() const + { + return false; + } + + virtual void Render(CairoContext& context, + double zoom); + + virtual void MouseUp(); + + virtual void MouseMove(int displayX, + int displayY, + double sceneX, + double sceneY, + const std::vector& displayTouches, + const std::vector& sceneTouches); + }; +} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyAlphaLayer.cpp --- a/Framework/Radiography/RadiographyAlphaLayer.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,152 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 -#include - -#include "../Toolbox/ImageGeometry.h" - -namespace OrthancStone -{ - - void RadiographyAlphaLayer::SetAlpha(Orthanc::ImageAccessor* image) - { - std::unique_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()); - -#if __cplusplus < 201103L - alpha_.reset(raii.release()); -#else - alpha_ = std::move(raii); -#endif - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyAlphaLayer::Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) 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); - - unsigned int x1, y1, x2, y2; - - if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, - t.GetHomogeneousMatrix(), - cropped.GetWidth(), - cropped.GetHeight(), - buffer.GetWidth(), - buffer.GetHeight())) - { - return; // layer is outside the buffer - } - - t.Apply(tmp, cropped, interpolation, true /* clear */); - - float value = foreground_; - - if (!applyWindowing) // if applying the windowing, it means we are ie rendering the image for a realtime visualization -> the foreground_ value is the value we want to see on the screen -> don't change it - { - // if not applying the windowing, it means ie that we are saving a dicom image to file and the windowing will be applied by a viewer later on -> we want the "foreground" value to be correct once the windowing will be applied - value = windowCenter - windowWidth/2 + (foreground_ / 65535.0f) * windowWidth; - - if (value < 0.0f) - { - value = 0.0f; - } - if (value > 65535.0f) - { - value = 65535.0f; - } - } - - for (unsigned int y = y1; y <= y2; y++) - { - float *q = reinterpret_cast(buffer.GetRow(y)) + x1; - const uint8_t *p = reinterpret_cast(tmp.GetRow(y)) + x1; - - for (unsigned int x = x1; x <= x2; 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 - { - minValue = 0; - maxValue = 0; - - if (foreground_ < 0) - { - minValue = foreground_; - } - - if (foreground_ > 0) - { - maxValue = foreground_; - } - - return true; - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyAlphaLayer.h --- a/Framework/Radiography/RadiographyAlphaLayer.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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; - - // 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: - std::unique_ptr alpha_; // Grayscale8 in the range [0, 255] 0 = transparent, 255 = opaque -> the foreground value will be displayed - float foreground_; // in the range [0.0, 65535.0] - - public: - RadiographyAlphaLayer(const RadiographyScene& scene) : - RadiographyLayer(scene), - foreground_(0) - { - } - - - void SetForegroundValue(float foreground) - { - foreground_ = foreground; - } - - float GetForegroundValue() const - { - return foreground_; - } - - 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, - float windowCenter, - float windowWidth, - bool applyWindowing) const; - - virtual bool GetRange(float& minValue, - float& maxValue) const; - - const Orthanc::ImageAccessor& GetAlpha() const - { - return *(alpha_.get()); - } - - - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyDicomLayer.cpp --- a/Framework/Radiography/RadiographyDicomLayer.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,264 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Deprecated/Toolbox/DicomFrameConverter.h" - -#include -#include -#include -#include -#include "../Toolbox/ImageGeometry.h" - -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_)); - } - } - - - RadiographyDicomLayer::RadiographyDicomLayer(const RadiographyScene& scene) : - RadiographyLayer(scene) - { - - } - - void RadiographyDicomLayer::SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset) - { - converter_.reset(new Deprecated::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]); - } - - 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); - } - - if (dataset.GetStringValue(tmp, ConvertTag(Orthanc::DICOM_TAG_PHOTOMETRIC_INTERPRETATION))) - { - if (tmp == "MONOCHROME1") - { - SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome1); - } - else if (tmp == "MONOCHROME2") - { - SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode_Monochrome2); - } - } - } - - void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image) // Takes ownership - { - std::unique_ptr raii(image); - - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - SetSize(image->GetWidth(), image->GetHeight()); - -#if __cplusplus < 201103L - source_.reset(raii.release()); -#else - source_ = std::move(raii); -#endif - - ApplyConverter(); - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyDicomLayer::SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent) // Takes ownership - { - std::unique_ptr raii(image); - - if (image == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - SetSize(image->GetWidth(), image->GetHeight(), false); - -#if __cplusplus < 201103L - source_.reset(raii.release()); -#else - source_ = std::move(raii); -#endif - - ApplyConverter(); - - SetPixelSpacing(newPixelSpacingX, newPixelSpacingY, false); - - if (emitLayerEditedEvent) - { - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - } - - - void RadiographyDicomLayer::SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter) - { - converter_.reset(converter); - } - - void RadiographyDicomLayer::Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) 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); - - unsigned int x1, y1, x2, y2; - if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, - t.GetHomogeneousMatrix(), - cropped.GetWidth(), - cropped.GetHeight(), - buffer.GetWidth(), - buffer.GetHeight())) - { - return; // layer is outside the buffer - } - - t.Apply(buffer, cropped, interpolation, false); - - if (applyWindowing) - { - // apply windowing but stay in the range [0.0, 65535.0] - float w0 = windowCenter - windowWidth / 2.0f; - float w1 = windowCenter + windowWidth / 2.0f; - - if (windowWidth >= 0.001f) // Avoid division by zero at (*) - { - float scaling = 1.0f / (w1 - w0) * 65535.0f; - for (unsigned int y = y1; y <= y2; y++) - { - float* p = reinterpret_cast(buffer.GetRow(y)) + x1; - - for (unsigned int x = x1; x <= x2; x++, p++) - { - if (*p >= w1) - { - *p = 65535.0; - } - else if (*p <= w0) - { - *p = 0; - } - else - { - // https://en.wikipedia.org/wiki/Linear_interpolation - *p = scaling * (*p - w0); // (*) - } - } - } - } - } - - } - } - - - 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 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyDicomLayer.h --- a/Framework/Radiography/RadiographyDicomLayer.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Deprecated/Toolbox/DicomFrameConverter.h" -#include "RadiographyLayer.h" - -#include - -namespace OrthancStone -{ - class RadiographyScene; - - class RadiographyDicomLayer : public RadiographyLayer - { - private: - std::unique_ptr source_; // Content of PixelData - std::unique_ptr converter_; - std::unique_ptr converted_; // Float32 - std::string instanceId_; - unsigned int frame_; - - void ApplyConverter(); - - public: - RadiographyDicomLayer(const RadiographyScene& scene); - - 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_; - } - - virtual size_t GetApproximateMemoryUsage() const - { - size_t size = 0; - if (source_.get() != NULL) - { - size += source_->GetPitch() * source_->GetHeight(); - } - if (converted_.get() != NULL) - { - size += converted_->GetPitch() * converted_->GetHeight(); - } - - return size; - } - - - void SetDicomTags(const OrthancPlugins::FullOrthancDataset& dataset); - - void SetSourceImage(Orthanc::ImageAccessor* image); // Takes ownership - - void SetSourceImage(Orthanc::ImageAccessor* image, double newPixelSpacingX, double newPixelSpacingY, bool emitLayerEditedEvent = true); // Takes ownership - - const Orthanc::ImageAccessor* GetSourceImage() const {return source_.get();} // currently need this access to serialize scene in plain old data to send to a WASM worker - - const Deprecated::DicomFrameConverter& GetDicomFrameConverter() const {return *converter_;} // currently need this access to serialize scene in plain old data to send to a WASM worker - - // Takes ownership - void SetDicomFrameConverter(Deprecated::DicomFrameConverter* converter); - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const; - - virtual bool GetDefaultWindowing(float& center, - float& width) const; - - virtual bool GetRange(float& minValue, - float& maxValue) const; - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayer.cpp --- a/Framework/Radiography/RadiographyLayer.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,402 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyLayer.h" - -#include - - -namespace OrthancStone -{ - static double Square(double x) - { - return x * x; - } - - - RadiographyLayer::Geometry::Geometry() : - hasCrop_(false), - flipVertical_(false), - flipHorizontal_(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 RadiographyLayer::GetCrop() or at least call HasCrop() before - - x = cropX_; - y = cropY_; - width = cropWidth_; - height = cropHeight_; - } - - void RadiographyLayer::UpdateTransform() - { - // important to update transform_ before getting the center to use the right scaling !!! - transform_ = AffineTransform2D::CreateScaling(geometry_.GetScalingX(), geometry_.GetScalingY()); - - double centerX, centerY; - GetCenter(centerX, centerY); - - transform_ = AffineTransform2D::Combine( - AffineTransform2D::CreateOffset(geometry_.GetPanX(), geometry_.GetPanY()), - AffineTransform2D::CreateRotation(geometry_.GetAngle(), centerX, centerY), - transform_); - - transformInverse_ = AffineTransform2D::Invert(transform_); - } - - - void RadiographyLayer::AddToExtent(Extent2D& extent, - double x, - double y) const - { - GetTransform().Apply(x, y); - extent.AddPoint(x, y); - } - - bool RadiographyLayer::Contains(double x, - double y) const - { - GetTransformInverse().Apply(x, y); - - unsigned int cropX, cropY, cropWidth, cropHeight; - GetCrop(cropX, cropY, cropWidth, cropHeight); - - return (x >= cropX && x <= cropX + cropWidth && - y >= cropY && y <= cropY + cropHeight); - } - - - void RadiographyLayer::DrawBorders(CairoContext& context, - double zoom) - { - if (GetControlPointCount() < 3 ) - return; - - cairo_t* cr = context.GetObject(); - cairo_set_line_width(cr, 2.0 / zoom); - - ControlPoint cp; - GetControlPoint(cp, 0); - cairo_move_to(cr, cp.x, cp.y); - - for (size_t i = 0; i < GetControlPointCount(); i++) - { - GetControlPoint(cp, i); - cairo_line_to(cr, cp.x, cp.y); - } - - cairo_close_path(cr); - cairo_stroke(cr); - } - - - RadiographyLayer::RadiographyLayer(const RadiographyScene& scene) : - index_(0), - hasSize_(false), - width_(0), - height_(0), - prefferedPhotometricDisplayMode_(RadiographyPhotometricDisplayMode_Default), - scene_(scene) - { - UpdateTransform(); - } - - void RadiographyLayer::ResetCrop() - { - geometry_.ResetCrop(); - UpdateTransform(); - } - - void RadiographyLayer::SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode) - { - prefferedPhotometricDisplayMode_ = prefferedPhotometricDisplayMode; - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyLayer::SetCrop(unsigned int x, - unsigned int y, - unsigned int width, - unsigned int height) - { - if (!hasSize_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - - if (x + width > width_ || - y + height > height_) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - geometry_.SetCrop(x, y, width, height); - UpdateTransform(); - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyLayer::SetGeometry(const Geometry& geometry) - { - geometry_ = geometry; - - if (hasSize_) - { - UpdateTransform(); - } - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - - void RadiographyLayer::GetCrop(unsigned int& x, - unsigned int& y, - unsigned int& width, - unsigned int& height) const - { - if (GetGeometry().HasCrop()) - { - GetGeometry().GetCrop(x, y, width, height); - } - else - { - x = 0; - y = 0; - width = width_; - height = height_; - } - } - - - void RadiographyLayer::SetAngle(double angle) - { - geometry_.SetAngle(angle); - UpdateTransform(); - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyLayer::SetFlipVertical(bool flip) - { - geometry_.SetFlipVertical(flip); - UpdateTransform(); - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyLayer::SetFlipHorizontal(bool flip) - { - geometry_.SetFlipHorizontal(flip); - UpdateTransform(); - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyLayer::SetSize(unsigned int width, - unsigned int height, - bool emitLayerEditedEvent) - { - hasSize_ = true; - width_ = width; - height_ = height; - - UpdateTransform(); - - if (emitLayerEditedEvent) - { - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - } - - Extent2D RadiographyLayer::GetSceneExtent(bool /*minimal*/) const - { - Extent2D extent; - - unsigned int x, y, width, height; - GetCrop(x, y, width, height); - - double dx = static_cast(x); - double dy = static_cast(y); - double dwidth = static_cast(width); - double dheight = static_cast(height); - - // AddToExtent transforms the coordinates from image to scene - AddToExtent(extent, dx, dy); - AddToExtent(extent, dx + dwidth, dy); - AddToExtent(extent, dx, dy + dheight); - AddToExtent(extent, dx + dwidth, dy + dheight); - - return extent; - } - - - bool RadiographyLayer::GetPixel(unsigned int& imageX, - unsigned int& imageY, - double sceneX, - double sceneY) const - { - if (width_ == 0 || - height_ == 0) - { - return false; - } - else - { - GetTransformInverse().Apply(sceneX, sceneY); - - int x = static_cast(std::floor(sceneX)); - int y = static_cast(std::floor(sceneY)); - - if (x < 0) - { - imageX = 0; - } - else if (x >= static_cast(width_)) - { - imageX = width_; - } - else - { - imageX = static_cast(x); - } - - if (y < 0) - { - imageY = 0; - } - else if (y >= static_cast(height_)) - { - imageY = height_; - } - else - { - imageY = static_cast(y); - } - - return true; - } - } - - - void RadiographyLayer::SetPan(double x, - double y) - { - geometry_.SetPan(x, y); - UpdateTransform(); - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - - void RadiographyLayer::SetPixelSpacing(double x, - double y, - bool emitLayerEditedEvent) - { - geometry_.SetPixelSpacing(x, y); - UpdateTransform(); - if (emitLayerEditedEvent) - { - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - } - - - void RadiographyLayer::GetCenter(double& centerX, - double& centerY) const - { - centerX = static_cast(width_) / 2.0; - centerY = static_cast(height_) / 2.0; - GetTransform().Apply(centerX, centerY); - } - - - - size_t RadiographyLayer::GetControlPointCount() const {return 4;} - - void RadiographyLayer::GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */, - size_t index) const - { - unsigned int cropX, cropY, cropWidth, cropHeight; - GetCrop(cropX, cropY, cropWidth, cropHeight); - - ControlPoint cp; - switch (index) - { - case RadiographyControlPointType_TopLeftCorner: - cp = ControlPoint(cropX, cropY, RadiographyControlPointType_TopLeftCorner); - break; - - case RadiographyControlPointType_TopRightCorner: - cp = ControlPoint(cropX + cropWidth, cropY, RadiographyControlPointType_TopRightCorner); - break; - - case RadiographyControlPointType_BottomLeftCorner: - cp = ControlPoint(cropX, cropY + cropHeight, RadiographyControlPointType_BottomLeftCorner); - break; - - case RadiographyControlPointType_BottomRightCorner: - cp = ControlPoint(cropX + cropWidth, cropY + cropHeight, RadiographyControlPointType_BottomRightCorner); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - // transforms image coordinates into scene coordinates - GetTransform().Apply(cp.x, cp.y); - cpScene = cp; - } - - bool RadiographyLayer::LookupControlPoint(ControlPoint& cpScene /* out */, - double x, - double y, - double zoom, - double viewportDistance) const - { - double threshold = Square(viewportDistance / zoom); - - for (size_t i = 0; i < GetControlPointCount(); i++) - { - ControlPoint cp; - GetControlPoint(cp, i); - - double d = Square(cp.x - x) + Square(cp.y - y); - - if (d <= threshold) - { - cpScene = cp; - return true; - } - } - - return false; - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayer.h --- a/Framework/Radiography/RadiographyLayer.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,395 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 Osimis S.A., Belgium - * - * This program is free software: you can redistribute it and/or - * modify it under the terms of the GNU Affero General Public License - * as published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - **/ - - -#pragma once - -#include - -#include "../Toolbox/AffineTransform2D.h" -#include "../Toolbox/Extent2D.h" -#include "../Wrappers/CairoContext.h" -#include "../Messages/IMessage.h" -#include "../Messages/IObservable.h" - -namespace OrthancStone -{ - class RadiographyScene; - - enum RadiographyControlPointType - { - RadiographyControlPointType_TopLeftCorner = 0, - RadiographyControlPointType_TopRightCorner = 1, - RadiographyControlPointType_BottomRightCorner = 2, - RadiographyControlPointType_BottomLeftCorner = 3 - }; - - enum RadiographyPhotometricDisplayMode - { - RadiographyPhotometricDisplayMode_Default, - - RadiographyPhotometricDisplayMode_Monochrome1, - RadiographyPhotometricDisplayMode_Monochrome2 - }; - - - struct ControlPoint - { - double x; - double y; - size_t index; - - ControlPoint(double x, double y, size_t index) - : x(x), - y(y), - index(index) - {} - - ControlPoint() - : x(0), - y(0), - index(std::numeric_limits::max()) - {} - }; - - class RadiographyLayer : public IObservable - { - friend class RadiographyScene; - - public: - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, LayerEditedMessage, RadiographyLayer); - - class Geometry - { - bool hasCrop_; - unsigned int cropX_; - unsigned int cropY_; - unsigned int cropWidth_; - unsigned int cropHeight_; - bool flipVertical_; - bool flipHorizontal_; - 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) - { - hasCrop_ = true; - 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_; - } - - void SetFlipVertical(bool flip) // mirrors image around an horizontal axis (note: flip is applied before the rotation !) - { - flipVertical_ = flip; - } - - void SetFlipHorizontal(bool flip) // mirrors image around a vertical axis (note: flip is applied before the rotation !) - { - flipHorizontal_ = flip; - } - - bool GetFlipVertical() const - { - return flipVertical_; - } - - bool GetFlipHorizontal() const - { - return flipHorizontal_; - } - - double GetScalingX() const - { - return (flipHorizontal_ ? - pixelSpacingX_: pixelSpacingX_); - } - - double GetScalingY() const - { - return (flipVertical_ ? - pixelSpacingY_: pixelSpacingY_); - } - }; - - private: - size_t index_; - bool hasSize_; - unsigned int width_; - unsigned int height_; - AffineTransform2D transform_; - AffineTransform2D transformInverse_; - Geometry geometry_; - RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode_; - const RadiographyScene& scene_; - - protected: - void SetPreferredPhotomotricDisplayMode(RadiographyPhotometricDisplayMode prefferedPhotometricDisplayMode); - - private: - void UpdateTransform(); - - void AddToExtent(Extent2D& extent, - double x, - double y) const; - - void SetIndex(size_t index) - { - index_ = index; - } - - bool Contains(double x, - double y) const; - - void DrawBorders(CairoContext& context, - double zoom); - - public: - RadiographyLayer(const RadiographyScene& scene); - - virtual ~RadiographyLayer() - { - } - - virtual const AffineTransform2D& GetTransform() const - { - return transform_; - } - - virtual const AffineTransform2D& GetTransformInverse() const - { - return transformInverse_; - } - - size_t GetIndex() const - { - return index_; - } - - const RadiographyScene& GetScene() const - { - return scene_; - } - - const Geometry& GetGeometry() const - { - return geometry_; - } - - void SetGeometry(const Geometry& geometry); - - void ResetCrop(); - - void SetCrop(unsigned int x, // those are pixel coordinates/size - unsigned int y, - unsigned int width, - unsigned int height); - - void SetCrop(const Extent2D& sceneExtent) - { - Extent2D imageCrop; - - { - double x = sceneExtent.GetX1(); - double y = sceneExtent.GetY1(); - GetTransformInverse().Apply(x, y); - imageCrop.AddPoint(x, y); - } - - { - double x = sceneExtent.GetX2(); - double y = sceneExtent.GetY2(); - GetTransformInverse().Apply(x, y); - imageCrop.AddPoint(x, y); - } - - SetCrop(static_cast(std::max(0.0, std::floor(imageCrop.GetX1()))), - static_cast(std::max(0.0, std::floor(imageCrop.GetY1()))), - std::min(width_, static_cast(std::ceil(imageCrop.GetWidth()))), - std::min(height_, static_cast(std::ceil(imageCrop.GetHeight()))) - ); - } - - - void GetCrop(unsigned int& x, - unsigned int& y, - unsigned int& width, - unsigned int& height) const; - - void SetAngle(double angle); - - void SetPan(double x, - double y); - - void SetFlipVertical(bool flip); // mirrors image around an horizontal axis (note: flip is applied before the rotation !) - - void SetFlipHorizontal(bool flip); // mirrors image around a vertical axis (note: flip is applied before the rotation !) - - void SetResizeable(bool resizeable) - { - geometry_.SetResizeable(resizeable); - } - - void SetSize(unsigned int width, - unsigned int height, - bool emitLayerEditedEvent = true); - - bool HasSize() const - { - return hasSize_; - } - - unsigned int GetWidth() const - { - return width_; - } - - unsigned int GetHeight() const - { - return height_; - } - - virtual Extent2D GetSceneExtent(bool minimal) const; - - virtual bool GetPixel(unsigned int& imageX, - unsigned int& imageY, - double sceneX, - double sceneY) const; - - void SetPixelSpacing(double x, - double y, - bool emitLayerEditedEvent = true); - - void GetCenter(double& centerX, - double& centerY) const; - - virtual void GetControlPoint(ControlPoint& cpScene /* out in scene coordinates */, - size_t index) const; - - virtual size_t GetControlPointCount() const; - - bool LookupControlPoint(ControlPoint& cpScene /* out */, - double x, - double y, - double zoom, - double viewportDistance) const; - - virtual bool GetDefaultWindowing(float& center, - float& width) const = 0; - - RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const - { - return prefferedPhotometricDisplayMode_; - } - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const = 0; - - virtual bool GetRange(float& minValue, - float& maxValue) const = 0; - - virtual size_t GetApproximateMemoryUsage() const // this is used to limit the number of scenes loaded in RAM when resources are limited (we actually only count the size used by the images, not the C structs) - { - return 0; - } - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayerCropTracker.cpp --- a/Framework/Radiography/RadiographyLayerCropTracker.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,145 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyLayerCropTracker.h" - -#include "RadiographySceneCommand.h" - -#include - -namespace OrthancStone -{ - class RadiographyLayerCropTracker::UndoRedoCommand : public RadiographySceneCommand - { - private: - unsigned int sourceCropX_; - unsigned int sourceCropY_; - unsigned int sourceCropWidth_; - unsigned int sourceCropHeight_; - unsigned int targetCropX_; - unsigned int targetCropY_; - unsigned int targetCropWidth_; - unsigned int targetCropHeight_; - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const - { - layer.SetCrop(sourceCropX_, sourceCropY_, sourceCropWidth_, sourceCropHeight_); - } - - virtual void RedoInternal(RadiographyLayer& layer) const - { - layer.SetCrop(targetCropX_, targetCropY_, targetCropWidth_, targetCropHeight_); - } - - public: - UndoRedoCommand(const RadiographyLayerCropTracker& tracker) : - RadiographySceneCommand(tracker.accessor_), - sourceCropX_(tracker.cropX_), - sourceCropY_(tracker.cropY_), - sourceCropWidth_(tracker.cropWidth_), - sourceCropHeight_(tracker.cropHeight_) - { - tracker.accessor_.GetLayer().GetCrop(targetCropX_, targetCropY_, - targetCropWidth_, targetCropHeight_); - } - }; - - - RadiographyLayerCropTracker::RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - const ControlPoint& startControlPoint) : - undoRedoStack_(undoRedoStack), - accessor_(scene, layer), - startControlPoint_(startControlPoint) - { - if (accessor_.IsValid()) - { - accessor_.GetLayer().GetCrop(cropX_, cropY_, cropWidth_, cropHeight_); - } - } - - - void RadiographyLayerCropTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyLayerCropTracker::MouseUp() - { - if (accessor_.IsValid()) - { - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - } - - - void RadiographyLayerCropTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - if (accessor_.IsValid()) - { - unsigned int x, y; - - RadiographyLayer& layer = accessor_.GetLayer(); - if (layer.GetPixel(x, y, sceneX, sceneY)) - { - unsigned int targetX, targetWidth; - - if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner || - startControlPoint_.index == RadiographyControlPointType_BottomLeftCorner) - { - targetX = std::min(x, cropX_ + cropWidth_); - targetWidth = cropX_ + cropWidth_ - targetX; - } - else - { - targetX = cropX_; - targetWidth = std::max(x, cropX_) - cropX_; - } - - unsigned int targetY, targetHeight; - - if (startControlPoint_.index == RadiographyControlPointType_TopLeftCorner || - startControlPoint_.index == RadiographyControlPointType_TopRightCorner) - { - targetY = std::min(y, cropY_ + cropHeight_); - targetHeight = cropY_ + cropHeight_ - targetY; - } - else - { - targetY = cropY_; - targetHeight = std::max(y, cropY_) - cropY_; - } - - layer.SetCrop(targetX, targetY, targetWidth, targetHeight); - } - } - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayerCropTracker.h --- a/Framework/Radiography/RadiographyLayerCropTracker.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Toolbox/ViewportGeometry.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - class RadiographyLayerCropTracker : public Deprecated::IWorldSceneMouseTracker - { - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene::LayerAccessor accessor_; - ControlPoint startControlPoint_; - unsigned int cropX_; - unsigned int cropY_; - unsigned int cropWidth_; - unsigned int cropHeight_; - - public: - RadiographyLayerCropTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - const ControlPoint& startControlPoint); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayerMaskTracker.cpp --- a/Framework/Radiography/RadiographyLayerMaskTracker.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,140 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyLayerMaskTracker.h" -#include "RadiographyMaskLayer.h" - -#include "RadiographySceneCommand.h" - -#include - -namespace OrthancStone -{ - class RadiographyLayerMaskTracker::UndoRedoCommand : public RadiographySceneCommand - { - private: - ControlPoint sourceSceneCp_; - ControlPoint targetSceneCp_; - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const - { - RadiographyMaskLayer* maskLayer = dynamic_cast(&layer); - if (maskLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - unsigned int ix, iy; // image coordinates - if (maskLayer->GetPixel(ix, iy, sourceSceneCp_.x, sourceSceneCp_.y)) - { - maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), sourceSceneCp_.index); - } - } - - virtual void RedoInternal(RadiographyLayer& layer) const - { - RadiographyMaskLayer* maskLayer = dynamic_cast(&layer); - if (maskLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - unsigned int ix, iy; // image coordinates - if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y)) - { - maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index); - } - } - - public: - UndoRedoCommand(const RadiographyLayerMaskTracker& tracker) : - RadiographySceneCommand(tracker.accessor_), - sourceSceneCp_(tracker.startSceneCp_), - targetSceneCp_(tracker.endSceneCp_) - { - RadiographyMaskLayer* maskLayer = dynamic_cast(&(tracker.accessor_.GetLayer())); - if (maskLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - unsigned int ix, iy; // image coordinates - if (maskLayer->GetPixel(ix, iy, targetSceneCp_.x, targetSceneCp_.y)) - { - maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), targetSceneCp_.index); - } - } - }; - - - RadiographyLayerMaskTracker::RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - const ControlPoint& startSceneControlPoint) : - undoRedoStack_(undoRedoStack), - accessor_(scene, layer), - startSceneCp_(startSceneControlPoint), - endSceneCp_(startSceneControlPoint) - { - } - - - void RadiographyLayerMaskTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyLayerMaskTracker::MouseUp() - { - if (accessor_.IsValid() && startSceneCp_.x != endSceneCp_.x && startSceneCp_.y != endSceneCp_.y) - { - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - } - - - void RadiographyLayerMaskTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - if (accessor_.IsValid()) - { - unsigned int ix, iy; // image coordinates - - RadiographyLayer& layer = accessor_.GetLayer(); - if (layer.GetPixel(ix, iy, sceneX, sceneY)) - { - endSceneCp_ = ControlPoint(sceneX, sceneY, startSceneCp_.index); - - RadiographyMaskLayer* maskLayer = dynamic_cast(&layer); - if (maskLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - maskLayer->SetCorner(Orthanc::ImageProcessing::ImagePoint((int32_t)ix, (int32_t)iy), startSceneCp_.index); - } - } - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayerMaskTracker.h --- a/Framework/Radiography/RadiographyLayerMaskTracker.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Toolbox/ViewportGeometry.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - class RadiographyLayerMaskTracker : public Deprecated::IWorldSceneMouseTracker - { - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene::LayerAccessor accessor_; - ControlPoint startSceneCp_; - ControlPoint endSceneCp_; - - public: - RadiographyLayerMaskTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - const ControlPoint& startSceneControlPoint); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayerMoveTracker.cpp --- a/Framework/Radiography/RadiographyLayerMoveTracker.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,126 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyLayerMoveTracker.h" - -#include "RadiographySceneCommand.h" - -#include - -namespace OrthancStone -{ - class RadiographyLayerMoveTracker::UndoRedoCommand : public RadiographySceneCommand - { - private: - double sourceX_; - double sourceY_; - double targetX_; - double targetY_; - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const - { - layer.SetPan(sourceX_, sourceY_); - } - - virtual void RedoInternal(RadiographyLayer& layer) const - { - layer.SetPan(targetX_, targetY_); - } - - public: - UndoRedoCommand(const RadiographyLayerMoveTracker& tracker) : - RadiographySceneCommand(tracker.accessor_), - sourceX_(tracker.panX_), - sourceY_(tracker.panY_), - targetX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()), - targetY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY()) - { - } - }; - - - RadiographyLayerMoveTracker::RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - size_t layer, - double x, - double y, - bool oneAxis) : - undoRedoStack_(undoRedoStack), - accessor_(scene, layer), - clickX_(x), - clickY_(y), - oneAxis_(oneAxis) - { - if (accessor_.IsValid()) - { - panX_ = accessor_.GetLayer().GetGeometry().GetPanX(); - panY_ = accessor_.GetLayer().GetGeometry().GetPanY(); - } - } - - - void RadiographyLayerMoveTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyLayerMoveTracker::MouseUp() - { - if (accessor_.IsValid()) - { - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - } - - - void RadiographyLayerMoveTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - if (accessor_.IsValid()) - { - double dx = sceneX - clickX_; - double dy = sceneY - clickY_; - - if (oneAxis_) - { - if (fabs(dx) > fabs(dy)) - { - accessor_.GetLayer().SetPan(dx + panX_, panY_); - } - else - { - accessor_.GetLayer().SetPan(panX_, dy + panY_); - } - } - else - { - accessor_.GetLayer().SetPan(dx + panX_, dy + panY_); - } - } - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayerMoveTracker.h --- a/Framework/Radiography/RadiographyLayerMoveTracker.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - class RadiographyLayerMoveTracker : public Deprecated::IWorldSceneMouseTracker - { - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene::LayerAccessor accessor_; - double clickX_; - double clickY_; - double panX_; - double panY_; - bool oneAxis_; - - public: - RadiographyLayerMoveTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - size_t layer, - double x, - double y, - bool oneAxis); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayerResizeTracker.cpp --- a/Framework/Radiography/RadiographyLayerResizeTracker.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,188 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyLayerResizeTracker.h" - -#include "RadiographySceneCommand.h" - -#include - -#include - - -namespace OrthancStone -{ - static double ComputeDistance(double x1, - double y1, - double x2, - double y2) - { - double dx = x1 - x2; - double dy = y1 - y2; - return sqrt(dx * dx + dy * dy); - } - - - class RadiographyLayerResizeTracker::UndoRedoCommand : public RadiographySceneCommand - { - private: - double sourceSpacingX_; - double sourceSpacingY_; - double sourcePanX_; - double sourcePanY_; - double targetSpacingX_; - double targetSpacingY_; - double targetPanX_; - double targetPanY_; - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const - { - layer.SetPixelSpacing(sourceSpacingX_, sourceSpacingY_); - layer.SetPan(sourcePanX_, sourcePanY_); - } - - virtual void RedoInternal(RadiographyLayer& layer) const - { - layer.SetPixelSpacing(targetSpacingX_, targetSpacingY_); - layer.SetPan(targetPanX_, targetPanY_); - } - - public: - UndoRedoCommand(const RadiographyLayerResizeTracker& tracker) : - RadiographySceneCommand(tracker.accessor_), - sourceSpacingX_(tracker.originalSpacingX_), - sourceSpacingY_(tracker.originalSpacingY_), - sourcePanX_(tracker.originalPanX_), - sourcePanY_(tracker.originalPanY_), - targetSpacingX_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingX()), - targetSpacingY_(tracker.accessor_.GetLayer().GetGeometry().GetPixelSpacingY()), - targetPanX_(tracker.accessor_.GetLayer().GetGeometry().GetPanX()), - targetPanY_(tracker.accessor_.GetLayer().GetGeometry().GetPanY()) - { - } - }; - - - RadiographyLayerResizeTracker::RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - size_t layer, - const ControlPoint& startControlPoint, - bool roundScaling) : - undoRedoStack_(undoRedoStack), - accessor_(scene, layer), - roundScaling_(roundScaling) - { - if (accessor_.IsValid() && - accessor_.GetLayer().GetGeometry().IsResizeable()) - { - originalSpacingX_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingX(); - originalSpacingY_ = accessor_.GetLayer().GetGeometry().GetPixelSpacingY(); - originalPanX_ = accessor_.GetLayer().GetGeometry().GetPanX(); - originalPanY_ = accessor_.GetLayer().GetGeometry().GetPanY(); - - size_t oppositeControlPointType; - switch (startControlPoint.index) - { - case RadiographyControlPointType_TopLeftCorner: - oppositeControlPointType = RadiographyControlPointType_BottomRightCorner; - break; - - case RadiographyControlPointType_TopRightCorner: - oppositeControlPointType = RadiographyControlPointType_BottomLeftCorner; - break; - - case RadiographyControlPointType_BottomLeftCorner: - oppositeControlPointType = RadiographyControlPointType_TopRightCorner; - break; - - case RadiographyControlPointType_BottomRightCorner: - oppositeControlPointType = RadiographyControlPointType_TopLeftCorner; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - accessor_.GetLayer().GetControlPoint(startOppositeControlPoint_, oppositeControlPointType); - - double d = ComputeDistance(startControlPoint.x, startControlPoint.y, startOppositeControlPoint_.x, startOppositeControlPoint_.y); - if (d >= std::numeric_limits::epsilon()) - { - baseScaling_ = 1.0 / d; - } - else - { - // Avoid division by zero in extreme cases - accessor_.Invalidate(); - } - } - } - - - void RadiographyLayerResizeTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyLayerResizeTracker::MouseUp() - { - if (accessor_.IsValid() && - accessor_.GetLayer().GetGeometry().IsResizeable()) - { - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - } - - - void RadiographyLayerResizeTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - static const double ROUND_SCALING = 0.1; - - if (accessor_.IsValid() && - accessor_.GetLayer().GetGeometry().IsResizeable()) - { - double scaling = ComputeDistance(startOppositeControlPoint_.x, startOppositeControlPoint_.y, sceneX, sceneY) * baseScaling_; - - if (roundScaling_) - { - scaling = boost::math::round((scaling / ROUND_SCALING) * ROUND_SCALING); - } - - RadiographyLayer& layer = accessor_.GetLayer(); - layer.SetPixelSpacing(scaling * originalSpacingX_, - scaling * originalSpacingY_); - - // Keep the opposite corner at a fixed location - ControlPoint currentOppositeCorner; - layer.GetControlPoint(currentOppositeCorner, startOppositeControlPoint_.index); - layer.SetPan(layer.GetGeometry().GetPanX() + startOppositeControlPoint_.x - currentOppositeCorner.x, - layer.GetGeometry().GetPanY() + startOppositeControlPoint_.y - currentOppositeCorner.y); - } - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayerResizeTracker.h --- a/Framework/Radiography/RadiographyLayerResizeTracker.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,69 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - class RadiographyLayerResizeTracker : public Deprecated::IWorldSceneMouseTracker - { - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene::LayerAccessor accessor_; - bool roundScaling_; - double originalSpacingX_; - double originalSpacingY_; - double originalPanX_; - double originalPanY_; - ControlPoint startOppositeControlPoint_; - double baseScaling_; - - public: - RadiographyLayerResizeTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - size_t layer, - const ControlPoint& startControlPoint, - bool roundScaling); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayerRotateTracker.cpp --- a/Framework/Radiography/RadiographyLayerRotateTracker.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyLayerRotateTracker.h" - -#include "RadiographySceneCommand.h" - -#include - -#include -#include - -namespace OrthancStone -{ - class RadiographyLayerRotateTracker::UndoRedoCommand : public RadiographySceneCommand - { - private: - double sourceAngle_; - double targetAngle_; - - static int ToDegrees(double angle) - { - return boost::math::iround(angle * 180.0 / boost::math::constants::pi()); - } - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const - { - LOG(INFO) << "Undo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; - layer.SetAngle(sourceAngle_); - } - - virtual void RedoInternal(RadiographyLayer& layer) const - { - LOG(INFO) << "Redo - Set angle to " << ToDegrees(sourceAngle_) << " degrees"; - layer.SetAngle(targetAngle_); - } - - public: - UndoRedoCommand(const RadiographyLayerRotateTracker& tracker) : - RadiographySceneCommand(tracker.accessor_), - sourceAngle_(tracker.originalAngle_), - targetAngle_(tracker.accessor_.GetLayer().GetGeometry().GetAngle()) - { - } - }; - - - bool RadiographyLayerRotateTracker::ComputeAngle(double& angle /* out */, - double sceneX, - double sceneY) const - { - Vector u; - LinearAlgebra::AssignVector(u, sceneX - centerX_, sceneY - centerY_); - - double nu = boost::numeric::ublas::norm_2(u); - - if (!LinearAlgebra::IsCloseToZero(nu)) - { - u /= nu; - angle = atan2(u[1], u[0]); - return true; - } - else - { - return false; - } - } - - - RadiographyLayerRotateTracker::RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - double x, - double y, - bool roundAngles) : - undoRedoStack_(undoRedoStack), - accessor_(scene, layer), - roundAngles_(roundAngles) - { - if (accessor_.IsValid()) - { - accessor_.GetLayer().GetCenter(centerX_, centerY_); - originalAngle_ = accessor_.GetLayer().GetGeometry().GetAngle(); - - double sceneX, sceneY; - view.MapDisplayToScene(sceneX, sceneY, x, y); - - if (!ComputeAngle(clickAngle_, x, y)) - { - accessor_.Invalidate(); - } - } - } - - - void RadiographyLayerRotateTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyLayerRotateTracker::MouseUp() - { - if (accessor_.IsValid()) - { - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - } - - - void RadiographyLayerRotateTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - static const double ROUND_ANGLE = 15.0 / 180.0 * boost::math::constants::pi(); - - double angle; - - if (accessor_.IsValid() && - ComputeAngle(angle, sceneX, sceneY)) - { - angle = angle - clickAngle_ + originalAngle_; - - if (roundAngles_) - { - angle = boost::math::round((angle / ROUND_ANGLE) * ROUND_ANGLE); - } - - accessor_.GetLayer().SetAngle(angle); - } - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyLayerRotateTracker.h --- a/Framework/Radiography/RadiographyLayerRotateTracker.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Toolbox/ViewportGeometry.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - - -namespace OrthancStone -{ - class RadiographyLayerRotateTracker : public Deprecated::IWorldSceneMouseTracker - { - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene::LayerAccessor accessor_; - double centerX_; - double centerY_; - double originalAngle_; - double clickAngle_; - bool roundAngles_; - - bool ComputeAngle(double& angle /* out */, - double sceneX, - double sceneY) const; - - public: - RadiographyLayerRotateTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - const Deprecated::ViewportGeometry& view, - size_t layer, - double x, - double y, - bool roundAngles); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyMaskLayer.cpp --- a/Framework/Radiography/RadiographyMaskLayer.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,199 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyMaskLayer.h" -#include "RadiographyDicomLayer.h" - -#include "RadiographyScene.h" -#include "Core/Images/Image.h" -#include "Core/Images/ImageProcessing.h" -#include -#include "../Toolbox/ImageGeometry.h" - -namespace OrthancStone -{ - const unsigned char IN_MASK_VALUE = 0x77; - const unsigned char OUT_MASK_VALUE = 0xFF; - - const AffineTransform2D& RadiographyMaskLayer::GetTransform() const - { - return dicomLayer_.GetTransform(); - } - - const AffineTransform2D& RadiographyMaskLayer::GetTransformInverse() const - { - return dicomLayer_.GetTransformInverse(); - } - - bool RadiographyMaskLayer::GetPixel(unsigned int& imageX, - unsigned int& imageY, - double sceneX, - double sceneY) const - { - return dicomLayer_.GetPixel(imageX, imageY, sceneX, sceneY); - } - - std::string RadiographyMaskLayer::GetInstanceId() const - { - return dicomLayer_.GetInstanceId(); - } - - void RadiographyMaskLayer::SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index) - { - if (index < corners_.size()) - corners_[index] = corner; - else - corners_.push_back(corner); - invalidated_ = true; - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - void RadiographyMaskLayer::SetCorners(const std::vector& corners) - { - corners_ = corners; - invalidated_ = true; - - BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this)); - } - - Extent2D RadiographyMaskLayer::GetSceneExtent(bool minimal) const - { - if (!minimal) - { - return RadiographyLayer::GetSceneExtent(minimal); - } - else - { // get the extent of the in-mask area - Extent2D sceneExtent; - - for (std::vector::const_iterator corner = corners_.begin(); corner != corners_.end(); ++corner) - { - double x = static_cast(corner->GetX()); - double y = static_cast(corner->GetY()); - - dicomLayer_.GetTransform().Apply(x, y); - sceneExtent.AddPoint(x, y); - } - return sceneExtent; - } - } - - - - void RadiographyMaskLayer::Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const - { - if (dicomLayer_.GetWidth() == 0 || dicomLayer_.GetSourceImage() == NULL) // nothing to do if the DICOM layer is not displayed (or not loaded) - return; - - if (invalidated_) - { - mask_.reset(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, dicomLayer_.GetWidth(), dicomLayer_.GetHeight(), false)); - - DrawMask(); - - invalidated_ = false; - } - - {// rendering - if (buffer.GetFormat() != Orthanc::PixelFormat_Float32) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); - } - - unsigned int cropX, cropY, cropWidth, cropHeight; - dicomLayer_.GetCrop(cropX, cropY, cropWidth, cropHeight); - - const AffineTransform2D t = AffineTransform2D::Combine( - viewTransform, dicomLayer_.GetTransform(), - AffineTransform2D::CreateOffset(cropX, cropY)); - - Orthanc::ImageAccessor cropped; - mask_->GetRegion(cropped, cropX, cropY, cropWidth, cropHeight); - - Orthanc::Image tmp(Orthanc::PixelFormat_Grayscale8, buffer.GetWidth(), buffer.GetHeight(), false); - - - unsigned int x1, y1, x2, y2; - if (!OrthancStone::GetProjectiveTransformExtent(x1, y1, x2, y2, - t.GetHomogeneousMatrix(), - cropped.GetWidth(), - cropped.GetHeight(), - buffer.GetWidth(), - buffer.GetHeight())) - { - return; // layer is outside the buffer - } - - t.Apply(tmp, cropped, ImageInterpolation_Nearest, true /* clear */); - - // we have observed vertical lines at the image border (probably due to bilinear filtering of the DICOM image when it is not aligned with the buffer pixels) - // -> draw the mask one line further on each side - if (x1 >= 1) - { - x1 = x1 - 1; - } - if (x2 < buffer.GetWidth() - 2) - { - x2 = x2 + 1; - } - - // Blit - for (unsigned int y = y1; y <= y2; y++) - { - float *q = reinterpret_cast(buffer.GetRow(y)) + x1; - const uint8_t *p = reinterpret_cast(tmp.GetRow(y)) + x1; - - for (unsigned int x = x1; x <= x2; x++, p++, q++) - { - if (*p != IN_MASK_VALUE) - *q = foreground_; - // else keep the underlying pixel value - } - } - - } - } - - void RadiographyMaskLayer::DrawMask() const - { - // first fill the complete image - Orthanc::ImageProcessing::Set(*mask_, OUT_MASK_VALUE); - - // clip corners - std::vector clippedCorners; - for (size_t i = 0; i < corners_.size(); i++) - { - clippedCorners.push_back(corners_[i]); - clippedCorners[i].ClipTo(0, mask_->GetWidth() - 1, 0, mask_->GetHeight() - 1); - } - - // fill mask - Orthanc::ImageProcessing::FillPolygon(*mask_, clippedCorners, IN_MASK_VALUE); - - } - -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyMaskLayer.h --- a/Framework/Radiography/RadiographyMaskLayer.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,146 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 -#include -#include - -namespace OrthancStone -{ - class RadiographyScene; - class RadiographyDicomLayer; - - class RadiographyMaskLayer : public RadiographyLayer - { - private: - std::vector corners_; - const RadiographyDicomLayer& dicomLayer_; - mutable bool invalidated_; - float foreground_; - - mutable std::unique_ptr mask_; - public: - RadiographyMaskLayer(const RadiographyScene& scene, const RadiographyDicomLayer& dicomLayer, - float foreground) : - RadiographyLayer(scene), - dicomLayer_(dicomLayer), - invalidated_(true), - foreground_(foreground) - { - } - - virtual size_t GetApproximateMemoryUsage() const - { - size_t size = 0; - if (mask_.get() != NULL) - { - size += mask_->GetPitch() * mask_->GetHeight(); - } - - return size; - } - - - void SetCorners(const std::vector& corners); - void SetCorner(const Orthanc::ImageProcessing::ImagePoint& corner, size_t index); - - const std::vector& GetCorners() const - { - return corners_; - } - - float GetForeground() const - { - return foreground_; - } - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - float windowCenter, - float windowWidth, - bool applyWindowing) const; - - std::string GetInstanceId() const; - - virtual size_t GetControlPointCount() const - { - return corners_.size(); - } - - virtual void GetControlPoint(ControlPoint& cpScene, - size_t index) const - { - ControlPoint cp(corners_[index].GetX(), corners_[index].GetY(), index); - - // transforms image coordinates into scene coordinates - GetTransform().Apply(cp.x, cp.y); - cpScene = cp; - } - - virtual Extent2D GetSceneExtent(bool minimal) const; - - virtual bool GetDefaultWindowing(float& center, - float& width) const - { - return false; - } - - virtual bool GetRange(float& minValue, - float& maxValue) const - { - minValue = 0; - maxValue = 0; - - if (foreground_ < 0) - { - minValue = foreground_; - } - - if (foreground_ > 0) - { - maxValue = foreground_; - } - - return true; - - } - - virtual bool GetPixel(unsigned int& imageX, - unsigned int& imageY, - double sceneX, - double sceneY) const; - - protected: - virtual const AffineTransform2D& GetTransform() const; - - virtual const AffineTransform2D& GetTransformInverse() const; - - - private: - void DrawMask() const; - - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyScene.cpp --- a/Framework/Radiography/RadiographyScene.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,968 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyScene.h" - -#include "RadiographyAlphaLayer.h" -#include "RadiographyDicomLayer.h" -#include "RadiographyTextLayer.h" -#include "RadiographyMaskLayer.h" -#include "../Deprecated/Toolbox/DicomFrameConverter.h" -#include "../Scene2D/CairoCompositor.h" -#include "../Scene2D/FloatTextureSceneLayer.h" -#include "../Scene2D/TextSceneLayer.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - - -namespace OrthancStone -{ - RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, - size_t index) : - scene_(scene), - index_(index) - { - Layers::iterator layer = scene.layers_.find(index); - if (layer == scene.layers_.end()) - { - layer_ = NULL; - } - else - { - assert(layer->second != NULL); - layer_ = layer->second; - } - } - - - RadiographyScene::LayerAccessor::LayerAccessor(RadiographyScene& scene, - double x, - double y) : - scene_(scene), - index_(0) // Dummy initialization - { - if (scene.LookupLayer(index_, x, y)) - { - Layers::iterator layer = scene.layers_.find(index_); - - if (layer == scene.layers_.end()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - else - { - assert(layer->second != NULL); - layer_ = layer->second; - } - } - else - { - layer_ = NULL; - } - } - - - RadiographyScene& RadiographyScene::LayerAccessor::GetScene() const - { - if (IsValid()) - { - return scene_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - - size_t RadiographyScene::LayerAccessor::GetIndex() const - { - if (IsValid()) - { - return index_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - - RadiographyLayer& RadiographyScene::LayerAccessor::GetLayer() const - { - if (IsValid()) - { - return *layer_; - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); - } - } - - void RadiographyScene::_RegisterLayer(RadiographyLayer* layer) - { - std::unique_ptr raii(layer); - - // LOG(INFO) << "Registering layer: " << countLayers_; - - size_t index = nextLayerIndex_++; - raii->SetIndex(index); - layers_[index] = raii.release(); - } - - RadiographyLayer& RadiographyScene::RegisterLayer(RadiographyLayer* layer) - { - if (layer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NullPointer); - } - - _RegisterLayer(layer); - - BroadcastMessage(GeometryChangedMessage(*this, *layer)); - BroadcastMessage(ContentChangedMessage(*this, *layer)); - Register(*layer, &RadiographyScene::OnLayerEdited); - - return *layer; - } - - size_t RadiographyScene::GetApproximateMemoryUsage() const - { - size_t size = 0; - for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) - { - size += it->second->GetApproximateMemoryUsage(); - } - return size; - } - - void RadiographyScene::OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message) - { - BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, message.GetOrigin())); - } - - - RadiographyScene::RadiographyScene() : - nextLayerIndex_(0), - hasWindowing_(false), - windowingCenter_(0), // Dummy initialization - windowingWidth_(0) // Dummy initialization - { - } - - - RadiographyScene::~RadiographyScene() - { - for (Layers::iterator it = layers_.begin(); it != layers_.end(); it++) - { - assert(it->second != NULL); - delete it->second; - } - } - - RadiographyPhotometricDisplayMode RadiographyScene::GetPreferredPhotomotricDisplayMode() const - { - // return the mode of the first layer who "cares" about its display mode (normaly, the one and only layer that is a DicomLayer) - for (Layers::const_iterator it = layers_.begin(); it != layers_.end(); it++) - { - if (it->second->GetPreferredPhotomotricDisplayMode() != RadiographyPhotometricDisplayMode_Default) - { - return it->second->GetPreferredPhotomotricDisplayMode(); - } - } - - return RadiographyPhotometricDisplayMode_Default; - } - - - 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; - - Layers::iterator found = layers_.find(layerIndex); - - if (found == layers_.end()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - assert(found->second != NULL); - delete found->second; - - layers_.erase(found); - - LOG(INFO) << "Removing layer, there are now : " << layers_.size() << " layers"; - - _OnLayerRemoved(); - - BroadcastMessage(RadiographyScene::LayerRemovedMessage(*this, layerIndex)); - } - } - - const RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) const - { - Layers::const_iterator found = layers_.find(layerIndex); - - if (found == layers_.end()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - assert(found->second != NULL); - return *found->second; - } - } - - RadiographyLayer& RadiographyScene::GetLayer(size_t layerIndex) - { - Layers::const_iterator found = layers_.find(layerIndex); - - if (found == layers_.end()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - else - { - assert(found->second != NULL); - return *found->second; - } - } - - bool RadiographyScene::GetWindowing(float& center, - float& width) const - { - if (hasWindowing_) - { - center = windowingCenter_; - width = windowingWidth_; - return true; - } - else - { - return false; - } - } - - - void RadiographyScene::GetWindowingWithDefault(float& center, - float& width) const - { - if (!GetWindowing(center, width)) - { - center = 128; - width = 256; - } - } - - - void RadiographyScene::SetWindowing(float center, - float width) - { - hasWindowing_ = true; - windowingCenter_ = center; - windowingWidth_ = width; - - BroadcastMessage(RadiographyScene::WindowingChangedMessage(*this)); - } - - - RadiographyLayer& RadiographyScene::UpdateText(size_t layerIndex, - const std::string& utf8, - const std::string& font, - unsigned int fontSize, - uint8_t foreground) - { - RadiographyTextLayer& textLayer = dynamic_cast(GetLayer(layerIndex)); - textLayer.SetText(utf8, font, fontSize, foreground); - - BroadcastMessage(RadiographyScene::ContentChangedMessage(*this, textLayer)); - BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, textLayer)); - return textLayer; - } - - - RadiographyLayer& RadiographyScene::LoadText(const std::string& utf8, - const std::string& font, - unsigned int fontSize, - uint8_t foreground, - RadiographyLayer::Geometry* centerGeometry, - bool isCenterGeometry) - { - std::unique_ptr alpha(new RadiographyTextLayer(*this)); - alpha->SetText(utf8, font, fontSize, foreground); - if (centerGeometry != NULL) - { - if (isCenterGeometry) - { - // modify geometry to reference the top left corner - double tlx = centerGeometry->GetPanX(); - double tly = centerGeometry->GetPanY(); - Extent2D textExtent = alpha->GetSceneExtent(false); - tlx = tlx - (textExtent.GetWidth() / 2) * centerGeometry->GetPixelSpacingX(); - tly = tly - (textExtent.GetHeight() / 2) * centerGeometry->GetPixelSpacingY(); - centerGeometry->SetPan(tlx, tly); - } - alpha->SetGeometry(*centerGeometry); - } - - RadiographyLayer& registeredLayer = RegisterLayer(alpha.release()); - - BroadcastMessage(RadiographyScene::LayerEditedMessage(*this, registeredLayer)); - return registeredLayer; - } - - - RadiographyLayer& RadiographyScene::LoadTestBlock(unsigned int width, - unsigned int height, - RadiographyLayer::Geometry* geometry) - { - std::unique_ptr block(new Orthanc::Image(Orthanc::PixelFormat_Grayscale8, width, height, false)); - - for (unsigned int padding = 0; - (width > 2 * padding) && (height > 2 * padding); - padding++) - { - uint8_t color; - if (255 > 10 * padding) - { - color = 255 - 10 * padding; - } - else - { - color = 0; - } - - Orthanc::ImageAccessor region; - block->GetRegion(region, padding, padding, width - 2 * padding, height - 2 * padding); - Orthanc::ImageProcessing::Set(region, color); - } - - return LoadAlphaBitmap(block.release(), geometry); - } - - RadiographyLayer& RadiographyScene::LoadMask(const std::vector& corners, - const RadiographyDicomLayer& dicomLayer, - float foreground, - RadiographyLayer::Geometry* geometry) - { - std::unique_ptr mask(new RadiographyMaskLayer(*this, dicomLayer, foreground)); - mask->SetCorners(corners); - if (geometry != NULL) - { - mask->SetGeometry(*geometry); - } - - return RegisterLayer(mask.release()); - } - - - RadiographyLayer& RadiographyScene::LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, RadiographyLayer::Geometry *geometry) - { - std::unique_ptr alpha(new RadiographyAlphaLayer(*this)); - alpha->SetAlpha(bitmap); - if (geometry != NULL) - { - alpha->SetGeometry(*geometry); - } - - return RegisterLayer(alpha.release()); - } - - RadiographyLayer& RadiographyScene::LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership - const std::string& instance, - unsigned int frame, - Deprecated::DicomFrameConverter* converter, // takes ownership - RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode, - RadiographyLayer::Geometry* geometry) - { - RadiographyDicomLayer& layer = dynamic_cast(RegisterLayer(new RadiographyDicomLayer(*this))); - - layer.SetInstance(instance, frame); - - if (geometry != NULL) - { - layer.SetGeometry(*geometry); - } - - layer.SetDicomFrameConverter(converter); - layer.SetSourceImage(dicomImage); - layer.SetPreferredPhotomotricDisplayMode(preferredPhotometricDisplayMode); - - return layer; - } - - RadiographyLayer& RadiographyScene::LoadDicomFrame(Deprecated::OrthancApiClient& orthanc, - const std::string& instance, - unsigned int frame, - bool httpCompression, - RadiographyLayer::Geometry* geometry) - { - RadiographyDicomLayer& layer = dynamic_cast(RegisterLayer(new RadiographyDicomLayer( *this))); - layer.SetInstance(instance, frame); - - if (geometry != NULL) - { - layer.SetGeometry(*geometry); - } - - { - Deprecated::IWebService::HttpHeaders headers; - std::string uri = "/instances/" + instance + "/tags"; - - orthanc.GetBinaryAsync( - uri, headers, - new Deprecated::DeprecatedCallable - (GetSharedObserver(), &RadiographyScene::OnTagsReceived), NULL, - new Orthanc::SingleValueObject(layer.GetIndex())); - } - - { - Deprecated::IWebService::HttpHeaders headers; - headers["Accept"] = "image/x-portable-arbitrarymap"; - - if (httpCompression) - { - headers["Accept-Encoding"] = "gzip"; - } - - std::string uri = ("/instances/" + instance + "/frames/" + - boost::lexical_cast(frame) + "/image-uint16"); - - orthanc.GetBinaryAsync( - uri, headers, - new Deprecated::DeprecatedCallable - (GetSharedObserver(), &RadiographyScene::OnFrameReceived), NULL, - new Orthanc::SingleValueObject(layer.GetIndex())); - } - - return layer; - } - - - RadiographyLayer& RadiographyScene::LoadDicomWebFrame(Deprecated::IWebService& web) - { - RadiographyLayer& layer = RegisterLayer(new RadiographyDicomLayer(*this)); - - - return layer; - } - - - - void RadiographyScene::OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message) - { - size_t index = dynamic_cast&> - (message.GetPayload()).GetValue(); - - VLOG(1) << "JSON received: " << message.GetUri().c_str() - << " (" << message.GetAnswerSize() << " bytes) for layer " << index; - - Layers::iterator layer = layers_.find(index); - if (layer != layers_.end()) - { - assert(layer->second != NULL); - - OrthancPlugins::FullOrthancDataset dicom(message.GetAnswer(), message.GetAnswerSize()); - dynamic_cast(layer->second)->SetDicomTags(dicom); - - float c, w; - if (!hasWindowing_ && - layer->second->GetDefaultWindowing(c, w)) - { - hasWindowing_ = true; - windowingCenter_ = c; - windowingWidth_ = w; - } - - BroadcastMessage(GeometryChangedMessage(*this, *(layer->second))); - } - } - - - void RadiographyScene::OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message) - { - size_t index = dynamic_cast&>(message.GetPayload()).GetValue(); - - VLOG(1) << "DICOM frame received: " << message.GetUri().c_str() - << " (" << message.GetAnswerSize() << " bytes) for layer " << index; - - Layers::iterator layer = layers_.find(index); - if (layer != layers_.end()) - { - assert(layer->second != NULL); - - std::string content; - if (message.GetAnswerSize() > 0) - { - content.assign(reinterpret_cast(message.GetAnswer()), message.GetAnswerSize()); - } - - std::unique_ptr reader(new Orthanc::PamReader); - reader->ReadFromMemory(content); - dynamic_cast(layer->second)->SetSourceImage(reader.release()); - - BroadcastMessage(ContentChangedMessage(*this, *(layer->second))); - } - } - - - Extent2D RadiographyScene::GetSceneExtent(bool minimal) const - { - Extent2D extent; - - for (Layers::const_iterator it = layers_.begin(); - it != layers_.end(); ++it) - { - assert(it->second != NULL); - extent.Union(it->second->GetSceneExtent(minimal)); - } - - return extent; - } - - - void RadiographyScene::Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - bool applyWindowing) const - { - // Render layers in the background-to-foreground order - for (size_t index = 0; index < nextLayerIndex_; index++) - { - try - { - Layers::const_iterator it = layers_.find(index); - if (it != layers_.end()) - { - assert(it->second != NULL); - it->second->Render(buffer, viewTransform, interpolation, windowingCenter_, windowingWidth_, applyWindowing); - } - } - catch (Orthanc::OrthancException& ex) - { - LOG(ERROR) << "RadiographyScene::Render: " << index << ", OrthancException: " << ex.GetDetails(); - throw ex; // rethrow because we want it to crash to see there's a problem ! - } - catch (...) - { - LOG(ERROR) << "RadiographyScene::Render: " << index << ", unkown exception: "; - throw; // rethrow because we want it to crash to see there's a problem ! - } - } - } - - - bool RadiographyScene::LookupLayer(size_t& index /* out */, - double x, - double y) const - { - // Render layers in the foreground-to-background order - for (size_t i = nextLayerIndex_; i > 0; i--) - { - index = i - 1; - Layers::const_iterator it = layers_.find(index); - if (it != layers_.end()) - { - assert(it->second != NULL); - if (it->second->Contains(x, y)) - { - return true; - } - } - } - - return false; - } - - - void RadiographyScene::DrawBorder(CairoContext& context, - unsigned int layer, - double zoom) - { - Layers::const_iterator found = layers_.find(layer); - - if (found != layers_.end()) - { - context.SetSourceColor(255, 0, 0); - found->second->DrawBorders(context, zoom); - } - } - - - void RadiographyScene::GetRange(float& minValue, - float& maxValue) const - { - bool first = true; - - for (Layers::const_iterator it = layers_.begin(); - it != layers_.end(); it++) - { - assert(it->second != NULL); - - float a, b; - if (it->second->GetRange(a, b)) - { - if (first) - { - minValue = a; - maxValue = b; - first = false; - } - else - { - minValue = std::min(a, minValue); - maxValue = std::max(b, maxValue); - } - } - } - - if (first) - { - minValue = 0; - maxValue = 0; - } - } - - void RadiographyScene::ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer, - const Orthanc::ImageAccessor& renderedScene, - size_t layerIndex, - bool isCropped, - ImageInterpolation interpolation) - { - Extent2D sceneExtent = GetSceneExtent(isCropped); - - double pixelSpacingX = sceneExtent.GetWidth() / renderedScene.GetWidth(); - double pixelSpacingY = sceneExtent.GetHeight() / renderedScene.GetHeight(); - - AffineTransform2D view = AffineTransform2D::Combine( - AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), - AffineTransform2D::CreateOffset(-sceneExtent.GetX1(), -sceneExtent.GetY1())); - - AffineTransform2D layerToSceneTransform = AffineTransform2D::Combine( - view, - GetLayer(layerIndex).GetTransform()); - - AffineTransform2D sceneToLayerTransform = AffineTransform2D::Invert(layerToSceneTransform); - sceneToLayerTransform.Apply(layer, renderedScene, interpolation, false); - } - - Orthanc::Image* RadiographyScene::ExportToImage(double pixelSpacingX, - double pixelSpacingY, - ImageInterpolation interpolation, - bool invert, - int64_t maxValue /* for inversion */, - bool autoCrop, - bool applyWindowing) - { - if (pixelSpacingX <= 0 || - pixelSpacingY <= 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); - } - - Extent2D extent = GetSceneExtent(autoCrop); - - int w = boost::math::iround(extent.GetWidth() / pixelSpacingX); - int h = boost::math::iround(extent.GetHeight() / pixelSpacingY); - - if (w < 0 || h < 0) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - Orthanc::Image layers(Orthanc::PixelFormat_Float32, - static_cast(w), - static_cast(h), false); - - AffineTransform2D view = AffineTransform2D::Combine( - AffineTransform2D::CreateScaling(1.0 / pixelSpacingX, 1.0 / pixelSpacingY), - AffineTransform2D::CreateOffset(-extent.GetX1(), -extent.GetY1())); - - // wipe background before rendering - if (GetPreferredPhotomotricDisplayMode() == RadiographyPhotometricDisplayMode_Monochrome1) - { - Orthanc::ImageProcessing::Set(layers, 65535); - } - else - { - Orthanc::ImageProcessing::Set(layers, 0); - } - - Render(layers, view, interpolation, applyWindowing); - - std::unique_ptr rendered(new Orthanc::Image(Orthanc::PixelFormat_Grayscale16, - layers.GetWidth(), layers.GetHeight(), false)); - - Orthanc::ImageProcessing::Convert(*rendered, layers); - if (invert) - Orthanc::ImageProcessing::Invert(*rendered, maxValue); - - return rendered.release(); - } - - - Orthanc::Image* RadiographyScene::ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation) - { - LOG(INFO) << "Exporting RadiographyScene to DICOM"; - - std::unique_ptr rendered(ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, autoCrop, false)); // note: we don't invert the image in the pixels data because we'll set the PhotometricDisplayMode correctly in the DICOM tags - - createDicomRequestContent["Tags"] = dicomTags; - - RadiographyPhotometricDisplayMode photometricMode = GetPreferredPhotomotricDisplayMode(); - if ((invert && photometricMode != RadiographyPhotometricDisplayMode_Monochrome2) || - (!invert && photometricMode == RadiographyPhotometricDisplayMode_Monochrome1)) - { - createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME1"; - } - else - { - createDicomRequestContent["Tags"]["PhotometricInterpretation"] = "MONOCHROME2"; - } - - // WARNING: The order of PixelSpacing is Y/X. We use "%0.8f" to - // avoid floating-point numbers to grow over 16 characters, - // which would be invalid according to DICOM standard - // ("dciodvfy" would complain). - char buf[32]; - sprintf(buf, "%0.8f\\%0.8f", pixelSpacingY, pixelSpacingX); - - createDicomRequestContent["Tags"]["PixelSpacing"] = buf; - - float center, width; - if (GetWindowing(center, width)) - { - createDicomRequestContent["Tags"]["WindowCenter"] = - boost::lexical_cast(boost::math::iround(center)); - - createDicomRequestContent["Tags"]["WindowWidth"] = - boost::lexical_cast(boost::math::iround(width)); - } - - if (!parentOrthancId.empty()) - { - createDicomRequestContent["Parent"] = parentOrthancId; - } - - return rendered.release(); - } - - - void RadiographyScene::ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam) - { - LOG(INFO) << "Exporting RadiographyScene to DICOM"; - VLOG(1) << "Exporting RadiographyScene to: export to image"; - - std::unique_ptr rendered(ExportToCreateDicomRequestAndImage(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation)); - - // convert the image into base64 for inclusing in the createDicomRequest - std::string base64; - - { - std::string content; - - if (usePam) - { - VLOG(1) << "Exporting RadiographyScene: convert to PAM"; - Orthanc::PamWriter writer; - writer.WriteToMemory(content, *rendered); - } - else - { - Orthanc::PngWriter writer; - writer.WriteToMemory(content, *rendered); - } - - VLOG(1) << "Exporting RadiographyScene: encoding to base64"; - Orthanc::Toolbox::EncodeBase64(base64, content); - } - - // This is Data URI scheme: https://en.wikipedia.org/wiki/Data_URI_scheme - createDicomRequestContent["Content"] = ("data:" + - std::string(usePam ? Orthanc::MIME_PAM : Orthanc::MIME_PNG) + - ";base64," + base64); - - VLOG(1) << "Exporting RadiographyScene: create-dicom request is ready"; - } - - - void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam) - { - Json::Value createDicomRequestContent; - - ExportToCreateDicomRequest(createDicomRequestContent, dicomTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam); - - orthanc.PostJsonAsyncExpectJson( - "/tools/create-dicom", createDicomRequestContent, - new Deprecated::DeprecatedCallable - (GetSharedObserver(), &RadiographyScene::OnDicomExported), - NULL, NULL); - - } - - - // Export using PAM is faster than using PNG, but requires Orthanc - // core >= 1.4.3 - void RadiographyScene::ExportDicom(Deprecated::OrthancApiClient& orthanc, - const Orthanc::DicomMap& dicom, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam) - { - std::set tags; - dicom.GetTags(tags); - - Json::Value jsonTags = Json::objectValue; - - for (std::set::const_iterator - tag = tags.begin(); tag != tags.end(); ++tag) - { - const Orthanc::DicomValue& value = dicom.GetValue(*tag); - if (!value.IsNull() && - !value.IsBinary()) - { - jsonTags[tag->Format()] = value.GetContent(); - } - } - - ExportDicom(orthanc, jsonTags, parentOrthancId, pixelSpacingX, pixelSpacingY, invert, autoCrop, interpolation, usePam); - } - - void RadiographyScene::OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message) - { - LOG(INFO) << "DICOM export was successful: " - << message.GetJson().toStyledString(); - } - - - void RadiographyScene::OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message) - { - LOG(INFO) << "DICOMweb WADO-RS received: " << message.GetAnswerSize() << " bytes"; - - const Deprecated::IWebService::HttpHeaders& h = message.GetAnswerHttpHeaders(); - for (Deprecated::IWebService::HttpHeaders::const_iterator - it = h.begin(); it != h.end(); ++it) - { - printf("[%s] = [%s]\n", it->first.c_str(), it->second.c_str()); - } - } - - void RadiographyScene::ExportToScene2D(Scene2D& output) const - { - int depth = 0; - for (Layers::const_iterator it = layers_.begin(); - it != layers_.end(); ++it) - { - assert(it->second != NULL); - - std::unique_ptr layer; - if (dynamic_cast(it->second)) - { - RadiographyDicomLayer* oldLayer = dynamic_cast(it->second); - - std::unique_ptr newLayer(new FloatTextureSceneLayer(*(oldLayer->GetSourceImage()))); - - newLayer->SetOrigin(oldLayer->GetGeometry().GetPanX(), - oldLayer->GetGeometry().GetPanY() - ); - newLayer->SetAngle(oldLayer->GetGeometry().GetAngle()); - - layer.reset(newLayer.release()); - - // TODO: windowing dynamic_cast - } - else if (dynamic_cast(it->second)) - { - RadiographyTextLayer* oldLayer = dynamic_cast(it->second); - - std::unique_ptr newLayer(new TextSceneLayer()); - - newLayer->SetText(oldLayer->GetText()); - newLayer->SetColor(oldLayer->GetForegroundGreyLevel(), - oldLayer->GetForegroundGreyLevel(), - oldLayer->GetForegroundGreyLevel() - ); - newLayer->SetPosition(oldLayer->GetGeometry().GetPanX(), - oldLayer->GetGeometry().GetPanY() - ); - newLayer->SetFontIndex(1); - newLayer->SetAnchor(BitmapAnchor_TopLeft); - //newLayer->SetAngle(oldLayer->GetGeometry().GetAngle()); - - layer.reset(newLayer.release()); - } - - output.SetLayer(depth++, layer.release()); - - } - - } - -} - diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyScene.h --- a/Framework/Radiography/RadiographyScene.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,370 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Messages/ObserverBase.h" -#include "../Deprecated/Toolbox/DicomFrameConverter.h" -#include "../Deprecated/Toolbox/OrthancApiClient.h" -#include "../StoneEnumerations.h" -#include "Core/Images/Image.h" -#include "Core/Images/ImageProcessing.h" - -#include "../Scene2D/Scene2D.h" - -namespace OrthancStone -{ - class RadiographyDicomLayer; - - class RadiographyScene : - public ObserverBase, - public IObservable - { - friend class RadiographySceneGeometryReader; - public: - class GeometryChangedMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - RadiographyLayer& layer_; - - public: - GeometryChangedMessage(const RadiographyScene& origin, - RadiographyLayer& layer) : - OriginMessage(origin), - layer_(layer) - { - } - - RadiographyLayer& GetLayer() const - { - return layer_; - } - }; - - class ContentChangedMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - RadiographyLayer& layer_; - - public: - ContentChangedMessage(const RadiographyScene& origin, - RadiographyLayer& layer) : - OriginMessage(origin), - layer_(layer) - { - } - - RadiographyLayer& GetLayer() const - { - return layer_; - } - }; - - class LayerEditedMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - const RadiographyLayer& layer_; - - public: - LayerEditedMessage(const RadiographyScene& origin, - const RadiographyLayer& layer) : - OriginMessage(origin), - layer_(layer) - { - } - - const RadiographyLayer& GetLayer() const - { - return layer_; - } - }; - - class LayerRemovedMessage : public OriginMessage - { - ORTHANC_STONE_MESSAGE(__FILE__, __LINE__); - - private: - size_t& layerIndex_; - - public: - LayerRemovedMessage(const RadiographyScene& origin, - size_t& layerIndex) : - OriginMessage(origin), - layerIndex_(layerIndex) - { - } - - size_t& GetLayerIndex() const - { - return layerIndex_; - } - }; - - - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, WindowingChangedMessage, RadiographyScene); - - - class LayerAccessor : public boost::noncopyable - { - private: - RadiographyScene& scene_; - size_t index_; - RadiographyLayer* layer_; - - public: - LayerAccessor(RadiographyScene& scene, - size_t index); - - LayerAccessor(RadiographyScene& scene, - double x, - double y); - - void Invalidate() - { - layer_ = NULL; - } - - bool IsValid() const - { - return layer_ != NULL; - } - - RadiographyScene& GetScene() const; - - size_t GetIndex() const; - - RadiographyLayer& GetLayer() const; - }; - - - protected: - typedef std::map Layers; - - size_t nextLayerIndex_; - bool hasWindowing_; - float windowingCenter_; - float windowingWidth_; - Layers layers_; - - public: - RadiographyLayer& RegisterLayer(RadiographyLayer* layer); - - protected: - virtual void _RegisterLayer(RadiographyLayer* layer); - virtual void _OnLayerRemoved() {} - - void SetLayerIndex(RadiographyLayer* layer, size_t index) - { - layer->SetIndex(index); - } - - virtual void OnTagsReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); - - virtual void OnFrameReceived(const Deprecated::OrthancApiClient::BinaryResponseReadyMessage& message); - - void OnDicomExported(const Deprecated::OrthancApiClient::JsonResponseReadyMessage& message); - - void OnDicomWebReceived(const Deprecated::IWebService::HttpRequestSuccessMessage& message); - - virtual void OnLayerEdited(const RadiographyLayer::LayerEditedMessage& message); - - public: - RadiographyScene(); - - virtual ~RadiographyScene(); - - virtual size_t GetApproximateMemoryUsage() const; - - bool GetWindowing(float& center, - float& width) const; - - void GetWindowingWithDefault(float& center, - float& width) const; - - virtual void SetWindowing(float center, - float width); - - RadiographyPhotometricDisplayMode GetPreferredPhotomotricDisplayMode() const; - - RadiographyLayer& LoadText(const std::string& utf8, - const std::string& font, - unsigned int fontSize, - uint8_t foreground, - RadiographyLayer::Geometry* geometry, - bool isCenterGeometry); - - RadiographyLayer& UpdateText(size_t layerIndex, - const std::string& font, - const std::string& utf8, - unsigned int fontSize, - uint8_t foreground); - - RadiographyLayer& LoadTestBlock(unsigned int width, - unsigned int height, - RadiographyLayer::Geometry* geometry); - - RadiographyLayer& LoadMask(const std::vector& corners, - const RadiographyDicomLayer& dicomLayer, - float foreground, - RadiographyLayer::Geometry* geometry); - - RadiographyLayer& LoadAlphaBitmap(Orthanc::ImageAccessor* bitmap, // takes ownership - RadiographyLayer::Geometry* geometry); - - virtual RadiographyLayer& LoadDicomImage(Orthanc::ImageAccessor* dicomImage, // takes ownership - const std::string& instance, - unsigned int frame, - Deprecated::DicomFrameConverter* converter, // takes ownership - RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode, - RadiographyLayer::Geometry* geometry); - - virtual RadiographyLayer& LoadDicomFrame(Deprecated::OrthancApiClient& orthanc, - const std::string& instance, - unsigned int frame, - bool httpCompression, - RadiographyLayer::Geometry* geometry); // pass NULL if you want default geometry - - RadiographyLayer& LoadDicomWebFrame(Deprecated::IWebService& web); - - void RemoveLayer(size_t layerIndex); - - RadiographyLayer& GetLayer(size_t layerIndex); - - const RadiographyLayer& GetLayer(size_t layerIndex) const; - - template - TypeLayer* GetTypedLayer(size_t indexOfType = 0) - { - std::vector layerIndexes; - GetLayersIndexes(layerIndexes); - - size_t count = 0; - - for (size_t i = 0; i < layerIndexes.size(); ++i) - { - TypeLayer* typedLayer = dynamic_cast(layers_[layerIndexes[i]]); - if (typedLayer != NULL) - { - if (count == indexOfType) - { - return typedLayer; - } - count++; - } - } - - return NULL; - } - - void GetLayersIndexes(std::vector& output) const; - - virtual Extent2D GetSceneExtent(bool minimal) const; - - virtual void Render(Orthanc::ImageAccessor& buffer, - const AffineTransform2D& viewTransform, - ImageInterpolation interpolation, - bool applyWindowing) const; - - bool LookupLayer(size_t& index /* out */, - double x, - double y) const; - - void DrawBorder(CairoContext& context, - unsigned int layer, - double zoom); - - void GetRange(float& minValue, - float& maxValue) const; - - void ExportToScene2D(Scene2D& output) const; - - // Export using PAM is faster than using PNG, but requires Orthanc - // core >= 1.4.3 - void ExportDicom(Deprecated::OrthancApiClient& orthanc, - const Orthanc::DicomMap& dicom, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam); - - void ExportDicom(Deprecated::OrthancApiClient& orthanc, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam); - - void ExportToCreateDicomRequest(Json::Value& createDicomRequestContent, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation, - bool usePam); - - Orthanc::Image* ExportToCreateDicomRequestAndImage(Json::Value& createDicomRequestContent, - const Json::Value& dicomTags, - const std::string& parentOrthancId, - double pixelSpacingX, - double pixelSpacingY, - bool invert, - bool autoCrop, - ImageInterpolation interpolation); - - Orthanc::Image* ExportToImage(double pixelSpacingX, - double pixelSpacingY, - ImageInterpolation interpolation, - bool autoCrop, - bool applyWindowing) - { - return ExportToImage(pixelSpacingX, pixelSpacingY, interpolation, false, 0, autoCrop, applyWindowing); - } - - Orthanc::Image* ExportToImage(double pixelSpacingX, - double pixelSpacingY, - ImageInterpolation interpolation, - bool invert, - int64_t maxValue /* for inversion */, - bool autoCrop, - bool applyWindowing); - - void ExtractLayerFromRenderedScene(Orthanc::ImageAccessor& layer, - const Orthanc::ImageAccessor& renderedScene, - size_t layerIndex, - bool isCropped, - ImageInterpolation interpolation); - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographySceneCommand.cpp --- a/Framework/Radiography/RadiographySceneCommand.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographySceneCommand.h" - - -namespace OrthancStone -{ - RadiographySceneCommand::RadiographySceneCommand(RadiographyScene& scene, - size_t layer) : - scene_(scene), - layer_(layer) - { - } - - - RadiographySceneCommand::RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor) : - scene_(accessor.GetScene()), - layer_(accessor.GetIndex()) - { - } - - - void RadiographySceneCommand::Undo() const - { - RadiographyScene::LayerAccessor accessor(scene_, layer_); - - if (accessor.IsValid()) - { - UndoInternal(accessor.GetLayer()); - } - } - - - void RadiographySceneCommand::Redo() const - { - RadiographyScene::LayerAccessor accessor(scene_, layer_); - - if (accessor.IsValid()) - { - RedoInternal(accessor.GetLayer()); - } - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographySceneCommand.h --- a/Framework/Radiography/RadiographySceneCommand.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - class RadiographySceneCommand : public UndoRedoStack::ICommand - { - private: - RadiographyScene& scene_; - size_t layer_; - - protected: - virtual void UndoInternal(RadiographyLayer& layer) const = 0; - - virtual void RedoInternal(RadiographyLayer& layer) const = 0; - - public: - RadiographySceneCommand(RadiographyScene& scene, - size_t layer); - - RadiographySceneCommand(const RadiographyScene::LayerAccessor& accessor); - - virtual void Undo() const; - - virtual void Redo() const; - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographySceneReader.cpp --- a/Framework/Radiography/RadiographySceneReader.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Deprecated/Toolbox/DicomFrameConverter.h" - -#include -#include -#include -#include - -namespace OrthancStone -{ - - void RadiographySceneBuilder::Read(const Json::Value& input, Orthanc::ImageAccessor* dicomImage /* takes ownership */, - Deprecated::DicomFrameConverter* dicomFrameConverter /* takes ownership */, - RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode - ) - { - dicomImage_.reset(dicomImage); - dicomFrameConverter_.reset(dicomFrameConverter); - preferredPhotometricDisplayMode_ = preferredPhotometricDisplayMode; - Read(input); - } - - RadiographyDicomLayer* RadiographySceneBuilder::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) - { - return dynamic_cast(&(scene_.LoadDicomImage(dicomImage_.release(), instanceId, frame, dicomFrameConverter_.release(), preferredPhotometricDisplayMode_, geometry))); - } - - - RadiographyDicomLayer* RadiographySceneReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) - { - return dynamic_cast(&(scene_.LoadDicomFrame(orthancApiClient_, instanceId, frame, false, geometry))); - } - - RadiographyDicomLayer* RadiographySceneGeometryReader::LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry) - { - std::unique_ptr layer(new RadiographyPlaceholderLayer(scene_)); - layer->SetGeometry(*geometry); - layer->SetSize(dicomImageWidth_, dicomImageHeight_); - scene_.RegisterLayer(layer.get()); - - return layer.release(); - } - - void RadiographySceneBuilder::Read(const Json::Value& input) - { - unsigned int version = input["version"].asUInt(); - - if (version != 1) - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - - if (input.isMember("hasWindowing") && input["hasWindowing"].asBool()) - { - scene_.SetWindowing(input["windowCenter"].asFloat(), input["windowWidth"].asFloat()); - } - - RadiographyDicomLayer* dicomLayer = NULL; - for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) - { - const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; - RadiographyLayer::Geometry geometry; - - if (jsonLayer["type"].asString() == "dicom") - { - ReadLayerGeometry(geometry, jsonLayer); - dicomLayer = LoadDicom(jsonLayer["instanceId"].asString(), jsonLayer["frame"].asUInt(), &geometry); - } - else if (jsonLayer["type"].asString() == "mask") - { - if (dicomLayer == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); // we always assumed the dicom layer was read before the mask - } - ReadLayerGeometry(geometry, jsonLayer); - - float foreground = jsonLayer["foreground"].asFloat(); - std::vector corners; - for (size_t i = 0; i < jsonLayer["corners"].size(); i++) - { - Orthanc::ImageProcessing::ImagePoint corner(jsonLayer["corners"][(int)i]["x"].asInt(), - jsonLayer["corners"][(int)i]["y"].asInt()); - corners.push_back(corner); - } - - scene_.LoadMask(corners, *dicomLayer, foreground, &geometry); - } - else if (jsonLayer["type"].asString() == "text") - { - ReadLayerGeometry(geometry, jsonLayer); - scene_.LoadText(jsonLayer["text"].asString(), jsonLayer["font"].asString(), jsonLayer["fontSize"].asUInt(), static_cast(jsonLayer["foreground"].asUInt()), &geometry, false); - } - 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::unique_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 RadiographySceneBuilder::ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input) - { - for(size_t layerIndex = 0; layerIndex < input["layers"].size(); layerIndex++) - { - const Json::Value& jsonLayer = input["layers"][(int)layerIndex]; - if (jsonLayer["type"].asString() == "dicom") - { - ReadLayerGeometry(geometry, jsonLayer); - return; - } - } - } - - void RadiographySceneBuilder::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()); - - // these fields were introduced later -> they might not exist - if (jsonLayer.isMember("flipVertical")) - { - geometry.SetFlipVertical(jsonLayer["flipVertical"].asBool()); - } - if (jsonLayer.isMember("flipHorizontal")) - { - geometry.SetFlipHorizontal(jsonLayer["flipHorizontal"].asBool()); - } - - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographySceneReader.h --- a/Framework/Radiography/RadiographySceneReader.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyMaskLayer.h" -#include "RadiographyTextLayer.h" -#include "../Deprecated/Toolbox/OrthancApiClient.h" - -#include -#include - -namespace OrthancStone -{ - // a layer containing only the geometry of a DICOM layer (bit hacky !) - class RadiographyPlaceholderLayer : public RadiographyDicomLayer - { - public: - RadiographyPlaceholderLayer(const RadiographyScene& scene) : - RadiographyDicomLayer(scene) - { - } - - }; - - - // HACK: I had to introduce this builder class in order to be able to recreate a RadiographyScene - // from a serialized scene that is passed to web-workers. - // It needs some architecturing... - class RadiographySceneBuilder : public boost::noncopyable - { - protected: - RadiographyScene& scene_; - std::unique_ptr dicomImage_; - std::unique_ptr dicomFrameConverter_; - RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode_; - - public: - RadiographySceneBuilder(RadiographyScene& scene) : - scene_(scene) - { - } - - void Read(const Json::Value& input); - void Read(const Json::Value& input, - Orthanc::ImageAccessor* dicomImage, // takes ownership - Deprecated::DicomFrameConverter* dicomFrameConverter, // takes ownership - RadiographyPhotometricDisplayMode preferredPhotometricDisplayMode - ); - - static void ReadLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input); - static void ReadDicomLayerGeometry(RadiographyLayer::Geometry& geometry, const Json::Value& input); - - protected: - virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); - - }; - - - class RadiographySceneReader : public RadiographySceneBuilder - { - Deprecated::OrthancApiClient& orthancApiClient_; - - public: - RadiographySceneReader(RadiographyScene& scene, Deprecated::OrthancApiClient& orthancApiClient) : - RadiographySceneBuilder(scene), - orthancApiClient_(orthancApiClient) - { - } - - protected: - virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); - }; - - // reads the whole scene but the DICOM image such that we have the full geometry - class RadiographySceneGeometryReader : public RadiographySceneBuilder - { - unsigned int dicomImageWidth_; - unsigned int dicomImageHeight_; - - public: - RadiographySceneGeometryReader(RadiographyScene& scene, unsigned int dicomImageWidth, unsigned int dicomImageHeight) : - RadiographySceneBuilder(scene), - dicomImageWidth_(dicomImageWidth), - dicomImageHeight_(dicomImageHeight) - { - } - - protected: - virtual RadiographyDicomLayer* LoadDicom(const std::string& instanceId, unsigned int frame, RadiographyLayer::Geometry* geometry); - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographySceneWriter.cpp --- a/Framework/Radiography/RadiographySceneWriter.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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; - float windowCenter, windowWidth; - bool hasWindowing = scene.GetWindowing(windowCenter, windowWidth); - output["hasWindowing"] = hasWindowing; - if (hasWindowing) - { - output["windowCenter"] = windowCenter; - output["windowWidth"] = windowWidth; - } - output["layers"] = Json::arrayValue; - - std::vector layersIndexes; - 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["font"] = layer.GetFont(); - output["fontSize"] = layer.GetFontSize(); - output["foreground"] = layer.GetForegroundGreyLevel(); - } - - void RadiographySceneWriter::WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer) - { - output["type"] = "mask"; - output["instanceId"] = layer.GetInstanceId(); // the dicom layer it's being linked to - output["foreground"] = layer.GetForeground(); - output["corners"] = Json::arrayValue; - const std::vector& corners = layer.GetCorners(); - for (size_t i = 0; i < corners.size(); i++) - { - Json::Value corner; - corner["x"] = corners[i].GetX(); - corner["y"] = corners[i].GetY(); - output["corners"].append(corner); - } - } - - 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(); - } - - 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; - } - - output["flipVertical"] = geometry.GetFlipVertical(); - output["flipHorizontal"] = geometry.GetFlipHorizontal(); - - 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 if (dynamic_cast(&layer) != NULL) - { - WriteLayer(output, dynamic_cast(layer)); - } - else - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographySceneWriter.h --- a/Framework/Radiography/RadiographySceneWriter.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyMaskLayer.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); - void WriteLayer(Json::Value& output, const RadiographyMaskLayer& layer); - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyTextLayer.cpp --- a/Framework/Radiography/RadiographyTextLayer.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "Core/OrthancException.h" -#include "RadiographyScene.h" -#include "../Toolbox/TextRenderer.h" - -namespace OrthancStone -{ - std::map RadiographyTextLayer::fonts_; - - void RadiographyTextLayer::SetText(const std::string& utf8, - const std::string& font, - unsigned int fontSize, - uint8_t foregroundGreyLevel) - { - if (fonts_.find(font) == fonts_.end()) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls, "The font has not been registered"); - } - - text_ = utf8; - font_ = font; - fontSize_ = fontSize; - foregroundGreyLevel_ = foregroundGreyLevel; - - SetAlpha(TextRenderer::Render(fonts_[font_], - fontSize_, - text_)); - - SetForegroundValue(foregroundGreyLevel * 256.0f); - } - -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyTextLayer.h --- a/Framework/Radiography/RadiographyTextLayer.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 font_; - unsigned int fontSize_; - uint8_t foregroundGreyLevel_; - - static std::map fonts_; - public: - RadiographyTextLayer(const RadiographyScene& scene) : - RadiographyAlphaLayer(scene) - { - } - - void SetText(const std::string& utf8, const std::string& font, unsigned int fontSize, uint8_t foregroundGreyLevel); - - const std::string& GetText() const - { - return text_; - } - - const std::string& GetFont() const - { - return font_; - } - - unsigned int GetFontSize() const - { - return fontSize_; - } - - uint8_t GetForegroundGreyLevel() const - { - return foregroundGreyLevel_; - } - - static void RegisterFont(const std::string& name, Orthanc::EmbeddedResources::FileResourceId fontResourceId) - { - fonts_[name] = fontResourceId; - } - }; -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyWidget.cpp --- a/Framework/Radiography/RadiographyWidget.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,284 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyWidget.h" - -#include -#include -#include - -#include "RadiographyMaskLayer.h" - -namespace OrthancStone -{ - - bool RadiographyWidget::IsInvertedInternal() const - { - // MONOCHROME1 images must be inverted and the user can invert the - // image, too -> XOR the two - return (scene_->GetPreferredPhotomotricDisplayMode() == - RadiographyPhotometricDisplayMode_Monochrome1) ^ invert_; - } - - void RadiographyWidget::RenderBackground( - Orthanc::ImageAccessor& image, float minValue, float maxValue) - { - // wipe background before rendering - float backgroundValue = minValue; - - switch (scene_->GetPreferredPhotomotricDisplayMode()) - { - case RadiographyPhotometricDisplayMode_Monochrome1: - case RadiographyPhotometricDisplayMode_Default: - if (IsInvertedInternal()) - backgroundValue = maxValue; - else - backgroundValue = minValue; - break; - case RadiographyPhotometricDisplayMode_Monochrome2: - if (IsInvertedInternal()) - backgroundValue = minValue; - else - backgroundValue = maxValue; - break; - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); - } - - Orthanc::ImageProcessing::Set(image, static_cast(backgroundValue)); - } - - bool RadiographyWidget::RenderInternal(unsigned int width, - unsigned int height, - ImageInterpolation interpolation) - { - if (floatBuffer_.get() == NULL || - floatBuffer_->GetWidth() != width || - floatBuffer_->GetHeight() != height) - { - floatBuffer_.reset(new Orthanc::Image( - Orthanc::PixelFormat_Float32, width, height, false)); - - if (floatBuffer_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate float buffer"); - } - } - - if (cairoBuffer_.get() == NULL || - cairoBuffer_->GetWidth() != width || - cairoBuffer_->GetHeight() != height) - { - cairoBuffer_.reset(new CairoSurface(width, height, false /* no alpha */)); - - if (cairoBuffer_.get() == NULL) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_NotEnoughMemory, "RadiographyWidget::RenderInternal: unable to allocate cairo buffer"); - } - } - - RenderBackground(*floatBuffer_, 0.0, 65535.0); - - scene_->Render(*floatBuffer_, GetView().GetMatrix(), interpolation, true); - - // Conversion from Float32 to BGRA32 (cairo). Very similar to - // GrayscaleFrameRenderer => TODO MERGE? - Orthanc::ImageAccessor target; - cairoBuffer_->GetWriteableAccessor(target); - - bool invert = IsInvertedInternal(); - - for (unsigned int y = 0; y < height; y++) - { - const float* p = reinterpret_cast(floatBuffer_->GetConstRow(y)); - uint8_t* q = reinterpret_cast(target.GetRow(y)); - - for (unsigned int x = 0; x < width; x++, p++, q += 4) - { - uint8_t v = 0; - if (*p >= 65535.0) - { - v = 255; - } - else if (*p <= 0.0) - { - v = 0; - } - else - { - v = static_cast(*p / 256.0); - } - - if (invert) - { - v = 255 - v; - } - - q[0] = v; - q[1] = v; - q[2] = v; - q[3] = 255; - } - } - - return true; - } - - - bool RadiographyWidget::RenderScene(CairoContext& context, - const Deprecated::ViewportGeometry& view) - { - cairo_t* cr = context.GetObject(); - - if (RenderInternal(context.GetWidth(), context.GetHeight(), interpolation_)) - { - // https://www.cairographics.org/FAQ/#paint_from_a_surface - cairo_save(cr); - cairo_identity_matrix(cr); - cairo_set_source_surface(cr, cairoBuffer_->GetObject(), 0, 0); - cairo_paint(cr); - cairo_restore(cr); - } - else - { - // https://www.cairographics.org/FAQ/#clear_a_surface - context.SetSourceColor(0, 0, 0); - cairo_paint(cr); - } - - if (hasSelection_) - { - scene_->DrawBorder( - context, static_cast(selectedLayer_), view.GetZoom()); - } - - return true; - } - - - RadiographyWidget::RadiographyWidget(boost::shared_ptr scene, - const std::string& name) : - WorldSceneWidget(name), - invert_(false), - interpolation_(ImageInterpolation_Nearest), - hasSelection_(false), - selectedLayer_(0) // Dummy initialization - { - SetScene(scene); - } - - - void RadiographyWidget::Select(size_t layer) - { - hasSelection_ = true; - selectedLayer_ = layer; - - NotifyContentChanged(); - BroadcastMessage(SelectionChangedMessage(*this)); - } - - void RadiographyWidget::Unselect() - { - hasSelection_ = false; - - NotifyContentChanged(); - BroadcastMessage(SelectionChangedMessage(*this)); - } - - bool RadiographyWidget::LookupSelectedLayer(size_t& layer) const - { - if (hasSelection_) - { - layer = selectedLayer_; - return true; - } - else - { - return false; - } - } - - - void RadiographyWidget::OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message) - { -// LOG(INFO) << "Scene geometry has changed"; - FitContent(); - } - - - void RadiographyWidget::OnContentChanged(const RadiographyScene::ContentChangedMessage& message) - { -// LOG(INFO) << "Scene content has changed"; - NotifyContentChanged(); - } - - void RadiographyWidget::OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message) - { - size_t removedLayerIndex = message.GetLayerIndex(); - if (hasSelection_ && selectedLayer_ == removedLayerIndex) - { - Unselect(); - } - NotifyContentChanged(); - } - - void RadiographyWidget::SetInvert(bool invert) - { - if (invert_ != invert) - { - invert_ = invert; - NotifyContentChanged(); - } - } - - - void RadiographyWidget::SwitchInvert() - { - invert_ = !invert_; - NotifyContentChanged(); - } - - - void RadiographyWidget::SetInterpolation(ImageInterpolation interpolation) - { - if (interpolation_ != interpolation) - { - interpolation_ = interpolation; - NotifyContentChanged(); - } - } - - void RadiographyWidget::SetScene(boost::shared_ptr scene) - { - scene_ = scene; - - Register(*scene_, &RadiographyWidget::OnGeometryChanged); - Register(*scene_, &RadiographyWidget::OnContentChanged); - Register(*scene_, &RadiographyWidget::OnLayerRemoved); - - Unselect(); - - NotifyContentChanged(); - - // force redraw - FitContent(); - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyWidget.h --- a/Framework/Radiography/RadiographyWidget.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Deprecated/Widgets/WorldSceneWidget.h" -#include "../Messages/ObserverBase.h" -#include "RadiographyScene.h" - - -namespace OrthancStone -{ - class RadiographyMaskLayer; - - class RadiographyWidget : - public Deprecated::WorldSceneWidget, - public ObserverBase, - public IObservable - { - public: - ORTHANC_STONE_DEFINE_ORIGIN_MESSAGE(__FILE__, __LINE__, SelectionChangedMessage, RadiographyWidget); - - private: - boost::shared_ptr scene_; - std::unique_ptr floatBuffer_; - std::unique_ptr cairoBuffer_; - bool invert_; - ImageInterpolation interpolation_; - bool hasSelection_; - size_t selectedLayer_; - - bool RenderInternal(unsigned int width, - unsigned int height, - ImageInterpolation interpolation); - - protected: - virtual Extent2D GetSceneExtent() - { - return scene_->GetSceneExtent(false); - } - - virtual bool RenderScene(CairoContext& context, - const Deprecated::ViewportGeometry& view); - - virtual void RenderBackground(Orthanc::ImageAccessor& image, float minValue, float maxValue); - - bool IsInvertedInternal() const; - - public: - RadiographyWidget(boost::shared_ptr scene, // TODO: check how we can avoid boost::shared_ptr here since we don't want them in the public API (app is keeping a boost::shared_ptr to this right now) - const std::string& name); - - RadiographyScene& GetScene() const - { - return *scene_; - } - - void SetScene(boost::shared_ptr scene); - - void Select(size_t layer); - - void Unselect(); - - template bool SelectLayerByType(size_t index = 0); - - bool LookupSelectedLayer(size_t& layer) const; - - void OnGeometryChanged(const RadiographyScene::GeometryChangedMessage& message); - - void OnContentChanged(const RadiographyScene::ContentChangedMessage& message); - - void OnLayerRemoved(const RadiographyScene::LayerRemovedMessage& message); - - void SetInvert(bool invert); - - void SwitchInvert(); - - bool IsInverted() const - { - return invert_; - } - - void SetInterpolation(ImageInterpolation interpolation); - - ImageInterpolation GetInterpolation() const - { - return interpolation_; - } - }; - - template bool RadiographyWidget::SelectLayerByType(size_t index) - { - std::vector layerIndexes; - size_t count = 0; - scene_->GetLayersIndexes(layerIndexes); - - for (size_t i = 0; i < layerIndexes.size(); ++i) - { - const LayerType* typedLayer = dynamic_cast(&(scene_->GetLayer(layerIndexes[i]))); - if (typedLayer != NULL) - { - if (count == index) - { - Select(layerIndexes[i]); - return true; - } - count++; - } - } - - return false; - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyWindowingTracker.cpp --- a/Framework/Radiography/RadiographyWindowingTracker.cpp Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,192 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "RadiographyWindowingTracker.h" -#include "RadiographyWidget.h" - -#include - - -namespace OrthancStone -{ - class RadiographyWindowingTracker::UndoRedoCommand : public UndoRedoStack::ICommand - { - private: - RadiographyScene& scene_; - float sourceCenter_; - float sourceWidth_; - float targetCenter_; - float targetWidth_; - - public: - UndoRedoCommand(const RadiographyWindowingTracker& tracker) : - scene_(tracker.scene_), - sourceCenter_(tracker.sourceCenter_), - sourceWidth_(tracker.sourceWidth_) - { - scene_.GetWindowingWithDefault(targetCenter_, targetWidth_); - } - - virtual void Undo() const - { - scene_.SetWindowing(sourceCenter_, sourceWidth_); - } - - virtual void Redo() const - { - scene_.SetWindowing(targetCenter_, targetWidth_); - } - }; - - - void RadiographyWindowingTracker::ComputeAxisEffect(int& deltaCenter, - int& deltaWidth, - int delta, - Action actionNegative, - Action actionPositive) - { - if (delta < 0) - { - switch (actionNegative) - { - case Action_IncreaseWidth: - deltaWidth = -delta; - break; - - case Action_DecreaseWidth: - deltaWidth = delta; - break; - - case Action_IncreaseCenter: - deltaCenter = -delta; - break; - - case Action_DecreaseCenter: - deltaCenter = delta; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - else if (delta > 0) - { - switch (actionPositive) - { - case Action_IncreaseWidth: - deltaWidth = delta; - break; - - case Action_DecreaseWidth: - deltaWidth = -delta; - break; - - case Action_IncreaseCenter: - deltaCenter = delta; - break; - - case Action_DecreaseCenter: - deltaCenter = -delta; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - } - - - RadiographyWindowingTracker::RadiographyWindowingTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - RadiographyWidget& widget, - ImageInterpolation interpolationDuringTracking, - int x, - int y, - Action leftAction, - Action rightAction, - Action upAction, - Action downAction) : - undoRedoStack_(undoRedoStack), - scene_(scene), - widget_(widget), - initialWidgetInterpolation_(widget.GetInterpolation()), - clickX_(x), - clickY_(y), - leftAction_(leftAction), - rightAction_(rightAction), - upAction_(upAction), - downAction_(downAction) - { - scene_.GetWindowingWithDefault(sourceCenter_, sourceWidth_); - widget_.SetInterpolation(interpolationDuringTracking); - - float minValue, maxValue; - scene.GetRange(minValue, maxValue); - - assert(minValue <= maxValue); - - float delta = (maxValue - minValue); - strength_ = delta / 1000.0f; // 1px move will change the ww/wc by 0.1% - - if (strength_ < 1) - { - strength_ = 1; - } - } - - - void RadiographyWindowingTracker::Render(CairoContext& context, - double zoom) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - - void RadiographyWindowingTracker::MouseUp() - { - widget_.SetInterpolation(initialWidgetInterpolation_); - undoRedoStack_.Add(new UndoRedoCommand(*this)); - } - - - void RadiographyWindowingTracker::MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches) - { - // This follows the behavior of the Osimis Web viewer: - // https://bitbucket.org/osimis/osimis-webviewer-plugin/src/master/frontend/src/app/viewport/image-plugins/windowing-viewport-tool.class.js - - static const float SCALE = 1.0; - - int deltaCenter = 0; - int deltaWidth = 0; - - ComputeAxisEffect(deltaCenter, deltaWidth, displayX - clickX_, leftAction_, rightAction_); - ComputeAxisEffect(deltaCenter, deltaWidth, displayY - clickY_, upAction_, downAction_); - - float newCenter = sourceCenter_ + (deltaCenter / SCALE * strength_); - float newWidth = sourceWidth_ + (deltaWidth / SCALE * strength_); - scene_.SetWindowing(newCenter, newWidth); - } -} diff -r 1c2d065ba372 -r c5403d52078c Framework/Radiography/RadiographyWindowingTracker.h --- a/Framework/Radiography/RadiographyWindowingTracker.h Wed Apr 29 20:41:36 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -/** - * Stone of Orthanc - * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics - * Department, University Hospital of Liege, Belgium - * Copyright (C) 2017-2020 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 "../Toolbox/UndoRedoStack.h" -#include "../Deprecated/Widgets/IWorldSceneMouseTracker.h" -#include "RadiographyScene.h" - -namespace OrthancStone -{ - - class RadiographyWidget; - - class RadiographyWindowingTracker : public Deprecated::IWorldSceneMouseTracker - { - public: - enum Action - { - Action_IncreaseWidth, - Action_DecreaseWidth, - Action_IncreaseCenter, - Action_DecreaseCenter - }; - - private: - class UndoRedoCommand; - - UndoRedoStack& undoRedoStack_; - RadiographyScene& scene_; - RadiographyWidget& widget_; - ImageInterpolation initialWidgetInterpolation_; - int clickX_; - int clickY_; - Action leftAction_; - Action rightAction_; - Action upAction_; - Action downAction_; - float strength_; - float sourceCenter_; - float sourceWidth_; - - static void ComputeAxisEffect(int& deltaCenter, - int& deltaWidth, - int delta, - Action actionNegative, - Action actionPositive); - - public: - RadiographyWindowingTracker(UndoRedoStack& undoRedoStack, - RadiographyScene& scene, - RadiographyWidget& widget, - ImageInterpolation interpolationDuringTracking, - int x, - int y, - Action leftAction, - Action rightAction, - Action upAction, - Action downAction); - - virtual bool HasRender() const - { - return false; - } - - virtual void Render(CairoContext& context, - double zoom); - - virtual void MouseUp(); - - virtual void MouseMove(int displayX, - int displayY, - double sceneX, - double sceneY, - const std::vector& displayTouches, - const std::vector& sceneTouches); - }; -}