# HG changeset patch # User Sebastien Jodogne # Date 1558616272 -7200 # Node ID f6438fdc447e1a232493a4a3d9a00a85cbe59225 # Parent f36a6f7e7bdf35e0ff69c1f26458ddf1542da4c5 cont diff -r f36a6f7e7bdf -r f6438fdc447e Framework/Oracle/IOracle.h --- a/Framework/Oracle/IOracle.h Thu May 23 12:54:10 2019 +0200 +++ b/Framework/Oracle/IOracle.h Thu May 23 14:57:52 2019 +0200 @@ -32,6 +32,10 @@ { } + virtual void Start() = 0; + + virtual void Stop() = 0; + virtual void Schedule(const IObserver& receiver, IOracleCommand* command) = 0; // Takes ownership }; diff -r f36a6f7e7bdf -r f6438fdc447e Framework/Oracle/ThreadedOracle.cpp --- a/Framework/Oracle/ThreadedOracle.cpp Thu May 23 12:54:10 2019 +0200 +++ b/Framework/Oracle/ThreadedOracle.cpp Thu May 23 14:57:52 2019 +0200 @@ -204,6 +204,9 @@ Orthanc::GzipCompressor compressor; compressor.Uncompress(answer, compressed.c_str(), compressed.size()); + + LOG(INFO) << "Uncompressing gzip Encoding: from " << compressed.size() + << " to " << answer.size() << " bytes"; } } @@ -424,6 +427,29 @@ } + ThreadedOracle::~ThreadedOracle() + { + if (state_ == State_Running) + { + LOG(ERROR) << "The threaded oracle is still running, explicit call to " + << "Stop() is mandatory to avoid crashes"; + } + + try + { + StopInternal(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "Exception while stopping the threaded oracle: " << e.What(); + } + catch (...) + { + LOG(ERROR) << "Native exception while stopping the threaded oracle"; + } + } + + void ThreadedOracle::SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc) { boost::mutex::scoped_lock lock(mutex_); diff -r f36a6f7e7bdf -r f6438fdc447e Framework/Oracle/ThreadedOracle.h --- a/Framework/Oracle/ThreadedOracle.h Thu May 23 12:54:10 2019 +0200 +++ b/Framework/Oracle/ThreadedOracle.h Thu May 23 14:57:52 2019 +0200 @@ -72,10 +72,7 @@ public: ThreadedOracle(IMessageEmitter& emitter); - virtual ~ThreadedOracle() - { - StopInternal(); - } + virtual ~ThreadedOracle(); void SetOrthancParameters(const Orthanc::WebServiceParameters& orthanc); @@ -83,9 +80,9 @@ void SetSleepingTimeResolution(unsigned int milliseconds); - void Start(); + virtual void Start(); - void Stop() + virtual void Stop() { StopInternal(); } diff -r f36a6f7e7bdf -r f6438fdc447e Framework/Volumes/ImageBuffer3D.h --- a/Framework/Volumes/ImageBuffer3D.h Thu May 23 12:54:10 2019 +0200 +++ b/Framework/Volumes/ImageBuffer3D.h Thu May 23 14:57:52 2019 +0200 @@ -99,6 +99,11 @@ return format_; } + unsigned int GetBytesPerPixel() const + { + return Orthanc::GetBytesPerPixel(format_); + } + uint64_t GetEstimatedMemorySize() const; bool GetRange(float& minValue, diff -r f36a6f7e7bdf -r f6438fdc447e Samples/Sdl/Loader.cpp --- a/Samples/Sdl/Loader.cpp Thu May 23 12:54:10 2019 +0200 +++ b/Samples/Sdl/Loader.cpp Thu May 23 14:57:52 2019 +0200 @@ -871,11 +871,27 @@ }; + class LoadUncompressedPixelData : public State + { + public: + LoadUncompressedPixelData(OrthancMultiframeVolumeLoader& that) : + State(that) + { + } + + virtual void Handle(const OrthancRestApiCommand::SuccessMessage& message) const + { + GetTarget().SetUncompressedPixelData(message.GetAnswer()); + } + }; + + IOracle& oracle_; bool active_; std::string instanceId_; std::string transferSyntaxUid_; + uint64_t revision_; std::auto_ptr dicom_; std::auto_ptr geometry_; @@ -903,9 +919,18 @@ return; } - if (transferSyntaxUid_ != "1.2.840.10008.1.2" && - transferSyntaxUid_ != "1.2.840.10008.1.2.1" && - transferSyntaxUid_ != "1.2.840.10008.1.2.2") + if (transferSyntaxUid_ == "1.2.840.10008.1.2" || + transferSyntaxUid_ == "1.2.840.10008.1.2.1" || + transferSyntaxUid_ == "1.2.840.10008.1.2.2") + { + std::auto_ptr command(new OrthancRestApiCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); + command->SetUri("/instances/" + instanceId_ + "/content/" + + Orthanc::DICOM_TAG_PIXEL_DATA.Format() + "/0"); + command->SetPayload(new LoadUncompressedPixelData(*this)); + oracle_.Schedule(*this, command.release()); + } + else { throw Orthanc::OrthancException( Orthanc::ErrorCode_NotImplemented, @@ -961,6 +986,78 @@ ScheduleFrameDownloads(); } + + ORTHANC_FORCE_INLINE + static void CopyPixel(uint32_t& target, + const void* source) + { + // TODO - check alignement? + target = le32toh(*reinterpret_cast(source)); + } + + + template + void CopyPixelData(const std::string& pixelData) + { + const Orthanc::PixelFormat format = image_->GetFormat(); + const unsigned int bpp = image_->GetBytesPerPixel(); + const unsigned int width = image_->GetWidth(); + const unsigned int height = image_->GetHeight(); + const unsigned int depth = image_->GetDepth(); + + if (pixelData.size() != bpp * width * height * depth) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, + "The pixel data has not the proper size"); + } + + if (pixelData.empty()) + { + return; + } + + const uint8_t* source = reinterpret_cast(pixelData.c_str()); + + for (unsigned int z = 0; z < depth; z++) + { + ImageBuffer3D::SliceWriter writer(*image_, VolumeProjection_Axial, z); + + assert (writer.GetAccessor().GetWidth() == width && + writer.GetAccessor().GetHeight() == height); + + for (unsigned int y = 0; y < height; y++) + { + assert(sizeof(T) == Orthanc::GetBytesPerPixel(format)); + + T* target = reinterpret_cast(writer.GetAccessor().GetRow(y)); + + for (unsigned int x = 0; x < width; x++) + { + CopyPixel(*target, source); + + target ++; + source += bpp; + } + } + } + } + + + void SetUncompressedPixelData(const std::string& pixelData) + { + switch (image_->GetFormat()) + { + case Orthanc::PixelFormat_Grayscale32: + CopyPixelData(pixelData); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + revision_ ++; + } + public: OrthancMultiframeVolumeLoader(IOracle& oracle, @@ -996,6 +1093,7 @@ { std::auto_ptr command(new OrthancRestApiCommand); + command->SetHttpHeader("Accept-Encoding", "gzip"); command->SetUri("/instances/" + instanceId + "/tags"); command->SetPayload(new LoadGeometry(*this)); oracle_.Schedule(*this, command.release()); @@ -1232,6 +1330,13 @@ } } + /** + * The sleep() leads to a crash if the oracle is still running, + * while this object is destroyed. Always stop the oracle before + * destroying active objects. (*) + **/ + // boost::this_thread::sleep(boost::posix_time::seconds(2)); + oracle_.Schedule(*this, new OrthancStone::SleepOracleCommand(message.GetOrigin().GetDelay())); } } @@ -1306,7 +1411,7 @@ void Run(OrthancStone::NativeApplicationContext& context, - OrthancStone::IOracle& oracle) + OrthancStone::ThreadedOracle& oracle) { boost::shared_ptr toto; boost::shared_ptr loader1, loader2; @@ -1411,9 +1516,23 @@ toto->SetVolume(0, new OrthancStone::OrthancSeriesVolumeProgressiveLoader::MPRSlicer(loader1)); - LOG(WARNING) << "...Waiting for Ctrl-C..."; - Orthanc::SystemToolbox::ServerBarrier(); - //boost::this_thread::sleep(boost::posix_time::seconds(1)); + { + oracle.Start(); + + LOG(WARNING) << "...Waiting for Ctrl-C..."; + Orthanc::SystemToolbox::ServerBarrier(); + + /** + * WARNING => The oracle must be stopped BEFORE the objects using + * it are destroyed!!! This forces to wait for the completion of + * the running callback methods. Otherwise, the callbacks methods + * might still be running while their parent object is destroyed, + * resulting in crashes. This is very visible if adding a sleep(), + * as in (*). + **/ + + oracle.Stop(); + } } @@ -1442,11 +1561,11 @@ oracle.SetOrthancParameters(p); } - oracle.Start(); + //oracle.Start(); Run(context, oracle); - - oracle.Stop(); + + //oracle.Stop(); } catch (Orthanc::OrthancException& e) {