<commit_before>// Copyright (c) 2010 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/autocomplete_history_manager.h"

#include <vector>

#include "base/string16.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/credit_card.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "webkit/glue/form_data.h"

using webkit_glue::FormData;

namespace {

// Limit on the number of suggestions to appear in the pop-up menu under an
// text input element in a form.
const int kMaxAutocompleteMenuItems = 6;

// The separator characters for SSNs.
const string16 kSSNSeparators = ASCIIToUTF16(" -");

bool IsSSN(const string16& text) {
  string16 number_string;
  RemoveChars(text, kSSNSeparators.c_str(), &number_string);

  // A SSN is of the form AAA-GG-SSSS (A = area number, G = group number, S =
  // serial number). The validation we do here is simply checking if the area,
  // group, and serial numbers are valid. It is possible to check if the group
  // number is valid for the given area, but that data changes all the time.
  //
  // See: http://www.socialsecurity.gov/history/ssn/geocard.html
  //      http://www.socialsecurity.gov/employer/stateweb.htm
  //      http://www.socialsecurity.gov/employer/ssnvhighgroup.htm
  if (number_string.length() != 9 || !IsStringASCII(number_string))
    return false;

  int area;
  if (!base::StringToInt(number_string.begin(),
                         number_string.begin() + 3,
                         &area))
    return false;
  if (area < 1 ||
      area == 666 ||
      (area > 733 && area < 750) ||
      area > 772)
    return false;

  int group;
  if (!base::StringToInt(number_string.begin() + 3,
                         number_string.begin() + 5,
                         &group) || group == 0)
    return false;

  int serial;
  if (!base::StringToInt(number_string.begin() + 5,
                         number_string.begin() + 9,
                         &serial) || serial == 0)
    return false;

  return true;
}

}  // namespace

AutocompleteHistoryManager::AutocompleteHistoryManager(
    TabContents* tab_contents)
    : tab_contents_(tab_contents),
      pending_query_handle_(0),
      query_id_(0) {
  profile_ = tab_contents_->profile();
  // May be NULL in unit tests.
  web_data_service_ = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS);
  autofill_enabled_.Init(prefs::kAutoFillEnabled, profile_->GetPrefs(), NULL);
}

AutocompleteHistoryManager::~AutocompleteHistoryManager() {
  CancelPendingQuery();
}

bool AutocompleteHistoryManager::OnMessageReceived(
    const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(AutocompleteHistoryManager, message)
    IPC_MESSAGE_HANDLER(ViewHostMsg_RemoveAutocompleteEntry,
                        OnRemoveAutocompleteEntry)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void AutocompleteHistoryManager::OnFormSubmitted(const FormData& form) {
  if (!*autofill_enabled_)
    return;

  if (profile_->IsOffTheRecord())
    return;

  // Don't save data that was submitted through JavaScript.
  if (!form.user_submitted)
    return;

  // We put the following restriction on stored FormFields:
  //  - non-empty name
  //  - non-empty value
  //  - text field
  //  - value is not a credit card number
  //  - value is not a SSN
  std::vector<webkit_glue::FormField> values;
  for (std::vector<webkit_glue::FormField>::const_iterator iter =
           form.fields.begin();
       iter != form.fields.end(); ++iter) {
    if (!iter->value().empty() &&
        !iter->name().empty() &&
        iter->form_control_type() == ASCIIToUTF16("text") &&
        !CreditCard::IsCreditCardNumber(iter->value()) &&
        !IsSSN(iter->value()))
      values.push_back(*iter);
  }

  if (!values.empty() && web_data_service_.get())
    web_data_service_->AddFormFields(values);
}

void AutocompleteHistoryManager::OnRemoveAutocompleteEntry(
    const string16& name, const string16& value) {
  if (web_data_service_.get())
    web_data_service_->RemoveFormValueForElementName(name, value);
}

void AutocompleteHistoryManager::OnGetAutocompleteSuggestions(
    int query_id,
    const string16& name,
    const string16& prefix,
    const std::vector<string16>& autofill_values,
    const std::vector<string16>& autofill_labels,
    const std::vector<string16>& autofill_icons,
    const std::vector<int>& autofill_unique_ids) {
  CancelPendingQuery();

  query_id_ = query_id;
  autofill_values_ = autofill_values;
  autofill_labels_ = autofill_labels;
  autofill_icons_ = autofill_icons;
  autofill_unique_ids_ = autofill_unique_ids;
  if (!*autofill_enabled_) {
    SendSuggestions(NULL);
    return;
  }

  if (web_data_service_.get()) {
    pending_query_handle_ = web_data_service_->GetFormValuesForElementName(
        name, prefix, kMaxAutocompleteMenuItems, this);
  }
}

void AutocompleteHistoryManager::OnWebDataServiceRequestDone(
    WebDataService::Handle h,
    const WDTypedResult* result) {
  DCHECK(pending_query_handle_);
  pending_query_handle_ = 0;

  if (!*autofill_enabled_) {
    SendSuggestions(NULL);
    return;
  }

  DCHECK(result);
  DCHECK(result->GetType() == AUTOFILL_VALUE_RESULT);
  const WDResult<std::vector<string16> >* autofill_result =
      static_cast<const WDResult<std::vector<string16> >*>(result);
  std::vector<string16> suggestions = autofill_result->GetValue();
  SendSuggestions(&suggestions);
}

AutocompleteHistoryManager::AutocompleteHistoryManager(
    Profile* profile, WebDataService* wds) : tab_contents_(NULL),
                                             profile_(profile),
                                             web_data_service_(wds),
                                             pending_query_handle_(0),
                                             query_id_(0) {
  autofill_enabled_.Init(
      prefs::kAutoFillEnabled, profile_->GetPrefs(), NULL);
}

void AutocompleteHistoryManager::CancelPendingQuery() {
  if (pending_query_handle_) {
    SendSuggestions(NULL);
    if (web_data_service_.get())
      web_data_service_->CancelRequest(pending_query_handle_);
    pending_query_handle_ = 0;
  }
}

void AutocompleteHistoryManager::SendSuggestions(
    const std::vector<string16>* suggestions) {
  if (suggestions) {
    // Combine AutoFill and Autocomplete values into values and labels.
    for (size_t i = 0; i < suggestions->size(); ++i) {
      bool unique = true;
      for (size_t j = 0; j < autofill_values_.size(); ++j) {
        // Don't add duplicate values.
        if (autofill_values_[j] == (*suggestions)[i]) {
          unique = false;
          break;
        }
      }

      if (unique) {
        autofill_values_.push_back((*suggestions)[i]);
        autofill_labels_.push_back(string16());
        autofill_icons_.push_back(string16());
        autofill_unique_ids_.push_back(0);  // 0 means no profile.
      }
    }
  }

  RenderViewHost* host = tab_contents_->render_view_host();
  if (host) {
    host->Send(new ViewMsg_AutoFillSuggestionsReturned(host->routing_id(),
                                                       query_id_,
                                                       autofill_values_,
                                                       autofill_labels_,
                                                       autofill_icons_,
                                                       autofill_unique_ids_));
  }

  query_id_ = 0;
  autofill_values_.clear();
  autofill_labels_.clear();
  autofill_icons_.clear();
  autofill_unique_ids_.clear();
}
<commit_msg>DCHECK(result) keeps firing in AutocompleteHistoryManager::OnWebDataServiceRequestDone()<commit_after>// Copyright (c) 2010 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/autocomplete_history_manager.h"

#include <vector>

#include "base/string16.h"
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/autofill/credit_card.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h"
#include "webkit/glue/form_data.h"

using webkit_glue::FormData;

namespace {

// Limit on the number of suggestions to appear in the pop-up menu under an
// text input element in a form.
const int kMaxAutocompleteMenuItems = 6;

// The separator characters for SSNs.
const string16 kSSNSeparators = ASCIIToUTF16(" -");

bool IsSSN(const string16& text) {
  string16 number_string;
  RemoveChars(text, kSSNSeparators.c_str(), &number_string);

  // A SSN is of the form AAA-GG-SSSS (A = area number, G = group number, S =
  // serial number). The validation we do here is simply checking if the area,
  // group, and serial numbers are valid. It is possible to check if the group
  // number is valid for the given area, but that data changes all the time.
  //
  // See: http://www.socialsecurity.gov/history/ssn/geocard.html
  //      http://www.socialsecurity.gov/employer/stateweb.htm
  //      http://www.socialsecurity.gov/employer/ssnvhighgroup.htm
  if (number_string.length() != 9 || !IsStringASCII(number_string))
    return false;

  int area;
  if (!base::StringToInt(number_string.begin(),
                         number_string.begin() + 3,
                         &area))
    return false;
  if (area < 1 ||
      area == 666 ||
      (area > 733 && area < 750) ||
      area > 772)
    return false;

  int group;
  if (!base::StringToInt(number_string.begin() + 3,
                         number_string.begin() + 5,
                         &group) || group == 0)
    return false;

  int serial;
  if (!base::StringToInt(number_string.begin() + 5,
                         number_string.begin() + 9,
                         &serial) || serial == 0)
    return false;

  return true;
}

}  // namespace

AutocompleteHistoryManager::AutocompleteHistoryManager(
    TabContents* tab_contents)
    : tab_contents_(tab_contents),
      pending_query_handle_(0),
      query_id_(0) {
  profile_ = tab_contents_->profile();
  // May be NULL in unit tests.
  web_data_service_ = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS);
  autofill_enabled_.Init(prefs::kAutoFillEnabled, profile_->GetPrefs(), NULL);
}

AutocompleteHistoryManager::~AutocompleteHistoryManager() {
  CancelPendingQuery();
}

bool AutocompleteHistoryManager::OnMessageReceived(
    const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(AutocompleteHistoryManager, message)
    IPC_MESSAGE_HANDLER(ViewHostMsg_RemoveAutocompleteEntry,
                        OnRemoveAutocompleteEntry)
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}

void AutocompleteHistoryManager::OnFormSubmitted(const FormData& form) {
  if (!*autofill_enabled_)
    return;

  if (profile_->IsOffTheRecord())
    return;

  // Don't save data that was submitted through JavaScript.
  if (!form.user_submitted)
    return;

  // We put the following restriction on stored FormFields:
  //  - non-empty name
  //  - non-empty value
  //  - text field
  //  - value is not a credit card number
  //  - value is not a SSN
  std::vector<webkit_glue::FormField> values;
  for (std::vector<webkit_glue::FormField>::const_iterator iter =
           form.fields.begin();
       iter != form.fields.end(); ++iter) {
    if (!iter->value().empty() &&
        !iter->name().empty() &&
        iter->form_control_type() == ASCIIToUTF16("text") &&
        !CreditCard::IsCreditCardNumber(iter->value()) &&
        !IsSSN(iter->value()))
      values.push_back(*iter);
  }

  if (!values.empty() && web_data_service_.get())
    web_data_service_->AddFormFields(values);
}

void AutocompleteHistoryManager::OnRemoveAutocompleteEntry(
    const string16& name, const string16& value) {
  if (web_data_service_.get())
    web_data_service_->RemoveFormValueForElementName(name, value);
}

void AutocompleteHistoryManager::OnGetAutocompleteSuggestions(
    int query_id,
    const string16& name,
    const string16& prefix,
    const std::vector<string16>& autofill_values,
    const std::vector<string16>& autofill_labels,
    const std::vector<string16>& autofill_icons,
    const std::vector<int>& autofill_unique_ids) {
  CancelPendingQuery();

  query_id_ = query_id;
  autofill_values_ = autofill_values;
  autofill_labels_ = autofill_labels;
  autofill_icons_ = autofill_icons;
  autofill_unique_ids_ = autofill_unique_ids;
  if (!*autofill_enabled_) {
    SendSuggestions(NULL);
    return;
  }

  if (web_data_service_.get()) {
    pending_query_handle_ = web_data_service_->GetFormValuesForElementName(
        name, prefix, kMaxAutocompleteMenuItems, this);
  }
}

void AutocompleteHistoryManager::OnWebDataServiceRequestDone(
    WebDataService::Handle h,
    const WDTypedResult* result) {
  DCHECK(pending_query_handle_);
  pending_query_handle_ = 0;

  if (!*autofill_enabled_) {
    SendSuggestions(NULL);
    return;
  }

  DCHECK(result);
  // Returning early here if |result| is NULL.  We've seen this happen on
  // Linux due to NFS dismounting and causing sql failures.
  // See http://crbug.com/68783.
  if (!result) {
    SendSuggestions(NULL);
    return;
  }

  DCHECK(result->GetType() == AUTOFILL_VALUE_RESULT);
  const WDResult<std::vector<string16> >* autofill_result =
      static_cast<const WDResult<std::vector<string16> >*>(result);
  std::vector<string16> suggestions = autofill_result->GetValue();
  SendSuggestions(&suggestions);
}

AutocompleteHistoryManager::AutocompleteHistoryManager(
    Profile* profile, WebDataService* wds) : tab_contents_(NULL),
                                             profile_(profile),
                                             web_data_service_(wds),
                                             pending_query_handle_(0),
                                             query_id_(0) {
  autofill_enabled_.Init(
      prefs::kAutoFillEnabled, profile_->GetPrefs(), NULL);
}

void AutocompleteHistoryManager::CancelPendingQuery() {
  if (pending_query_handle_) {
    SendSuggestions(NULL);
    if (web_data_service_.get())
      web_data_service_->CancelRequest(pending_query_handle_);
    pending_query_handle_ = 0;
  }
}

void AutocompleteHistoryManager::SendSuggestions(
    const std::vector<string16>* suggestions) {
  if (suggestions) {
    // Combine AutoFill and Autocomplete values into values and labels.
    for (size_t i = 0; i < suggestions->size(); ++i) {
      bool unique = true;
      for (size_t j = 0; j < autofill_values_.size(); ++j) {
        // Don't add duplicate values.
        if (autofill_values_[j] == (*suggestions)[i]) {
          unique = false;
          break;
        }
      }

      if (unique) {
        autofill_values_.push_back((*suggestions)[i]);
        autofill_labels_.push_back(string16());
        autofill_icons_.push_back(string16());
        autofill_unique_ids_.push_back(0);  // 0 means no profile.
      }
    }
  }

  RenderViewHost* host = tab_contents_->render_view_host();
  if (host) {
    host->Send(new ViewMsg_AutoFillSuggestionsReturned(host->routing_id(),
                                                       query_id_,
                                                       autofill_values_,
                                                       autofill_labels_,
                                                       autofill_icons_,
                                                       autofill_unique_ids_));
  }

  query_id_ = 0;
  autofill_values_.clear();
  autofill_labels_.clear();
  autofill_icons_.clear();
  autofill_unique_ids_.clear();
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 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/gtk/tab_contents_drag_source.h"

#include "app/gtk_dnd_util.h"
#include "base/mime_util.h"
#include "base/string_util.h"
#include "chrome/browser/renderer_host/render_view_host_delegate.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
#include "chrome/common/gtk_util.h"
#include "webkit/glue/webdropdata.h"

using WebKit::WebDragOperation;
using WebKit::WebDragOperationNone;

TabContentsDragSource::TabContentsDragSource(
    TabContentsView* tab_contents_view)
    : tab_contents_view_(tab_contents_view),
      drag_failed_(false),
      drag_widget_(NULL) {
  drag_widget_ = gtk_invisible_new();
  g_signal_connect(drag_widget_, "drag-failed",
                   G_CALLBACK(OnDragFailedThunk), this);
  g_signal_connect(drag_widget_, "drag-end", G_CALLBACK(OnDragEndThunk), this);
  g_signal_connect(drag_widget_, "drag-data-get",
                   G_CALLBACK(OnDragDataGetThunk), this);
  g_object_ref_sink(drag_widget_);
}

TabContentsDragSource::~TabContentsDragSource() {
  g_signal_handlers_disconnect_by_func(drag_widget_,
      reinterpret_cast<gpointer>(OnDragFailedThunk), this);
  g_signal_handlers_disconnect_by_func(drag_widget_,
      reinterpret_cast<gpointer>(OnDragEndThunk), this);
  g_signal_handlers_disconnect_by_func(drag_widget_,
      reinterpret_cast<gpointer>(OnDragDataGetThunk), this);

  // Break the current drag, if any.
  if (drop_data_.get()) {
    gtk_grab_add(drag_widget_);
    gtk_grab_remove(drag_widget_);
    MessageLoopForUI::current()->RemoveObserver(this);
    drop_data_.reset();
  }

  gtk_widget_destroy(drag_widget_);
  g_object_unref(drag_widget_);
  drag_widget_ = NULL;
}

TabContents* TabContentsDragSource::tab_contents() const {
  return tab_contents_view_->tab_contents();
}

void TabContentsDragSource::StartDragging(const WebDropData& drop_data,
                                          GdkEventButton* last_mouse_down) {
  int targets_mask = 0;

  if (!drop_data.plain_text.empty())
    targets_mask |= GtkDndUtil::TEXT_PLAIN;
  if (drop_data.url.is_valid()) {
    targets_mask |= GtkDndUtil::TEXT_URI_LIST;
    targets_mask |= GtkDndUtil::CHROME_NAMED_URL;
    targets_mask |= GtkDndUtil::NETSCAPE_URL;
  }
  if (!drop_data.text_html.empty())
    targets_mask |= GtkDndUtil::TEXT_HTML;
  if (!drop_data.file_contents.empty())
    targets_mask |= GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS;

  if (targets_mask == 0) {
    NOTIMPLEMENTED();
    if (tab_contents()->render_view_host())
      tab_contents()->render_view_host()->DragSourceSystemDragEnded();
  }

  drop_data_.reset(new WebDropData(drop_data));

  GtkTargetList* list = GtkDndUtil::GetTargetListFromCodeMask(targets_mask);
  if (targets_mask & GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS) {
    drag_file_mime_type_ = gdk_atom_intern(
        mime_util::GetDataMimeType(drop_data.file_contents).c_str(), FALSE);
    gtk_target_list_add(list, drag_file_mime_type_,
                        0, GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS);
  }

  drag_failed_ = false;
  // If we don't pass an event, GDK won't know what event time to start grabbing
  // mouse events. Technically it's the mouse motion event and not the mouse
  // down event that causes the drag, but there's no reliable way to know
  // *which* motion event initiated the drag, so this will have to do.
  // TODO(estade): This can sometimes be very far off, e.g. if the user clicks
  // and holds and doesn't start dragging for a long time. I doubt it matters
  // much, but we should probably look into the possibility of getting the
  // initiating event from webkit.
  gtk_drag_begin(drag_widget_, list,
                 static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_LINK),
                 1,  // Drags are always initiated by the left button.
                 reinterpret_cast<GdkEvent*>(last_mouse_down));
  MessageLoopForUI::current()->AddObserver(this);
  // The drag adds a ref; let it own the list.
  gtk_target_list_unref(list);
}

void TabContentsDragSource::WillProcessEvent(GdkEvent* event) {
  // No-op.
}

void TabContentsDragSource::DidProcessEvent(GdkEvent* event) {
  if (event->type != GDK_MOTION_NOTIFY)
    return;

  GdkEventMotion* event_motion = reinterpret_cast<GdkEventMotion*>(event);
  gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());

  if (tab_contents()->render_view_host()) {
    tab_contents()->render_view_host()->DragSourceMovedTo(
        client.x(), client.y(),
        static_cast<int>(event_motion->x_root),
        static_cast<int>(event_motion->y_root));
  }
}

void TabContentsDragSource::OnDragDataGet(
    GdkDragContext* context, GtkSelectionData* selection_data,
    guint target_type, guint time) {
  const int kBitsPerByte = 8;

  switch (target_type) {
    case GtkDndUtil::TEXT_PLAIN: {
      std::string utf8_text = UTF16ToUTF8(drop_data_->plain_text);
      gtk_selection_data_set_text(selection_data, utf8_text.c_str(),
                                  utf8_text.length());
      break;
    }

    case GtkDndUtil::TEXT_HTML: {
      // TODO(estade): change relative links to be absolute using
      // |html_base_url|.
      std::string utf8_text = UTF16ToUTF8(drop_data_->text_html);
      gtk_selection_data_set(selection_data,
                             GtkDndUtil::GetAtomForTarget(
                                 GtkDndUtil::TEXT_HTML),
                             kBitsPerByte,
                             reinterpret_cast<const guchar*>(utf8_text.c_str()),
                             utf8_text.length());
      break;
    }

    case GtkDndUtil::TEXT_URI_LIST:
    case GtkDndUtil::CHROME_NAMED_URL:
    case GtkDndUtil::NETSCAPE_URL: {
      GtkDndUtil::WriteURLWithName(selection_data, drop_data_->url,
                                   drop_data_->url_title, target_type);
      break;
    }

    case GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS: {
      gtk_selection_data_set(
          selection_data,
          drag_file_mime_type_, kBitsPerByte,
          reinterpret_cast<const guchar*>(drop_data_->file_contents.data()),
          drop_data_->file_contents.length());
      break;
    }

    default:
      NOTREACHED();
  }
}

gboolean TabContentsDragSource::OnDragFailed() {
  drag_failed_ = true;

  gfx::Point root = gtk_util::ScreenPoint(GetContentNativeView());
  gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());

  if (tab_contents()->render_view_host()) {
    tab_contents()->render_view_host()->DragSourceEndedAt(
        client.x(), client.y(), root.x(), root.y(),
        WebDragOperationNone);
  }

  // Let the native failure animation run.
  return FALSE;
}

void TabContentsDragSource::OnDragEnd(WebDragOperation operation) {
  MessageLoopForUI::current()->RemoveObserver(this);

  if (!drag_failed_) {
    gfx::Point root = gtk_util::ScreenPoint(GetContentNativeView());
    gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());

    if (tab_contents()->render_view_host()) {
      tab_contents()->render_view_host()->DragSourceEndedAt(
          client.x(), client.y(), root.x(), root.y(), operation);
    }
  }

  if (tab_contents()->render_view_host())
    tab_contents()->render_view_host()->DragSourceSystemDragEnded();

  drop_data_.reset();
}

gfx::NativeView TabContentsDragSource::GetContentNativeView() const {
  return tab_contents_view_->GetContentNativeView();
}
<commit_msg>GTK: don't get stuck in render view drag.<commit_after>// Copyright (c) 2009 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/gtk/tab_contents_drag_source.h"

#include "app/gtk_dnd_util.h"
#include "base/mime_util.h"
#include "base/string_util.h"
#include "chrome/browser/renderer_host/render_view_host_delegate.h"
#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/browser/tab_contents/tab_contents_view.h"
#include "chrome/common/gtk_util.h"
#include "webkit/glue/webdropdata.h"

using WebKit::WebDragOperation;
using WebKit::WebDragOperationNone;

TabContentsDragSource::TabContentsDragSource(
    TabContentsView* tab_contents_view)
    : tab_contents_view_(tab_contents_view),
      drag_failed_(false),
      drag_widget_(NULL) {
  drag_widget_ = gtk_invisible_new();
  g_signal_connect(drag_widget_, "drag-failed",
                   G_CALLBACK(OnDragFailedThunk), this);
  g_signal_connect(drag_widget_, "drag-end", G_CALLBACK(OnDragEndThunk), this);
  g_signal_connect(drag_widget_, "drag-data-get",
                   G_CALLBACK(OnDragDataGetThunk), this);
  g_object_ref_sink(drag_widget_);
}

TabContentsDragSource::~TabContentsDragSource() {
  g_signal_handlers_disconnect_by_func(drag_widget_,
      reinterpret_cast<gpointer>(OnDragFailedThunk), this);
  g_signal_handlers_disconnect_by_func(drag_widget_,
      reinterpret_cast<gpointer>(OnDragEndThunk), this);
  g_signal_handlers_disconnect_by_func(drag_widget_,
      reinterpret_cast<gpointer>(OnDragDataGetThunk), this);

  // Break the current drag, if any.
  if (drop_data_.get()) {
    gtk_grab_add(drag_widget_);
    gtk_grab_remove(drag_widget_);
    MessageLoopForUI::current()->RemoveObserver(this);
    drop_data_.reset();
  }

  gtk_widget_destroy(drag_widget_);
  g_object_unref(drag_widget_);
  drag_widget_ = NULL;
}

TabContents* TabContentsDragSource::tab_contents() const {
  return tab_contents_view_->tab_contents();
}

void TabContentsDragSource::StartDragging(const WebDropData& drop_data,
                                          GdkEventButton* last_mouse_down) {
  int targets_mask = 0;

  if (!drop_data.plain_text.empty())
    targets_mask |= GtkDndUtil::TEXT_PLAIN;
  if (drop_data.url.is_valid()) {
    targets_mask |= GtkDndUtil::TEXT_URI_LIST;
    targets_mask |= GtkDndUtil::CHROME_NAMED_URL;
    targets_mask |= GtkDndUtil::NETSCAPE_URL;
  }
  if (!drop_data.text_html.empty())
    targets_mask |= GtkDndUtil::TEXT_HTML;
  if (!drop_data.file_contents.empty())
    targets_mask |= GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS;

  if (targets_mask == 0) {
    NOTIMPLEMENTED();
    if (tab_contents()->render_view_host())
      tab_contents()->render_view_host()->DragSourceSystemDragEnded();
  }

  drop_data_.reset(new WebDropData(drop_data));

  GtkTargetList* list = GtkDndUtil::GetTargetListFromCodeMask(targets_mask);
  if (targets_mask & GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS) {
    drag_file_mime_type_ = gdk_atom_intern(
        mime_util::GetDataMimeType(drop_data.file_contents).c_str(), FALSE);
    gtk_target_list_add(list, drag_file_mime_type_,
                        0, GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS);
  }

  drag_failed_ = false;
  // If we don't pass an event, GDK won't know what event time to start grabbing
  // mouse events. Technically it's the mouse motion event and not the mouse
  // down event that causes the drag, but there's no reliable way to know
  // *which* motion event initiated the drag, so this will have to do.
  // TODO(estade): This can sometimes be very far off, e.g. if the user clicks
  // and holds and doesn't start dragging for a long time. I doubt it matters
  // much, but we should probably look into the possibility of getting the
  // initiating event from webkit.
  GdkDragContext* context = gtk_drag_begin(
      drag_widget_, list,
      static_cast<GdkDragAction>(GDK_ACTION_COPY | GDK_ACTION_LINK),
      1,  // Drags are always initiated by the left button.
      reinterpret_cast<GdkEvent*>(last_mouse_down));
  // The drag adds a ref; let it own the list.
  gtk_target_list_unref(list);

  // Sometimes the drag fails to start; |context| will be NULL and we won't
  // get a drag-end signal.
  if (!context) {
    drop_data_.reset();
    if (tab_contents()->render_view_host())
      tab_contents()->render_view_host()->DragSourceSystemDragEnded();
    return;
  }

  MessageLoopForUI::current()->AddObserver(this);
}

void TabContentsDragSource::WillProcessEvent(GdkEvent* event) {
  // No-op.
}

void TabContentsDragSource::DidProcessEvent(GdkEvent* event) {
  if (event->type != GDK_MOTION_NOTIFY)
    return;

  GdkEventMotion* event_motion = reinterpret_cast<GdkEventMotion*>(event);
  gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());

  if (tab_contents()->render_view_host()) {
    tab_contents()->render_view_host()->DragSourceMovedTo(
        client.x(), client.y(),
        static_cast<int>(event_motion->x_root),
        static_cast<int>(event_motion->y_root));
  }
}

void TabContentsDragSource::OnDragDataGet(
    GdkDragContext* context, GtkSelectionData* selection_data,
    guint target_type, guint time) {
  const int kBitsPerByte = 8;

  switch (target_type) {
    case GtkDndUtil::TEXT_PLAIN: {
      std::string utf8_text = UTF16ToUTF8(drop_data_->plain_text);
      gtk_selection_data_set_text(selection_data, utf8_text.c_str(),
                                  utf8_text.length());
      break;
    }

    case GtkDndUtil::TEXT_HTML: {
      // TODO(estade): change relative links to be absolute using
      // |html_base_url|.
      std::string utf8_text = UTF16ToUTF8(drop_data_->text_html);
      gtk_selection_data_set(selection_data,
                             GtkDndUtil::GetAtomForTarget(
                                 GtkDndUtil::TEXT_HTML),
                             kBitsPerByte,
                             reinterpret_cast<const guchar*>(utf8_text.c_str()),
                             utf8_text.length());
      break;
    }

    case GtkDndUtil::TEXT_URI_LIST:
    case GtkDndUtil::CHROME_NAMED_URL:
    case GtkDndUtil::NETSCAPE_URL: {
      GtkDndUtil::WriteURLWithName(selection_data, drop_data_->url,
                                   drop_data_->url_title, target_type);
      break;
    }

    case GtkDndUtil::CHROME_WEBDROP_FILE_CONTENTS: {
      gtk_selection_data_set(
          selection_data,
          drag_file_mime_type_, kBitsPerByte,
          reinterpret_cast<const guchar*>(drop_data_->file_contents.data()),
          drop_data_->file_contents.length());
      break;
    }

    default:
      NOTREACHED();
  }
}

gboolean TabContentsDragSource::OnDragFailed() {
  drag_failed_ = true;

  gfx::Point root = gtk_util::ScreenPoint(GetContentNativeView());
  gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());

  if (tab_contents()->render_view_host()) {
    tab_contents()->render_view_host()->DragSourceEndedAt(
        client.x(), client.y(), root.x(), root.y(),
        WebDragOperationNone);
  }

  // Let the native failure animation run.
  return FALSE;
}

void TabContentsDragSource::OnDragEnd(WebDragOperation operation) {
  MessageLoopForUI::current()->RemoveObserver(this);

  if (!drag_failed_) {
    gfx::Point root = gtk_util::ScreenPoint(GetContentNativeView());
    gfx::Point client = gtk_util::ClientPoint(GetContentNativeView());

    if (tab_contents()->render_view_host()) {
      tab_contents()->render_view_host()->DragSourceEndedAt(
          client.x(), client.y(), root.x(), root.y(), operation);
    }
  }

  if (tab_contents()->render_view_host())
    tab_contents()->render_view_host()->DragSourceSystemDragEnded();

  drop_data_.reset();
}

gfx::NativeView TabContentsDragSource::GetContentNativeView() const {
  return tab_contents_view_->GetContentNativeView();
}
<|endoftext|>


<commit_before>/* This file is part of the Pangolin HDR extension project
 *
 * http://github.com/akramhussein/hdr
 *
 * Copyright (c) 2012 Akram Hussein
 *
 * Original source code by Steven Lovegrove
 *
 * http://github.com/stevenlovegrove/Pangolin
 *
 * 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 "image.h"

using namespace std;

namespace pangolin
{

    void CopyPPMToJPG(const char* filename_ppm, const char* filename_jpg){

        try {
            Magick::Image img;
            img.read(filename_ppm);
            img.write(filename_jpg);
        }
        catch( Magick::ErrorFileOpen &error ) {
            // Process Magick++ file open error
            cerr << "Error: " << error.what() << endl;
        }
    }

    void WriteExifData(const pangolin::FirewireVideo* video, const std::string& filename)
    {

        Exiv2::ExifData exifData;

        try {

            // TO DO - change to metadata instead
            exifData["Exif.Image.Make"] = video->GetCameraVendor();
            exifData["Exif.Image.Model"] = video->GetCameraModel();
            exifData["Exif.Photo.FNumber"] = Exiv2::Rational(7, 5); // hard coded
            exifData["Exif.Photo.ExposureTime"] = Exiv2::floatToRationalCast(video->GetFeatureValue(DC1394_FEATURE_SHUTTER));
            exifData["Exif.Photo.ColorSpace"] = uint16_t(1);
            exifData["Exif.Photo.WhiteBalance"] = uint16_t(video->GetFeatureMode(DC1394_FEATURE_WHITE_BALANCE));   // 0=auto,1=man
            exifData["Exif.Photo.GainControl"] = uint16_t(video->GetFeatureValue(DC1394_FEATURE_GAIN));
            exifData["Exif.Photo.Saturation"] = uint16_t(video->GetFeatureValue(DC1394_FEATURE_SATURATION));
            exifData["Exif.Photo.Sharpness"] = uint16_t(video->GetFeatureValue(DC1394_FEATURE_SHARPNESS));

            Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filename);
            assert(image.get() != 0);

            image->setExifData(exifData);
            image->writeMetadata();

        }
        catch (Exiv2::AnyError& e) {
            cout << "Exiv error: '" << e << "'\n";
        }

    }

    /* Borrowed from LuminanceHDR package
     *
     * Credits to Giuseppe Rota <grota@users.sourceforge.net>
     *
     * This function obtains the "average scene luminance" (cd/m^2) from an image file.
     * "average scene luminance" is the L (aka B) value mentioned in [1]
     * You have to take a log2f of the returned value to get an EV value.
     *
     * We are using K=12.07488f and the exif-implied value of N=1/3.125 (see [1]).
     * K=12.07488f is the 1.0592f * 11.4f value in pfscalibration's pfshdrcalibrate.cpp file.
     * Based on [3] we can say that the value can also be 12.5 or even 14.
     * Another reference for APEX is [4] where N is 0.3, closer to the APEX specification of 2^(-7/4)=0.2973.
     *
     * [1] http://en.wikipedia.org/wiki/APEX_system
     * [2] http://en.wikipedia.org/wiki/Exposure_value
     * [3] http://en.wikipedia.org/wiki/Light_meter
     * [4] http://doug.kerr.home.att.net/pumpkin/#APEX
     *
     * This function tries first to obtain the shutter speed from either of
     * two exif tags (there is no standard between camera manifacturers):
     * ExposureTime or ShutterSpeedValue.
     * Same thing for f-number: it can be found in FNumber or in ApertureValue.
     *
     * F-number and shutter speed are mandatory in exif data for EV calculation, iso is not.
     */
    float GetAvgLuminance(const std::string& filename)
    {
        try
        {

            Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filename);
            image->readMetadata();

            Exiv2::ExifData exifData;
            exifData = image->exifData();

            if (exifData.empty()){
                throw VideoException("Exiv Error: No Exif Data in image");
            }

            Exiv2::ExifData::const_iterator _expo = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ExposureTime"));
            Exiv2::ExifData::const_iterator _expo2 = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ShutterSpeedValue"));
            Exiv2::ExifData::const_iterator _iso  = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ISOSpeedRatings"));
            Exiv2::ExifData::const_iterator _fnum = exifData.findKey(Exiv2::ExifKey("Exif.Photo.FNumber"));
            Exiv2::ExifData::const_iterator _fnum2 = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ApertureValue"));

            // default not valid values
            float expo  = -1;
            float iso   = -1;
            float fnum  = -1;

            if (_expo != exifData.end())
            {
                expo=_expo->toFloat();
            }
            else if (_expo2 != exifData.end())
            {
                long num=1, div=1;
                double tmp = std::exp(std::log(2.0) * _expo2->toFloat());
                if (tmp > 1)
                {
                    div = static_cast<long>(tmp + 0.5);
                }
                else
                {
                    num = static_cast<long>(1/tmp + 0.5);
                }
                expo = static_cast<float>(num)/static_cast<float>(div);
            }

            if (_fnum != exifData.end())
            {
                fnum = _fnum->toFloat();
            }
            else if (_fnum2 != exifData.end())
            {
                fnum = static_cast<float>(std::exp(std::log(2.0) * _fnum2->toFloat() / 2));
            }
            // some cameras/lens DO print the fnum but with value 0, and this is not allowed for ev computation purposes.
            if (fnum == 0)
                return -1;

            //if iso is found use that value, otherwise assume a value of iso=100. (again, some cameras do not print iso in exif).
            if (_iso == exifData.end())
            {
                iso = 100.0;
            }
            else
            {
                iso = _iso->toFloat();
            }

            //At this point the three variables have to be != -1
            if (expo!=-1 && iso!=-1 && fnum!=-1)
            {
                // 		std::cerr << "expo=" << expo << " fnum=" << fnum << " iso=" << iso << " |returned=" << (expo * iso) / (fnum*fnum*12.07488f) << std::endl;
                return ( (expo * iso) / (fnum*fnum*12.07488f) );
            }
            else
            {
                return -1;
            }
        }
        catch (Exiv2::AnyError& e)
        {
            throw VideoException("Exiv Error");
        }
    }

    void CopyExifData(const std::string& from, const std::string& to,  bool dont_overwrite)
    {

        Exiv2::Image::AutoPtr sourceimage = Exiv2::ImageFactory::open(from);
        Exiv2::Image::AutoPtr destimage = Exiv2::ImageFactory::open(to);

        sourceimage->readMetadata();
        Exiv2::ExifData &src_exifData = sourceimage->exifData();
        if (src_exifData.empty())
        {
            throw Exiv2::Error(1, "No exif data found in the image");
        }
        if (dont_overwrite)
        {
            destimage->readMetadata();
            Exiv2::ExifData &dest_exifData = destimage->exifData();
            Exiv2::ExifData::const_iterator end_src = src_exifData.end();

            for (Exiv2::ExifData::const_iterator i = src_exifData.begin(); i != end_src; ++i)
            {
				//check if current source key exists in destination file
				Exiv2::ExifData::iterator maybe_exists = dest_exifData.findKey( Exiv2::ExifKey(i->key()) );
				//if exists AND not to overwrite
				if (maybe_exists != dest_exifData.end())
                {
					continue;
				}
                else
                {
					// copy the value
					// we create a new tag in the destination file, the tag has the key of the source
					Exiv2::Exifdatum& dest_tag = dest_exifData[i->key()];
					//now the tag has also the value of the source
					dest_tag.setValue(&(i->value()));
				}
            }
        }
        else
        {
            destimage->setExifData(src_exifData);
        }

        destimage->writeMetadata();
    }

    bool JpgToHDRGEN(const char* filename, FILE* hdrgen, int frame_number)
    {

        char file_path[128];
        sprintf(file_path, "./%s/jpg/image0000%d.jpg", filename, frame_number);

        try
        {

            Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file_path);
            image->readMetadata();

            Exiv2::ExifData exifData;
            exifData = image->exifData();

            if (exifData.empty()){
                throw VideoException("Exiv Error: No Exif Data in image");
            }

            Exiv2::ExifData::const_iterator _expo = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ExposureTime"));
            Exiv2::ExifData::const_iterator _expo2 = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ShutterSpeedValue"));
            Exiv2::ExifData::const_iterator _iso  = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ISOSpeedRatings"));
            Exiv2::ExifData::const_iterator _fnum = exifData.findKey(Exiv2::ExifKey("Exif.Photo.FNumber"));
            Exiv2::ExifData::const_iterator _fnum2 = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ApertureValue"));

            // default not valid values
            float expo  = -1;
            int iso   = -1;
            double fnum  = -1;

            if (_expo != exifData.end())
            {
                expo=_expo->toFloat();
            }
            else if (_expo2 != exifData.end())
            {
                long num=1, div=1;
                double tmp = std::exp(std::log(2.0) * _expo2->toFloat());
                if (tmp > 1)
                {
                    div = static_cast<long>(tmp + 0.5);
                }
                else
                {
                    num = static_cast<long>(1/tmp + 0.5);
                }
                expo = static_cast<float>(num)/static_cast<float>(div);
            }

            if (_fnum != exifData.end())
            {
                fnum = _fnum->toFloat();
            }
            else if (_fnum2 != exifData.end())
            {
                fnum = static_cast<float>(std::exp(std::log(2.0) * _fnum2->toFloat() / 2));
            }
            // some cameras/lens DO print the fnum but with value 0, and this is not allowed for ev computation purposes.
            if (fnum == 0)
                return false;

            //if iso is found use that value, otherwise assume a value of iso=100. (again, some cameras do not print iso in exif).
            if (_iso == exifData.end())
            {
                iso = 100;
            }
            else
            {
                iso = _iso->toFloat();
            }

            //At this point the three variables have to be != -1
            if (expo!=-1 && iso!=-1 && fnum!=-1)
            {

                // write new line to file in format:
                // path_to_an_image inverse_of_exposure_time_in_seconds aperture_size iso_speed 0
                fprintf(hdrgen,"%s %f %2.2f %d 0\n",
                        file_path,
                        1/expo,
                        fnum,
                        iso
                        );

            }
            else
            {
                return false;
            }
        }
        catch (Exiv2::AnyError& e)
        {
            throw VideoException("Exiv Error");
        }

        return true;
    }

}<commit_msg>Added ExposureBias to Exif tags (EV)<commit_after>/* This file is part of the Pangolin HDR extension project
 *
 * http://github.com/akramhussein/hdr
 *
 * Copyright (c) 2012 Akram Hussein
 *
 * Original source code by Steven Lovegrove
 *
 * http://github.com/stevenlovegrove/Pangolin
 *
 * 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 "image.h"

using namespace std;

namespace pangolin
{

    void CopyPPMToJPG(const char* filename_ppm, const char* filename_jpg){

        try {
            Magick::Image img;
            img.read(filename_ppm);
            img.write(filename_jpg);
        }
        catch( Magick::ErrorFileOpen &error ) {
            // Process Magick++ file open error
            cerr << "Error: " << error.what() << endl;
        }
    }

    void WriteExifData(const pangolin::FirewireVideo* video, const std::string& filename)
    {

        Exiv2::ExifData exifData;

        try {

            // TO DO - change to metadata instead
            exifData["Exif.Image.Make"] = video->GetCameraVendor();
            exifData["Exif.Image.Model"] = video->GetCameraModel();
            exifData["Exif.Photo.FNumber"] = Exiv2::Rational(7, 5); // hard coded
            exifData["Exif.Photo.ExposureTime"] = Exiv2::floatToRationalCast(video->GetFeatureValue(DC1394_FEATURE_SHUTTER));
            exifData["Exif.Photo.ExposureBiasValue"] = Exiv2::floatToRationalCast(video->GetFeatureValue(DC1394_FEATURE_EXPOSURE));
            exifData["Exif.Photo.ColorSpace"] = uint16_t(1);
            exifData["Exif.Photo.WhiteBalance"] = uint16_t(video->GetFeatureMode(DC1394_FEATURE_WHITE_BALANCE));   // 0=auto,1=man
            exifData["Exif.Photo.GainControl"] = uint16_t(video->GetFeatureValue(DC1394_FEATURE_GAIN));
            exifData["Exif.Photo.Saturation"] = uint16_t(video->GetFeatureValue(DC1394_FEATURE_SATURATION));
            exifData["Exif.Photo.Sharpness"] = uint16_t(video->GetFeatureValue(DC1394_FEATURE_SHARPNESS));

            Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filename);
            assert(image.get() != 0);

            image->setExifData(exifData);
            image->writeMetadata();

        }
        catch (Exiv2::AnyError& e) {
            cout << "Exiv error: '" << e << "'\n";
        }

    }

    /* Borrowed from LuminanceHDR package
     *
     * Credits to Giuseppe Rota <grota@users.sourceforge.net>
     *
     * This function obtains the "average scene luminance" (cd/m^2) from an image file.
     * "average scene luminance" is the L (aka B) value mentioned in [1]
     * You have to take a log2f of the returned value to get an EV value.
     *
     * We are using K=12.07488f and the exif-implied value of N=1/3.125 (see [1]).
     * K=12.07488f is the 1.0592f * 11.4f value in pfscalibration's pfshdrcalibrate.cpp file.
     * Based on [3] we can say that the value can also be 12.5 or even 14.
     * Another reference for APEX is [4] where N is 0.3, closer to the APEX specification of 2^(-7/4)=0.2973.
     *
     * [1] http://en.wikipedia.org/wiki/APEX_system
     * [2] http://en.wikipedia.org/wiki/Exposure_value
     * [3] http://en.wikipedia.org/wiki/Light_meter
     * [4] http://doug.kerr.home.att.net/pumpkin/#APEX
     *
     * This function tries first to obtain the shutter speed from either of
     * two exif tags (there is no standard between camera manifacturers):
     * ExposureTime or ShutterSpeedValue.
     * Same thing for f-number: it can be found in FNumber or in ApertureValue.
     *
     * F-number and shutter speed are mandatory in exif data for EV calculation, iso is not.
     */
    float GetAvgLuminance(const std::string& filename)
    {
        try
        {

            Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(filename);
            image->readMetadata();

            Exiv2::ExifData exifData;
            exifData = image->exifData();

            if (exifData.empty()){
                throw VideoException("Exiv Error: No Exif Data in image");
            }

            Exiv2::ExifData::const_iterator _expo = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ExposureTime"));
            Exiv2::ExifData::const_iterator _expo2 = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ShutterSpeedValue"));
            Exiv2::ExifData::const_iterator _iso  = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ISOSpeedRatings"));
            Exiv2::ExifData::const_iterator _fnum = exifData.findKey(Exiv2::ExifKey("Exif.Photo.FNumber"));
            Exiv2::ExifData::const_iterator _fnum2 = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ApertureValue"));

            // default not valid values
            float expo  = -1;
            float iso   = -1;
            float fnum  = -1;

            if (_expo != exifData.end())
            {
                expo=_expo->toFloat();
            }
            else if (_expo2 != exifData.end())
            {
                long num=1, div=1;
                double tmp = std::exp(std::log(2.0) * _expo2->toFloat());
                if (tmp > 1)
                {
                    div = static_cast<long>(tmp + 0.5);
                }
                else
                {
                    num = static_cast<long>(1/tmp + 0.5);
                }
                expo = static_cast<float>(num)/static_cast<float>(div);
            }

            if (_fnum != exifData.end())
            {
                fnum = _fnum->toFloat();
            }
            else if (_fnum2 != exifData.end())
            {
                fnum = static_cast<float>(std::exp(std::log(2.0) * _fnum2->toFloat() / 2));
            }
            // some cameras/lens DO print the fnum but with value 0, and this is not allowed for ev computation purposes.
            if (fnum == 0)
                return -1;

            //if iso is found use that value, otherwise assume a value of iso=100. (again, some cameras do not print iso in exif).
            if (_iso == exifData.end())
            {
                iso = 100.0;
            }
            else
            {
                iso = _iso->toFloat();
            }

            //At this point the three variables have to be != -1
            if (expo!=-1 && iso!=-1 && fnum!=-1)
            {
                // 		std::cerr << "expo=" << expo << " fnum=" << fnum << " iso=" << iso << " |returned=" << (expo * iso) / (fnum*fnum*12.07488f) << std::endl;
                return ( (expo * iso) / (fnum*fnum*12.07488f) );
            }
            else
            {
                return -1;
            }
        }
        catch (Exiv2::AnyError& e)
        {
            throw VideoException("Exiv Error");
        }
    }

    void CopyExifData(const std::string& from, const std::string& to,  bool dont_overwrite)
    {

        Exiv2::Image::AutoPtr sourceimage = Exiv2::ImageFactory::open(from);
        Exiv2::Image::AutoPtr destimage = Exiv2::ImageFactory::open(to);

        sourceimage->readMetadata();
        Exiv2::ExifData &src_exifData = sourceimage->exifData();
        if (src_exifData.empty())
        {
            throw Exiv2::Error(1, "No exif data found in the image");
        }
        if (dont_overwrite)
        {
            destimage->readMetadata();
            Exiv2::ExifData &dest_exifData = destimage->exifData();
            Exiv2::ExifData::const_iterator end_src = src_exifData.end();

            for (Exiv2::ExifData::const_iterator i = src_exifData.begin(); i != end_src; ++i)
            {
				//check if current source key exists in destination file
				Exiv2::ExifData::iterator maybe_exists = dest_exifData.findKey( Exiv2::ExifKey(i->key()) );
				//if exists AND not to overwrite
				if (maybe_exists != dest_exifData.end())
                {
					continue;
				}
                else
                {
					// copy the value
					// we create a new tag in the destination file, the tag has the key of the source
					Exiv2::Exifdatum& dest_tag = dest_exifData[i->key()];
					//now the tag has also the value of the source
					dest_tag.setValue(&(i->value()));
				}
            }
        }
        else
        {
            destimage->setExifData(src_exifData);
        }

        destimage->writeMetadata();
    }

    bool JpgToHDRGEN(const char* filename, FILE* hdrgen, int frame_number)
    {

        char file_path[128];
        sprintf(file_path, "./%s/jpg/image0000%d.jpg", filename, frame_number);

        try
        {

            Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file_path);
            image->readMetadata();

            Exiv2::ExifData exifData;
            exifData = image->exifData();

            if (exifData.empty()){
                throw VideoException("Exiv Error: No Exif Data in image");
            }

            Exiv2::ExifData::const_iterator _expo = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ExposureTime"));
            Exiv2::ExifData::const_iterator _expo2 = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ShutterSpeedValue"));
            Exiv2::ExifData::const_iterator _iso  = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ISOSpeedRatings"));
            Exiv2::ExifData::const_iterator _fnum = exifData.findKey(Exiv2::ExifKey("Exif.Photo.FNumber"));
            Exiv2::ExifData::const_iterator _fnum2 = exifData.findKey(Exiv2::ExifKey("Exif.Photo.ApertureValue"));

            // default not valid values
            float expo  = -1;
            int iso   = -1;
            double fnum  = -1;

            if (_expo != exifData.end())
            {
                expo=_expo->toFloat();
            }
            else if (_expo2 != exifData.end())
            {
                long num=1, div=1;
                double tmp = std::exp(std::log(2.0) * _expo2->toFloat());
                if (tmp > 1)
                {
                    div = static_cast<long>(tmp + 0.5);
                }
                else
                {
                    num = static_cast<long>(1/tmp + 0.5);
                }
                expo = static_cast<float>(num)/static_cast<float>(div);
            }

            if (_fnum != exifData.end())
            {
                fnum = _fnum->toFloat();
            }
            else if (_fnum2 != exifData.end())
            {
                fnum = static_cast<float>(std::exp(std::log(2.0) * _fnum2->toFloat() / 2));
            }
            // some cameras/lens DO print the fnum but with value 0, and this is not allowed for ev computation purposes.
            if (fnum == 0)
                return false;

            //if iso is found use that value, otherwise assume a value of iso=100. (again, some cameras do not print iso in exif).
            if (_iso == exifData.end())
            {
                iso = 100;
            }
            else
            {
                iso = _iso->toFloat();
            }

            //At this point the three variables have to be != -1
            if (expo!=-1 && iso!=-1 && fnum!=-1)
            {

                // write new line to file in format:
                // path_to_an_image inverse_of_exposure_time_in_seconds aperture_size iso_speed 0
                fprintf(hdrgen,"%s %f %2.2f %d 0\n",
                        file_path,
                        1/expo,
                        fnum,
                        iso
                        );

            }
            else
            {
                return false;
            }
        }
        catch (Exiv2::AnyError& e)
        {
            throw VideoException("Exiv Error");
        }

        return true;
    }

}<|endoftext|>


<commit_before>#include <cstdint>
#include <SDL2/SDL.h>

#include "Screen.h"
#include "WinAPI.h"

#define WINDOW_HEIGHT SCREEN_HEIGHT+20	//Windows menu bar adds 20 pixels

Screen::Screen(void)
{
	if (SDL_Init(SDL_INIT_VIDEO)<0)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to init SDL video", SDL_GetError(), NULL);
		exit(1);
	}
	atexit(SDL_Quit);

	window = SDL_CreateWindow("Captain PlaneEd", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
	if (window == NULL)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to init SDL Window", SDL_GetError(), NULL);
		exit(1);
	}

	render = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
	if (render == NULL)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to init SDL Renderer", SDL_GetError(), NULL);
		exit(1);
	}

	SDL_RenderSetLogicalSize(render, SCREEN_WIDTH, SCREEN_HEIGHT);
	SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");

	surface = SDL_CreateRGBSurface(0, SCREEN_WIDTH, SCREEN_HEIGHT, 32, 0, 0, 0, 0);	// Implicitly ARGB8888, compatible with the below texture
	if (surface==NULL)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to init screen SDL Surface", SDL_GetError(), NULL);
		exit(1);
	}

	texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT);
	if (texture==NULL)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to init screen SDL Texture", SDL_GetError(), NULL);
		exit(1);
	}

	this->BackgroundColour = {.red = 0, .green = 0, .blue = 0};

	// Windows-only crap to generate a menu bar
	WinAPI::SaveHWND(window);
	WinAPI::CreateMenuBar();
}

void Screen::ProcessDisplay(void)
{
	void* pixels;
	int pitch;
	SDL_LockTexture(texture, NULL, &pixels, &pitch);
	memcpy(pixels, surface->pixels, pitch*surface->h);
	SDL_UnlockTexture(texture);

	SDL_RenderCopy(render, texture, NULL, NULL);
	SDL_RenderPresent(render);
}

void Screen::Fill(uint8_t red, uint8_t green, uint8_t blue)
{
	SDL_FillRect(this->surface, NULL, SDL_MapRGB(this->surface->format, red, green, blue));
}
<commit_msg>Adding 'this' pointers<commit_after>#include <cstdint>
#include <SDL2/SDL.h>

#include "Screen.h"
#include "WinAPI.h"

#define WINDOW_HEIGHT SCREEN_HEIGHT+20	//Windows menu bar adds 20 pixels

Screen::Screen(void)
{
	if (SDL_Init(SDL_INIT_VIDEO)<0)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to init SDL video", SDL_GetError(), NULL);
		exit(1);
	}
	atexit(SDL_Quit);

	this->window = SDL_CreateWindow("Captain PlaneEd", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, WINDOW_HEIGHT, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
	if (this->window == NULL)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to init SDL Window", SDL_GetError(), NULL);
		exit(1);
	}

	this->render = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
	if (this->render == NULL)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to init SDL Renderer", SDL_GetError(), NULL);
		exit(1);
	}

	SDL_RenderSetLogicalSize(render, SCREEN_WIDTH, SCREEN_HEIGHT);
	SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");

	this->surface = SDL_CreateRGBSurface(0, SCREEN_WIDTH, SCREEN_HEIGHT, 32, 0, 0, 0, 0);	// Implicitly ARGB8888, compatible with the below texture
	if (this->surface==NULL)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to init screen SDL Surface", SDL_GetError(), NULL);
		exit(1);
	}

	this->texture = SDL_CreateTexture(this->render, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT);
	if (this->texture==NULL)
	{
		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Unable to init screen SDL Texture", SDL_GetError(), NULL);
		exit(1);
	}

	this->BackgroundColour = {.red = 0, .green = 0, .blue = 0};

	// Windows-only crap to generate a menu bar
	WinAPI::SaveHWND(this->window);
	WinAPI::CreateMenuBar();
}

void Screen::ProcessDisplay(void)
{
	void* pixels;
	int pitch;
	SDL_LockTexture(this->texture, NULL, &pixels, &pitch);
	memcpy(pixels, this->surface->pixels, pitch*this->surface->h);
	SDL_UnlockTexture(this->texture);

	SDL_RenderCopy(this->render, this->texture, NULL, NULL);
	SDL_RenderPresent(this->render);
}

void Screen::Fill(uint8_t red, uint8_t green, uint8_t blue)
{
	SDL_FillRect(this->surface, NULL, SDL_MapRGB(this->surface->format, red, green, blue));
}
<|endoftext|>


<commit_before>#include "RenderThread.hpp"
#include <Core/Engine.hh>
#include <Context/SdlContext.hh>
#include <Utils/ThreadName.hpp>
#include <Threads/Tasks/ToRenderTasks.hpp>
#include <Threads/Commands/ToRenderCommands.hpp>
#include <Threads/Tasks/BasicTasks.hpp>
#include <Context/SdlContext.hh>
#include <Utils/Containers/Vector.hpp>
#include <Threads/ThreadManager.hpp>
#include <Core/Engine.hh>
#include <Context/SdlContext.hh>
#include <Render/GeometryManagement/Painting/Painter.hh>
#include <Render/Pipelining/Pipelines/CustomPipeline/BasicPipeline.hh>
#include <Render/Pipelining/Pipelines/CustomPipeline/DeferredShading.hh>
#include <Utils/OpenGL.hh>
#include <Utils/Age_Imgui.hpp>
#include <Render/Properties/Transformation.hh>
#include <SpacePartitioning/Ouptut/RenderCamera.hh>
#include <SpacePartitioning/Ouptut/RenderLight.hh>
#include <SpacePartitioning/Ouptut/RenderPipeline.hh>
#include <Utils/Debug.hpp>
#include <Render/GeometryManagement/SimpleGeometry.hpp>

namespace AGE
{
	RenderThread::RenderThread()
		: Thread(AGE::Thread::ThreadType::Render)
		, _context(nullptr),
		paintingManager(std::make_shared<PaintingManager>()),
		pipelines(2)
	{
	}

	RenderThread::~RenderThread()
	{}

	void RenderThread::_recompileShaders()
	{
		// to be sure that this function is only called in render thread
		AGE_ASSERT(GetThreadManager()->getCurrentThread() == (AGE::Thread*)GetRenderThread());

		for (auto &e : pipelines)
		{
			if (!e)
			{
				continue;
			}
			e->recompileShaders();
		}
	}

	void RenderThread::getQuadGeometry(Key<Vertices> &v, Key<Painter> &p)
	{
		// to be sure that this function is only called in render thread
		AGE_ASSERT(GetThreadManager()->getCurrentThread() == (AGE::Thread*)GetRenderThread());

		if (Quad::VerticesKey.isValid())
		{
			v = Quad::VerticesKey;
			p = Quad::PainterKey;
			return;
		}

		auto type = std::make_pair<GLenum, std::string>(GL_FLOAT_VEC4, "position");
		std::vector<std::pair < GLenum, std::string > > types;
		types.push_back(type);

		if (!paintingManager->has_painter(types))
		{
			Quad::PainterKey = paintingManager->add_painter(std::move(types));
		}
		else
		{
			Quad::PainterKey = paintingManager->get_painter(types);
		}
		auto &painterPtr = paintingManager->get_painter(Quad::PainterKey);

		Quad::VerticesKey = painterPtr->add_vertices(Quad::PositionsNumber, Quad::IndicesNumber);
		auto vertices = painterPtr->get_vertices(Quad::VerticesKey);

		vertices->set_data<glm::vec4>(Quad::Positions, std::string("position"));
		vertices->set_indices(Quad::Indices);

		v = Quad::VerticesKey;
		p = Quad::PainterKey;
	}

	bool RenderThread::init()
	{
		registerCallback<Tasks::Render::CreateRenderContext>([this](Tasks::Render::CreateRenderContext &msg)
		{
			_context = msg.engine->setInstance<SdlContext, IRenderContext>();
			if (!_context->init(0, 1280, 720, "~AGE~ V0.00001 Demo"))
			{
				msg.setValue(false);
				return;
			}
			pipelines[DEFERRED] = std::make_unique<DeferredShading>(_context->getScreenSize(), paintingManager);
			pipelines[BASIC] = std::make_unique<BasicPipeline>(paintingManager);
			_recompileShaders();
			msg.setValue(true);
		});

 		registerCallback<Commands::ToRender::Flush>([&](Commands::ToRender::Flush& msg)
		{
			_context->swapContext();
			glClear(GL_COLOR_BUFFER_BIT);
		});

		registerCallback<Tasks::Render::ReloadShaders>([&](Tasks::Render::ReloadShaders& msg)
		{
#ifdef AGE_DEBUG
			_recompileShaders();
#else
			std::cerr << "Error : You cannot recompile shader at runtime. This feature is enabled only in debug mode\n";
#endif
		});

		registerCallback<Tasks::Render::GetWindowSize>([&](Tasks::Render::GetWindowSize &msg)
		{
			msg.setValue(_context->getScreenSize());
		});

		registerCallback<Tasks::Render::SetWindowSize>([&](Tasks::Render::SetWindowSize& msg)
		{
			_context->setScreenSize(msg.size);
		});

		registerCallback<Commands::ToRender::CopyDrawLists>([&](Commands::ToRender::CopyDrawLists& msg)
		{
			_drawlistPtr = msg.listContainer;
		});

		registerCallback<Commands::ToRender::RenderDrawLists>([&](Commands::ToRender::RenderDrawLists& msg)
		{
			uint32_t pipelineIdx = 0;

			if (!_drawlistPtr) // nothing to draw
				return;
			AGE_ASSERT(_drawlistPtr != nullptr);

			for (auto &curPipeline : pipelines)
			{
				auto &drawlist = _drawlistPtr->container.cameras;
				for (auto &curCamera : drawlist)
				{
					if (pipelineIdx < curCamera.pipelines.size()) {
						curPipeline->render(curCamera.pipelines[pipelineIdx], curCamera.lights, curCamera.camInfos);
					}
				}
				++pipelineIdx;
			}
			_drawlistPtr = nullptr;
		});

		registerSharedCallback<AGE::Tasks::Basic::BoolFunction>([&](AGE::Tasks::Basic::BoolFunction& msg)
		{
			msg.setValue(msg.function());
		});

		registerCallback<AGE::Tasks::Basic::VoidFunction>([&](AGE::Tasks::Basic::VoidFunction& msg)
		{
			if (msg.function)
				msg.function();
		});

		registerCallback<AGE::Tasks::Basic::Exit>([&](AGE::Tasks::Basic::Exit& msg)
		{
			AGE::CreateEngine()->deleteInstance<IRenderContext>();
			this->_insideRun = false;
		});

#ifdef USE_IMGUI
		registerCallback<AGE::RenderImgui>([&](AGE::RenderImgui &msg)
		{
			AGE::Imgui::getInstance()->renderThreadRenderFn(msg.cmd_lists);
		});
#endif

		return true;
	}

	bool RenderThread::release()
	{
		return true;
	}

	bool RenderThread::launch()
	{
		if (!init())
			return false;
		_threadHandle = std::thread(&RenderThread::update, std::ref(*this));
		return true;
	}

	bool RenderThread::stop()
	{
		getQueue()->emplaceTask<Tasks::Basic::Exit>();
		if (_threadHandle.joinable())
			_threadHandle.join();
		return true;
	}

	bool RenderThread::update()
	{
		/*
		- Tant qu'il n'y a pas de command
		-> je pop des task
		- Une fois que j'ai mes commandes
		-> pour chacunes d'elles
		-> je regarde si c'est a moi de les traiter (ex : changement de scene)
		-> si ca n'est pas a moi de les traiter
		-> je les passe au render context actif
		-> si il n'y a pas de render context actif, c'est que j'ai fait une erreur, et j'assert dans ta face :D
		*/

		_registerId();

		_run = true;
		_insideRun = true;
		DWORD threadId = GetThreadId(static_cast<HANDLE>(_threadHandle.native_handle()));
		SetThreadName(threadId, this->_name.c_str());

		TMQ::PtrQueue commands;
		TMQ::PtrQueue tasks;
		bool commandSuccess;
		bool taskSuccess;

		std::chrono::system_clock::time_point waitStart;
		std::chrono::system_clock::time_point waitEnd;
		std::chrono::system_clock::time_point workStart;
		std::chrono::system_clock::time_point workEnd;

		while (_run && _insideRun)
		{
			waitStart = std::chrono::high_resolution_clock::now();
			taskSuccess = commandSuccess = false;
			do {
				if (_context)
					_context->refreshInputs();
				getQueue()->getTaskAndCommandQueue(tasks, taskSuccess, commands, commandSuccess, TMQ::HybridQueue::Block);
			} while (!taskSuccess && !commandSuccess);
			waitEnd = std::chrono::high_resolution_clock::now();
			workStart = std::chrono::high_resolution_clock::now();
			if (taskSuccess)
			{
				while (!tasks.empty() && _insideRun)
				{
					//pop all tasks
					auto task = tasks.front();
					auto success = execute(task); // we receive a task that we cannot treat
					AGE_ASSERT(success);
					tasks.pop();
					taskCounter--;
					workEnd = std::chrono::high_resolution_clock::now();
					const std::size_t toWait = 33;
					const std::size_t elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(workEnd - workStart).count();
					if (elapsed >= toWait)
					{
						std::cout << elapsed << ", ";
						while (!tasks.empty() && _insideRun)
						{
							auto task = tasks.front();
							getQueue()->moveTask(task, tasks.getFrontSize());
							tasks.pop();
						}
					}
				}
			}
			if (commandSuccess)
			{
				// pop all commands
				while (!commands.empty() && _insideRun)
				{
					auto command = commands.front();
					auto success = execute(command);
					AGE_ASSERT(success);
					commands.pop();
				}

				workEnd = std::chrono::high_resolution_clock::now();
				GetThreadManager()->updateThreadStatistics(this->_id
					, std::chrono::duration_cast<std::chrono::microseconds>(workEnd - workStart).count()
					, std::chrono::duration_cast<std::chrono::microseconds>(waitEnd - waitStart).count());
			}
		}
		return true;
	}
}<commit_msg>small changes<commit_after>#include "RenderThread.hpp"
#include <Core/Engine.hh>
#include <Context/SdlContext.hh>
#include <Utils/ThreadName.hpp>
#include <Threads/Tasks/ToRenderTasks.hpp>
#include <Threads/Commands/ToRenderCommands.hpp>
#include <Threads/Tasks/BasicTasks.hpp>
#include <Context/SdlContext.hh>
#include <Utils/Containers/Vector.hpp>
#include <Threads/ThreadManager.hpp>
#include <Core/Engine.hh>
#include <Context/SdlContext.hh>
#include <Render/GeometryManagement/Painting/Painter.hh>
#include <Render/Pipelining/Pipelines/CustomPipeline/BasicPipeline.hh>
#include <Render/Pipelining/Pipelines/CustomPipeline/DeferredShading.hh>
#include <Utils/OpenGL.hh>
#include <Utils/Age_Imgui.hpp>
#include <Render/Properties/Transformation.hh>
#include <SpacePartitioning/Ouptut/RenderCamera.hh>
#include <SpacePartitioning/Ouptut/RenderLight.hh>
#include <SpacePartitioning/Ouptut/RenderPipeline.hh>
#include <Utils/Debug.hpp>
#include <Render/GeometryManagement/SimpleGeometry.hpp>

namespace AGE
{
	RenderThread::RenderThread()
		: Thread(AGE::Thread::ThreadType::Render)
		, _context(nullptr),
		paintingManager(std::make_shared<PaintingManager>()),
		pipelines(2)
	{
	}

	RenderThread::~RenderThread()
	{}

	void RenderThread::_recompileShaders()
	{
		// to be sure that this function is only called in render thread
		AGE_ASSERT(GetThreadManager()->getCurrentThread() == (AGE::Thread*)GetRenderThread());

		for (auto &e : pipelines)
		{
			if (!e)
			{
				continue;
			}
			e->recompileShaders();
		}
	}

	void RenderThread::getQuadGeometry(Key<Vertices> &v, Key<Painter> &p)
	{
		// to be sure that this function is only called in render thread
		AGE_ASSERT(GetThreadManager()->getCurrentThread() == (AGE::Thread*)GetRenderThread());

		if (Quad::VerticesKey.isValid() && Quad::PainterKey.isValid())
		{
			v = Quad::VerticesKey;
			p = Quad::PainterKey;
			return;
		}

		auto type = std::make_pair<GLenum, std::string>(GL_FLOAT_VEC4, "position");
		std::vector<std::pair < GLenum, std::string > > types;
		types.push_back(type);

		if (!paintingManager->has_painter(types))
		{
			Quad::PainterKey = paintingManager->add_painter(std::move(types));
		}
		else
		{
			Quad::PainterKey = paintingManager->get_painter(types);
		}
		auto &painterPtr = paintingManager->get_painter(Quad::PainterKey);

		Quad::VerticesKey = painterPtr->add_vertices(Quad::Positions.size(), Quad::Indices.size());
		auto vertices = painterPtr->get_vertices(Quad::VerticesKey);

		vertices->set_data<glm::vec4>(Quad::Positions, std::string("position"));
		vertices->set_indices(Quad::Indices);

		v = Quad::VerticesKey;
		p = Quad::PainterKey;
	}

	bool RenderThread::init()
	{
		registerCallback<Tasks::Render::CreateRenderContext>([this](Tasks::Render::CreateRenderContext &msg)
		{
			_context = msg.engine->setInstance<SdlContext, IRenderContext>();
			if (!_context->init(0, 1280, 720, "~AGE~ V0.00001 Demo"))
			{
				msg.setValue(false);
				return;
			}
			pipelines[DEFERRED] = std::make_unique<DeferredShading>(_context->getScreenSize(), paintingManager);
			pipelines[BASIC] = std::make_unique<BasicPipeline>(paintingManager);
			_recompileShaders();
			msg.setValue(true);
		});

 		registerCallback<Commands::ToRender::Flush>([&](Commands::ToRender::Flush& msg)
		{
			_context->swapContext();
			glClear(GL_COLOR_BUFFER_BIT);
		});

		registerCallback<Tasks::Render::ReloadShaders>([&](Tasks::Render::ReloadShaders& msg)
		{
#ifdef AGE_DEBUG
			_recompileShaders();
#else
			std::cerr << "Error : You cannot recompile shader at runtime. This feature is enabled only in debug mode\n";
#endif
		});

		registerCallback<Tasks::Render::GetWindowSize>([&](Tasks::Render::GetWindowSize &msg)
		{
			msg.setValue(_context->getScreenSize());
		});

		registerCallback<Tasks::Render::SetWindowSize>([&](Tasks::Render::SetWindowSize& msg)
		{
			_context->setScreenSize(msg.size);
		});

		registerCallback<Commands::ToRender::CopyDrawLists>([&](Commands::ToRender::CopyDrawLists& msg)
		{
			_drawlistPtr = msg.listContainer;
		});

		registerCallback<Commands::ToRender::RenderDrawLists>([&](Commands::ToRender::RenderDrawLists& msg)
		{
			uint32_t pipelineIdx = 0;

			if (!_drawlistPtr) // nothing to draw
				return;
			AGE_ASSERT(_drawlistPtr != nullptr);

			for (auto &curPipeline : pipelines)
			{
				auto &drawlist = _drawlistPtr->container.cameras;
				for (auto &curCamera : drawlist)
				{
					if (pipelineIdx < curCamera.pipelines.size()) {
						curPipeline->render(curCamera.pipelines[pipelineIdx], curCamera.lights, curCamera.camInfos);
					}
				}
				++pipelineIdx;
			}
			_drawlistPtr = nullptr;
		});

		registerSharedCallback<AGE::Tasks::Basic::BoolFunction>([&](AGE::Tasks::Basic::BoolFunction& msg)
		{
			msg.setValue(msg.function());
		});

		registerCallback<AGE::Tasks::Basic::VoidFunction>([&](AGE::Tasks::Basic::VoidFunction& msg)
		{
			if (msg.function)
				msg.function();
		});

		registerCallback<AGE::Tasks::Basic::Exit>([&](AGE::Tasks::Basic::Exit& msg)
		{
			AGE::CreateEngine()->deleteInstance<IRenderContext>();
			this->_insideRun = false;
		});

#ifdef USE_IMGUI
		registerCallback<AGE::RenderImgui>([&](AGE::RenderImgui &msg)
		{
			AGE::Imgui::getInstance()->renderThreadRenderFn(msg.cmd_lists);
		});
#endif

		return true;
	}

	bool RenderThread::release()
	{
		return true;
	}

	bool RenderThread::launch()
	{
		if (!init())
			return false;
		_threadHandle = std::thread(&RenderThread::update, std::ref(*this));
		return true;
	}

	bool RenderThread::stop()
	{
		getQueue()->emplaceTask<Tasks::Basic::Exit>();
		if (_threadHandle.joinable())
			_threadHandle.join();
		return true;
	}

	bool RenderThread::update()
	{
		/*
		- Tant qu'il n'y a pas de command
		-> je pop des task
		- Une fois que j'ai mes commandes
		-> pour chacunes d'elles
		-> je regarde si c'est a moi de les traiter (ex : changement de scene)
		-> si ca n'est pas a moi de les traiter
		-> je les passe au render context actif
		-> si il n'y a pas de render context actif, c'est que j'ai fait une erreur, et j'assert dans ta face :D
		*/

		_registerId();

		_run = true;
		_insideRun = true;
		DWORD threadId = GetThreadId(static_cast<HANDLE>(_threadHandle.native_handle()));
		SetThreadName(threadId, this->_name.c_str());

		TMQ::PtrQueue commands;
		TMQ::PtrQueue tasks;
		bool commandSuccess;
		bool taskSuccess;

		std::chrono::system_clock::time_point waitStart;
		std::chrono::system_clock::time_point waitEnd;
		std::chrono::system_clock::time_point workStart;
		std::chrono::system_clock::time_point workEnd;

		while (_run && _insideRun)
		{
			waitStart = std::chrono::high_resolution_clock::now();
			taskSuccess = commandSuccess = false;
			do {
				if (_context)
					_context->refreshInputs();
				getQueue()->getTaskAndCommandQueue(tasks, taskSuccess, commands, commandSuccess, TMQ::HybridQueue::Block);
			} while (!taskSuccess && !commandSuccess);
			waitEnd = std::chrono::high_resolution_clock::now();
			workStart = std::chrono::high_resolution_clock::now();
			if (taskSuccess)
			{
				while (!tasks.empty() && _insideRun)
				{
					//pop all tasks
					auto task = tasks.front();
					auto success = execute(task); // we receive a task that we cannot treat
					AGE_ASSERT(success);
					tasks.pop();
					taskCounter--;
					workEnd = std::chrono::high_resolution_clock::now();
					const std::size_t toWait = 33;
					const std::size_t elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(workEnd - workStart).count();
					if (elapsed >= toWait)
					{
						std::cout << elapsed << ", ";
						while (!tasks.empty() && _insideRun)
						{
							auto task = tasks.front();
							getQueue()->moveTask(task, tasks.getFrontSize());
							tasks.pop();
						}
					}
				}
			}
			if (commandSuccess)
			{
				// pop all commands
				while (!commands.empty() && _insideRun)
				{
					auto command = commands.front();
					auto success = execute(command);
					AGE_ASSERT(success);
					commands.pop();
				}

				workEnd = std::chrono::high_resolution_clock::now();
				GetThreadManager()->updateThreadStatistics(this->_id
					, std::chrono::duration_cast<std::chrono::microseconds>(workEnd - workStart).count()
					, std::chrono::duration_cast<std::chrono::microseconds>(waitEnd - waitStart).count());
			}
		}
		return true;
	}
}<|endoftext|>


<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2015 Scientific Computing and Imaging Institute,
   University of Utah.

   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 <Dataflow/Serialization/Network/Importer/NetworkIO.h>
#include <Dataflow/Serialization/Network/ModuleDescriptionSerialization.h>
#include <Dataflow/Serialization/Network/NetworkDescriptionSerialization.h>
#include <Dataflow/Serialization/Network/NetworkXMLSerializer.h>
#include <Modules/Factory/HardCodedModuleFactory.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <Dataflow/Network/Network.h>
#include <Dataflow/Network/ModuleInterface.h>
#include <Dataflow/Network/ModuleStateInterface.h>
#include <Dataflow/Network/ConnectionId.h>
#include <Dataflow/Network/Tests/MockNetwork.h>
#include <Core/Datatypes/DenseMatrix.h>
#include <Core/Datatypes/MatrixComparison.h>
#include <Core/Datatypes/MatrixIO.h>
#include <Modules/Basic/SendTestMatrix.h>
#include <Modules/Basic/ReceiveTestMatrix.h>
#include <Modules/Math/EvaluateLinearAlgebraUnary.h>
#include <Modules/Factory/HardCodedModuleFactory.h>
#include <Core/Algorithms/Math/EvaluateLinearAlgebraUnaryAlgo.h>
#include <Core/Algorithms/Math/EvaluateLinearAlgebraBinaryAlgo.h>
#include <Core/Algorithms/Math/ReportMatrixInfo.h>
#include <Dataflow/Network/Tests/MockModuleState.h>
#include <Dataflow/State/SimpleMapModuleState.h>
#include <Dataflow/Engine/Controller/NetworkEditorController.h>
#include <Dataflow/Serialization/Network/XMLSerializer.h>
#include <Dataflow/Engine/Scheduler/DesktopExecutionStrategyFactory.h>
#include <Core/Algorithms/Base/AlgorithmVariableNames.h>
#include <Testing/Utils/SCIRunUnitTests.h>

using namespace SCIRun;
using namespace SCIRun::Dataflow::Engine;
using namespace SCIRun::Modules::Basic;
using namespace SCIRun::Modules::Math;
using namespace SCIRun::Modules::Factory;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Dataflow::Networks;
using namespace SCIRun::Dataflow::Networks::Mocks;
using namespace SCIRun::Core::Algorithms::Math;
using namespace SCIRun::Dataflow::State;
using namespace SCIRun::Core::Algorithms;
using namespace SCIRun::TestUtils;

#include <stdexcept>
#include <fstream>
#include <boost/assign.hpp>

using namespace SCIRun::Dataflow::Networks;
using namespace boost::assign;

TEST(LegacyNetworkFileImporterTests, CanLoadEmptyNetworkFile)
{
  auto dtdpath = TestResources::rootDir() / "Other";
  LegacyNetworkIO lnio(dtdpath.string());;
  auto v4file1 = TestResources::rootDir() / "Other" / "v4nets" / "empty.srn";
  auto networkFile = lnio.load_net(v4file1.string());
  ASSERT_TRUE(networkFile != nullptr);

  EXPECT_EQ(0, networkFile->network.modules.size());
  EXPECT_EQ(0, networkFile->network.connections.size());
  EXPECT_EQ(0, networkFile->modulePositions.modulePositions.size());
  EXPECT_EQ(0, networkFile->moduleNotes.notes.size());
  EXPECT_EQ(0, networkFile->connectionNotes.notes.size());
  EXPECT_EQ(0, networkFile->moduleTags.tags.size());
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithSingleModuleNoState)
{
  auto dtdpath = TestResources::rootDir() / "Other";
  LegacyNetworkIO lnio(dtdpath.string());
  auto v4file1 = TestResources::rootDir() / "Other" / "v4nets" / "oneModule.srn";
  auto networkFile = lnio.load_net(v4file1.string());
  ASSERT_TRUE(networkFile != nullptr);

  EXPECT_EQ(1, networkFile->network.modules.size());
  auto mod = *networkFile->network.modules.begin();
  EXPECT_EQ("m1", mod.first); //TODO: ID conversion??
  EXPECT_EQ("CreateLatVol", mod.second.module.module_name_);
  EXPECT_EQ("NewField", mod.second.module.category_name_);
  EXPECT_EQ("SCIRun", mod.second.module.package_name_);

  EXPECT_EQ(0, networkFile->network.connections.size());

  EXPECT_EQ(1, networkFile->modulePositions.modulePositions.size());
  EXPECT_EQ(std::make_pair(289.0,151.0), networkFile->modulePositions.modulePositions.begin()->second);

  EXPECT_EQ(0, networkFile->moduleNotes.notes.size());
  EXPECT_EQ(0, networkFile->connectionNotes.notes.size());
  EXPECT_EQ(0, networkFile->moduleTags.tags.size());
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithSingleModuleWithState)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithTwoModulesNoConnections)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithTwoModulesOneConnection)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithTwoModulesTwoConnections)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithLotsOfObjects)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithModuleNotes)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithConnectionNotes)
{
  FAIL() << "todo";
}<commit_msg>Multiple modules are good<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2015 Scientific Computing and Imaging Institute,
   University of Utah.

   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 <Dataflow/Serialization/Network/Importer/NetworkIO.h>
#include <Dataflow/Serialization/Network/ModuleDescriptionSerialization.h>
#include <Dataflow/Serialization/Network/NetworkDescriptionSerialization.h>
#include <Dataflow/Serialization/Network/NetworkXMLSerializer.h>
#include <Modules/Factory/HardCodedModuleFactory.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <Dataflow/Network/Network.h>
#include <Dataflow/Network/ModuleInterface.h>
#include <Dataflow/Network/ModuleStateInterface.h>
#include <Dataflow/Network/ConnectionId.h>
#include <Dataflow/Network/Tests/MockNetwork.h>
#include <Core/Datatypes/DenseMatrix.h>
#include <Core/Datatypes/MatrixComparison.h>
#include <Core/Datatypes/MatrixIO.h>
#include <Modules/Basic/SendTestMatrix.h>
#include <Modules/Basic/ReceiveTestMatrix.h>
#include <Modules/Math/EvaluateLinearAlgebraUnary.h>
#include <Modules/Factory/HardCodedModuleFactory.h>
#include <Core/Algorithms/Math/EvaluateLinearAlgebraUnaryAlgo.h>
#include <Core/Algorithms/Math/EvaluateLinearAlgebraBinaryAlgo.h>
#include <Core/Algorithms/Math/ReportMatrixInfo.h>
#include <Dataflow/Network/Tests/MockModuleState.h>
#include <Dataflow/State/SimpleMapModuleState.h>
#include <Dataflow/Engine/Controller/NetworkEditorController.h>
#include <Dataflow/Serialization/Network/XMLSerializer.h>
#include <Dataflow/Engine/Scheduler/DesktopExecutionStrategyFactory.h>
#include <Core/Algorithms/Base/AlgorithmVariableNames.h>
#include <Testing/Utils/SCIRunUnitTests.h>

using namespace SCIRun;
using namespace SCIRun::Dataflow::Engine;
using namespace SCIRun::Modules::Basic;
using namespace SCIRun::Modules::Math;
using namespace SCIRun::Modules::Factory;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Dataflow::Networks;
using namespace SCIRun::Dataflow::Networks::Mocks;
using namespace SCIRun::Core::Algorithms::Math;
using namespace SCIRun::Dataflow::State;
using namespace SCIRun::Core::Algorithms;
using namespace SCIRun::TestUtils;

#include <stdexcept>
#include <fstream>
#include <boost/assign.hpp>

using namespace SCIRun::Dataflow::Networks;
using namespace boost::assign;

TEST(LegacyNetworkFileImporterTests, CanLoadEmptyNetworkFile)
{
  auto dtdpath = TestResources::rootDir() / "Other";
  LegacyNetworkIO lnio(dtdpath.string());;
  auto v4file1 = TestResources::rootDir() / "Other" / "v4nets" / "empty.srn";
  auto networkFile = lnio.load_net(v4file1.string());
  ASSERT_TRUE(networkFile != nullptr);

  EXPECT_EQ(0, networkFile->network.modules.size());
  EXPECT_EQ(0, networkFile->network.connections.size());
  EXPECT_EQ(0, networkFile->modulePositions.modulePositions.size());
  EXPECT_EQ(0, networkFile->moduleNotes.notes.size());
  EXPECT_EQ(0, networkFile->connectionNotes.notes.size());
  EXPECT_EQ(0, networkFile->moduleTags.tags.size());
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithSingleModuleNoState)
{
  auto dtdpath = TestResources::rootDir() / "Other";
  LegacyNetworkIO lnio(dtdpath.string());
  auto v4file1 = TestResources::rootDir() / "Other" / "v4nets" / "oneModule.srn";
  auto networkFile = lnio.load_net(v4file1.string());
  ASSERT_TRUE(networkFile != nullptr);

  EXPECT_EQ(1, networkFile->network.modules.size());
  auto mod = *networkFile->network.modules.begin();
  EXPECT_EQ("m1", mod.first); //TODO: ID conversion??
  EXPECT_EQ("CreateLatVol", mod.second.module.module_name_);
  EXPECT_EQ("NewField", mod.second.module.category_name_);
  EXPECT_EQ("SCIRun", mod.second.module.package_name_);

  EXPECT_EQ(0, networkFile->network.connections.size());

  EXPECT_EQ(1, networkFile->modulePositions.modulePositions.size());
  EXPECT_EQ(std::make_pair(289.0,151.0), networkFile->modulePositions.modulePositions.begin()->second);

  EXPECT_EQ(0, networkFile->moduleNotes.notes.size());
  EXPECT_EQ(0, networkFile->connectionNotes.notes.size());
  EXPECT_EQ(0, networkFile->moduleTags.tags.size());
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithSingleModuleWithState)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithTwoModulesNoConnections)
{
  auto dtdpath = TestResources::rootDir() / "Other";
  LegacyNetworkIO lnio(dtdpath.string());
  auto v4file1 = TestResources::rootDir() / "Other" / "v4nets" / "threeModulesNoConnections.srn";
  auto networkFile = lnio.load_net(v4file1.string());
  ASSERT_TRUE(networkFile != nullptr);

  EXPECT_EQ(3, networkFile->network.modules.size());
  auto modIter = networkFile->network.modules.begin();
  EXPECT_EQ("m1", modIter->first); //TODO: ID conversion??
  EXPECT_EQ("ComputeSVD", modIter->second.module.module_name_);
  EXPECT_EQ("Math", modIter->second.module.category_name_);
  EXPECT_EQ("SCIRun", modIter->second.module.package_name_);
  ++modIter;
  EXPECT_EQ("m2", modIter->first); //TODO: ID conversion??
  EXPECT_EQ("CreateLatVol", modIter->second.module.module_name_);
  EXPECT_EQ("NewField", modIter->second.module.category_name_);
  EXPECT_EQ("SCIRun", modIter->second.module.package_name_);
  ++modIter;
  EXPECT_EQ("m3", modIter->first); //TODO: ID conversion??
  EXPECT_EQ("ShowField", modIter->second.module.module_name_);
  EXPECT_EQ("Visualization", modIter->second.module.category_name_);
  EXPECT_EQ("SCIRun", modIter->second.module.package_name_);

  EXPECT_EQ(0, networkFile->network.connections.size());

  EXPECT_EQ(3, networkFile->modulePositions.modulePositions.size());
  auto posIter = networkFile->modulePositions.modulePositions.begin();
  EXPECT_EQ(std::make_pair(357.0,134.0), posIter->second); ++posIter;
  EXPECT_EQ(std::make_pair(304.0,258.0), posIter->second); ++posIter;
  EXPECT_EQ(std::make_pair(386.0,376.0), posIter->second);

  EXPECT_EQ(0, networkFile->moduleNotes.notes.size());
  EXPECT_EQ(0, networkFile->connectionNotes.notes.size());
  EXPECT_EQ(0, networkFile->moduleTags.tags.size());
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithTwoModulesOneConnection)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithTwoModulesTwoConnections)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithLotsOfObjects)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithModuleNotes)
{
  FAIL() << "todo";
}

TEST(LegacyNetworkFileImporterTests, CanLoadNetworkFileWithConnectionNotes)
{
  FAIL() << "todo";
}
<|endoftext|>