<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/external_tab_container.h"

#include <string>

#include "app/l10n_util.h"
#include "app/win_util.h"
#include "base/logging.h"
#include "base/win_util.h"
#include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/load_notification_details.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/provisional_load_details.h"
#include "chrome/browser/views/tab_contents/render_view_context_menu_external_win.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/views/tab_contents/tab_contents_container.h"
#include "chrome/common/bindings_policy.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/automation/automation_messages.h"

#include "grit/generated_resources.h"

static const wchar_t kWindowObjectKey[] = L"ChromeWindowObject";

ExternalTabContainer::ExternalTabContainer(
    AutomationProvider* automation, AutomationResourceMessageFilter* filter)
    : automation_(automation),
      tab_contents_(NULL),
      tab_contents_container_(NULL),
      tab_handle_(0),
      ignore_next_load_notification_(false),
      automation_resource_message_filter_(filter),
      load_requests_via_automation_(false) {
}

ExternalTabContainer::~ExternalTabContainer() {
  Uninitialize(GetNativeView());
}

bool ExternalTabContainer::Init(Profile* profile,
                                HWND parent,
                                const gfx::Rect& bounds,
                                DWORD style,
                                bool load_requests_via_automation) {
  if (IsWindow()) {
    NOTREACHED();
    return false;
  }

  load_requests_via_automation_ = load_requests_via_automation;

  set_window_style(WS_POPUP);
  views::WidgetWin::Init(NULL, bounds);
  if (!IsWindow()) {
    NOTREACHED();
    return false;
  }
  // TODO(jcampan): limit focus traversal to contents.

  // We don't ever remove the prop because the lifetime of this object
  // is the same as the lifetime of the window
  SetProp(GetNativeView(), kWindowObjectKey, this);

  tab_contents_ = new TabContents(profile, NULL, MSG_ROUTING_NONE, NULL);
  tab_contents_->set_delegate(this);
  tab_contents_->render_view_host()->AllowBindings(
      BindingsPolicy::EXTERNAL_HOST);

  // Create a TabContentsContainer to handle focus cycling using Tab and
  // Shift-Tab.
  tab_contents_container_ = new TabContentsContainer;
  SetContentsView(tab_contents_container_);

  // Note that SetTabContents must be called after AddChildView is called
  tab_contents_container_->ChangeTabContents(tab_contents_);

  NavigationController* controller = &tab_contents_->controller();
  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
                 Source<NavigationController>(controller));
  registrar_.Add(this, NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR,
                 Source<NavigationController>(controller));
  registrar_.Add(this, NotificationType::LOAD_STOP,
                 Source<NavigationController>(controller));
  registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB,
                 Source<TabContents>(tab_contents_));
  registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_DELETED,
                 Source<TabContents>(tab_contents_));

  NotificationService::current()->Notify(
      NotificationType::EXTERNAL_TAB_CREATED,
      Source<NavigationController>(controller),
      NotificationService::NoDetails());

  // We need WS_POPUP to be on the window during initialization, but
  // once initialized we apply the requested style which may or may not
  // include the popup bit.
  // Note that it's important to do this before we call SetParent since
  // during the SetParent call we will otherwise get a WA_ACTIVATE call
  // that causes us to steal the current focus.
  SetWindowLong(GWL_STYLE, (GetWindowLong(GWL_STYLE) & ~WS_POPUP) | style);

  // Now apply the parenting and style
  if (parent)
    SetParent(GetNativeView(), parent);

  ::ShowWindow(tab_contents_->GetNativeView(), SW_SHOWNA);

  disabled_context_menu_ids_.push_back(
      IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD);
  return true;
}

void ExternalTabContainer::ProcessUnhandledAccelerator(const MSG& msg) {
  // We just received an accelerator key that we had sent to external host
  // back. Since the external host was not interested in handling this, we
  // need to dispatch this message as if we had just peeked this out. (we
  // also need to call TranslateMessage to generate a WM_CHAR if needed).
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}

void ExternalTabContainer::FocusThroughTabTraversal(bool reverse) {
  DCHECK(tab_contents_);
  if (tab_contents_) {
    static_cast<TabContents*>(tab_contents_)->Focus();
    static_cast<TabContents*>(tab_contents_)->FocusThroughTabTraversal(reverse);
  }
}

// static
bool ExternalTabContainer::IsExternalTabContainer(HWND window) {
  if (GetProp(window, kWindowObjectKey) != NULL)
    return true;

  return false;
}

// static
ExternalTabContainer* ExternalTabContainer::GetContainerForTab(
    HWND tab_window) {
  HWND parent_window = ::GetParent(tab_window);
  if (!::IsWindow(parent_window)) {
    return NULL;
  }
  if (!IsExternalTabContainer(parent_window)) {
    return NULL;
  }
  ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>(
      GetProp(parent_window, kWindowObjectKey));
  return container;
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, TabContentsDelegate implementation:

void ExternalTabContainer::OpenURLFromTab(TabContents* source,
                           const GURL& url,
                           const GURL& referrer,
                           WindowOpenDisposition disposition,
                           PageTransition::Type transition) {
  switch (disposition) {
    case CURRENT_TAB:
    case SINGLETON_TAB:
    case NEW_FOREGROUND_TAB:
    case NEW_BACKGROUND_TAB:
    case NEW_WINDOW:
      if (automation_) {
        automation_->Send(new AutomationMsg_OpenURL(0, tab_handle_,
                                                    url, disposition));
      }
      break;
    default:
      break;
  }
}

void ExternalTabContainer::NavigationStateChanged(const TabContents* source,
                                                  unsigned changed_flags) {
  if (automation_) {
    automation_->Send(new AutomationMsg_NavigationStateChanged(0, tab_handle_,
                                                               changed_flags));
  }
}

void ExternalTabContainer::AddNewContents(TabContents* source,
                            TabContents* new_contents,
                            WindowOpenDisposition disposition,
                            const gfx::Rect& initial_pos,
                            bool user_gesture) {
  if (disposition == NEW_POPUP || disposition == NEW_WINDOW) {
    Browser::BuildPopupWindowHelper(source, new_contents, initial_pos,
                                    Browser::TYPE_POPUP,
                                    tab_contents_->profile(), true);
  } else {
    NOTREACHED();
  }
}

void ExternalTabContainer::ActivateContents(TabContents* contents) {
}

void ExternalTabContainer::LoadingStateChanged(TabContents* source) {
}

void ExternalTabContainer::CloseContents(TabContents* source) {
}

void ExternalTabContainer::MoveContents(TabContents* source,
                                        const gfx::Rect& pos) {
}

bool ExternalTabContainer::IsPopup(TabContents* source) {
  return false;
}

void ExternalTabContainer::URLStarredChanged(TabContents* source,
                                             bool starred) {
}

void ExternalTabContainer::UpdateTargetURL(TabContents* source,
                                           const GURL& url) {
  if (automation_) {
    std::wstring url_string = CA2W(url.spec().c_str());
    automation_->Send(
        new AutomationMsg_UpdateTargetUrl(0, tab_handle_, url_string));
  }
}

void ExternalTabContainer::ContentsZoomChange(bool zoom_in) {
}

void ExternalTabContainer::ToolbarSizeChanged(TabContents* source,
                                              bool finished) {
}

void ExternalTabContainer::ForwardMessageToExternalHost(
    const std::string& message, const std::string& origin,
    const std::string& target) {
  if (automation_) {
    automation_->Send(
        new AutomationMsg_ForwardMessageToExternalHost(0, tab_handle_,
            message, origin, target));
  }
}

bool ExternalTabContainer::TakeFocus(bool reverse) {
  if (automation_) {
    automation_->Send(new AutomationMsg_TabbedOut(0, tab_handle_,
        win_util::IsShiftPressed()));
  }

  return true;
}

bool ExternalTabContainer::HandleContextMenu(const ContextMenuParams& params) {
  if (!automation_) {
    NOTREACHED();
    return false;
  }

  external_context_menu_.reset(
      new RenderViewContextMenuExternalWin(tab_contents(),
                                           params,
                                           disabled_context_menu_ids_));
  external_context_menu_->Init();

  POINT screen_pt = { params.x, params.y };
  MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1);

  bool rtl = l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT;
  automation_->Send(
      new AutomationMsg_ForwardContextMenuToExternalHost(0, tab_handle_,
          external_context_menu_->GetMenuHandle(), screen_pt.x, screen_pt.y,
          rtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN));

  return true;
}

bool ExternalTabContainer::ExecuteContextMenuCommand(int command) {
  if (!external_context_menu_.get()) {
    NOTREACHED();
    return false;
  }

  external_context_menu_->ExecuteCommand(command);
  return true;
}

bool ExternalTabContainer::HandleKeyboardEvent(
    const NativeWebKeyboardEvent& event) {
  return ProcessUnhandledKeyStroke(event.os_event.hwnd,
                                   event.os_event.message,
                                   event.os_event.wParam,
                                   event.os_event.lParam);
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, NotificationObserver implementation:

void ExternalTabContainer::Observe(NotificationType type,
                                   const NotificationSource& source,
                                   const NotificationDetails& details) {
  if (!automation_)
    return;

  static const int kHttpClientErrorStart = 400;
  static const int kHttpServerErrorEnd = 510;

  switch (type.value) {
    case NotificationType::LOAD_STOP: {
        const LoadNotificationDetails* load =
            Details<LoadNotificationDetails>(details).ptr();
        if (load != NULL && PageTransition::IsMainFrame(load->origin())) {
          automation_->Send(new AutomationMsg_TabLoaded(0, tab_handle_,
                                                        load->url()));
        }
        break;
      }
    case NotificationType::NAV_ENTRY_COMMITTED: {
        if (ignore_next_load_notification_) {
          ignore_next_load_notification_ = false;
          return;
        }

        const NavigationController::LoadCommittedDetails* commit =
            Details<NavigationController::LoadCommittedDetails>(details).ptr();

        if (commit->http_status_code >= kHttpClientErrorStart &&
            commit->http_status_code <= kHttpServerErrorEnd) {
          automation_->Send(new AutomationMsg_NavigationFailed(
              0, tab_handle_, commit->http_status_code, commit->entry->url()));

          ignore_next_load_notification_ = true;
        } else {
          // When the previous entry index is invalid, it will be -1, which
          // will still make the computation come out right (navigating to the
          // 0th entry will be +1).
          automation_->Send(new AutomationMsg_DidNavigate(
              0, tab_handle_, commit->type,
              commit->previous_entry_index -
                  tab_contents_->controller().last_committed_entry_index(),
              commit->entry->url()));
        }
        break;
      }
    case NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR: {
      const ProvisionalLoadDetails* load_details =
          Details<ProvisionalLoadDetails>(details).ptr();
      automation_->Send(new AutomationMsg_NavigationFailed(
          0, tab_handle_, load_details->error_code(), load_details->url()));

      ignore_next_load_notification_ = true;
      break;
    }
    case NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB: {
      if (load_requests_via_automation_) {
        RenderViewHost* rvh = Details<RenderViewHost>(details).ptr();
        if (rvh) {
          AutomationResourceMessageFilter::RegisterRenderView(
              rvh->process()->pid(), rvh->routing_id(), tab_handle_,
              automation_resource_message_filter_);
        }
      }
      break;
    }
    case NotificationType::RENDER_VIEW_HOST_DELETED: {
      if (load_requests_via_automation_) {
        RenderViewHost* rvh = Details<RenderViewHost>(details).ptr();
        if (rvh) {
          AutomationResourceMessageFilter::UnRegisterRenderView(
              rvh->process()->pid(), rvh->routing_id());
        }
      }
      break;
    }
    default:
      NOTREACHED();
  }
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, views::WidgetWin overrides:

void ExternalTabContainer::OnDestroy() {
  Uninitialize(GetNativeView());
  WidgetWin::OnDestroy();
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, private:

void ExternalTabContainer::Uninitialize(HWND window) {
  registrar_.RemoveAll();
  if (tab_contents_) {
    NotificationService::current()->Notify(
        NotificationType::EXTERNAL_TAB_CLOSED,
        Source<NavigationController>(&tab_contents_->controller()),
        Details<ExternalTabContainer>(this));

    delete tab_contents_;
    tab_contents_ = NULL;
  }
}

bool ExternalTabContainer::ProcessUnhandledKeyStroke(HWND window,
                                                     UINT message,
                                                     WPARAM wparam,
                                                     LPARAM lparam) {
  if (!automation_) {
    return false;
  }
  if ((wparam == VK_TAB) && !win_util::IsCtrlPressed()) {
    // Tabs are handled separately (except if this is Ctrl-Tab or
    // Ctrl-Shift-Tab)
    return false;
  }

  unsigned int flags = HIWORD(lparam);
  bool alt = (flags & KF_ALTDOWN) != 0;
  if (!alt && (message == WM_SYSKEYUP || message == WM_KEYUP)) {
    // In case the Alt key is being released.
    alt = (wparam == VK_MENU);
  }

  if ((flags & KF_EXTENDED) || alt || (wparam >= VK_F1 && wparam <= VK_F24) ||
      wparam == VK_ESCAPE || wparam == VK_RETURN ||
      win_util::IsShiftPressed() || win_util::IsCtrlPressed()) {
    // If this is an extended key or if one or more of Alt, Shift and Control
    // are pressed, this might be an accelerator that the external host wants
    // to handle. If the host does not handle this accelerator, it will reflect
    // the accelerator back to us via the ProcessUnhandledAccelerator method.
    MSG msg = {0};
    msg.hwnd = window;
    msg.message = message;
    msg.wParam = wparam;
    msg.lParam = lparam;
    automation_->Send(new AutomationMsg_HandleAccelerator(0, tab_handle_, msg));
    return true;
  }

  return false;
}
<commit_msg>When the unhandled accelerator messages come back from the external host, we should only DefProc the message. The current implementaton calls TranslateMessage and DispatchMessage which causes the whole cycle to repeat.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/external_tab_container.h"

#include <string>

#include "app/l10n_util.h"
#include "app/win_util.h"
#include "base/logging.h"
#include "base/win_util.h"
#include "chrome/browser/automation/automation_provider.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/load_notification_details.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/tab_contents/provisional_load_details.h"
#include "chrome/browser/views/tab_contents/render_view_context_menu_external_win.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/views/tab_contents/tab_contents_container.h"
#include "chrome/common/bindings_policy.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/automation/automation_messages.h"

#include "grit/generated_resources.h"

static const wchar_t kWindowObjectKey[] = L"ChromeWindowObject";

ExternalTabContainer::ExternalTabContainer(
    AutomationProvider* automation, AutomationResourceMessageFilter* filter)
    : automation_(automation),
      tab_contents_(NULL),
      tab_contents_container_(NULL),
      tab_handle_(0),
      ignore_next_load_notification_(false),
      automation_resource_message_filter_(filter),
      load_requests_via_automation_(false) {
}

ExternalTabContainer::~ExternalTabContainer() {
  Uninitialize(GetNativeView());
}

bool ExternalTabContainer::Init(Profile* profile,
                                HWND parent,
                                const gfx::Rect& bounds,
                                DWORD style,
                                bool load_requests_via_automation) {
  if (IsWindow()) {
    NOTREACHED();
    return false;
  }

  load_requests_via_automation_ = load_requests_via_automation;

  set_window_style(WS_POPUP);
  views::WidgetWin::Init(NULL, bounds);
  if (!IsWindow()) {
    NOTREACHED();
    return false;
  }
  // TODO(jcampan): limit focus traversal to contents.

  // We don't ever remove the prop because the lifetime of this object
  // is the same as the lifetime of the window
  SetProp(GetNativeView(), kWindowObjectKey, this);

  tab_contents_ = new TabContents(profile, NULL, MSG_ROUTING_NONE, NULL);
  tab_contents_->set_delegate(this);
  tab_contents_->render_view_host()->AllowBindings(
      BindingsPolicy::EXTERNAL_HOST);

  // Create a TabContentsContainer to handle focus cycling using Tab and
  // Shift-Tab.
  tab_contents_container_ = new TabContentsContainer;
  SetContentsView(tab_contents_container_);

  // Note that SetTabContents must be called after AddChildView is called
  tab_contents_container_->ChangeTabContents(tab_contents_);

  NavigationController* controller = &tab_contents_->controller();
  registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
                 Source<NavigationController>(controller));
  registrar_.Add(this, NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR,
                 Source<NavigationController>(controller));
  registrar_.Add(this, NotificationType::LOAD_STOP,
                 Source<NavigationController>(controller));
  registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB,
                 Source<TabContents>(tab_contents_));
  registrar_.Add(this, NotificationType::RENDER_VIEW_HOST_DELETED,
                 Source<TabContents>(tab_contents_));

  NotificationService::current()->Notify(
      NotificationType::EXTERNAL_TAB_CREATED,
      Source<NavigationController>(controller),
      NotificationService::NoDetails());

  // We need WS_POPUP to be on the window during initialization, but
  // once initialized we apply the requested style which may or may not
  // include the popup bit.
  // Note that it's important to do this before we call SetParent since
  // during the SetParent call we will otherwise get a WA_ACTIVATE call
  // that causes us to steal the current focus.
  SetWindowLong(GWL_STYLE, (GetWindowLong(GWL_STYLE) & ~WS_POPUP) | style);

  // Now apply the parenting and style
  if (parent)
    SetParent(GetNativeView(), parent);

  ::ShowWindow(tab_contents_->GetNativeView(), SW_SHOWNA);

  disabled_context_menu_ids_.push_back(
      IDS_CONTENT_CONTEXT_OPENLINKOFFTHERECORD);
  return true;
}

void ExternalTabContainer::ProcessUnhandledAccelerator(const MSG& msg) {
  DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
}

void ExternalTabContainer::FocusThroughTabTraversal(bool reverse) {
  DCHECK(tab_contents_);
  if (tab_contents_) {
    static_cast<TabContents*>(tab_contents_)->Focus();
    static_cast<TabContents*>(tab_contents_)->FocusThroughTabTraversal(reverse);
  }
}

// static
bool ExternalTabContainer::IsExternalTabContainer(HWND window) {
  if (GetProp(window, kWindowObjectKey) != NULL)
    return true;

  return false;
}

// static
ExternalTabContainer* ExternalTabContainer::GetContainerForTab(
    HWND tab_window) {
  HWND parent_window = ::GetParent(tab_window);
  if (!::IsWindow(parent_window)) {
    return NULL;
  }
  if (!IsExternalTabContainer(parent_window)) {
    return NULL;
  }
  ExternalTabContainer* container = reinterpret_cast<ExternalTabContainer*>(
      GetProp(parent_window, kWindowObjectKey));
  return container;
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, TabContentsDelegate implementation:

void ExternalTabContainer::OpenURLFromTab(TabContents* source,
                           const GURL& url,
                           const GURL& referrer,
                           WindowOpenDisposition disposition,
                           PageTransition::Type transition) {
  switch (disposition) {
    case CURRENT_TAB:
    case SINGLETON_TAB:
    case NEW_FOREGROUND_TAB:
    case NEW_BACKGROUND_TAB:
    case NEW_WINDOW:
      if (automation_) {
        automation_->Send(new AutomationMsg_OpenURL(0, tab_handle_,
                                                    url, disposition));
      }
      break;
    default:
      break;
  }
}

void ExternalTabContainer::NavigationStateChanged(const TabContents* source,
                                                  unsigned changed_flags) {
  if (automation_) {
    automation_->Send(new AutomationMsg_NavigationStateChanged(0, tab_handle_,
                                                               changed_flags));
  }
}

void ExternalTabContainer::AddNewContents(TabContents* source,
                            TabContents* new_contents,
                            WindowOpenDisposition disposition,
                            const gfx::Rect& initial_pos,
                            bool user_gesture) {
  if (disposition == NEW_POPUP || disposition == NEW_WINDOW) {
    Browser::BuildPopupWindowHelper(source, new_contents, initial_pos,
                                    Browser::TYPE_POPUP,
                                    tab_contents_->profile(), true);
  } else {
    NOTREACHED();
  }
}

void ExternalTabContainer::ActivateContents(TabContents* contents) {
}

void ExternalTabContainer::LoadingStateChanged(TabContents* source) {
}

void ExternalTabContainer::CloseContents(TabContents* source) {
}

void ExternalTabContainer::MoveContents(TabContents* source,
                                        const gfx::Rect& pos) {
}

bool ExternalTabContainer::IsPopup(TabContents* source) {
  return false;
}

void ExternalTabContainer::URLStarredChanged(TabContents* source,
                                             bool starred) {
}

void ExternalTabContainer::UpdateTargetURL(TabContents* source,
                                           const GURL& url) {
  if (automation_) {
    std::wstring url_string = CA2W(url.spec().c_str());
    automation_->Send(
        new AutomationMsg_UpdateTargetUrl(0, tab_handle_, url_string));
  }
}

void ExternalTabContainer::ContentsZoomChange(bool zoom_in) {
}

void ExternalTabContainer::ToolbarSizeChanged(TabContents* source,
                                              bool finished) {
}

void ExternalTabContainer::ForwardMessageToExternalHost(
    const std::string& message, const std::string& origin,
    const std::string& target) {
  if (automation_) {
    automation_->Send(
        new AutomationMsg_ForwardMessageToExternalHost(0, tab_handle_,
            message, origin, target));
  }
}

bool ExternalTabContainer::TakeFocus(bool reverse) {
  if (automation_) {
    automation_->Send(new AutomationMsg_TabbedOut(0, tab_handle_,
        win_util::IsShiftPressed()));
  }

  return true;
}

bool ExternalTabContainer::HandleContextMenu(const ContextMenuParams& params) {
  if (!automation_) {
    NOTREACHED();
    return false;
  }

  external_context_menu_.reset(
      new RenderViewContextMenuExternalWin(tab_contents(),
                                           params,
                                           disabled_context_menu_ids_));
  external_context_menu_->Init();

  POINT screen_pt = { params.x, params.y };
  MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_pt, 1);

  bool rtl = l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT;
  automation_->Send(
      new AutomationMsg_ForwardContextMenuToExternalHost(0, tab_handle_,
          external_context_menu_->GetMenuHandle(), screen_pt.x, screen_pt.y,
          rtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN));

  return true;
}

bool ExternalTabContainer::ExecuteContextMenuCommand(int command) {
  if (!external_context_menu_.get()) {
    NOTREACHED();
    return false;
  }

  external_context_menu_->ExecuteCommand(command);
  return true;
}

bool ExternalTabContainer::HandleKeyboardEvent(
    const NativeWebKeyboardEvent& event) {
  return ProcessUnhandledKeyStroke(event.os_event.hwnd,
                                   event.os_event.message,
                                   event.os_event.wParam,
                                   event.os_event.lParam);
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, NotificationObserver implementation:

void ExternalTabContainer::Observe(NotificationType type,
                                   const NotificationSource& source,
                                   const NotificationDetails& details) {
  if (!automation_)
    return;

  static const int kHttpClientErrorStart = 400;
  static const int kHttpServerErrorEnd = 510;

  switch (type.value) {
    case NotificationType::LOAD_STOP: {
        const LoadNotificationDetails* load =
            Details<LoadNotificationDetails>(details).ptr();
        if (load != NULL && PageTransition::IsMainFrame(load->origin())) {
          automation_->Send(new AutomationMsg_TabLoaded(0, tab_handle_,
                                                        load->url()));
        }
        break;
      }
    case NotificationType::NAV_ENTRY_COMMITTED: {
        if (ignore_next_load_notification_) {
          ignore_next_load_notification_ = false;
          return;
        }

        const NavigationController::LoadCommittedDetails* commit =
            Details<NavigationController::LoadCommittedDetails>(details).ptr();

        if (commit->http_status_code >= kHttpClientErrorStart &&
            commit->http_status_code <= kHttpServerErrorEnd) {
          automation_->Send(new AutomationMsg_NavigationFailed(
              0, tab_handle_, commit->http_status_code, commit->entry->url()));

          ignore_next_load_notification_ = true;
        } else {
          // When the previous entry index is invalid, it will be -1, which
          // will still make the computation come out right (navigating to the
          // 0th entry will be +1).
          automation_->Send(new AutomationMsg_DidNavigate(
              0, tab_handle_, commit->type,
              commit->previous_entry_index -
                  tab_contents_->controller().last_committed_entry_index(),
              commit->entry->url()));
        }
        break;
      }
    case NotificationType::FAIL_PROVISIONAL_LOAD_WITH_ERROR: {
      const ProvisionalLoadDetails* load_details =
          Details<ProvisionalLoadDetails>(details).ptr();
      automation_->Send(new AutomationMsg_NavigationFailed(
          0, tab_handle_, load_details->error_code(), load_details->url()));

      ignore_next_load_notification_ = true;
      break;
    }
    case NotificationType::RENDER_VIEW_HOST_CREATED_FOR_TAB: {
      if (load_requests_via_automation_) {
        RenderViewHost* rvh = Details<RenderViewHost>(details).ptr();
        if (rvh) {
          AutomationResourceMessageFilter::RegisterRenderView(
              rvh->process()->pid(), rvh->routing_id(), tab_handle_,
              automation_resource_message_filter_);
        }
      }
      break;
    }
    case NotificationType::RENDER_VIEW_HOST_DELETED: {
      if (load_requests_via_automation_) {
        RenderViewHost* rvh = Details<RenderViewHost>(details).ptr();
        if (rvh) {
          AutomationResourceMessageFilter::UnRegisterRenderView(
              rvh->process()->pid(), rvh->routing_id());
        }
      }
      break;
    }
    default:
      NOTREACHED();
  }
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, views::WidgetWin overrides:

void ExternalTabContainer::OnDestroy() {
  Uninitialize(GetNativeView());
  WidgetWin::OnDestroy();
}

////////////////////////////////////////////////////////////////////////////////
// ExternalTabContainer, private:

void ExternalTabContainer::Uninitialize(HWND window) {
  registrar_.RemoveAll();
  if (tab_contents_) {
    NotificationService::current()->Notify(
        NotificationType::EXTERNAL_TAB_CLOSED,
        Source<NavigationController>(&tab_contents_->controller()),
        Details<ExternalTabContainer>(this));

    delete tab_contents_;
    tab_contents_ = NULL;
  }
}

bool ExternalTabContainer::ProcessUnhandledKeyStroke(HWND window,
                                                     UINT message,
                                                     WPARAM wparam,
                                                     LPARAM lparam) {
  if (!automation_) {
    return false;
  }
  if ((wparam == VK_TAB) && !win_util::IsCtrlPressed()) {
    // Tabs are handled separately (except if this is Ctrl-Tab or
    // Ctrl-Shift-Tab)
    return false;
  }

  unsigned int flags = HIWORD(lparam);
  bool alt = (flags & KF_ALTDOWN) != 0;
  if (!alt && (message == WM_SYSKEYUP || message == WM_KEYUP)) {
    // In case the Alt key is being released.
    alt = (wparam == VK_MENU);
  }

  if ((flags & KF_EXTENDED) || alt || (wparam >= VK_F1 && wparam <= VK_F24) ||
      wparam == VK_ESCAPE || wparam == VK_RETURN ||
      win_util::IsShiftPressed() || win_util::IsCtrlPressed()) {
    // If this is an extended key or if one or more of Alt, Shift and Control
    // are pressed, this might be an accelerator that the external host wants
    // to handle. If the host does not handle this accelerator, it will reflect
    // the accelerator back to us via the ProcessUnhandledAccelerator method.
    MSG msg = {0};
    msg.hwnd = window;
    msg.message = message;
    msg.wParam = wparam;
    msg.lParam = lparam;
    automation_->Send(new AutomationMsg_HandleAccelerator(0, tab_handle_, msg));
    return true;
  }

  return false;
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <sys/time.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <sstream>

using namespace std;

const int NX=256;
const int NY=NX;
int T_FINAL;

const int X_MASK = NX-1, Y_MASK=NY-1;

double dens_initial[NX][NX];
double dens_final[NX][NX];

static double second()
{
  struct timeval tm;
  double t ;

  static int base_sec = 0,base_usec = 0;

  gettimeofday(&tm, NULL);

  if(base_sec == 0 && base_usec == 0)
    {
      base_sec = tm.tv_sec;
      base_usec = tm.tv_usec;
      t = 0.0;
  } else {
    t = (double) (tm.tv_sec-base_sec) +
      ((double) (tm.tv_usec-base_usec))/1.0e6 ;
  }
  return t ;
}

void initialize() {
  for (int y=0;y<NY; ++y) {
    for (int x=0; x<NX; ++x) {
      int p1=x, p2=y, fac=1, sum=0;
      int pat[4] = {0,1,2,-3};
      while(p1!=0 || p2!=0) {
        sum += fac * pat[(p1&1)*2+(p2&1)];
        fac *= 2;p1>>=1; p2>>=1;
      }
      dens_initial[y][x]=sum;
      dens_final[y][x]=0xdeadbeef;
    }
  }
}

void dump(const char* fn) {
  ofstream ofs(fn);
  for (int y=0; y<NY; ++y) {
    for(int x=0;x<NX;++x) {
      ofs << x << " " << y << " " << dens_final[y][x] << endl;
    }
    ofs << endl;
  }
}

inline double stencil_function(double o, double a, double b, double c, double d) {
  return 0.5*o+0.125*(a+b+c+d);
}

double benchmark_self_reported_wct;
int benchmark_self_reported_delta_t;

#include "body.cpp"

int main ()
{
  int good_scale;
  cerr << "calibrating..." << endl;
  for(good_scale=1;;good_scale*=2){
    initialize();
    double t1 = second();
    T_FINAL=NX*good_scale;
    solve();
    double t2 = second();
    cerr << t2 << " " << t1 << endl;
    if(t2-t1>1.0) break;
  }
  cerr << "scale: " << good_scale << endl;

  for(int iter=0;iter<10;++iter) {
    cout << algorithm_tag_str << "\tNX: " << NX << "\t" ;
    T_FINAL = NX*3*good_scale;
    initialize();
    solve();
    double n_flop=6.0*NX*NX*double(benchmark_self_reported_delta_t);
    double wct = benchmark_self_reported_wct;
    cout << n_flop << " flop\t" << wct << " second\t" ;
    cout << n_flop/wct << " flop/s" << endl;
  }
  ostringstream fn;
  fn << "gen/nx-" << NX << "-" << algorithm_tag_str << ".txt";
  dump(fn.str().c_str());

}
<commit_msg>add file output facility.<commit_after>#include <algorithm>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <sys/time.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <sstream>

using namespace std;

const int NX=256;
const int NY=NX;
int T_FINAL;

const int X_MASK = NX-1, Y_MASK=NY-1;

double dens_initial[NX][NX];
double dens_final[NX][NX];

static double second()
{
  struct timeval tm;
  double t ;

  static int base_sec = 0,base_usec = 0;

  gettimeofday(&tm, NULL);

  if(base_sec == 0 && base_usec == 0)
    {
      base_sec = tm.tv_sec;
      base_usec = tm.tv_usec;
      t = 0.0;
  } else {
    t = (double) (tm.tv_sec-base_sec) +
      ((double) (tm.tv_usec-base_usec))/1.0e6 ;
  }
  return t ;
}

void initialize() {
  for (int y=0;y<NY; ++y) {
    for (int x=0; x<NX; ++x) {
      int p1=x, p2=y, fac=1, sum=0;
      int pat[4] = {0,1,2,-3};
      while(p1!=0 || p2!=0) {
        sum += fac * pat[(p1&1)*2+(p2&1)];
        fac *= 2;p1>>=1; p2>>=1;
      }
      dens_initial[y][x]=sum;
      dens_final[y][x]=0xdeadbeef;
    }
  }
}

void dump(const char* fn) {
  ofstream ofs(fn);
  for (int y=0; y<NY; ++y) {
    for(int x=0;x<NX;++x) {
      ofs << x << " " << y << " " << dens_final[y][x] << endl;
    }
    ofs << endl;
  }
}

inline double stencil_function(double o, double a, double b, double c, double d) {
  return 0.5*o+0.125*(a+b+c+d);
}

double benchmark_self_reported_wct;
int benchmark_self_reported_delta_t;

#include "body.cpp"

int main ()
{
  int good_scale;
  cerr << "calibrating..." << endl;
  for(good_scale=1;;good_scale*=2){
    initialize();
    double t1 = second();
    T_FINAL=NX*good_scale;
    solve();
    double t2 = second();
    cerr << t2 << " " << t1 << endl;
    if(t2-t1>1.0) break;
  }
  cerr << "scale: " << good_scale << endl;

  ofstream fs_log("benchmark.txt", ofstream::app | ofstream::out);

  for(int iter=0;iter<10;++iter) {
    ostringstream msg;
    msg << algorithm_tag_str << "\tNX: " << NX << "\t" ;
    T_FINAL = NX*3*good_scale;
    initialize();
    solve();
    double n_flop=6.0*NX*NX*double(benchmark_self_reported_delta_t);
    double wct = benchmark_self_reported_wct;
    msg << n_flop << " flop\t" << wct << " second\t" ;
    msg << n_flop/wct << " flop/s";
    cerr << msg.str() << endl;
    fs_log << msg.str() << endl;
  }
  ostringstream fn;
  fn << "gen/nx-" << NX << "-" << algorithm_tag_str << ".txt";
  dump(fn.str().c_str());
}
<|endoftext|>


<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2015-2016 Esteban Tovagliari, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "normalizeddiffusionbssrdf.h"

// appleseed.renderer headers.
#include "renderer/modeling/bssrdf/bssrdfsample.h"
#include "renderer/modeling/bssrdf/separablebssrdf.h"
#include "renderer/modeling/bssrdf/sss.h"
#include "renderer/modeling/input/inputevaluator.h"

// appleseed.foundation headers.
#include "foundation/math/cdf.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/utility/containers/dictionary.h"
#include "foundation/utility/containers/specializedarrays.h"
#include "foundation/utility/memory.h"

// Standard headers.
#include <cmath>
#include <cstddef>

// Forward declarations.
namespace renderer  { class Assembly; }
namespace renderer  { class ShadingContext; }

using namespace foundation;
using namespace std;

namespace renderer
{

namespace
{
    //
    // Normalized diffusion BSSRDF.
    //
    // Reference:
    //
    //   Approximate Reflectance Profiles for Efficient Subsurface Scattering
    //   Per H. Christensen, Brent Burley
    //   http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf
    //

    const char* Model = "normalized_diffusion_bssrdf";

    class NormalizedDiffusionBSSRDF
      : public SeparableBSSRDF
    {
      public:
        NormalizedDiffusionBSSRDF(
            const char*         name,
            const ParamArray&   params)
          : SeparableBSSRDF(name, params)
        {
            m_inputs.declare("weight", InputFormatScalar, "1.0");
            m_inputs.declare("reflectance", InputFormatSpectralReflectance);
            m_inputs.declare("reflectance_multiplier", InputFormatScalar, "1.0");
            m_inputs.declare("dmfp", InputFormatSpectralReflectance);
            m_inputs.declare("dmfp_multiplier", InputFormatScalar, "1.0");
            m_inputs.declare("outside_ior", InputFormatScalar);
            m_inputs.declare("inside_ior", InputFormatScalar);
        }

        virtual void release() APPLESEED_OVERRIDE
        {
            delete this;
        }

        virtual const char* get_model() const APPLESEED_OVERRIDE
        {
            return Model;
        }

        virtual size_t compute_input_data_size(
            const Assembly&     assembly) const APPLESEED_OVERRIDE
        {
            return align(sizeof(NormalizedDiffusionBSSRDFInputValues), 16);
        }

        virtual void prepare_inputs(void* data) const APPLESEED_OVERRIDE
        {
            NormalizedDiffusionBSSRDFInputValues* values =
                reinterpret_cast<NormalizedDiffusionBSSRDFInputValues*>(data);

            // Precompute the relative index of refraction.
            values->m_eta = values->m_outside_ior / values->m_inside_ior;

            // Apply multipliers.
            values->m_reflectance *= static_cast<float>(values->m_reflectance_multiplier);
            values->m_dmfp *= static_cast<float>(values->m_dmfp_multiplier);

            if (values->m_reflectance.size() != values->m_dmfp.size())
            {
                // Since it does not really make sense to convert a dmfp,
                // a per channel distance, as if it were a color,
                // we instead always convert the reflectance to match the
                // size of the dmfp.
                if (values->m_dmfp.is_spectral())
                {
                    Spectrum::upgrade(
                        values->m_reflectance,
                        values->m_reflectance);
                }
                else
                {
                    Spectrum::downgrade(
                        m_lighting_conditions,
                        values->m_reflectance,
                        values->m_reflectance);
                }
            }

            // Clamp input values.
            values->m_reflectance = clamp(values->m_reflectance, 0.001f, 0.999f);
            values->m_dmfp = clamp_low(values->m_dmfp, 1.0e-5f);

            // Build a CDF for channel sampling.

            values->m_s.resize(values->m_reflectance.size());
            values->m_channel_pdf.resize(values->m_reflectance.size());
            values->m_channel_cdf.resize(values->m_reflectance.size());

            float cumulated_pdf = 0.0f;
            for (size_t i = 0, e = values->m_channel_pdf.size(); i < e; ++i)
            {
                const double a = static_cast<double>(values->m_reflectance[i]);
                const double s = normalized_diffusion_s(a);
                values->m_s[i] = static_cast<float>(s);

                const double l = values->m_dmfp[i];
                const float pdf = static_cast<float>(s / l);
                values->m_channel_pdf[i] = pdf;

                cumulated_pdf += pdf;
                values->m_channel_cdf[i] = cumulated_pdf;
            }

            const float rcp_cumulated_pdf = 1.0f / cumulated_pdf;
            values->m_channel_pdf *= rcp_cumulated_pdf;
            values->m_channel_cdf *= rcp_cumulated_pdf;
            values->m_channel_cdf[values->m_channel_cdf.size() - 1] = 1.0f;

            // Precompute the (square of the) max radius.
            values->m_rmax2 = 0.0;
            for (size_t i = 0, e = values->m_dmfp.size(); i < e; ++i)
            {
                const double l = values->m_dmfp[i];
                values->m_rmax2 =
                    max(
                        normalized_diffusion_max_radius(l,values->m_s[i]),
                        values->m_rmax2);
            }

            values->m_rmax2 *= values->m_rmax2;
        }

        virtual bool sample(
            SamplingContext&    sampling_context,
            const void*         data,
            BSSRDFSample&       sample) const APPLESEED_OVERRIDE
        {
            const NormalizedDiffusionBSSRDFInputValues* values =
                reinterpret_cast<const NormalizedDiffusionBSSRDFInputValues*>(data);

            if (values->m_weight == 0.0)
                return false;

            sampling_context.split_in_place(3, 1);
            const Vector3d s = sampling_context.next_vector2<3>();

            // Sample a channel.
            const float* cdf_begin = &values->m_channel_cdf[0];
            const size_t channel =
                sample_cdf(
                    cdf_begin,
                    cdf_begin + values->m_channel_cdf.size(),
                    s[0]);

            // Sample a radius.
            const double l = values->m_dmfp[channel];
            const double radius =
                normalized_diffusion_sample(s[1], l, values->m_s[channel]);

            // Sample an angle.
            const double phi = TwoPi * s[2];

            sample.m_eta = values->m_eta;
            sample.m_channel = channel;
            sample.m_point = Vector2d(radius * cos(phi), radius * sin(phi));
            sample.m_rmax2 = values->m_rmax2;

            return true;
        }

        virtual double get_eta(
            const void*         data) const APPLESEED_OVERRIDE
        {
            return reinterpret_cast<const NormalizedDiffusionBSSRDFInputValues*>(data)->m_eta;
        }

        virtual void evaluate_profile(
            const void*         data,
            const double        square_radius,
            Spectrum&           value) const APPLESEED_OVERRIDE
        {
            const NormalizedDiffusionBSSRDFInputValues* values =
                reinterpret_cast<const NormalizedDiffusionBSSRDFInputValues*>(data);

            const double radius = sqrt(square_radius);

            value.resize(values->m_reflectance.size());

            for (size_t i = 0, e = value.size(); i < e; ++i)
            {
                const double a = values->m_reflectance[i];
                const double s = values->m_s[i];
                const double l = values->m_dmfp[i];
                value[i] = static_cast<float>(normalized_diffusion_profile(radius, l, s, a));
            }

            // Return r * R(r) * weight.
            value *= static_cast<float>(radius * values->m_weight);
        }

        virtual double evaluate_pdf(
            const void*         data,
            const size_t        channel,
            const double        radius) const APPLESEED_OVERRIDE
        {
            const NormalizedDiffusionBSSRDFInputValues* values =
                reinterpret_cast<const NormalizedDiffusionBSSRDFInputValues*>(data);

            // PDF of the sampled radius.
            double pdf_radius = 0.0;
            for (size_t i = 0, e = values->m_reflectance.size(); i < e; ++i)
            {
                const double l = values->m_dmfp[i];
                pdf_radius +=
                    normalized_diffusion_pdf(
                        radius,
                        l,
                        values->m_s[i])
                    * values->m_channel_pdf[i];
            }

            // PDF of the sampled angle.
            const double pdf_angle = RcpTwoPi;

            // Compute and return the final PDF.
            return pdf_radius * pdf_angle;
        }

        static const LightingConditions m_lighting_conditions;
    };

    const LightingConditions NormalizedDiffusionBSSRDF::m_lighting_conditions(
        IlluminantCIED65,
        XYZCMFCIE196410Deg);
}

//
// NormalizedDiffusionBSSRDFFactory class implementation.
//

const char* NormalizedDiffusionBSSRDFFactory::get_model() const
{
    return Model;
}

Dictionary NormalizedDiffusionBSSRDFFactory::get_model_metadata() const
{
    return
        Dictionary()
            .insert("name", Model)
            .insert("label", "Normalized Diffusion BSSRDF");
}

DictionaryArray NormalizedDiffusionBSSRDFFactory::get_input_metadata() const
{
    DictionaryArray metadata;

    metadata.push_back(
        Dictionary()
            .insert("name", "weight")
            .insert("label", "Weight")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Textures"))
            .insert("use", "optional")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance")
            .insert("label", "Diffuse Surface Reflectance")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary()
                    .insert("color", "Colors")
                    .insert("texture_instance", "Textures"))
            .insert("use", "required")
            .insert("default", "0.5"));

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance_multiplier")
            .insert("label", "Diffuse Surface Reflectance Multiplier")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Textures"))
            .insert("use", "optional")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "dmfp")
            .insert("label", "Diffuse Mean Free Path")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary()
                    .insert("color", "Colors")
                    .insert("texture_instance", "Textures"))
            .insert("use", "required")
            .insert("default", "0.5"));

    metadata.push_back(
        Dictionary()
            .insert("name", "dmfp_multiplier")
            .insert("label", "Diffuse Mean Free Path Multiplier")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Textures"))
            .insert("use", "optional")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "outside_ior")
            .insert("label", "Outside Index of Refraction")
            .insert("type", "numeric")
            .insert("min_value", "1.0")
            .insert("max_value", "2.5")
            .insert("use", "required")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "inside_ior")
            .insert("label", "Inside Index of Refraction")
            .insert("type", "numeric")
            .insert("min_value", "1.0")
            .insert("max_value", "2.5")
            .insert("use", "required")
            .insert("default", "1.3"));

    return metadata;
}

auto_release_ptr<BSSRDF> NormalizedDiffusionBSSRDFFactory::create(
    const char*         name,
    const ParamArray&   params) const
{
    return auto_release_ptr<BSSRDF>(new NormalizedDiffusionBSSRDF(name, params));
}

auto_release_ptr<BSSRDF> NormalizedDiffusionBSSRDFFactory::static_create(
    const char*         name,
    const ParamArray&   params)
{
    return auto_release_ptr<BSSRDF>(new NormalizedDiffusionBSSRDF(name, params));
}

}   // namespace renderer
<commit_msg>Code tweaks<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2015-2016 Esteban Tovagliari, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

// Interface header.
#include "normalizeddiffusionbssrdf.h"

// appleseed.renderer headers.
#include "renderer/modeling/bssrdf/bssrdfsample.h"
#include "renderer/modeling/bssrdf/separablebssrdf.h"
#include "renderer/modeling/bssrdf/sss.h"
#include "renderer/modeling/input/inputevaluator.h"

// appleseed.foundation headers.
#include "foundation/image/colorspace.h"
#include "foundation/math/cdf.h"
#include "foundation/math/scalar.h"
#include "foundation/math/vector.h"
#include "foundation/utility/containers/dictionary.h"
#include "foundation/utility/containers/specializedarrays.h"
#include "foundation/utility/memory.h"

// Standard headers.
#include <cmath>
#include <cstddef>

// Forward declarations.
namespace renderer  { class Assembly; }
namespace renderer  { class ShadingContext; }

using namespace foundation;
using namespace std;

namespace renderer
{

namespace
{
    //
    // Normalized diffusion BSSRDF.
    //
    // Reference:
    //
    //   Approximate Reflectance Profiles for Efficient Subsurface Scattering
    //   Per H. Christensen, Brent Burley
    //   http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf
    //

    const char* Model = "normalized_diffusion_bssrdf";

    class NormalizedDiffusionBSSRDF
      : public SeparableBSSRDF
    {
      public:
        NormalizedDiffusionBSSRDF(
            const char*         name,
            const ParamArray&   params)
          : SeparableBSSRDF(name, params)
        {
            m_inputs.declare("weight", InputFormatScalar, "1.0");
            m_inputs.declare("reflectance", InputFormatSpectralReflectance);
            m_inputs.declare("reflectance_multiplier", InputFormatScalar, "1.0");
            m_inputs.declare("dmfp", InputFormatSpectralReflectance);
            m_inputs.declare("dmfp_multiplier", InputFormatScalar, "1.0");
            m_inputs.declare("outside_ior", InputFormatScalar);
            m_inputs.declare("inside_ior", InputFormatScalar);
        }

        virtual void release() APPLESEED_OVERRIDE
        {
            delete this;
        }

        virtual const char* get_model() const APPLESEED_OVERRIDE
        {
            return Model;
        }

        virtual size_t compute_input_data_size(
            const Assembly&     assembly) const APPLESEED_OVERRIDE
        {
            return align(sizeof(NormalizedDiffusionBSSRDFInputValues), 16);
        }

        virtual void prepare_inputs(void* data) const APPLESEED_OVERRIDE
        {
            NormalizedDiffusionBSSRDFInputValues* values =
                reinterpret_cast<NormalizedDiffusionBSSRDFInputValues*>(data);

            // Precompute the relative index of refraction.
            values->m_eta = values->m_outside_ior / values->m_inside_ior;

            // Apply multipliers.
            values->m_reflectance *= static_cast<float>(values->m_reflectance_multiplier);
            values->m_dmfp *= static_cast<float>(values->m_dmfp_multiplier);

            if (values->m_reflectance.size() != values->m_dmfp.size())
            {
                // Since it does not really make sense to convert a dmfp,
                // a per channel distance, as if it were a color,
                // we instead always convert the reflectance to match the
                // size of the dmfp.
                if (values->m_dmfp.is_spectral())
                {
                    Spectrum::upgrade(
                        values->m_reflectance,
                        values->m_reflectance);
                }
                else
                {
                    Spectrum::downgrade(
                        m_lighting_conditions,
                        values->m_reflectance,
                        values->m_reflectance);
                }
            }

            // Clamp input values.
            values->m_reflectance = clamp(values->m_reflectance, 0.001f, 0.999f);
            values->m_dmfp = clamp_low(values->m_dmfp, 1.0e-5f);

            // Build a CDF for channel sampling.

            values->m_s.resize(values->m_reflectance.size());
            values->m_channel_pdf.resize(values->m_reflectance.size());
            values->m_channel_cdf.resize(values->m_reflectance.size());

            float cumulated_pdf = 0.0f;
            for (size_t i = 0, e = values->m_channel_pdf.size(); i < e; ++i)
            {
                const double a = static_cast<double>(values->m_reflectance[i]);
                const double s = normalized_diffusion_s(a);
                values->m_s[i] = static_cast<float>(s);

                const double l = values->m_dmfp[i];
                const float pdf = static_cast<float>(s / l);
                values->m_channel_pdf[i] = pdf;

                cumulated_pdf += pdf;
                values->m_channel_cdf[i] = cumulated_pdf;
            }

            const float rcp_cumulated_pdf = 1.0f / cumulated_pdf;
            values->m_channel_pdf *= rcp_cumulated_pdf;
            values->m_channel_cdf *= rcp_cumulated_pdf;
            values->m_channel_cdf[values->m_channel_cdf.size() - 1] = 1.0f;

            // Precompute the (square of the) max radius.
            values->m_rmax2 = 0.0;
            for (size_t i = 0, e = values->m_dmfp.size(); i < e; ++i)
            {
                const double l = static_cast<double>(values->m_dmfp[i]);
                values->m_rmax2 =
                    max(
                        normalized_diffusion_max_radius(l, values->m_s[i]),
                        values->m_rmax2);
            }

            values->m_rmax2 *= values->m_rmax2;
        }

        virtual bool sample(
            SamplingContext&    sampling_context,
            const void*         data,
            BSSRDFSample&       sample) const APPLESEED_OVERRIDE
        {
            const NormalizedDiffusionBSSRDFInputValues* values =
                reinterpret_cast<const NormalizedDiffusionBSSRDFInputValues*>(data);

            if (values->m_weight == 0.0)
                return false;

            sampling_context.split_in_place(3, 1);
            const Vector3d s = sampling_context.next_vector2<3>();

            // Sample a channel.
            const float* cdf_begin = &values->m_channel_cdf[0];
            const size_t channel =
                sample_cdf(
                    cdf_begin,
                    cdf_begin + values->m_channel_cdf.size(),
                    s[0]);

            // Sample a radius.
            const double l = values->m_dmfp[channel];
            const double radius =
                normalized_diffusion_sample(s[1], l, values->m_s[channel]);

            // Sample an angle.
            const double phi = TwoPi * s[2];

            sample.m_eta = values->m_eta;
            sample.m_channel = channel;
            sample.m_point = Vector2d(radius * cos(phi), radius * sin(phi));
            sample.m_rmax2 = values->m_rmax2;

            return true;
        }

        virtual double get_eta(
            const void*         data) const APPLESEED_OVERRIDE
        {
            return reinterpret_cast<const NormalizedDiffusionBSSRDFInputValues*>(data)->m_eta;
        }

        virtual void evaluate_profile(
            const void*         data,
            const double        square_radius,
            Spectrum&           value) const APPLESEED_OVERRIDE
        {
            const NormalizedDiffusionBSSRDFInputValues* values =
                reinterpret_cast<const NormalizedDiffusionBSSRDFInputValues*>(data);

            const double radius = sqrt(square_radius);

            value.resize(values->m_reflectance.size());

            for (size_t i = 0, e = value.size(); i < e; ++i)
            {
                const double a = values->m_reflectance[i];
                const double s = values->m_s[i];
                const double l = values->m_dmfp[i];
                value[i] = static_cast<float>(normalized_diffusion_profile(radius, l, s, a));
            }

            // Return r * R(r) * weight.
            value *= static_cast<float>(radius * values->m_weight);
        }

        virtual double evaluate_pdf(
            const void*         data,
            const size_t        channel,
            const double        radius) const APPLESEED_OVERRIDE
        {
            const NormalizedDiffusionBSSRDFInputValues* values =
                reinterpret_cast<const NormalizedDiffusionBSSRDFInputValues*>(data);

            // PDF of the sampled radius.
            double pdf_radius = 0.0;
            for (size_t i = 0, e = values->m_reflectance.size(); i < e; ++i)
            {
                const double l = values->m_dmfp[i];
                pdf_radius +=
                    normalized_diffusion_pdf(
                        radius,
                        l,
                        values->m_s[i])
                    * values->m_channel_pdf[i];
            }

            // PDF of the sampled angle.
            const double pdf_angle = RcpTwoPi;

            // Compute and return the final PDF.
            return pdf_radius * pdf_angle;
        }

      private:
        static const LightingConditions m_lighting_conditions;
    };

    const LightingConditions NormalizedDiffusionBSSRDF::m_lighting_conditions(
        IlluminantCIED65,
        XYZCMFCIE196410Deg);
}

//
// NormalizedDiffusionBSSRDFFactory class implementation.
//

const char* NormalizedDiffusionBSSRDFFactory::get_model() const
{
    return Model;
}

Dictionary NormalizedDiffusionBSSRDFFactory::get_model_metadata() const
{
    return
        Dictionary()
            .insert("name", Model)
            .insert("label", "Normalized Diffusion BSSRDF");
}

DictionaryArray NormalizedDiffusionBSSRDFFactory::get_input_metadata() const
{
    DictionaryArray metadata;

    metadata.push_back(
        Dictionary()
            .insert("name", "weight")
            .insert("label", "Weight")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Textures"))
            .insert("use", "optional")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance")
            .insert("label", "Diffuse Surface Reflectance")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary()
                    .insert("color", "Colors")
                    .insert("texture_instance", "Textures"))
            .insert("use", "required")
            .insert("default", "0.5"));

    metadata.push_back(
        Dictionary()
            .insert("name", "reflectance_multiplier")
            .insert("label", "Diffuse Surface Reflectance Multiplier")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Textures"))
            .insert("use", "optional")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "dmfp")
            .insert("label", "Diffuse Mean Free Path")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary()
                    .insert("color", "Colors")
                    .insert("texture_instance", "Textures"))
            .insert("use", "required")
            .insert("default", "0.5"));

    metadata.push_back(
        Dictionary()
            .insert("name", "dmfp_multiplier")
            .insert("label", "Diffuse Mean Free Path Multiplier")
            .insert("type", "colormap")
            .insert("entity_types",
                Dictionary().insert("texture_instance", "Textures"))
            .insert("use", "optional")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "outside_ior")
            .insert("label", "Outside Index of Refraction")
            .insert("type", "numeric")
            .insert("min_value", "1.0")
            .insert("max_value", "2.5")
            .insert("use", "required")
            .insert("default", "1.0"));

    metadata.push_back(
        Dictionary()
            .insert("name", "inside_ior")
            .insert("label", "Inside Index of Refraction")
            .insert("type", "numeric")
            .insert("min_value", "1.0")
            .insert("max_value", "2.5")
            .insert("use", "required")
            .insert("default", "1.3"));

    return metadata;
}

auto_release_ptr<BSSRDF> NormalizedDiffusionBSSRDFFactory::create(
    const char*         name,
    const ParamArray&   params) const
{
    return auto_release_ptr<BSSRDF>(new NormalizedDiffusionBSSRDF(name, params));
}

auto_release_ptr<BSSRDF> NormalizedDiffusionBSSRDFFactory::static_create(
    const char*         name,
    const ParamArray&   params)
{
    return auto_release_ptr<BSSRDF>(new NormalizedDiffusionBSSRDF(name, params));
}

}   // namespace renderer
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "datetime.h"
#include "utils.h"
#include "serialise.h"

#include <xapian.h>

std::string
Serialise::serialise(char field_type, const std::string &field_value)
{
	switch (field_type) {
		case NUMERIC_TYPE:
			return numeric(field_value);
		case DATE_TYPE:
			return date(field_value);
		case BOOLEAN_TYPE:
			return boolean(field_value);
		case STRING_TYPE:
			return field_value;
	}
	return "";
}

std::string
Serialise::numeric(const std::string &field_value)
{
	double val;
	if (isNumeric(field_value)) {
		val = strtodouble(field_value);
		return Xapian::sortable_serialise(val);
	}
	return "";
}

std::string
Serialise::date(const std::string &field_value)
{
	double timestamp;
	if (isNumeric(field_value)) {
		timestamp = strtodouble(field_value);
		return Xapian::sortable_serialise(timestamp);
	} else {
		timestamp = Datetime::timestamp(field_value);
		return Xapian::sortable_serialise(timestamp);
	}
	return "";
}

std::string
Serialise::date(int timeinfo_[])
{
	time_t tt = 0;
	struct tm *timeinfo = gmtime(&tt);
	timeinfo->tm_year   = timeinfo_[5];
	timeinfo->tm_mon    = timeinfo_[4];
	timeinfo->tm_mday   = timeinfo_[3];
	timeinfo->tm_hour   = timeinfo_[2];
	timeinfo->tm_min    = timeinfo_[1];
	timeinfo->tm_sec    = timeinfo_[0];
	return std::to_string(Datetime::timegm(timeinfo));
}

std::string
Serialise::cartesian(const Cartesian &norm_cartesian)
{
	unsigned int x = Swap4Bytes(((unsigned int)(norm_cartesian.x * DOUBLE2INT) + MAXDOU2INT));
	unsigned int y = Swap4Bytes(((unsigned int)(norm_cartesian.y * DOUBLE2INT) + MAXDOU2INT));
	unsigned int z = Swap4Bytes(((unsigned int)(norm_cartesian.z * DOUBLE2INT) + MAXDOU2INT));
	const char serialise[] = { (char)(x & 0xFF), (char)((x >>  8) & 0xFF), (char)((x >> 16) & 0xFF), (char)((x >> 24) & 0xFF),
							   (char)(y & 0xFF), (char)((y >>  8) & 0xFF), (char)((y >> 16) & 0xFF), (char)((y >> 24) & 0xFF),
							   (char)(z & 0xFF), (char)((z >>  8) & 0xFF), (char)((z >> 16) & 0xFF), (char)((z >> 24) & 0xFF) };
	return std::string(serialise, SIZE_SERIALISE_CARTESIAN);
}

std::string
Serialise::trixel_id(uInt64 id)
{
	id = Swap7Bytes(id);
	const char serialise[] = { (char)(id & 0xFF), (char)((id >>  8) & 0xFF), (char)((id >> 16) & 0xFF), (char)((id >> 24) & 0xFF),
							   (char)((id >> 32) & 0xFF), (char)((id >> 40) & 0xFF), (char)((id >> 48) & 0xFF) };
	return std::string(serialise, SIZE_BYTES_ID);
}

std::string
Serialise::boolean(const std::string &field_value)
{
	if (field_value.empty()) {
		return "f";
	} else if (strcasecmp(field_value.c_str(), "true") == 0) {
		return "t";
	} else if (strcasecmp(field_value.c_str(), "false") == 0) {
		return "f";
	} else {
		return "";
	}
}

std::string
Serialise::type(char type)
{
	switch (type) {
		case STRING_TYPE:  return STRING_STR;
		case NUMERIC_TYPE: return NUMERIC_STR;
		case BOOLEAN_TYPE: return BOOLEAN_STR;
		case GEO_TYPE:     return GEO_STR;
		case DATE_TYPE:    return DATE_STR;
		case OBJECT_TYPE:  return OBJECT_STR;
		case ARRAY_TYPE:   return ARRAY_STR;
	}
	return "";
}

std::string
Unserialise::unserialise(char field_type, const std::string &field_name, const std::string &serialise_val)
{
	switch (field_type) {
		case NUMERIC_TYPE:
			return numeric(serialise_val);
		case DATE_TYPE:
			return date(serialise_val);
		case BOOLEAN_TYPE:
			return boolean(serialise_val);
		case STRING_TYPE:
			return serialise_val;
	}
	return "";
}

std::string
Unserialise::numeric(const std::string &serialise_val)
{
	return std::to_string(Xapian::sortable_unserialise(serialise_val));
}

std::string
Unserialise::date(const std::string &serialise_val)
{
	static char date[25];
	double epoch = Xapian::sortable_unserialise(serialise_val);
	time_t timestamp = (time_t) epoch;
	int msec = round((epoch - timestamp) * 1000);
	struct tm *timeinfo = gmtime(&timestamp);
	sprintf(date, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3d", timeinfo->tm_year + _START_YEAR,
		timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min,
		timeinfo->tm_sec, msec);
	return date;
}

Cartesian
Unserialise::cartesian(const std::string &str)
{
	double x = (((unsigned int)str.at(0) << 24) & 0xFF000000) | (((unsigned int)str.at(1) << 16) & 0xFF0000) | (((unsigned int)str.at(2) << 8) & 0xFF00) | (((unsigned int)str.at(3)) & 0xFF);
	double y = (((unsigned int)str.at(4) << 24) & 0xFF000000) | (((unsigned int)str.at(5) << 16) & 0xFF0000) | (((unsigned int)str.at(6) << 8) & 0xFF00) | (((unsigned int)str.at(7)) & 0xFF);
	double z = (((unsigned int)str.at(8) << 24) & 0xFF000000) | (((unsigned int)str.at(9) << 16) & 0xFF0000) | (((unsigned int)str.at(10) << 8) & 0xFF00) | (((unsigned int)str.at(11)) & 0xFF);
	return Cartesian((x - MAXDOU2INT) / DOUBLE2INT, (y - MAXDOU2INT) / DOUBLE2INT, (z - MAXDOU2INT) / DOUBLE2INT);
}

uInt64
Unserialise::trixel_id(const std::string &str)
{
	uInt64 id = (((uInt64)str.at(0) << 48) & 0xFF000000000000) | (((uInt64)str.at(1) << 40) & 0xFF0000000000) | \
				(((uInt64)str.at(2) << 32) & 0xFF00000000)     | (((uInt64)str.at(3) << 24) & 0xFF000000)     | \
				(((uInt64)str.at(4) << 16) & 0xFF0000)         | (((uInt64)str.at(5) <<  8) & 0xFF00)         | \
				(str.at(6) & 0xFF);
	return id;
}

std::string
Unserialise::boolean(const std::string &serialise_val)
{
	return serialise_val.at(0) == 'f' ? "false" : "true";
}

std::string
Unserialise::type(const std::string &str)
{
	std::string low(stringtolower(str));
	if (low.compare(NUMERIC_STR) == 0 || (low.size() == 1 && low[0] == NUMERIC_TYPE)) {
		return std::string(1, toupper(NUMERIC_TYPE));
	} else if (low.compare(GEO_STR) == 0     || (low.size() == 1 && low[0] == GEO_TYPE)) {
		return std::string(1, toupper(GEO_TYPE));
	} else if (low.compare(STRING_STR) == 0  || (low.size() == 1 && low[0] == STRING_TYPE)) {
		return std::string(1, toupper(STRING_TYPE));
	} else if (low.compare(BOOLEAN_STR) == 0 || (low.size() == 1 && low[0] == BOOLEAN_TYPE)) {
		return std::string(1, toupper(BOOLEAN_TYPE));
	} else if (low.compare(DATE_STR) == 0    || (low.size() == 1 && low[0] == DATE_TYPE)) {
		return std::string(1, toupper(DATE_TYPE));
	} else {
		return std::string(1, toupper(STRING_TYPE));
	}
}<commit_msg>Datetime::timestamp return a double<commit_after>/*
 * Copyright (C) 2015 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "datetime.h"
#include "utils.h"
#include "serialise.h"

#include <xapian.h>

std::string
Serialise::serialise(char field_type, const std::string &field_value)
{
	switch (field_type) {
		case NUMERIC_TYPE:
			return numeric(field_value);
		case DATE_TYPE:
			return date(field_value);
		case BOOLEAN_TYPE:
			return boolean(field_value);
		case STRING_TYPE:
			return field_value;
	}
	return "";
}

std::string
Serialise::numeric(const std::string &field_value)
{
	double val;
	if (isNumeric(field_value)) {
		val = strtodouble(field_value);
		return Xapian::sortable_serialise(val);
	}
	return "";
}

std::string
Serialise::date(const std::string &field_value)
{
	try {
		double timestamp = Datetime::timestamp(field_value);
		return Xapian::sortable_serialise(timestamp);
	} catch (const std::exception &err) {
		LOG_ERR(NULL, "ERROR: %s\n", err.what());
	}
	return "";
}

std::string
Serialise::date(int timeinfo_[])
{
	time_t tt = 0;
	struct tm *timeinfo = gmtime(&tt);
	timeinfo->tm_year   = timeinfo_[5];
	timeinfo->tm_mon    = timeinfo_[4];
	timeinfo->tm_mday   = timeinfo_[3];
	timeinfo->tm_hour   = timeinfo_[2];
	timeinfo->tm_min    = timeinfo_[1];
	timeinfo->tm_sec    = timeinfo_[0];
	return std::to_string(Datetime::timegm(timeinfo));
}

std::string
Serialise::cartesian(const Cartesian &norm_cartesian)
{
	unsigned int x = Swap4Bytes(((unsigned int)(norm_cartesian.x * DOUBLE2INT) + MAXDOU2INT));
	unsigned int y = Swap4Bytes(((unsigned int)(norm_cartesian.y * DOUBLE2INT) + MAXDOU2INT));
	unsigned int z = Swap4Bytes(((unsigned int)(norm_cartesian.z * DOUBLE2INT) + MAXDOU2INT));
	const char serialise[] = { (char)(x & 0xFF), (char)((x >>  8) & 0xFF), (char)((x >> 16) & 0xFF), (char)((x >> 24) & 0xFF),
							   (char)(y & 0xFF), (char)((y >>  8) & 0xFF), (char)((y >> 16) & 0xFF), (char)((y >> 24) & 0xFF),
							   (char)(z & 0xFF), (char)((z >>  8) & 0xFF), (char)((z >> 16) & 0xFF), (char)((z >> 24) & 0xFF) };
	return std::string(serialise, SIZE_SERIALISE_CARTESIAN);
}

std::string
Serialise::trixel_id(uInt64 id)
{
	id = Swap7Bytes(id);
	const char serialise[] = { (char)(id & 0xFF), (char)((id >>  8) & 0xFF), (char)((id >> 16) & 0xFF), (char)((id >> 24) & 0xFF),
							   (char)((id >> 32) & 0xFF), (char)((id >> 40) & 0xFF), (char)((id >> 48) & 0xFF) };
	return std::string(serialise, SIZE_BYTES_ID);
}

std::string
Serialise::boolean(const std::string &field_value)
{
	if (field_value.empty()) {
		return "f";
	} else if (strcasecmp(field_value.c_str(), "true") == 0) {
		return "t";
	} else if (strcasecmp(field_value.c_str(), "false") == 0) {
		return "f";
	} else {
		return "";
	}
}

std::string
Serialise::type(char type)
{
	switch (type) {
		case STRING_TYPE:  return STRING_STR;
		case NUMERIC_TYPE: return NUMERIC_STR;
		case BOOLEAN_TYPE: return BOOLEAN_STR;
		case GEO_TYPE:     return GEO_STR;
		case DATE_TYPE:    return DATE_STR;
		case OBJECT_TYPE:  return OBJECT_STR;
		case ARRAY_TYPE:   return ARRAY_STR;
	}
	return "";
}

std::string
Unserialise::unserialise(char field_type, const std::string &field_name, const std::string &serialise_val)
{
	switch (field_type) {
		case NUMERIC_TYPE:
			return numeric(serialise_val);
		case DATE_TYPE:
			return date(serialise_val);
		case BOOLEAN_TYPE:
			return boolean(serialise_val);
		case STRING_TYPE:
			return serialise_val;
	}
	return "";
}

std::string
Unserialise::numeric(const std::string &serialise_val)
{
	return std::to_string(Xapian::sortable_unserialise(serialise_val));
}

std::string
Unserialise::date(const std::string &serialise_val)
{
	static char date[25];
	double epoch = Xapian::sortable_unserialise(serialise_val);
	time_t timestamp = (time_t) epoch;
	int msec = round((epoch - timestamp) * 1000);
	struct tm *timeinfo = gmtime(&timestamp);
	sprintf(date, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3d", timeinfo->tm_year + _START_YEAR,
		timeinfo->tm_mon + 1, timeinfo->tm_mday, timeinfo->tm_hour, timeinfo->tm_min,
		timeinfo->tm_sec, msec);
	return date;
}

Cartesian
Unserialise::cartesian(const std::string &str)
{
	double x = (((unsigned int)str.at(0) << 24) & 0xFF000000) | (((unsigned int)str.at(1) << 16) & 0xFF0000) | (((unsigned int)str.at(2) << 8) & 0xFF00) | (((unsigned int)str.at(3)) & 0xFF);
	double y = (((unsigned int)str.at(4) << 24) & 0xFF000000) | (((unsigned int)str.at(5) << 16) & 0xFF0000) | (((unsigned int)str.at(6) << 8) & 0xFF00) | (((unsigned int)str.at(7)) & 0xFF);
	double z = (((unsigned int)str.at(8) << 24) & 0xFF000000) | (((unsigned int)str.at(9) << 16) & 0xFF0000) | (((unsigned int)str.at(10) << 8) & 0xFF00) | (((unsigned int)str.at(11)) & 0xFF);
	return Cartesian((x - MAXDOU2INT) / DOUBLE2INT, (y - MAXDOU2INT) / DOUBLE2INT, (z - MAXDOU2INT) / DOUBLE2INT);
}

uInt64
Unserialise::trixel_id(const std::string &str)
{
	uInt64 id = (((uInt64)str.at(0) << 48) & 0xFF000000000000) | (((uInt64)str.at(1) << 40) & 0xFF0000000000) | \
				(((uInt64)str.at(2) << 32) & 0xFF00000000)     | (((uInt64)str.at(3) << 24) & 0xFF000000)     | \
				(((uInt64)str.at(4) << 16) & 0xFF0000)         | (((uInt64)str.at(5) <<  8) & 0xFF00)         | \
				(str.at(6) & 0xFF);
	return id;
}

std::string
Unserialise::boolean(const std::string &serialise_val)
{
	return serialise_val.at(0) == 'f' ? "false" : "true";
}

std::string
Unserialise::type(const std::string &str)
{
	std::string low(stringtolower(str));
	if (low.compare(NUMERIC_STR) == 0 || (low.size() == 1 && low[0] == NUMERIC_TYPE)) {
		return std::string(1, toupper(NUMERIC_TYPE));
	} else if (low.compare(GEO_STR) == 0     || (low.size() == 1 && low[0] == GEO_TYPE)) {
		return std::string(1, toupper(GEO_TYPE));
	} else if (low.compare(STRING_STR) == 0  || (low.size() == 1 && low[0] == STRING_TYPE)) {
		return std::string(1, toupper(STRING_TYPE));
	} else if (low.compare(BOOLEAN_STR) == 0 || (low.size() == 1 && low[0] == BOOLEAN_TYPE)) {
		return std::string(1, toupper(BOOLEAN_TYPE));
	} else if (low.compare(DATE_STR) == 0    || (low.size() == 1 && low[0] == DATE_TYPE)) {
		return std::string(1, toupper(DATE_TYPE));
	} else {
		return std::string(1, toupper(STRING_TYPE));
	}
}<|endoftext|>