comparison Samples/Sdl/RtViewer/RtViewerSdl.cpp @ 1404:3e644f6fadd4

Three-viewport is now OK in SDL and Wasm
author Benjamin Golinvaux <bgo@osimis.io>
date Wed, 29 Apr 2020 22:06:24 +0200
parents 27e0a00bd3e8
children 5d7ee14dc1eb
comparison
equal deleted inserted replaced
1395:62dc0d737e7b 1404:3e644f6fadd4
16 * 16 *
17 * You should have received a copy of the GNU Affero General Public License 17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 **/ 19 **/
20 20
21 #include "RtViewer.h" 21 #include "RtViewerApp.h"
22 #include "RtViewerView.h"
22 #include "../SdlHelpers.h" 23 #include "../SdlHelpers.h"
23 24
24 // Stone of Orthanc includes 25 // Stone of Orthanc includes
25 #include <Framework/Loaders/GenericLoadersContext.h> 26 #include <Framework/Loaders/GenericLoadersContext.h>
26 #include <Framework/OpenGL/SdlOpenGLContext.h> 27 #include <Framework/OpenGL/SdlOpenGLContext.h>
59 } 60 }
60 } 61 }
61 62
62 namespace OrthancStone 63 namespace OrthancStone
63 { 64 {
64 void RtViewerApp::CreateViewport() 65 void RtViewerView::EnableGLDebugOutput()
66 {
67 glEnable(GL_DEBUG_OUTPUT);
68 glDebugMessageCallback(OpenGLMessageCallback, 0);
69 }
70
71 boost::shared_ptr<IViewport> RtViewerView::CreateViewport(const std::string& canvasId)
65 { 72 {
66 // False means we do NOT let Windows treat this as a legacy application that needs to be scaled 73 // False means we do NOT let Windows treat this as a legacy application that needs to be scaled
67 viewport_ = SdlOpenGLViewport::Create("CT RTDOSE RTSTRUCT viewer", 1024, 1024, false); 74 return SdlOpenGLViewport::Create(canvasId, 1024, 1024, false);
68 } 75 }
69 76
70 void RtViewerApp::ProcessOptions(int argc, char* argv[]) 77 void RtViewerApp::ProcessOptions(int argc, char* argv[])
71 { 78 {
72 namespace po = boost::program_options; 79 namespace po = boost::program_options;
110 } 117 }
111 118
112 void RtViewerApp::RunSdl(int argc, char* argv[]) 119 void RtViewerApp::RunSdl(int argc, char* argv[])
113 { 120 {
114 ProcessOptions(argc, argv); 121 ProcessOptions(argc, argv);
115
116 {
117 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
118 ViewportController& controller = lock->GetController();
119 Scene2D& scene = controller.GetScene();
120 ICompositor& compositor = lock->GetCompositor();
121
122 // False means we do NOT let a hi-DPI aware desktop managedr treat this as a legacy application that requires
123 // scaling.
124 controller.FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight());
125
126 glEnable(GL_DEBUG_OUTPUT);
127 glDebugMessageCallback(OpenGLMessageCallback, 0);
128
129 compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
130 FONT_SIZE_0, Orthanc::Encoding_Latin1);
131 compositor.SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
132 FONT_SIZE_1, Orthanc::Encoding_Latin1);
133 }
134 122
135 /** 123 /**
136 Create the shared loaders context 124 Create the shared loaders context
137 */ 125 */
138 loadersContext_.reset(new GenericLoadersContext(1, 4, 1)); 126 loadersContext_.reset(new GenericLoadersContext(1, 4, 1));
168 loadersContext->SetOrthancParameters(p); 156 loadersContext->SetOrthancParameters(p);
169 } 157 }
170 158
171 loadersContext->StartOracle(); 159 loadersContext->StartOracle();
172 160
161 CreateLoaders();
162
163 /**
164 Create viewports
165 */
166 CreateView("RtViewer Axial", VolumeProjection_Axial);
167 CreateView("RtViewer Coronal", VolumeProjection_Coronal);
168 CreateView("RtViewer Sagittal", VolumeProjection_Sagittal);
169
170 for (size_t i = 0; i < views_.size(); ++i)
171 {
172 views_[i]->PrepareViewport();
173 views_[i]->EnableGLDebugOutput();
174 }
175
176 DefaultViewportInteractor interactor;
177
173 /** 178 /**
174 It is very important that the Oracle (responsible for network I/O) be started before creating and firing the 179 It is very important that the Oracle (responsible for network I/O) be started before creating and firing the
175 loaders, for any command scheduled by the loader before the oracle is started will be lost. 180 loaders, for any command scheduled by the loader before the oracle is started will be lost.
176 */ 181 */
177 PrepareLoadersAndSlicers(); 182 StartLoaders();
178 183
179 DefaultViewportInteractor interactor; 184
180 185 std::vector<boost::shared_ptr<SdlViewport>> sdlViewports;
181 boost::shared_ptr<SdlViewport> viewport = boost::dynamic_pointer_cast<SdlViewport>(viewport_); 186 for(size_t i = 0; i < views_.size(); ++i)
182 187 {
183 OrthancStoneHelpers::SdlRunLoop(viewport, interactor); 188 boost::shared_ptr<RtViewerView> view = views_[i];
184 189 boost::shared_ptr<IViewport> viewport = view->GetViewport();
190 boost::shared_ptr<SdlViewport> sdlViewport =
191 boost::dynamic_pointer_cast<SdlViewport>(viewport);
192 sdlViewports.push_back(sdlViewport);
193 }
194 OrthancStoneHelpers::SdlRunLoop(sdlViewports, interactor);
185 loadersContext->StopOracle(); 195 loadersContext->StopOracle();
186 } 196 }
187 197
188 void RtViewerApp::TakeScreenshot(const std::string& target, 198 void RtViewerView::TakeScreenshot(const std::string& target,
189 unsigned int canvasWidth, 199 unsigned int canvasWidth,
190 unsigned int canvasHeight) 200 unsigned int canvasHeight)
191 { 201 {
192 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock()); 202 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
193 ViewportController& controller = lock->GetController(); 203 ViewportController& controller = lock->GetController();
204 Orthanc::ImageProcessing::Convert(png, canvas); 214 Orthanc::ImageProcessing::Convert(png, canvas);
205 215
206 Orthanc::PngWriter writer; 216 Orthanc::PngWriter writer;
207 writer.WriteToFile(target, png); 217 writer.WriteToFile(target, png);
208 } 218 }
209
210
211 #if 0
212 void RtViewerApp::HandleApplicationEvent(
213 const SDL_Event& event)
214 {
215 //DisplayInfoText();
216
217 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
218 ViewportController& controller = lock->GetController();
219 Scene2D& scene = controller.GetScene();
220 ICompositor& compositor = lock->GetCompositor();
221
222 if (event.type == SDL_MOUSEMOTION)
223 {
224 int scancodeCount = 0;
225 const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
226
227 if (activeTracker_.get() == NULL &&
228 SDL_SCANCODE_LALT < scancodeCount &&
229 keyboardState[SDL_SCANCODE_LALT])
230 {
231 // The "left-ctrl" key is down, while no tracker is present
232 // Let's display the info text
233 PointerEvent e;
234 e.AddPosition(compositor.GetPixelCenterCoordinates(
235 event.button.x, event.button.y));
236
237 DisplayFloatingCtrlInfoText(e);
238 }
239 else
240 {
241 HideInfoText();
242 //LOG(TRACE) << "(event.type == SDL_MOUSEMOTION)";
243 if (activeTracker_.get() != NULL)
244 {
245 //LOG(TRACE) << "(activeTracker_.get() != NULL)";
246 PointerEvent e;
247 e.AddPosition(compositor.GetPixelCenterCoordinates(
248 event.button.x, event.button.y));
249
250 //LOG(TRACE) << "event.button.x = " << event.button.x << " " <<
251 // "event.button.y = " << event.button.y;
252 LOG(TRACE) << "activeTracker_->PointerMove(e); " <<
253 e.GetMainPosition().GetX() << " " << e.GetMainPosition().GetY();
254
255 activeTracker_->PointerMove(e);
256 if (!activeTracker_->IsAlive())
257 activeTracker_.reset();
258 }
259 }
260 }
261 else if (event.type == SDL_MOUSEBUTTONUP)
262 {
263 if (activeTracker_)
264 {
265 PointerEvent e;
266 e.AddPosition(compositor.GetPixelCenterCoordinates(event.button.x, event.button.y));
267 activeTracker_->PointerUp(e);
268 if (!activeTracker_->IsAlive())
269 activeTracker_.reset();
270 }
271 }
272 else if (event.type == SDL_MOUSEBUTTONDOWN)
273 {
274 PointerEvent e;
275 e.AddPosition(compositor.GetPixelCenterCoordinates(
276 event.button.x, event.button.y));
277 if (activeTracker_)
278 {
279 activeTracker_->PointerDown(e);
280 if (!activeTracker_->IsAlive())
281 activeTracker_.reset();
282 }
283 else
284 {
285 // we ATTEMPT to create a tracker if need be
286 activeTracker_ = CreateSuitableTracker(event, e);
287 }
288 }
289 else if (event.type == SDL_KEYDOWN &&
290 event.key.repeat == 0 /* Ignore key bounce */)
291 {
292 switch (event.key.keysym.sym)
293 {
294 case SDLK_ESCAPE:
295 if (activeTracker_)
296 {
297 activeTracker_->Cancel();
298 if (!activeTracker_->IsAlive())
299 activeTracker_.reset();
300 }
301 break;
302
303 case SDLK_r:
304 UpdateLayers();
305 {
306 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
307 lock->Invalidate();
308 }
309 break;
310
311 case SDLK_s:
312 compositor.FitContent(scene);
313 break;
314
315 case SDLK_t:
316 if (!activeTracker_)
317 SelectNextTool();
318 else
319 {
320 LOG(WARNING) << "You cannot change the active tool when an interaction"
321 " is taking place";
322 }
323 break;
324
325 case SDLK_z:
326 LOG(TRACE) << "SDLK_z has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
327 if (event.key.keysym.mod & KMOD_CTRL)
328 {
329 if (controller.CanUndo())
330 {
331 LOG(TRACE) << "Undoing...";
332 controller.Undo();
333 }
334 else
335 {
336 LOG(WARNING) << "Nothing to undo!!!";
337 }
338 }
339 break;
340
341 case SDLK_y:
342 LOG(TRACE) << "SDLK_y has been pressed. event.key.keysym.mod == " << event.key.keysym.mod;
343 if (event.key.keysym.mod & KMOD_CTRL)
344 {
345 if (controller.CanRedo())
346 {
347 LOG(TRACE) << "Redoing...";
348 controller.Redo();
349 }
350 else
351 {
352 LOG(WARNING) << "Nothing to redo!!!";
353 }
354 }
355 break;
356
357 case SDLK_c:
358 TakeScreenshot(
359 "screenshot.png",
360 compositor.GetCanvasWidth(),
361 compositor.GetCanvasHeight());
362 break;
363
364 default:
365 break;
366 }
367 }
368 else if (viewport_->IsRefreshEvent(event))
369 {
370 // the viewport has been invalidated and requires repaint
371 viewport_->Paint();
372 }
373 }
374 #endif
375
376 #if 0
377 void RtViewerApp::RunSdl(int argc, char* argv[])
378 {
379 ProcessOptions(argc, argv);
380
381 {
382 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
383 ViewportController& controller = lock->GetController();
384 Scene2D& scene = controller.GetScene();
385 ICompositor& compositor = lock->GetCompositor();
386
387 // False means we do NOT let a hi-DPI aware desktop managedr treat this as a legacy application that requires
388 // scaling.
389 controller.FitContent(compositor.GetCanvasWidth(), compositor.GetCanvasHeight());
390
391 glEnable(GL_DEBUG_OUTPUT);
392 glDebugMessageCallback(OpenGLMessageCallback, 0);
393
394 compositor.SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
395 FONT_SIZE_0, Orthanc::Encoding_Latin1);
396 compositor.SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
397 FONT_SIZE_1, Orthanc::Encoding_Latin1);
398 }
399 //////// from loader
400
401 loadersContext_.reset(new GenericLoadersContext(1, 4, 1));
402 loadersContext_->StartOracle();
403
404 /**
405 It is very important that the Oracle (responsible for network I/O) be started before creating and firing the
406 loaders, for any command scheduled by the loader before the oracle is started will be lost.
407 */
408 PrepareLoadersAndSlicers();
409
410 bool stopApplication = false;
411
412 while (!stopApplication)
413 {
414 SDL_Event event;
415 while (!stopApplication && SDL_PollEvent(&event))
416 {
417 if (event.type == SDL_QUIT)
418 {
419 stopApplication = true;
420 break;
421 }
422 else if (event.type == SDL_WINDOWEVENT &&
423 event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
424 {
425 DisableTracker();
426 }
427 else if (event.type == SDL_KEYDOWN &&
428 event.key.repeat == 0 /* Ignore key bounce */)
429 {
430 switch (event.key.keysym.sym)
431 {
432 case SDLK_f:
433 // TODO: implement GetWindow to be able to do:
434 // viewport_->GetWindow().ToggleMaximize();
435 ORTHANC_ASSERT(false, "Please implement GetWindow()");
436 break;
437 case SDLK_q:
438 stopApplication = true;
439 break;
440 default:
441 break;
442 }
443 }
444 // the code above is rather application-neutral.
445 // the following call handles events specific to the application
446 HandleApplicationEvent(event);
447 }
448 SDL_Delay(1);
449 }
450 loadersContext_->StopOracle();
451 }
452 #endif
453
454 #if 0
455 boost::shared_ptr<IFlexiblePointerTracker> RtViewerApp::CreateSuitableTracker(
456 const SDL_Event& event,
457 const PointerEvent& e)
458 {
459 std::unique_ptr<IViewport::ILock> lock(viewport_->Lock());
460 ViewportController& controller = lock->GetController();
461 Scene2D& scene = controller.GetScene();
462 ICompositor& compositor = lock->GetCompositor();
463
464 using namespace Orthanc;
465
466 switch (event.button.button)
467 {
468 case SDL_BUTTON_MIDDLE:
469 return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker
470 (viewport_, e));
471
472 case SDL_BUTTON_RIGHT:
473 return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker
474 (viewport_, e, compositor.GetCanvasHeight()));
475
476 case SDL_BUTTON_LEFT:
477 {
478 //LOG(TRACE) << "CreateSuitableTracker: case SDL_BUTTON_LEFT:";
479 // TODO: we need to iterate on the set of measuring tool and perform
480 // a hit test to check if a tracker needs to be created for edition.
481 // Otherwise, depending upon the active tool, we might want to create
482 // a "measuring tool creation" tracker
483
484 // TODO: if there are conflicts, we should prefer a tracker that
485 // pertains to the type of measuring tool currently selected (TBD?)
486 boost::shared_ptr<IFlexiblePointerTracker> hitTestTracker = TrackerHitTest(e);
487
488 if (hitTestTracker != NULL)
489 {
490 //LOG(TRACE) << "hitTestTracker != NULL";
491 return hitTestTracker;
492 }
493 else
494 {
495 switch (currentTool_)
496 {
497 case RtViewerGuiTool_Rotate:
498 //LOG(TRACE) << "Creating RotateSceneTracker";
499 return boost::shared_ptr<IFlexiblePointerTracker>(new RotateSceneTracker(viewport_, e));
500 case RtViewerGuiTool_Pan:
501 return boost::shared_ptr<IFlexiblePointerTracker>(new PanSceneTracker(viewport_, e));
502 case RtViewerGuiTool_Zoom:
503 return boost::shared_ptr<IFlexiblePointerTracker>(new ZoomSceneTracker(viewport_, e, compositor.GetCanvasHeight()));
504 //case GuiTool_AngleMeasure:
505 // return new AngleMeasureTracker(GetScene(), e);
506 //case GuiTool_CircleMeasure:
507 // return new CircleMeasureTracker(GetScene(), e);
508 //case GuiTool_EllipseMeasure:
509 // return new EllipseMeasureTracker(GetScene(), e);
510 case RtViewerGuiTool_LineMeasure:
511 return boost::shared_ptr<IFlexiblePointerTracker>(new CreateLineMeasureTracker(viewport_, e));
512 case RtViewerGuiTool_AngleMeasure:
513 return boost::shared_ptr<IFlexiblePointerTracker>(new CreateAngleMeasureTracker(viewport_, e));
514 case RtViewerGuiTool_CircleMeasure:
515 LOG(ERROR) << "Not implemented yet!";
516 return boost::shared_ptr<IFlexiblePointerTracker>();
517 case RtViewerGuiTool_EllipseMeasure:
518 LOG(ERROR) << "Not implemented yet!";
519 return boost::shared_ptr<IFlexiblePointerTracker>();
520 default:
521 throw OrthancException(ErrorCode_InternalError, "Wrong tool!");
522 }
523 }
524 }
525 default:
526 return boost::shared_ptr<IFlexiblePointerTracker>();
527 }
528 }
529 #endif
530
531 } 219 }
532 220
533 boost::weak_ptr<OrthancStone::RtViewerApp> g_app; 221 boost::weak_ptr<OrthancStone::RtViewerApp> g_app;
534 222
535 /** 223 /**