view Samples/Sdl/RadiographyEditor.cpp @ 981:c20dbaab360c

Ability to cope with empty "Referenced SOP Instance UID" (dicom path (3006,0039)[i] / (0x3006, 0x0040)[0] / (0x3006, 0x0016)[0] / (0x0008, 0x1155)) + better logs + code formating
author Benjamin Golinvaux <bgo@osimis.io>
date Fri, 06 Sep 2019 09:38:18 +0200
parents 80829436ce0c
children 2d8ab34c8c91
line wrap: on
line source

/**
 * Stone of Orthanc
 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
 * Department, University Hospital of Liege, Belgium
 * Copyright (C) 2017-2019 Osimis S.A., Belgium
 *
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * 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 <http://www.gnu.org/licenses/>.
 **/

#include "../Shared/RadiographyEditorApp.h"

// From Stone
#include "../../Framework/Oracle/SleepOracleCommand.h"
#include "../../Framework/Oracle/ThreadedOracle.h"
#include "../../Applications/Sdl/SdlOpenGLWindow.h"
#include "../../Framework/Scene2D/OpenGLCompositor.h"
#include "../../Framework/Scene2D/CairoCompositor.h"
#include "../../Framework/Scene2D/ColorTextureSceneLayer.h"
#include "../../Framework/Scene2D/OpenGLCompositor.h"
#include "../../Framework/StoneInitialization.h"

#include <Core/Logging.h>
#include <Core/OrthancException.h>


#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>

#include <SDL.h>
#include <stdio.h>

using namespace OrthancStone;

namespace OrthancStone
{
  class NativeApplicationContext : public IMessageEmitter
  {
  private:
    boost::shared_mutex  mutex_;
    MessageBroker        broker_;
    IObservable          oracleObservable_;

  public:
    NativeApplicationContext() :
      oracleObservable_(broker_)
    {
    }


    virtual void EmitMessage(const IObserver& observer,
                             const IMessage& message) ORTHANC_OVERRIDE
    {
      try
      {
        boost::unique_lock<boost::shared_mutex>  lock(mutex_);
        oracleObservable_.EmitMessage(observer, message);
      }
      catch (Orthanc::OrthancException& e)
      {
        LOG(ERROR) << "Exception while emitting a message: " << e.What();
      }
    }


    class ReaderLock : public boost::noncopyable
    {
    private:
      NativeApplicationContext&                that_;
      boost::shared_lock<boost::shared_mutex>  lock_;

    public:
      ReaderLock(NativeApplicationContext& that) :
        that_(that),
        lock_(that.mutex_)
      {
      }
    };


    class WriterLock : public boost::noncopyable
    {
    private:
      NativeApplicationContext&                that_;
      boost::unique_lock<boost::shared_mutex>  lock_;

    public:
      WriterLock(NativeApplicationContext& that) :
        that_(that),
        lock_(that.mutex_)
      {
      }

      MessageBroker& GetBroker()
      {
        return that_.broker_;
      }

      IObservable& GetOracleObservable()
      {
        return that_.oracleObservable_;
      }
    };
  };
}

class OpenGlSdlCompositorFactory : public ICompositorFactory
{
  OpenGL::IOpenGLContext& openGlContext_;

public:
  OpenGlSdlCompositorFactory(OpenGL::IOpenGLContext& openGlContext) :
    openGlContext_(openGlContext)
  {}

  ICompositor* GetCompositor(const Scene2D& scene)
  {

    OpenGLCompositor* compositor = new OpenGLCompositor(openGlContext_, scene);
    compositor->SetFont(0, Orthanc::EmbeddedResources::UBUNTU_FONT,
                         FONT_SIZE_0, Orthanc::Encoding_Latin1);
    compositor->SetFont(1, Orthanc::EmbeddedResources::UBUNTU_FONT,
                         FONT_SIZE_1, Orthanc::Encoding_Latin1);
    return compositor;
  }
};

static void GLAPIENTRY
OpenGLMessageCallback(GLenum source,
                      GLenum type,
                      GLuint id,
                      GLenum severity,
                      GLsizei length,
                      const GLchar* message,
                      const void* userParam)
{
  if (severity != GL_DEBUG_SEVERITY_NOTIFICATION)
  {
    fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
            (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
            type, severity, message);
  }
}


/**
 * IMPORTANT: The full arguments to "main()" are needed for SDL on
 * Windows. Otherwise, one gets the linking error "undefined reference
 * to `SDL_main'". https://wiki.libsdl.org/FAQWindows
 **/
int main(int argc, char* argv[])
{
  using namespace OrthancStone;

  StoneInitialize();
  Orthanc::Logging::EnableInfoLevel(true);
  //  Orthanc::Logging::EnableTraceLevel(true);

  try
  {
    OrthancStone::NativeApplicationContext context;
    OrthancStone::NativeApplicationContext::WriterLock lock(context);
    OrthancStone::ThreadedOracle oracle(context);

    // False means we do NOT let Windows treat this as a legacy application
    // that needs to be scaled
    SdlOpenGLWindow window("Hello", 1024, 1024, false);

    glEnable(GL_DEBUG_OUTPUT);
    glDebugMessageCallback(OpenGLMessageCallback, 0);

    std::auto_ptr<OpenGlSdlCompositorFactory> compositorFactory(new OpenGlSdlCompositorFactory(window));
    boost::shared_ptr<RadiographyEditorApp> app(new RadiographyEditorApp(oracle, lock.GetOracleObservable(), compositorFactory.release()));
    app->PrepareScene();
    app->FitContent(window.GetCanvasWidth(), window.GetCanvasHeight());

    bool stopApplication = false;

    while (!stopApplication)
    {
      app->Refresh();

      SDL_Event event;
      while (!stopApplication && SDL_PollEvent(&event))
      {
        OrthancStone::KeyboardModifiers modifiers = OrthancStone::KeyboardModifiers_None;
        if (event.key.keysym.mod & KMOD_CTRL)
          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Control));
        if (event.key.keysym.mod & KMOD_ALT)
          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Alt));
        if (event.key.keysym.mod & KMOD_SHIFT)
          modifiers = static_cast<OrthancStone::KeyboardModifiers>(static_cast<int>(modifiers) | static_cast<int>(OrthancStone::KeyboardModifiers_Shift));

        OrthancStone::MouseButton button;
        if (event.button.button == SDL_BUTTON_LEFT)
          button = OrthancStone::MouseButton_Left;
        else if (event.button.button == SDL_BUTTON_MIDDLE)
          button = OrthancStone::MouseButton_Middle;
        else if (event.button.button == SDL_BUTTON_RIGHT)
          button = OrthancStone::MouseButton_Right;

        if (event.type == SDL_QUIT)
        {
          stopApplication = true;
          break;
        }
        else if (event.type == SDL_WINDOWEVENT &&
                 event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
        {
          app->DisableTracker(); // was: tracker.reset(NULL);
          app->UpdateSize();
        }
        else if (event.type == SDL_KEYDOWN &&
                 event.key.repeat == 0 /* Ignore key bounce */)
        {
          switch (event.key.keysym.sym)
          {
          case SDLK_f:
            window.GetWindow().ToggleMaximize();
            break;

          case SDLK_q:
            stopApplication = true;
            break;
          default:
          {
            app->OnKeyPressed(event.key.keysym.sym, modifiers);
           }
          }
        }
        else if (event.type == SDL_MOUSEBUTTONDOWN)
        {
          app->OnMouseDown(event.button.x, event.button.y, modifiers, button);
        }
        else if (event.type == SDL_MOUSEMOTION)
        {
          app->OnMouseMove(event.button.x, event.button.y, modifiers);
        }
        else if (event.type == SDL_MOUSEBUTTONUP)
        {
          app->OnMouseUp(event.button.x, event.button.y, modifiers, button);
        }
      }
      SDL_Delay(1);
    }
  }
  catch (Orthanc::OrthancException& e)
  {
    LOG(ERROR) << "EXCEPTION: " << e.What();
  }

  StoneFinalize();

  return 0;
}