Mercurial > hg > orthanc-stone
comparison Applications/StoneWebViewer/WebAssembly/StoneWebViewer.cpp @ 1668:ab1bc8de1798
refactoring
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Thu, 19 Nov 2020 18:39:31 +0100 |
parents | 9584df157a9e |
children | d82a141e08d7 |
comparison
equal
deleted
inserted
replaced
1667:9584df157a9e | 1668:ab1bc8de1798 |
---|---|
110 ThumbnailType_NoPreview, | 110 ThumbnailType_NoPreview, |
111 ThumbnailType_Pdf, | 111 ThumbnailType_Pdf, |
112 ThumbnailType_Video, | 112 ThumbnailType_Video, |
113 ThumbnailType_Loading, | 113 ThumbnailType_Loading, |
114 ThumbnailType_Unknown | 114 ThumbnailType_Unknown |
115 }; | 115 }; |
116 | 116 |
117 | 117 |
118 enum STONE_WEB_VIEWER_EXPORT DisplayedFrameQuality | 118 enum STONE_WEB_VIEWER_EXPORT DisplayedFrameQuality |
119 { | 119 { |
120 DisplayedFrameQuality_None, | 120 DisplayedFrameQuality_None, |
1080 LOG(INFO) << "No default windowing"; | 1080 LOG(INFO) << "No default windowing"; |
1081 GetViewport().ResetDefaultWindowing(); | 1081 GetViewport().ResetDefaultWindowing(); |
1082 } | 1082 } |
1083 } | 1083 } |
1084 | 1084 |
1085 GetViewport().DisplayCurrentFrame(); | 1085 GetViewport().Redraw(); |
1086 } | 1086 } |
1087 }; | 1087 }; |
1088 | 1088 |
1089 | 1089 |
1090 class SetLowQualityFrame : public ICommand | 1090 class SetLowQualityFrame : public ICommand |
1373 size_t cursorIndex = cursor_->GetCurrentIndex(); | 1373 size_t cursorIndex = cursor_->GetCurrentIndex(); |
1374 | 1374 |
1375 if (frames_->GetInstanceOfFrame(cursorIndex).GetSopInstanceUid() == sopInstanceUid && | 1375 if (frames_->GetInstanceOfFrame(cursorIndex).GetSopInstanceUid() == sopInstanceUid && |
1376 frames_->GetFrameNumberInInstance(cursorIndex) == frameNumber) | 1376 frames_->GetFrameNumberInInstance(cursorIndex) == frameNumber) |
1377 { | 1377 { |
1378 DisplayCurrentFrame(); | 1378 Redraw(); |
1379 } | |
1380 } | |
1381 } | |
1382 | |
1383 | |
1384 void DisplayCurrentFrame() | |
1385 { | |
1386 DisplayedFrameQuality quality = DisplayedFrameQuality_None; | |
1387 | |
1388 if (cursor_.get() != NULL && | |
1389 frames_.get() != NULL) | |
1390 { | |
1391 const size_t cursorIndex = cursor_->GetCurrentIndex(); | |
1392 | |
1393 unsigned int cachedQuality; | |
1394 if (!DisplayFrame(cachedQuality, cursorIndex)) | |
1395 { | |
1396 // This frame is not cached yet: Load it | |
1397 if (source_.HasDicomWebRendered()) | |
1398 { | |
1399 ScheduleLoadRenderedFrame(cursorIndex, PRIORITY_HIGH, false /* not a prefetch */); | |
1400 } | |
1401 else | |
1402 { | |
1403 ScheduleLoadFullDicomFrame(cursorIndex, PRIORITY_HIGH, false /* not a prefetch */); | |
1404 } | |
1405 } | |
1406 else if (cachedQuality < QUALITY_FULL) | |
1407 { | |
1408 // This frame is only available in low-res: Download the full DICOM | |
1409 ScheduleLoadFullDicomFrame(cursorIndex, PRIORITY_HIGH, false /* not a prefetch */); | |
1410 quality = DisplayedFrameQuality_Low; | |
1411 } | |
1412 else | |
1413 { | |
1414 quality = DisplayedFrameQuality_High; | |
1415 } | |
1416 | |
1417 { | |
1418 // Prepare prefetching | |
1419 prefetchQueue_.clear(); | |
1420 for (size_t i = 0; i < cursor_->GetPrefetchSize() && i < 16; i++) | |
1421 { | |
1422 size_t a = cursor_->GetPrefetchIndex(i); | |
1423 if (a != cursorIndex) | |
1424 { | |
1425 prefetchQueue_.push_back(PrefetchItem(a, i < 2)); | |
1426 } | |
1427 } | |
1428 | |
1429 ScheduleNextPrefetch(); | |
1430 } | |
1431 | |
1432 if (observer_.get() != NULL) | |
1433 { | |
1434 observer_->SignalFrameUpdated(*this, cursor_->GetCurrentIndex(), | |
1435 frames_->GetFramesCount(), quality); | |
1436 } | 1379 } |
1437 } | 1380 } |
1438 } | 1381 } |
1439 | 1382 |
1440 | 1383 |
1467 layer.GetWindowing(windowingCenter_, windowingWidth_); | 1410 layer.GetWindowing(windowingCenter_, windowingWidth_); |
1468 } | 1411 } |
1469 } | 1412 } |
1470 | 1413 |
1471 | 1414 |
1472 bool DisplayFrame(unsigned int& quality, | 1415 void RenderCurrentScene(const Orthanc::ImageAccessor& frame, |
1473 size_t cursorIndex) | 1416 const OrthancStone::DicomInstanceParameters& instance, |
1474 { | 1417 const OrthancStone::CoordinateSystem3D& plane) |
1475 if (frames_.get() == NULL) | 1418 { |
1476 { | 1419 SaveCurrentWindowing(); |
1477 return false; | 1420 |
1478 } | 1421 bool isMonochrome1 = (instance.GetImageInformation().GetPhotometricInterpretation() == |
1479 | 1422 Orthanc::PhotometricInterpretation_Monochrome1); |
1423 | |
1424 std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer; | |
1425 | |
1426 switch (frame.GetFormat()) | |
1427 { | |
1428 case Orthanc::PixelFormat_RGB24: | |
1429 layer.reset(new OrthancStone::ColorTextureSceneLayer(frame)); | |
1430 break; | |
1431 | |
1432 case Orthanc::PixelFormat_Float32: | |
1433 { | |
1434 std::unique_ptr<OrthancStone::FloatTextureSceneLayer> tmp( | |
1435 new OrthancStone::FloatTextureSceneLayer(frame)); | |
1436 tmp->SetCustomWindowing(windowingCenter_, windowingWidth_); | |
1437 tmp->SetInverted(inverted_ ^ isMonochrome1); | |
1438 layer.reset(tmp.release()); | |
1439 break; | |
1440 } | |
1441 | |
1442 default: | |
1443 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
1444 } | |
1445 | |
1446 assert(layer.get() != NULL); | |
1447 | |
1448 layer->SetLinearInterpolation(true); | |
1449 layer->SetFlipX(flipX_); | |
1450 layer->SetFlipY(flipY_); | |
1451 | |
1452 double pixelSpacingX, pixelSpacingY; | |
1453 OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX, pixelSpacingY, instance.GetTags()); | |
1454 layer->SetPixelSpacing(pixelSpacingX, pixelSpacingY); | |
1455 | |
1456 std::unique_ptr<OrthancStone::MacroSceneLayer> annotationsLayer; | |
1457 | |
1458 if (annotations_) | |
1459 { | |
1460 std::set<size_t> a; | |
1461 annotations_->LookupSopInstanceUid(a, instance.GetSopInstanceUid()); | |
1462 if (plane.IsValid() && | |
1463 !a.empty()) | |
1464 { | |
1465 annotationsLayer.reset(new OrthancStone::MacroSceneLayer); | |
1466 annotationsLayer->Reserve(a.size()); | |
1467 | |
1468 OrthancStone::OsiriXLayerFactory factory; | |
1469 factory.SetColor(0, 255, 0); | |
1470 | |
1471 for (std::set<size_t>::const_iterator it = a.begin(); it != a.end(); ++it) | |
1472 { | |
1473 const OrthancStone::OsiriX::Annotation& annotation = annotations_->GetAnnotation(*it); | |
1474 annotationsLayer->AddLayer(factory.Create(annotation, plane)); | |
1475 } | |
1476 } | |
1477 } | |
1478 | |
1479 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); | |
1480 | |
1481 OrthancStone::Scene2D& scene = lock->GetController().GetScene(); | |
1482 | |
1483 scene.SetLayer(LAYER_TEXTURE, layer.release()); | |
1484 | |
1485 if (annotationsLayer.get() != NULL) | |
1486 { | |
1487 scene.SetLayer(LAYER_ANNOTATIONS, annotationsLayer.release()); | |
1488 } | |
1489 else | |
1490 { | |
1491 scene.DeleteLayer(LAYER_ANNOTATIONS); | |
1492 } | |
1493 | |
1494 if (fitNextContent_) | |
1495 { | |
1496 lock->RefreshCanvasSize(); | |
1497 lock->GetCompositor().FitContent(scene); | |
1498 fitNextContent_ = false; | |
1499 } | |
1500 | |
1501 //lock->GetCompositor().Refresh(scene); | |
1502 lock->Invalidate(); | |
1503 } | |
1504 | |
1505 | |
1506 bool RenderCurrentSceneUsingCache(unsigned int& quality) | |
1507 { | |
1508 assert(cursor_.get() != NULL); | |
1509 assert(frames_.get() != NULL); | |
1510 | |
1511 const size_t cursorIndex = cursor_->GetCurrentIndex(); | |
1480 const OrthancStone::DicomInstanceParameters& instance = frames_->GetInstanceOfFrame(cursorIndex); | 1512 const OrthancStone::DicomInstanceParameters& instance = frames_->GetInstanceOfFrame(cursorIndex); |
1481 const std::string sopInstanceUid = instance.GetSopInstanceUid(); | 1513 const OrthancStone::CoordinateSystem3D plane = frames_->GetFrameGeometry(cursorIndex); |
1482 size_t frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); | 1514 |
1483 | 1515 const size_t frameNumber = frames_->GetFrameNumberInInstance(cursorIndex); |
1484 FramesCache::Accessor accessor(*cache_, sopInstanceUid, frameNumber); | 1516 |
1517 FramesCache::Accessor accessor(*cache_, instance.GetSopInstanceUid(), frameNumber); | |
1485 if (accessor.IsValid()) | 1518 if (accessor.IsValid()) |
1486 { | 1519 { |
1487 SaveCurrentWindowing(); | |
1488 | |
1489 quality = accessor.GetQuality(); | 1520 quality = accessor.GetQuality(); |
1490 | 1521 RenderCurrentScene(accessor.GetImage(), instance, plane); |
1491 bool isMonochrome1 = (instance.GetImageInformation().GetPhotometricInterpretation() == | 1522 return true; |
1492 Orthanc::PhotometricInterpretation_Monochrome1); | |
1493 | |
1494 std::unique_ptr<OrthancStone::TextureBaseSceneLayer> layer; | |
1495 | |
1496 switch (accessor.GetImage().GetFormat()) | |
1497 { | |
1498 case Orthanc::PixelFormat_RGB24: | |
1499 layer.reset(new OrthancStone::ColorTextureSceneLayer(accessor.GetImage())); | |
1500 break; | |
1501 | |
1502 case Orthanc::PixelFormat_Float32: | |
1503 { | |
1504 std::unique_ptr<OrthancStone::FloatTextureSceneLayer> tmp( | |
1505 new OrthancStone::FloatTextureSceneLayer(accessor.GetImage())); | |
1506 tmp->SetCustomWindowing(windowingCenter_, windowingWidth_); | |
1507 tmp->SetInverted(inverted_ ^ isMonochrome1); | |
1508 layer.reset(tmp.release()); | |
1509 break; | |
1510 } | |
1511 | |
1512 default: | |
1513 throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); | |
1514 } | |
1515 | |
1516 layer->SetLinearInterpolation(true); | |
1517 layer->SetFlipX(flipX_); | |
1518 layer->SetFlipY(flipY_); | |
1519 | |
1520 double pixelSpacingX, pixelSpacingY; | |
1521 OrthancStone::GeometryToolbox::GetPixelSpacing(pixelSpacingX, pixelSpacingY, instance.GetTags()); | |
1522 layer->SetPixelSpacing(pixelSpacingX, pixelSpacingY); | |
1523 | |
1524 std::unique_ptr<OrthancStone::MacroSceneLayer> annotationsLayer; | |
1525 | |
1526 if (annotations_ && | |
1527 cursor_.get() != NULL) | |
1528 { | |
1529 const OrthancStone::CoordinateSystem3D plane = frames_->GetFrameGeometry(cursor_->GetCurrentIndex()); | |
1530 | |
1531 std::set<size_t> a; | |
1532 annotations_->LookupSopInstanceUid(a, sopInstanceUid); | |
1533 if (plane.IsValid() && | |
1534 !a.empty()) | |
1535 { | |
1536 annotationsLayer.reset(new OrthancStone::MacroSceneLayer); | |
1537 annotationsLayer->Reserve(a.size()); | |
1538 | |
1539 OrthancStone::OsiriXLayerFactory factory; | |
1540 factory.SetColor(0, 255, 0); | |
1541 | |
1542 for (std::set<size_t>::const_iterator it = a.begin(); it != a.end(); ++it) | |
1543 { | |
1544 const OrthancStone::OsiriX::Annotation& annotation = annotations_->GetAnnotation(*it); | |
1545 annotationsLayer->AddLayer(factory.Create(annotation, plane)); | |
1546 } | |
1547 } | |
1548 } | |
1549 | |
1550 | |
1551 if (layer.get() == NULL) | |
1552 { | |
1553 return false; | |
1554 } | |
1555 else | |
1556 { | |
1557 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); | |
1558 | |
1559 OrthancStone::Scene2D& scene = lock->GetController().GetScene(); | |
1560 | |
1561 scene.SetLayer(LAYER_TEXTURE, layer.release()); | |
1562 | |
1563 if (annotationsLayer.get() != NULL) | |
1564 { | |
1565 scene.SetLayer(LAYER_ANNOTATIONS, annotationsLayer.release()); | |
1566 } | |
1567 else | |
1568 { | |
1569 scene.DeleteLayer(LAYER_ANNOTATIONS); | |
1570 } | |
1571 | |
1572 if (fitNextContent_) | |
1573 { | |
1574 lock->RefreshCanvasSize(); | |
1575 lock->GetCompositor().FitContent(scene); | |
1576 fitNextContent_ = false; | |
1577 } | |
1578 | |
1579 //lock->GetCompositor().Refresh(scene); | |
1580 lock->Invalidate(); | |
1581 return true; | |
1582 } | |
1583 } | 1523 } |
1584 else | 1524 else |
1585 { | 1525 { |
1586 return false; | 1526 return false; |
1587 } | 1527 } |
1860 | 1800 |
1861 ApplyScheduledFocus(); | 1801 ApplyScheduledFocus(); |
1862 } | 1802 } |
1863 | 1803 |
1864 | 1804 |
1865 void Redraw() | |
1866 { | |
1867 if (cursor_.get() != NULL) | |
1868 { | |
1869 unsigned int quality; | |
1870 DisplayFrame(quality, cursor_->GetCurrentIndex()); | |
1871 } | |
1872 } | |
1873 | |
1874 | |
1875 // This method is used when the layout of the HTML page changes, | 1805 // This method is used when the layout of the HTML page changes, |
1876 // which does not trigger the "emscripten_set_resize_callback()" | 1806 // which does not trigger the "emscripten_set_resize_callback()" |
1877 void UpdateSize(bool fitContent) | 1807 void UpdateSize(bool fitContent) |
1878 { | 1808 { |
1879 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); | 1809 std::unique_ptr<OrthancStone::IViewport::ILock> lock(viewport_->Lock()); |
1895 const std::string& GetCanvasId() const | 1825 const std::string& GetCanvasId() const |
1896 { | 1826 { |
1897 assert(viewport_); | 1827 assert(viewport_); |
1898 return viewport_->GetCanvasId(); | 1828 return viewport_->GetCanvasId(); |
1899 } | 1829 } |
1830 | |
1831 | |
1832 void Redraw() | |
1833 { | |
1834 DisplayedFrameQuality quality = DisplayedFrameQuality_None; | |
1835 | |
1836 if (cursor_.get() != NULL && | |
1837 frames_.get() != NULL) | |
1838 { | |
1839 const size_t cursorIndex = cursor_->GetCurrentIndex(); | |
1840 | |
1841 unsigned int cachedQuality; | |
1842 if (!RenderCurrentSceneUsingCache(cachedQuality)) | |
1843 { | |
1844 // This frame is not cached yet: Load it | |
1845 if (source_.HasDicomWebRendered()) | |
1846 { | |
1847 ScheduleLoadRenderedFrame(cursorIndex, PRIORITY_HIGH, false /* not a prefetch */); | |
1848 } | |
1849 else | |
1850 { | |
1851 ScheduleLoadFullDicomFrame(cursorIndex, PRIORITY_HIGH, false /* not a prefetch */); | |
1852 } | |
1853 } | |
1854 else if (cachedQuality < QUALITY_FULL) | |
1855 { | |
1856 // This frame is only available in low-res: Download the full DICOM | |
1857 ScheduleLoadFullDicomFrame(cursorIndex, PRIORITY_HIGH, false /* not a prefetch */); | |
1858 quality = DisplayedFrameQuality_Low; | |
1859 } | |
1860 else | |
1861 { | |
1862 quality = DisplayedFrameQuality_High; | |
1863 } | |
1864 | |
1865 { | |
1866 // Prepare prefetching | |
1867 prefetchQueue_.clear(); | |
1868 for (size_t i = 0; i < cursor_->GetPrefetchSize() && i < 16; i++) | |
1869 { | |
1870 size_t a = cursor_->GetPrefetchIndex(i); | |
1871 if (a != cursorIndex) | |
1872 { | |
1873 prefetchQueue_.push_back(PrefetchItem(a, i < 2)); | |
1874 } | |
1875 } | |
1876 | |
1877 ScheduleNextPrefetch(); | |
1878 } | |
1879 | |
1880 if (observer_.get() != NULL) | |
1881 { | |
1882 observer_->SignalFrameUpdated(*this, cursor_->GetCurrentIndex(), | |
1883 frames_->GetFramesCount(), quality); | |
1884 } | |
1885 } | |
1886 } | |
1887 | |
1900 | 1888 |
1901 void ChangeFrame(SeriesCursor::Action action) | 1889 void ChangeFrame(SeriesCursor::Action action) |
1902 { | 1890 { |
1903 if (cursor_.get() != NULL) | 1891 if (cursor_.get() != NULL) |
1904 { | 1892 { |
1907 cursor_->Apply(action); | 1895 cursor_->Apply(action); |
1908 | 1896 |
1909 size_t current = cursor_->GetCurrentIndex(); | 1897 size_t current = cursor_->GetCurrentIndex(); |
1910 if (previous != current) | 1898 if (previous != current) |
1911 { | 1899 { |
1912 DisplayCurrentFrame(); | 1900 Redraw(); |
1913 } | 1901 } |
1914 } | 1902 } |
1915 } | 1903 } |
1916 | 1904 |
1917 bool GetCurrentFrameOfReferenceUid(std::string& frameOfReferenceUid) const | 1905 bool GetCurrentFrameOfReferenceUid(std::string& frameOfReferenceUid) const |
2177 size_t current = cursor_->GetCurrentIndex(); | 2165 size_t current = cursor_->GetCurrentIndex(); |
2178 | 2166 |
2179 if (current != cursorIndex) | 2167 if (current != cursorIndex) |
2180 { | 2168 { |
2181 cursor_->SetCurrentIndex(cursorIndex); | 2169 cursor_->SetCurrentIndex(cursorIndex); |
2182 DisplayCurrentFrame(); | 2170 Redraw(); |
2183 } | 2171 } |
2184 | 2172 |
2185 hasFocusOnInstance_ = false; | 2173 hasFocusOnInstance_ = false; |
2186 } | 2174 } |
2187 } | 2175 } |
2195 if (cursor_.get() != NULL && | 2183 if (cursor_.get() != NULL && |
2196 frames_.get() != NULL && | 2184 frames_.get() != NULL && |
2197 frames_->FindClosestFrame(cursorIndex, p, MAX_DISTANCE)) | 2185 frames_->FindClosestFrame(cursorIndex, p, MAX_DISTANCE)) |
2198 { | 2186 { |
2199 cursor_->SetCurrentIndex(cursorIndex); | 2187 cursor_->SetCurrentIndex(cursorIndex); |
2200 DisplayCurrentFrame(); | 2188 Redraw(); |
2201 } | 2189 } |
2202 } | 2190 } |
2203 }; | 2191 }; |
2204 | 2192 |
2205 | 2193 |