# HG changeset patch # User am@osimis.io # Date 1530606416 -7200 # Node ID 40b21c1f8b8dcc0dba0347fa509b1b5d85df4b01 # Parent 192e6e349e697c75bc43f7e2c0a89ab303aa8497 more usage of IObservable/IObserver diff -r 192e6e349e69 -r 40b21c1f8b8d Framework/Layers/ILayerSource.h --- a/Framework/Layers/ILayerSource.h Mon Jul 02 18:13:46 2018 +0200 +++ b/Framework/Layers/ILayerSource.h Tue Jul 03 10:26:56 2018 +0200 @@ -28,32 +28,32 @@ namespace OrthancStone { - class ILayerSource : public boost::noncopyable, public IObservable + class ILayerSource : public IObservable { public: struct SliceChangedMessage : public IMessage { - const Slice& slice; + const Slice& slice_; SliceChangedMessage(const Slice& slice) : IMessage(MessageType_SliceChanged), - slice(slice) + slice_(slice) { } }; struct LayerReadyMessage : public IMessage { - std::auto_ptr& layer; - const CoordinateSystem3D& slice; - bool isError; + std::auto_ptr& layer_; + const CoordinateSystem3D& slice_; + bool isError_; LayerReadyMessage(std::auto_ptr& layer, const CoordinateSystem3D& slice, bool isError) // TODO Shouldn't this be separate as NotifyLayerError? : IMessage(MessageType_LayerReady), - layer(layer), - slice(slice), - isError(isError) + layer_(layer), + slice_(slice), + isError_(isError) { } }; diff -r 192e6e349e69 -r 40b21c1f8b8d Framework/Layers/OrthancFrameLayerSource.cpp --- a/Framework/Layers/OrthancFrameLayerSource.cpp Mon Jul 02 18:13:46 2018 +0200 +++ b/Framework/Layers/OrthancFrameLayerSource.cpp Tue Jul 03 10:26:56 2018 +0200 @@ -31,7 +31,7 @@ namespace OrthancStone { - void OrthancFrameLayerSource::NotifyGeometryReady(const OrthancSlicesLoader& loader) + void OrthancFrameLayerSource::OnSliceGeometryReady(const OrthancSlicesLoader& loader) { if (loader.GetSliceCount() > 0) { @@ -43,12 +43,12 @@ } } - void OrthancFrameLayerSource::NotifyGeometryError(const OrthancSlicesLoader& loader) + void OrthancFrameLayerSource::OnSliceGeometryError(const OrthancSlicesLoader& loader) { LayerSourceBase::NotifyGeometryError(); } - void OrthancFrameLayerSource::NotifySliceImageReady(const OrthancSlicesLoader& loader, + void OrthancFrameLayerSource::OnSliceImageReady(const OrthancSlicesLoader& loader, unsigned int sliceIndex, const Slice& slice, std::auto_ptr& image, @@ -59,7 +59,7 @@ slice.GetGeometry(), false); } - void OrthancFrameLayerSource::NotifySliceImageError(const OrthancSlicesLoader& loader, + void OrthancFrameLayerSource::OnSliceImageError(const OrthancSlicesLoader& loader, unsigned int sliceIndex, const Slice& slice, SliceImageQuality quality) @@ -70,6 +70,7 @@ OrthancFrameLayerSource::OrthancFrameLayerSource(MessageBroker& broker, IWebService& orthanc) : LayerSourceBase(broker), + OrthancSlicesLoader::ISliceLoaderObserver(broker), loader_(broker, *this, orthanc), quality_(SliceImageQuality_Full) { diff -r 192e6e349e69 -r 40b21c1f8b8d Framework/Layers/OrthancFrameLayerSource.h --- a/Framework/Layers/OrthancFrameLayerSource.h Mon Jul 02 18:13:46 2018 +0200 +++ b/Framework/Layers/OrthancFrameLayerSource.h Tue Jul 03 10:26:56 2018 +0200 @@ -29,23 +29,23 @@ { class OrthancFrameLayerSource : public LayerSourceBase, - private OrthancSlicesLoader::ICallback + private OrthancSlicesLoader::ISliceLoaderObserver { private: OrthancSlicesLoader loader_; SliceImageQuality quality_; - virtual void NotifyGeometryReady(const OrthancSlicesLoader& loader); + virtual void OnSliceGeometryReady(const OrthancSlicesLoader& loader); - virtual void NotifyGeometryError(const OrthancSlicesLoader& loader); + virtual void OnSliceGeometryError(const OrthancSlicesLoader& loader); - virtual void NotifySliceImageReady(const OrthancSlicesLoader& loader, + virtual void OnSliceImageReady(const OrthancSlicesLoader& loader, unsigned int sliceIndex, const Slice& slice, std::auto_ptr& image, SliceImageQuality quality); - virtual void NotifySliceImageError(const OrthancSlicesLoader& loader, + virtual void OnSliceImageError(const OrthancSlicesLoader& loader, unsigned int sliceIndex, const Slice& slice, SliceImageQuality quality); diff -r 192e6e349e69 -r 40b21c1f8b8d Framework/Messages/MessageType.h --- a/Framework/Messages/MessageType.h Mon Jul 02 18:13:46 2018 +0200 +++ b/Framework/Messages/MessageType.h Tue Jul 03 10:26:56 2018 +0200 @@ -32,6 +32,11 @@ MessageType_SliceChanged, MessageType_LayerReady, + MessageType_SliceGeometryReady, + MessageType_SliceGeometryError, + MessageType_SliceImageReady, + MessageType_SliceImageError, + MessageType_HttpRequestSuccess, MessageType_HttpRequestError diff -r 192e6e349e69 -r 40b21c1f8b8d Framework/Toolbox/IWebService.h --- a/Framework/Toolbox/IWebService.h Mon Jul 02 18:13:46 2018 +0200 +++ b/Framework/Toolbox/IWebService.h Tue Jul 03 10:26:56 2018 +0200 @@ -24,6 +24,7 @@ #include #include "../../Framework/Messages/IObserver.h" #include +#include namespace OrthancStone { @@ -90,7 +91,8 @@ msg.AnswerSize, msg.Payload); }; break; - + default: + VLOG("unhandled message type" << message.GetType()); } } diff -r 192e6e349e69 -r 40b21c1f8b8d Framework/Toolbox/OrthancSlicesLoader.cpp --- a/Framework/Toolbox/OrthancSlicesLoader.cpp Mon Jul 02 18:13:46 2018 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.cpp Tue Jul 03 10:26:56 2018 +0200 @@ -13,7 +13,7 @@ * 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 . **/ @@ -47,10 +47,10 @@ static std::string base64_decode(const std::string &in) { std::string out; - + std::vector T(256,-1); - for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; - + for (int i=0; i<64; i++) T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i; + int val=0, valb=-8; for (size_t i = 0; i < in.size(); i++) { unsigned char c = in[i]; @@ -78,32 +78,32 @@ const Slice* slice_; std::string instanceId_; SliceImageQuality quality_; - + Operation(Mode mode) : mode_(mode) { } - + public: Mode GetMode() const { return mode_; } - + SliceImageQuality GetQuality() const { assert(mode_ == Mode_LoadImage || mode_ == Mode_LoadRawImage); return quality_; } - + unsigned int GetSliceIndex() const { assert(mode_ == Mode_LoadImage || mode_ == Mode_LoadRawImage); return sliceIndex_; } - + const Slice& GetSlice() const { assert(mode_ == Mode_LoadImage || @@ -111,32 +111,32 @@ assert(slice_ != NULL); return *slice_; } - + unsigned int GetFrame() const { assert(mode_ == Mode_FrameGeometry); return frame_; } - + const std::string& GetInstanceId() const { assert(mode_ == Mode_FrameGeometry || mode_ == Mode_InstanceGeometry); return instanceId_; } - + static Operation* DownloadSeriesGeometry() { return new Operation(Mode_SeriesGeometry); } - + static Operation* DownloadInstanceGeometry(const std::string& instanceId) { std::auto_ptr operation(new Operation(Mode_InstanceGeometry)); operation->instanceId_ = instanceId; return operation.release(); } - + static Operation* DownloadFrameGeometry(const std::string& instanceId, unsigned int frame) { @@ -145,7 +145,7 @@ operation->frame_ = frame; return operation.release(); } - + static Operation* DownloadSliceImage(unsigned int sliceIndex, const Slice& slice, SliceImageQuality quality) @@ -156,7 +156,7 @@ tmp->quality_ = quality; return tmp.release(); } - + static Operation* DownloadSliceRawImage(unsigned int sliceIndex, const Slice& slice) { @@ -167,96 +167,127 @@ return tmp.release(); } }; - + class OrthancSlicesLoader::WebCallback : public IWebService::ICallback { private: OrthancSlicesLoader& that_; - + public: WebCallback(MessageBroker& broker, OrthancSlicesLoader& that) : IWebService::ICallback(broker), that_(that) { } - + virtual void OnHttpRequestSuccess(const std::string& uri, - const void* answer, - size_t answerSize, - Orthanc::IDynamicObject* payload) + const void* answer, + size_t answerSize, + Orthanc::IDynamicObject* payload) { std::auto_ptr operation(dynamic_cast(payload)); - + switch (operation->GetMode()) { - case Mode_SeriesGeometry: - that_.ParseSeriesGeometry(answer, answerSize); - break; - - case Mode_InstanceGeometry: - that_.ParseInstanceGeometry(operation->GetInstanceId(), answer, answerSize); - break; - - case Mode_FrameGeometry: - that_.ParseFrameGeometry(operation->GetInstanceId(), - operation->GetFrame(), answer, answerSize); + case Mode_SeriesGeometry: + that_.ParseSeriesGeometry(answer, answerSize); + break; + + case Mode_InstanceGeometry: + that_.ParseInstanceGeometry(operation->GetInstanceId(), answer, answerSize); + break; + + case Mode_FrameGeometry: + that_.ParseFrameGeometry(operation->GetInstanceId(), + operation->GetFrame(), answer, answerSize); + break; + + case Mode_LoadImage: + switch (operation->GetQuality()) + { + case SliceImageQuality_Full: + that_.ParseSliceImagePng(*operation, answer, answerSize); break; - - case Mode_LoadImage: - switch (operation->GetQuality()) - { - case SliceImageQuality_Full: - that_.ParseSliceImagePng(*operation, answer, answerSize); - break; - - case SliceImageQuality_Jpeg50: - case SliceImageQuality_Jpeg90: - case SliceImageQuality_Jpeg95: - that_.ParseSliceImageJpeg(*operation, answer, answerSize); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - - break; - - case Mode_LoadRawImage: - that_.ParseSliceRawImage(*operation, answer, answerSize); - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); - } - } - - virtual void OnHttpRequestError(const std::string& uri, - Orthanc::IDynamicObject* payload) - { - std::auto_ptr operation(dynamic_cast(payload)); - LOG(ERROR) << "Cannot download " << uri; - - switch (operation->GetMode()) - { - case Mode_FrameGeometry: - case Mode_SeriesGeometry: - that_.userCallback_.NotifyGeometryError(that_); - that_.state_ = State_Error; - break; - - case Mode_LoadImage: - that_.userCallback_.NotifySliceImageError(that_, operation->GetSliceIndex(), - operation->GetSlice(), - operation->GetQuality()); + + case SliceImageQuality_Jpeg50: + case SliceImageQuality_Jpeg90: + case SliceImageQuality_Jpeg95: + that_.ParseSliceImageJpeg(*operation, answer, answerSize); break; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + break; + + case Mode_LoadRawImage: + that_.ParseSliceRawImage(*operation, answer, answerSize); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } - } + } + + virtual void OnHttpRequestError(const std::string& uri, + Orthanc::IDynamicObject* payload) + { + std::auto_ptr operation(dynamic_cast(payload)); + LOG(ERROR) << "Cannot download " << uri; + + switch (operation->GetMode()) + { + case Mode_FrameGeometry: + case Mode_SeriesGeometry: + that_.userCallback_.OnSliceGeometryError(that_); + that_.state_ = State_Error; + break; + + case Mode_LoadImage: + that_.userCallback_.OnSliceImageError(that_, operation->GetSliceIndex(), + operation->GetSlice(), + operation->GetQuality()); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } }; - + + void OrthancSlicesLoader::ISliceLoaderObserver::HandleMessage(IObservable& from, const IMessage& message) + { + switch (message.GetType()) + { + case MessageType_SliceGeometryReady: + OnSliceGeometryReady(dynamic_cast(from)); + break; + case MessageType_SliceGeometryError: + OnSliceGeometryError(dynamic_cast(from)); + break; + case MessageType_SliceImageReady: + { + const SliceImageReadyMessage& msg = dynamic_cast(message); + OnSliceImageReady(dynamic_cast(from), + msg.sliceIndex_, + msg.slice_, + msg.image_, + msg.effectiveQuality_); + }; break; + case MessageType_SliceImageError: + { + const SliceImageErrorMessage& msg = dynamic_cast(message); + OnSliceImageError(dynamic_cast(from), + msg.sliceIndex_, + msg.slice_, + msg.effectiveQuality_); + }; break; + default: + VLOG("unhandled message type" << message.GetType()); + } + } void OrthancSlicesLoader::NotifySliceImageSuccess(const Operation& operation, @@ -268,19 +299,19 @@ } else { - userCallback_.NotifySliceImageReady - (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality()); + userCallback_.OnSliceImageReady + (*this, operation.GetSliceIndex(), operation.GetSlice(), image, operation.GetQuality()); } } - + void OrthancSlicesLoader::NotifySliceImageError(const Operation& operation) const { - userCallback_.NotifySliceImageError - (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality()); + userCallback_.OnSliceImageError + (*this, operation.GetSliceIndex(), operation.GetSlice(), operation.GetQuality()); } - - + + void OrthancSlicesLoader::SortAndFinalizeSlices() { bool ok = false; @@ -296,21 +327,21 @@ ok = true; } } - + state_ = State_GeometryReady; - + if (ok) { LOG(INFO) << "Loaded a series with " << slices_.GetSliceCount() << " slice(s)"; - userCallback_.NotifyGeometryReady(*this); + userCallback_.OnSliceGeometryReady(*this); } else { LOG(ERROR) << "This series is empty"; - userCallback_.NotifyGeometryError(*this); + userCallback_.OnSliceGeometryError(*this); } } - + void OrthancSlicesLoader::ParseSeriesGeometry(const void* answer, size_t size) @@ -319,18 +350,18 @@ if (!MessagingToolbox::ParseJson(series, answer, size) || series.type() != Json::objectValue) { - userCallback_.NotifyGeometryError(*this); + userCallback_.OnSliceGeometryError(*this); return; } - + Json::Value::Members instances = series.getMemberNames(); - + slices_.Reserve(instances.size()); - + for (size_t i = 0; i < instances.size(); i++) { OrthancPlugins::FullOrthancDataset dataset(series[instances[i]]); - + Orthanc::DicomMap dicom; MessagingToolbox::ConvertDataset(dicom, dataset); @@ -339,7 +370,7 @@ { frames = 1; } - + for (unsigned int frame = 0; frame < frames; frame++) { std::auto_ptr slice(new Slice); @@ -353,11 +384,11 @@ } } } - + SortAndFinalizeSlices(); } - - + + void OrthancSlicesLoader::ParseInstanceGeometry(const std::string& instanceId, const void* answer, size_t size) @@ -366,15 +397,15 @@ if (!MessagingToolbox::ParseJson(tags, answer, size) || tags.type() != Json::objectValue) { - userCallback_.NotifyGeometryError(*this); + userCallback_.OnSliceGeometryError(*this); return; } - + OrthancPlugins::FullOrthancDataset dataset(tags); - + Orthanc::DicomMap dicom; MessagingToolbox::ConvertDataset(dicom, dataset); - + unsigned int frames; if (!dicom.ParseUnsignedInteger32(frames, Orthanc::DICOM_TAG_NUMBER_OF_FRAMES)) { @@ -382,7 +413,7 @@ } LOG(INFO) << "Instance " << instanceId << " contains " << frames << " frame(s)"; - + for (unsigned int frame = 0; frame < frames; frame++) { std::auto_ptr slice(new Slice); @@ -393,15 +424,15 @@ else { LOG(WARNING) << "Skipping invalid multi-frame instance " << instanceId; - userCallback_.NotifyGeometryError(*this); + userCallback_.OnSliceGeometryError(*this); return; } } - + SortAndFinalizeSlices(); } - - + + void OrthancSlicesLoader::ParseFrameGeometry(const std::string& instanceId, unsigned int frame, const void* answer, @@ -411,38 +442,38 @@ if (!MessagingToolbox::ParseJson(tags, answer, size) || tags.type() != Json::objectValue) { - userCallback_.NotifyGeometryError(*this); + userCallback_.OnSliceGeometryError(*this); return; } - + OrthancPlugins::FullOrthancDataset dataset(tags); - + state_ = State_GeometryReady; - + Orthanc::DicomMap dicom; MessagingToolbox::ConvertDataset(dicom, dataset); - + std::auto_ptr slice(new Slice); if (slice->ParseOrthancFrame(dicom, instanceId, frame)) { LOG(INFO) << "Loaded instance " << instanceId; slices_.AddSlice(slice.release()); - userCallback_.NotifyGeometryReady(*this); + userCallback_.OnSliceGeometryReady(*this); } else { LOG(WARNING) << "Skipping invalid instance " << instanceId; - userCallback_.NotifyGeometryError(*this); + userCallback_.OnSliceGeometryError(*this); } } - - + + void OrthancSlicesLoader::ParseSliceImagePng(const Operation& operation, const void* answer, size_t size) { std::auto_ptr image; - + try { image.reset(new Orthanc::PngReader); @@ -453,14 +484,14 @@ NotifySliceImageError(operation); return; } - + if (image->GetWidth() != operation.GetSlice().GetWidth() || image->GetHeight() != operation.GetSlice().GetHeight()) { NotifySliceImageError(operation); return; } - + if (operation.GetSlice().GetConverter().GetExpectedPixelFormat() == Orthanc::PixelFormat_SignedGrayscale16) { @@ -474,10 +505,10 @@ return; } } - + NotifySliceImageSuccess(operation, image); - } - + } + void OrthancSlicesLoader::ParseSliceImageJpeg(const Operation& operation, const void* answer, @@ -492,7 +523,7 @@ NotifySliceImageError(operation); return; } - + Json::Value& info = encoded["Orthanc"]; if (!info.isMember("PixelData") || !info.isMember("Stretched") || @@ -505,30 +536,30 @@ NotifySliceImageError(operation); return; } - + bool isSigned = false; bool isStretched = info["Stretched"].asBool(); - + if (info.isMember("IsSigned")) { if (info["IsSigned"].type() != Json::booleanValue) { NotifySliceImageError(operation); return; - } + } else { isSigned = info["IsSigned"].asBool(); } } - + std::auto_ptr reader; - + { std::string jpeg; //Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); jpeg = base64_decode(info["PixelData"].asString()); - + try { reader.reset(new Orthanc::JpegReader); @@ -540,10 +571,10 @@ return; } } - + Orthanc::PixelFormat expectedFormat = - operation.GetSlice().GetConverter().GetExpectedPixelFormat(); - + operation.GetSlice().GetConverter().GetExpectedPixelFormat(); + if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image { if (expectedFormat != Orthanc::PixelFormat_RGB24) @@ -551,7 +582,7 @@ NotifySliceImageError(operation); return; } - + if (isSigned || isStretched) { NotifySliceImageError(operation); @@ -563,13 +594,13 @@ return; } } - + if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) { NotifySliceImageError(operation); return; } - + if (!isStretched) { if (expectedFormat != reader->GetFormat()) @@ -583,10 +614,10 @@ return; } } - + int32_t stretchLow = 0; int32_t stretchHigh = 0; - + if (!info.isMember("StretchLow") || !info.isMember("StretchHigh") || info["StretchLow"].type() != Json::intValue || @@ -595,10 +626,10 @@ NotifySliceImageError(operation); return; } - + stretchLow = info["StretchLow"].asInt(); stretchHigh = info["StretchHigh"].asInt(); - + if (stretchLow < -32768 || stretchHigh > 65535 || (stretchLow < 0 && stretchHigh > 32767)) @@ -607,29 +638,29 @@ NotifySliceImageError(operation); return; } - + // Decode a grayscale JPEG 8bpp image coming from the Web viewer std::auto_ptr image - (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false)); - + (new Orthanc::Image(expectedFormat, reader->GetWidth(), reader->GetHeight(), false)); + Orthanc::ImageProcessing::Convert(*image, *reader); reader.reset(NULL); - + float scaling = static_cast(stretchHigh - stretchLow) / 255.0f; - + if (!LinearAlgebra::IsCloseToZero(scaling)) { float offset = static_cast(stretchLow) / scaling; Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling, true); } - + NotifySliceImageSuccess(operation, image); } - - + + class StringImage : - public Orthanc::ImageAccessor, - public boost::noncopyable + public Orthanc::ImageAccessor, + public boost::noncopyable { private: std::string buffer_; @@ -644,23 +675,23 @@ { throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); } - + buffer_.swap(buffer); // The source buffer is now empty - + void* data = (buffer_.empty() ? NULL : &buffer_[0]); - + AssignWritable(format, width, height, Orthanc::GetBytesPerPixel(format) * width, data); } }; - + void OrthancSlicesLoader::ParseSliceRawImage(const Operation& operation, const void* answer, size_t size) { Orthanc::GzipCompressor compressor; - + std::string raw; compressor.Uncompress(raw, answer, size); @@ -677,9 +708,9 @@ // This is the case of RT-DOSE (uint32_t values) std::auto_ptr image - (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(), - info.GetHeight(), raw)); - + (new StringImage(Orthanc::PixelFormat_Grayscale32, info.GetWidth(), + info.GetHeight(), raw)); + // TODO - Only for big endian for (unsigned int y = 0; y < image->GetHeight(); y++) { @@ -689,7 +720,7 @@ *p = le32toh(*p); } } - + NotifySliceImageSuccess(operation, image); } else if (info.GetBitsAllocated() == 16 && @@ -701,23 +732,23 @@ raw.size() == info.GetWidth() * info.GetHeight() * 2) { std::auto_ptr image - (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(), - info.GetHeight(), raw)); - + (new StringImage(Orthanc::PixelFormat_Grayscale16, info.GetWidth(), + info.GetHeight(), raw)); + // TODO - Big endian ? - + NotifySliceImageSuccess(operation, image); } else { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } - + } - - + + OrthancSlicesLoader::OrthancSlicesLoader(MessageBroker& broker, - ICallback& callback, + ISliceLoaderObserver& callback, IWebService& orthanc) : webCallback_(new WebCallback(broker, *this)), userCallback_(callback), @@ -725,7 +756,7 @@ state_(State_Initialization) { } - + void OrthancSlicesLoader::ScheduleLoadSeries(const std::string& seriesId) { @@ -740,8 +771,8 @@ orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSeriesGeometry()); } } - - + + void OrthancSlicesLoader::ScheduleLoadInstance(const std::string& instanceId) { if (state_ != State_Initialization) @@ -751,16 +782,16 @@ else { state_ = State_LoadingGeometry; - + // Tag "3004-000c" is "Grid Frame Offset Vector", which is // mandatory to read RT DOSE, but is too long to be returned by default std::string uri = "/instances/" + instanceId + "/tags?ignore-length=3004-000c"; orthanc_.ScheduleGetRequest - (*webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId)); + (*webCallback_, uri, Operation::DownloadInstanceGeometry(instanceId)); } } - + void OrthancSlicesLoader::ScheduleLoadFrame(const std::string& instanceId, unsigned int frame) { @@ -773,27 +804,27 @@ state_ = State_LoadingGeometry; std::string uri = "/instances/" + instanceId + "/tags"; orthanc_.ScheduleGetRequest - (*webCallback_, uri, Operation::DownloadFrameGeometry(instanceId, frame)); + (*webCallback_, uri, Operation::DownloadFrameGeometry(instanceId, frame)); } } - + bool OrthancSlicesLoader::IsGeometryReady() const { return state_ == State_GeometryReady; } - - + + size_t OrthancSlicesLoader::GetSliceCount() const { if (state_ != State_GeometryReady) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - + return slices_.GetSliceCount(); } - + const Slice& OrthancSlicesLoader::GetSlice(size_t index) const { @@ -801,11 +832,11 @@ { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - + return slices_.GetSlice(index); } - + bool OrthancSlicesLoader::LookupSlice(size_t& index, const CoordinateSystem3D& plane) const { @@ -813,76 +844,76 @@ { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - + return slices_.LookupSlice(index, plane); } - + void OrthancSlicesLoader::ScheduleSliceImagePng(const Slice& slice, size_t index) { - std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + + std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + boost::lexical_cast(slice.GetFrame())); - + switch (slice.GetConverter().GetExpectedPixelFormat()) { - case Orthanc::PixelFormat_RGB24: - uri += "/preview"; - break; - - case Orthanc::PixelFormat_Grayscale16: - uri += "/image-uint16"; - break; - - case Orthanc::PixelFormat_SignedGrayscale16: - uri += "/image-int16"; - break; - - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + case Orthanc::PixelFormat_RGB24: + uri += "/preview"; + break; + + case Orthanc::PixelFormat_Grayscale16: + uri += "/image-uint16"; + break; + + case Orthanc::PixelFormat_SignedGrayscale16: + uri += "/image-int16"; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - + orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSliceImage(index, slice, SliceImageQuality_Full)); } - - + + void OrthancSlicesLoader::ScheduleSliceImageJpeg(const Slice& slice, size_t index, SliceImageQuality quality) { unsigned int value; - + switch (quality) { - case SliceImageQuality_Jpeg50: - value = 50; - break; - - case SliceImageQuality_Jpeg90: - value = 90; - break; - - case SliceImageQuality_Jpeg95: - value = 95; - break; + case SliceImageQuality_Jpeg50: + value = 50; + break; - default: - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + case SliceImageQuality_Jpeg90: + value = 90; + break; + + case SliceImageQuality_Jpeg95: + value = 95; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } // This requires the official Web viewer plugin to be installed! - std::string uri = ("/web-viewer/instances/jpeg" + - boost::lexical_cast(value) + - "-" + slice.GetOrthancInstanceId() + "_" + + std::string uri = ("/web-viewer/instances/jpeg" + + boost::lexical_cast(value) + + "-" + slice.GetOrthancInstanceId() + "_" + boost::lexical_cast(slice.GetFrame())); - + orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSliceImage(index, slice, quality)); } - - - + + + void OrthancSlicesLoader::ScheduleLoadSliceImage(size_t index, SliceImageQuality quality) { @@ -890,9 +921,9 @@ { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); } - + const Slice& slice = GetSlice(index); - + if (slice.HasOrthancDecoding()) { if (quality == SliceImageQuality_Full) @@ -906,7 +937,7 @@ } else { - std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + + std::string uri = ("/instances/" + slice.GetOrthancInstanceId() + "/frames/" + boost::lexical_cast(slice.GetFrame()) + "/raw.gz"); orthanc_.ScheduleGetRequest(*webCallback_, uri, Operation::DownloadSliceRawImage(index, slice)); diff -r 192e6e349e69 -r 40b21c1f8b8d Framework/Toolbox/OrthancSlicesLoader.h --- a/Framework/Toolbox/OrthancSlicesLoader.h Mon Jul 02 18:13:46 2018 +0200 +++ b/Framework/Toolbox/OrthancSlicesLoader.h Tue Jul 03 10:26:56 2018 +0200 @@ -32,24 +32,70 @@ class OrthancSlicesLoader : public boost::noncopyable { public: - class ICallback : public boost::noncopyable + struct SliceImageReadyMessage : public IMessage + { + unsigned int sliceIndex_; + const Slice& slice_; + std::auto_ptr& image_; + SliceImageQuality effectiveQuality_; + + SliceImageReadyMessage(unsigned int sliceIndex, + const Slice& slice, + std::auto_ptr& image, + SliceImageQuality effectiveQuality) + : IMessage(MessageType_SliceImageReady), + sliceIndex_(sliceIndex), + slice_(slice), + image_(image), + effectiveQuality_(effectiveQuality) + { + } + }; + + struct SliceImageErrorMessage : public IMessage + { + const Slice& slice_; + unsigned int sliceIndex_; + SliceImageQuality effectiveQuality_; + + SliceImageErrorMessage(unsigned int sliceIndex, + const Slice& slice, + SliceImageQuality effectiveQuality) + : IMessage(MessageType_SliceImageError), + slice_(slice), + sliceIndex_(sliceIndex), + effectiveQuality_(effectiveQuality) + { + } + }; + + public: + class ISliceLoaderObserver : public IObserver { public: - virtual ~ICallback() + + ISliceLoaderObserver(MessageBroker& broker) + : IObserver(broker) { } - virtual void NotifyGeometryReady(const OrthancSlicesLoader& loader) = 0; + virtual ~ISliceLoaderObserver() + { + } + + virtual void HandleMessage(IObservable& from, const IMessage& message); - virtual void NotifyGeometryError(const OrthancSlicesLoader& loader) = 0; + virtual void OnSliceGeometryReady(const OrthancSlicesLoader& loader) = 0; - virtual void NotifySliceImageReady(const OrthancSlicesLoader& loader, + virtual void OnSliceGeometryError(const OrthancSlicesLoader& loader) = 0; + + virtual void OnSliceImageReady(const OrthancSlicesLoader& loader, unsigned int sliceIndex, const Slice& slice, std::auto_ptr& image, SliceImageQuality effectiveQuality) = 0; - virtual void NotifySliceImageError(const OrthancSlicesLoader& loader, + virtual void OnSliceImageError(const OrthancSlicesLoader& loader, unsigned int sliceIndex, const Slice& slice, SliceImageQuality quality) = 0; @@ -78,7 +124,7 @@ boost::shared_ptr webCallback_; // This is a PImpl pattern - ICallback& userCallback_; + ISliceLoaderObserver& userCallback_; IWebService& orthanc_; State state_; SlicesSorter slices_; @@ -123,7 +169,7 @@ public: OrthancSlicesLoader(MessageBroker& broker, - ICallback& callback, + ISliceLoaderObserver& callback, IWebService& orthanc); void ScheduleLoadSeries(const std::string& seriesId); diff -r 192e6e349e69 -r 40b21c1f8b8d Framework/Widgets/LayerWidget.cpp --- a/Framework/Widgets/LayerWidget.cpp Mon Jul 02 18:13:46 2018 +0200 +++ b/Framework/Widgets/LayerWidget.cpp Tue Jul 03 10:26:56 2018 +0200 @@ -493,15 +493,15 @@ OnContentChanged(dynamic_cast(from)); break; case MessageType_SliceChanged: - OnSliceChanged(dynamic_cast(from), dynamic_cast(message).slice); + OnSliceChanged(dynamic_cast(from), dynamic_cast(message).slice_); break; case MessageType_LayerReady: { const ILayerSource::LayerReadyMessage& layerReadyMessage = dynamic_cast(message); - OnLayerReady(layerReadyMessage.layer, + OnLayerReady(layerReadyMessage.layer_, dynamic_cast(from), - layerReadyMessage.slice, - layerReadyMessage.isError); + layerReadyMessage.slice_, + layerReadyMessage.isError_); }; break; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); diff -r 192e6e349e69 -r 40b21c1f8b8d Framework/dev.h --- a/Framework/dev.h Mon Jul 02 18:13:46 2018 +0200 +++ b/Framework/dev.h Tue Jul 03 10:26:56 2018 +0200 @@ -43,7 +43,7 @@ // TODO: Handle errors while loading class OrthancVolumeImage : public SlicedVolumeBase, - private OrthancSlicesLoader::ICallback + private OrthancSlicesLoader::ISliceLoaderObserver { private: OrthancSlicesLoader loader_; @@ -106,7 +106,7 @@ } - virtual void NotifyGeometryReady(const OrthancSlicesLoader& loader) + virtual void OnSliceGeometryReady(const OrthancSlicesLoader& loader) { if (loader.GetSliceCount() == 0) { @@ -173,13 +173,13 @@ SlicedVolumeBase::NotifyGeometryReady(); } - virtual void NotifyGeometryError(const OrthancSlicesLoader& loader) + virtual void OnSliceGeometryError(const OrthancSlicesLoader& loader) { LOG(ERROR) << "Unable to download a volume image"; SlicedVolumeBase::NotifyGeometryError(); } - virtual void NotifySliceImageReady(const OrthancSlicesLoader& loader, + virtual void OnSliceImageReady(const OrthancSlicesLoader& loader, unsigned int sliceIndex, const Slice& slice, std::auto_ptr& image, @@ -205,7 +205,7 @@ ScheduleSliceDownload(); } - virtual void NotifySliceImageError(const OrthancSlicesLoader& loader, + virtual void OnSliceImageError(const OrthancSlicesLoader& loader, unsigned int sliceIndex, const Slice& slice, SliceImageQuality quality) @@ -218,6 +218,7 @@ OrthancVolumeImage(MessageBroker& broker, IWebService& orthanc, bool computeRange) : + OrthancSlicesLoader::ISliceLoaderObserver(broker), loader_(broker, *this, orthanc), computeRange_(computeRange), pendingSlices_(0)