Mercurial > hg > orthanc-stone
diff Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp @ 1672:570398585b5f
start support of cine sequences
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Mon, 23 Nov 2020 15:39:27 +0100 |
parents | 2c2512918a0f |
children | 0621e523b670 |
line wrap: on
line diff
--- a/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Fri Nov 20 10:14:36 2020 +0100 +++ b/Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp Mon Nov 23 15:39:27 2020 +0100 @@ -167,6 +167,9 @@ static const unsigned int QUALITY_JPEG = 0; static const unsigned int QUALITY_FULL = 1; +static const unsigned int DEFAULT_CINE_RATE = 30; + + class ResourcesLoader : public OrthancStone::ObserverBase<ResourcesLoader> { public: @@ -687,12 +690,13 @@ std::vector<size_t> prefetch_; int framesCount_; int currentFrame_; - bool isCircular_; + bool isCircularPrefetch_; int fastDelta_; Action lastAction_; int ComputeNextFrame(int currentFrame, - Action action) const + Action action, + bool isCircular) const { if (framesCount_ == 0) { @@ -727,7 +731,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } - if (isCircular_) + if (isCircular) { while (nextFrame < 0) { @@ -797,31 +801,31 @@ { case Action_None: case Action_Plus: - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus), Action_Plus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus), Action_Minus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus), Action_FastPlus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus), Action_FastMinus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus, isCircularPrefetch_), Action_Plus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus, isCircularPrefetch_), Action_Minus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus, isCircularPrefetch_), Action_FastPlus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus, isCircularPrefetch_), Action_FastMinus)); break; case Action_Minus: - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus), Action_Minus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus), Action_Plus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus), Action_FastMinus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus), Action_FastPlus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus, isCircularPrefetch_), Action_Minus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus, isCircularPrefetch_), Action_Plus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus, isCircularPrefetch_), Action_FastMinus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus, isCircularPrefetch_), Action_FastPlus)); break; case Action_FastPlus: - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus), Action_FastPlus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus), Action_FastMinus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus), Action_Plus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus), Action_Minus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus, isCircularPrefetch_), Action_FastPlus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus, isCircularPrefetch_), Action_FastMinus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus, isCircularPrefetch_), Action_Plus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus, isCircularPrefetch_), Action_Minus)); break; case Action_FastMinus: - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus), Action_FastMinus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus), Action_FastPlus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus), Action_Minus)); - queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus), Action_Plus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastMinus, isCircularPrefetch_), Action_FastMinus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_FastPlus, isCircularPrefetch_), Action_FastPlus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Minus, isCircularPrefetch_), Action_Minus)); + queue.push_back(std::make_pair(ComputeNextFrame(frame, Action_Plus, isCircularPrefetch_), Action_Plus)); break; default: @@ -843,16 +847,16 @@ explicit SeriesCursor(size_t framesCount) : framesCount_(framesCount), currentFrame_(framesCount / 2), // Start at the middle frame - isCircular_(false), + isCircularPrefetch_(false), lastAction_(Action_None) { SetFastDelta(framesCount / 20); UpdatePrefetch(); } - void SetCircular(bool isCircular) + void SetCircularPrefetch(bool isCircularPrefetch) { - isCircular_ = isCircular; + isCircularPrefetch_ = isCircularPrefetch; UpdatePrefetch(); } @@ -886,9 +890,10 @@ return static_cast<size_t>(currentFrame_); } - void Apply(Action action) + void Apply(Action action, + bool isCircular) { - currentFrame_ = ComputeNextFrame(currentFrame_, action); + currentFrame_ = ComputeNextFrame(currentFrame_, action, isCircular); lastAction_ = action; UpdatePrefetch(); } @@ -990,6 +995,8 @@ { } + virtual void SignalSeriesDetailsReady(const ViewerViewport& viewport) = 0; + virtual void SignalFrameUpdated(const ViewerViewport& viewport, size_t currentFrame, size_t countFrames, @@ -1045,10 +1052,10 @@ } }; - class SetDefaultWindowingCommand : public ICommand + class LoadSeriesDetailsFromInstance : public ICommand { public: - explicit SetDefaultWindowingCommand(boost::shared_ptr<ViewerViewport> viewport) : + explicit LoadSeriesDetailsFromInstance(boost::shared_ptr<ViewerViewport> viewport) : ICommand(viewport) { } @@ -1082,7 +1089,28 @@ } } + uint32_t cineRate; + if (dicom.ParseUnsignedInteger32(cineRate, Orthanc::DICOM_TAG_CINE_RATE) && + cineRate > 0) + { + /** + * If we detect a cine sequence, start on the first frame + * instead of on the middle frame. + **/ + GetViewport().cursor_->SetCurrentIndex(0); + GetViewport().cineRate_ = cineRate; + } + else + { + GetViewport().cineRate_ = DEFAULT_CINE_RATE; + } + GetViewport().Redraw(); + + if (GetViewport().observer_.get() != NULL) + { + GetViewport().observer_->SignalSeriesDetailsReady(GetViewport()); + } } }; @@ -1297,6 +1325,7 @@ float windowingWidth_; float defaultWindowingCenter_; float defaultWindowingWidth_; + unsigned int cineRate_; bool inverted_; bool flipX_; bool flipY_; @@ -1719,11 +1748,11 @@ { if (wheelEvent->deltaY < 0) { - that.ChangeFrame(that.isCtrlDown_ ? SeriesCursor::Action_FastMinus : SeriesCursor::Action_Minus); + that.ChangeFrame(that.isCtrlDown_ ? SeriesCursor::Action_FastMinus : SeriesCursor::Action_Minus, false /* not circular */); } else if (wheelEvent->deltaY > 0) { - that.ChangeFrame(that.isCtrlDown_ ? SeriesCursor::Action_FastPlus : SeriesCursor::Action_Plus); + that.ChangeFrame(that.isCtrlDown_ ? SeriesCursor::Action_FastPlus : SeriesCursor::Action_Plus, false /* not circular */); } } @@ -1788,7 +1817,8 @@ flipX_ = false; flipY_ = false; fitNextContent_ = true; - + cineRate_ = DEFAULT_CINE_RATE; + frames_.reset(frames); cursor_.reset(new SeriesCursor(frames_->GetFramesCount())); @@ -1821,14 +1851,14 @@ uid != OrthancStone::SopClassUid_RTStruct && GetSeriesThumbnailType(uid) != OrthancStone::SeriesThumbnailType_Video) { - // Fetch the default windowing for the central instance + // Fetch the details of the series from the central instance const std::string uri = ("studies/" + frames_->GetStudyInstanceUid() + "/series/" + frames_->GetSeriesInstanceUid() + "/instances/" + centralInstance.GetSopInstanceUid() + "/metadata"); loader_->ScheduleGetDicomWeb( boost::make_shared<OrthancStone::LoadedDicomResources>(Orthanc::DICOM_TAG_SOP_INSTANCE_UID), - 0, source_, uri, new SetDefaultWindowingCommand(GetSharedObserver())); + 0, source_, uri, new LoadSeriesDetailsFromInstance(GetSharedObserver())); } } @@ -1911,21 +1941,27 @@ } - void ChangeFrame(SeriesCursor::Action action) + // Returns "true" iff the frame has indeed changed + bool ChangeFrame(SeriesCursor::Action action, + bool isCircular) { if (cursor_.get() != NULL) { size_t previous = cursor_->GetCurrentIndex(); - cursor_->Apply(action); + cursor_->Apply(action, isCircular); size_t current = cursor_->GetCurrentIndex(); if (previous != current) { Redraw(); + return true; } } + + return false; } + bool GetCurrentFrameOfReferenceUid(std::string& frameOfReferenceUid) const { @@ -2213,6 +2249,11 @@ Redraw(); } } + + unsigned int GetCineRate() const + { + return cineRate_; + } }; @@ -2297,6 +2338,18 @@ } } + virtual void SignalSeriesDetailsReady(const ViewerViewport& viewport) ORTHANC_OVERRIDE + { + EM_ASM({ + const customEvent = document.createEvent("CustomEvent"); + customEvent.initCustomEvent("SeriesDetailsReady", false, false, + { "canvasId" : UTF8ToString($0) }); + window.dispatchEvent(customEvent); + }, + viewport.GetCanvasId().c_str() + ); + } + virtual void SignalFrameUpdated(const ViewerViewport& viewport, size_t currentFrame, size_t countFrames, @@ -2654,26 +2707,28 @@ EMSCRIPTEN_KEEPALIVE - void DecrementFrame(const char* canvas, - int fitContent) + int DecrementFrame(const char* canvas, + int isCircular) { try { - GetViewport(canvas)->ChangeFrame(SeriesCursor::Action_Minus); + return GetViewport(canvas)->ChangeFrame(SeriesCursor::Action_Minus, isCircular) ? 1 : 0; } EXTERN_CATCH_EXCEPTIONS; + return 0; } EMSCRIPTEN_KEEPALIVE - void IncrementFrame(const char* canvas, - int fitContent) + int IncrementFrame(const char* canvas, + int isCircular) { try { - GetViewport(canvas)->ChangeFrame(SeriesCursor::Action_Plus); + return GetViewport(canvas)->ChangeFrame(SeriesCursor::Action_Plus, isCircular) ? 1 : 0; } EXTERN_CATCH_EXCEPTIONS; + return 0; } @@ -2866,4 +2921,16 @@ } EXTERN_CATCH_EXCEPTIONS; } + + + EMSCRIPTEN_KEEPALIVE + unsigned int GetCineRate(const char* canvas) + { + try + { + return GetViewport(canvas)->GetCineRate(); + } + EXTERN_CATCH_EXCEPTIONS; + return 0; + } }