<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 "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/json_value_serializer.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace errors = extension_manifest_errors;

class ManifestTest : public testing::Test {
 public:
  ManifestTest() : enable_apps_(true) {}

 protected:
  Extension* LoadExtension(const std::string& name,
                           std::string* error) {
    return LoadExtensionWithLocation(name, Extension::INTERNAL, error);
  }

  Extension* LoadExtensionWithLocation(const std::string& name,
                                       Extension::Location location,
                                       std::string* error) {
    FilePath path;
    PathService::Get(chrome::DIR_TEST_DATA, &path);
    path = path.AppendASCII("extensions")
        .AppendASCII("manifest_tests")
        .AppendASCII(name.c_str());
    EXPECT_TRUE(file_util::PathExists(path));

    JSONFileValueSerializer serializer(path);
    scoped_ptr<DictionaryValue> value(
        static_cast<DictionaryValue*>(serializer.Deserialize(NULL, error)));
    if (!value.get())
      return NULL;

    scoped_ptr<Extension> extension(new Extension(path.DirName()));
    extension->set_location(location);
    extension->set_apps_enabled(enable_apps_);

    if (!extension->InitFromValue(*value, false, error))
      return NULL;

    return extension.release();
  }

  Extension* LoadAndExpectSuccess(const std::string& name) {
    std::string error;
    Extension* extension = LoadExtension(name, &error);
    EXPECT_TRUE(extension);
    EXPECT_EQ("", error);
    return extension;
  }

  void LoadAndExpectError(const std::string& name,
                          const std::string& expected_error) {
    std::string error;
    scoped_ptr<Extension> extension(LoadExtension(name, &error));
    EXPECT_FALSE(extension.get()) <<
        "Expected failure loading extension '" << name <<
        "', but didn't get one.";
    EXPECT_TRUE(MatchPatternASCII(error, expected_error)) << name <<
        " expected '" << expected_error << "' but got '" << error << "'";
  }

  bool enable_apps_;
};

TEST_F(ManifestTest, AppsDisabledByDefault) {
#if defined(OS_CHROMEOS)
  // On ChromeOS, apps are enabled by default.
  if (Extension::AppsAreEnabled())
    return;
#endif

  enable_apps_ = false;
  LoadAndExpectError("launch_local_path.json", errors::kAppsNotEnabled);
}

TEST_F(ManifestTest, ValidApp) {
  scoped_ptr<Extension> extension(LoadAndExpectSuccess("valid_app.json"));
  EXPECT_EQ(GURL("http://www.google.com/"), extension->web_extent().origin());
  EXPECT_EQ(2u, extension->web_extent().paths().size());
  EXPECT_EQ("mail/", extension->web_extent().paths()[0]);
  EXPECT_EQ("foobar/", extension->web_extent().paths()[1]);
  EXPECT_EQ(Extension::LAUNCH_WINDOW, extension->launch_container());
  EXPECT_EQ(false, extension->launch_fullscreen());
  EXPECT_EQ("mail/", extension->launch_web_url());
}

TEST_F(ManifestTest, AppWebOrigin) {
  LoadAndExpectError("web_origin_wrong_type.json",
                     errors::kInvalidWebOrigin);
  LoadAndExpectError("web_origin_invalid_1.json",
                     errors::kInvalidWebOrigin);
  LoadAndExpectError("web_origin_invalid_2.json",
                     errors::kInvalidWebOrigin);
  LoadAndExpectError("web_origin_invalid_3.json",
                     errors::kInvalidWebOrigin);
}

TEST_F(ManifestTest, AppWebPaths) {
  LoadAndExpectError("web_paths_wrong_type.json",
                     errors::kInvalidWebPaths);
  LoadAndExpectError("web_paths_invalid_path_1.json",
                     ExtensionErrorUtils::FormatErrorMessage(
                         errors::kInvalidWebPath, "0"));
  LoadAndExpectError("web_paths_invalid_path_2.json",
                     ExtensionErrorUtils::FormatErrorMessage(
                         errors::kInvalidWebPath, "0"));
}

// This test crashes. http://crbug.com/47230
TEST_F(ManifestTest, DISABLED_AppLaunchContainer) {
  scoped_ptr<Extension> extension;

  extension.reset(LoadAndExpectSuccess("launch_tab.json"));
  EXPECT_EQ(Extension::LAUNCH_TAB, extension->launch_container());

  extension.reset(LoadAndExpectSuccess("launch_window.json"));
  EXPECT_EQ(Extension::LAUNCH_WINDOW, extension->launch_container());

  extension.reset(LoadAndExpectSuccess("launch_panel.json"));
  EXPECT_EQ(Extension::LAUNCH_PANEL, extension->launch_container());

  extension.reset(LoadAndExpectSuccess("launch_default.json"));
  EXPECT_EQ(Extension::LAUNCH_TAB, extension->launch_container());

  extension.reset(LoadAndExpectSuccess("launch_fullscreen.json"));
  EXPECT_EQ(true, extension->launch_fullscreen());

  extension.reset(LoadAndExpectSuccess("launch_width.json"));
  EXPECT_EQ(640, extension->launch_width());

  extension.reset(LoadAndExpectSuccess("launch_height.json"));
  EXPECT_EQ(480, extension->launch_height());

  LoadAndExpectError("launch_container_invalid_type.json",
                     errors::kInvalidLaunchContainer);
  LoadAndExpectError("launch_container_invalid_value.json",
                     errors::kInvalidLaunchContainer);
  LoadAndExpectError("launch_container_without_launch_url.json",
                     errors::kLaunchURLRequired);
  LoadAndExpectError("launch_fullscreen_invalid.json",
                     errors::kInvalidLaunchFullscreen);
  LoadAndExpectError("launch_width_invalid.json",
                     errors::kInvalidLaunchWidthContainer);
  LoadAndExpectError("launch_width_negative.json",
                     errors::kInvalidLaunchWidth);
  LoadAndExpectError("launch_height_invalid.json",
                     errors::kInvalidLaunchHeightContainer);
  LoadAndExpectError("launch_height_negative.json",
                     errors::kInvalidLaunchHeight);
}

TEST_F(ManifestTest, AppLaunchURL) {
  LoadAndExpectError("launch_path_and_url.json",
                     errors::kLaunchPathAndURLAreExclusive);
  LoadAndExpectError("launch_path_invalid_type.json",
                     errors::kInvalidLaunchLocalPath);
  LoadAndExpectError("launch_path_invalid_value.json",
                     errors::kInvalidLaunchLocalPath);
  LoadAndExpectError("launch_url_invalid_type.json",
                     errors::kInvalidLaunchWebURL);

  scoped_ptr<Extension> extension;
  extension.reset(LoadAndExpectSuccess("launch_local_path.json"));
  EXPECT_EQ(extension->url().spec() + "launch.html",
            extension->GetFullLaunchURL().spec());

  extension.reset(LoadAndExpectSuccess("launch_web_url_relative.json"));
  EXPECT_EQ(GURL("http://www.google.com/launch.html"),
            extension->GetFullLaunchURL());

  extension.reset(LoadAndExpectSuccess("launch_web_url_absolute.json"));
  EXPECT_EQ(GURL("http://www.google.com/launch.html"),
            extension->GetFullLaunchURL());
}

TEST_F(ManifestTest, Override) {
  LoadAndExpectError("override_newtab_and_history.json",
                     errors::kMultipleOverrides);
  LoadAndExpectError("override_invalid_page.json",
                     errors::kInvalidChromeURLOverrides);

  scoped_ptr<Extension> extension;

  extension.reset(LoadAndExpectSuccess("override_new_tab.json"));
  EXPECT_EQ(extension->url().spec() + "newtab.html",
            extension->GetChromeURLOverrides().find("newtab")->second.spec());

  extension.reset(LoadAndExpectSuccess("override_history.json"));
  EXPECT_EQ(extension->url().spec() + "history.html",
            extension->GetChromeURLOverrides().find("history")->second.spec());
}

TEST_F(ManifestTest, ChromeURLPermissionInvalid) {
  LoadAndExpectError("permission_chrome_url_invalid.json",
      errors::kInvalidPermissionScheme);
}

TEST_F(ManifestTest, ChromeResourcesPermissionValidOnlyForComponents) {
  LoadAndExpectError("permission_chrome_resources_url.json",
      errors::kInvalidPermissionScheme);
  std::string error;
  scoped_ptr<Extension> extension;
  extension.reset(LoadExtensionWithLocation(
      "permission_chrome_resources_url.json",
      Extension::COMPONENT,
      &error));
  EXPECT_EQ("", error);
}

TEST_F(ManifestTest, ChromeURLContentScriptInvalid) {
  LoadAndExpectError("content_script_chrome_url_invalid.json",
      errors::kInvalidMatch);
}
<commit_msg>Enable ManifestTest.AppLaunchContainer as it should be passing now.<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 "base/file_path.h"
#include "base/file_util.h"
#include "base/path_service.h"
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_error_utils.h"
#include "chrome/common/json_value_serializer.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace errors = extension_manifest_errors;

class ManifestTest : public testing::Test {
 public:
  ManifestTest() : enable_apps_(true) {}

 protected:
  Extension* LoadExtension(const std::string& name,
                           std::string* error) {
    return LoadExtensionWithLocation(name, Extension::INTERNAL, error);
  }

  Extension* LoadExtensionWithLocation(const std::string& name,
                                       Extension::Location location,
                                       std::string* error) {
    FilePath path;
    PathService::Get(chrome::DIR_TEST_DATA, &path);
    path = path.AppendASCII("extensions")
        .AppendASCII("manifest_tests")
        .AppendASCII(name.c_str());
    EXPECT_TRUE(file_util::PathExists(path));

    JSONFileValueSerializer serializer(path);
    scoped_ptr<DictionaryValue> value(
        static_cast<DictionaryValue*>(serializer.Deserialize(NULL, error)));
    if (!value.get())
      return NULL;

    scoped_ptr<Extension> extension(new Extension(path.DirName()));
    extension->set_location(location);
    extension->set_apps_enabled(enable_apps_);

    if (!extension->InitFromValue(*value, false, error))
      return NULL;

    return extension.release();
  }

  Extension* LoadAndExpectSuccess(const std::string& name) {
    std::string error;
    Extension* extension = LoadExtension(name, &error);
    EXPECT_TRUE(extension);
    EXPECT_EQ("", error);
    return extension;
  }

  void LoadAndExpectError(const std::string& name,
                          const std::string& expected_error) {
    std::string error;
    scoped_ptr<Extension> extension(LoadExtension(name, &error));
    EXPECT_FALSE(extension.get()) <<
        "Expected failure loading extension '" << name <<
        "', but didn't get one.";
    EXPECT_TRUE(MatchPatternASCII(error, expected_error)) << name <<
        " expected '" << expected_error << "' but got '" << error << "'";
  }

  bool enable_apps_;
};

TEST_F(ManifestTest, AppsDisabledByDefault) {
#if defined(OS_CHROMEOS)
  // On ChromeOS, apps are enabled by default.
  if (Extension::AppsAreEnabled())
    return;
#endif

  enable_apps_ = false;
  LoadAndExpectError("launch_local_path.json", errors::kAppsNotEnabled);
}

TEST_F(ManifestTest, ValidApp) {
  scoped_ptr<Extension> extension(LoadAndExpectSuccess("valid_app.json"));
  EXPECT_EQ(GURL("http://www.google.com/"), extension->web_extent().origin());
  EXPECT_EQ(2u, extension->web_extent().paths().size());
  EXPECT_EQ("mail/", extension->web_extent().paths()[0]);
  EXPECT_EQ("foobar/", extension->web_extent().paths()[1]);
  EXPECT_EQ(Extension::LAUNCH_WINDOW, extension->launch_container());
  EXPECT_EQ(false, extension->launch_fullscreen());
  EXPECT_EQ("mail/", extension->launch_web_url());
}

TEST_F(ManifestTest, AppWebOrigin) {
  LoadAndExpectError("web_origin_wrong_type.json",
                     errors::kInvalidWebOrigin);
  LoadAndExpectError("web_origin_invalid_1.json",
                     errors::kInvalidWebOrigin);
  LoadAndExpectError("web_origin_invalid_2.json",
                     errors::kInvalidWebOrigin);
  LoadAndExpectError("web_origin_invalid_3.json",
                     errors::kInvalidWebOrigin);
}

TEST_F(ManifestTest, AppWebPaths) {
  LoadAndExpectError("web_paths_wrong_type.json",
                     errors::kInvalidWebPaths);
  LoadAndExpectError("web_paths_invalid_path_1.json",
                     ExtensionErrorUtils::FormatErrorMessage(
                         errors::kInvalidWebPath, "0"));
  LoadAndExpectError("web_paths_invalid_path_2.json",
                     ExtensionErrorUtils::FormatErrorMessage(
                         errors::kInvalidWebPath, "0"));
}

TEST_F(ManifestTest, AppLaunchContainer) {
  scoped_ptr<Extension> extension;

  extension.reset(LoadAndExpectSuccess("launch_tab.json"));
  EXPECT_EQ(Extension::LAUNCH_TAB, extension->launch_container());

  extension.reset(LoadAndExpectSuccess("launch_window.json"));
  EXPECT_EQ(Extension::LAUNCH_WINDOW, extension->launch_container());

  extension.reset(LoadAndExpectSuccess("launch_panel.json"));
  EXPECT_EQ(Extension::LAUNCH_PANEL, extension->launch_container());

  extension.reset(LoadAndExpectSuccess("launch_default.json"));
  EXPECT_EQ(Extension::LAUNCH_TAB, extension->launch_container());

  extension.reset(LoadAndExpectSuccess("launch_fullscreen.json"));
  EXPECT_EQ(true, extension->launch_fullscreen());

  extension.reset(LoadAndExpectSuccess("launch_width.json"));
  EXPECT_EQ(640, extension->launch_width());

  extension.reset(LoadAndExpectSuccess("launch_height.json"));
  EXPECT_EQ(480, extension->launch_height());

  LoadAndExpectError("launch_container_invalid_type.json",
                     errors::kInvalidLaunchContainer);
  LoadAndExpectError("launch_container_invalid_value.json",
                     errors::kInvalidLaunchContainer);
  LoadAndExpectError("launch_container_without_launch_url.json",
                     errors::kLaunchURLRequired);
  LoadAndExpectError("launch_fullscreen_invalid.json",
                     errors::kInvalidLaunchFullscreen);
  LoadAndExpectError("launch_width_invalid.json",
                     errors::kInvalidLaunchWidthContainer);
  LoadAndExpectError("launch_width_negative.json",
                     errors::kInvalidLaunchWidth);
  LoadAndExpectError("launch_height_invalid.json",
                     errors::kInvalidLaunchHeightContainer);
  LoadAndExpectError("launch_height_negative.json",
                     errors::kInvalidLaunchHeight);
}

TEST_F(ManifestTest, AppLaunchURL) {
  LoadAndExpectError("launch_path_and_url.json",
                     errors::kLaunchPathAndURLAreExclusive);
  LoadAndExpectError("launch_path_invalid_type.json",
                     errors::kInvalidLaunchLocalPath);
  LoadAndExpectError("launch_path_invalid_value.json",
                     errors::kInvalidLaunchLocalPath);
  LoadAndExpectError("launch_url_invalid_type.json",
                     errors::kInvalidLaunchWebURL);

  scoped_ptr<Extension> extension;
  extension.reset(LoadAndExpectSuccess("launch_local_path.json"));
  EXPECT_EQ(extension->url().spec() + "launch.html",
            extension->GetFullLaunchURL().spec());

  extension.reset(LoadAndExpectSuccess("launch_web_url_relative.json"));
  EXPECT_EQ(GURL("http://www.google.com/launch.html"),
            extension->GetFullLaunchURL());

  extension.reset(LoadAndExpectSuccess("launch_web_url_absolute.json"));
  EXPECT_EQ(GURL("http://www.google.com/launch.html"),
            extension->GetFullLaunchURL());
}

TEST_F(ManifestTest, Override) {
  LoadAndExpectError("override_newtab_and_history.json",
                     errors::kMultipleOverrides);
  LoadAndExpectError("override_invalid_page.json",
                     errors::kInvalidChromeURLOverrides);

  scoped_ptr<Extension> extension;

  extension.reset(LoadAndExpectSuccess("override_new_tab.json"));
  EXPECT_EQ(extension->url().spec() + "newtab.html",
            extension->GetChromeURLOverrides().find("newtab")->second.spec());

  extension.reset(LoadAndExpectSuccess("override_history.json"));
  EXPECT_EQ(extension->url().spec() + "history.html",
            extension->GetChromeURLOverrides().find("history")->second.spec());
}

TEST_F(ManifestTest, ChromeURLPermissionInvalid) {
  LoadAndExpectError("permission_chrome_url_invalid.json",
      errors::kInvalidPermissionScheme);
}

TEST_F(ManifestTest, ChromeResourcesPermissionValidOnlyForComponents) {
  LoadAndExpectError("permission_chrome_resources_url.json",
      errors::kInvalidPermissionScheme);
  std::string error;
  scoped_ptr<Extension> extension;
  extension.reset(LoadExtensionWithLocation(
      "permission_chrome_resources_url.json",
      Extension::COMPONENT,
      &error));
  EXPECT_EQ("", error);
}

TEST_F(ManifestTest, ChromeURLContentScriptInvalid) {
  LoadAndExpectError("content_script_chrome_url_invalid.json",
      errors::kInvalidMatch);
}
<|endoftext|>


<commit_before>//===--- GlobalSymbolBuilderMain.cpp -----------------------------*- C++-*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// GlobalSymbolBuilder is a tool to generate YAML-format symbols across the
// whole project. This tools is for **experimental** only. Don't use it in
// production code.
//
//===----------------------------------------------------------------------===//

#include "index/CanonicalIncludes.h"
#include "index/Index.h"
#include "index/Merge.h"
#include "index/SymbolCollector.h"
#include "index/SymbolYAML.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexingAction.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Execution.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/YAMLTraits.h"

using namespace llvm;
using namespace clang::tooling;
using clang::clangd::SymbolSlab;

namespace clang {
namespace clangd {
namespace {

static llvm::cl::opt<std::string> AssumedHeaderDir(
    "assume-header-dir",
    llvm::cl::desc("The index includes header that a symbol is defined in. "
                   "If the absolute path cannot be determined (e.g. an "
                   "in-memory VFS) then the relative path is resolved against "
                   "this directory, which must be absolute. If this flag is "
                   "not given, such headers will have relative paths."),
    llvm::cl::init(""));

class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
public:
  SymbolIndexActionFactory(tooling::ExecutionContext *Ctx) : Ctx(Ctx) {}

  clang::FrontendAction *create() override {
    // Wraps the index action and reports collected symbols to the execution
    // context at the end of each translation unit.
    class WrappedIndexAction : public WrapperFrontendAction {
    public:
      WrappedIndexAction(std::shared_ptr<SymbolCollector> C,
                         std::unique_ptr<CanonicalIncludes> Includes,
                         const index::IndexingOptions &Opts,
                         tooling::ExecutionContext *Ctx)
          : WrapperFrontendAction(
                index::createIndexingAction(C, Opts, nullptr)),
            Ctx(Ctx), Collector(C), Includes(std::move(Includes)),
            PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {}

      std::unique_ptr<ASTConsumer>
      CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
        CI.getPreprocessor().addCommentHandler(PragmaHandler.get());
        return WrapperFrontendAction::CreateASTConsumer(CI, InFile);
      }

      bool BeginInvocation(CompilerInstance &CI) override {
        // We want all comments, not just the doxygen ones.
        CI.getLangOpts().CommentOpts.ParseAllComments = true;
        return WrapperFrontendAction::BeginInvocation(CI);
      }

      void EndSourceFileAction() override {
        WrapperFrontendAction::EndSourceFileAction();

        const auto &CI = getCompilerInstance();
        if (CI.hasDiagnostics() &&
            CI.getDiagnostics().hasUncompilableErrorOccurred()) {
          llvm::errs()
              << "Found uncompilable errors in the translation unit. Igoring "
                 "collected symbols...\n";
          return;
        }

        auto Symbols = Collector->takeSymbols();
        for (const auto &Sym : Symbols) {
          Ctx->reportResult(Sym.ID.str(), SymbolToYAML(Sym));
        }
      }

    private:
      tooling::ExecutionContext *Ctx;
      std::shared_ptr<SymbolCollector> Collector;
      std::unique_ptr<CanonicalIncludes> Includes;
      std::unique_ptr<CommentHandler> PragmaHandler;
    };

    index::IndexingOptions IndexOpts;
    IndexOpts.SystemSymbolFilter =
        index::IndexingOptions::SystemSymbolFilterKind::All;
    IndexOpts.IndexFunctionLocals = false;
    auto CollectorOpts = SymbolCollector::Options();
    CollectorOpts.FallbackDir = AssumedHeaderDir;
    CollectorOpts.CollectIncludePath = true;
    CollectorOpts.CountReferences = true;
    CollectorOpts.Origin = SymbolOrigin::Static;
    auto Includes = llvm::make_unique<CanonicalIncludes>();
    addSystemHeadersMapping(Includes.get());
    CollectorOpts.Includes = Includes.get();
    return new WrappedIndexAction(
        std::make_shared<SymbolCollector>(std::move(CollectorOpts)),
        std::move(Includes), IndexOpts, Ctx);
  }

  tooling::ExecutionContext *Ctx;
};

// Combine occurrences of the same symbol across translation units.
SymbolSlab mergeSymbols(tooling::ToolResults *Results) {
  SymbolSlab::Builder UniqueSymbols;
  llvm::BumpPtrAllocator Arena;
  Symbol::Details Scratch;
  Results->forEachResult([&](llvm::StringRef Key, llvm::StringRef Value) {
    Arena.Reset();
    llvm::yaml::Input Yin(Value, &Arena);
    auto Sym = clang::clangd::SymbolFromYAML(Yin, Arena);
    clang::clangd::SymbolID ID;
    Key >> ID;
    if (const auto *Existing = UniqueSymbols.find(ID))
      UniqueSymbols.insert(mergeSymbol(*Existing, Sym, &Scratch));
    else
      UniqueSymbols.insert(Sym);
  });
  return std::move(UniqueSymbols).build();
}

} // namespace
} // namespace clangd
} // namespace clang

int main(int argc, const char **argv) {
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);

  const char *Overview = R"(
  This is an **experimental** tool to generate YAML-format project-wide symbols
  for clangd (global code completion). It would be changed and deprecated
  eventually. Don't use it in production code!

  Example usage for building index for the whole project using CMake compile
  commands:

  $ global-symbol-builder --executor=all-TUs compile_commands.json > index.yaml

  Example usage for file sequence index without flags:

  $ global-symbol-builder File1.cpp File2.cpp ... FileN.cpp > index.yaml

  Note: only symbols from header files will be collected.
  )";

  auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
      argc, argv, cl::GeneralCategory, Overview);

  if (!Executor) {
    llvm::errs() << llvm::toString(Executor.takeError()) << "\n";
    return 1;
  }

  if (!clang::clangd::AssumedHeaderDir.empty() &&
      !llvm::sys::path::is_absolute(clang::clangd::AssumedHeaderDir)) {
    llvm::errs() << "--assume-header-dir must be an absolute path.\n";
    return 1;
  }

  // Map phase: emit symbols found in each translation unit.
  auto Err = Executor->get()->execute(
      llvm::make_unique<clang::clangd::SymbolIndexActionFactory>(
          Executor->get()->getExecutionContext()));
  if (Err) {
    llvm::errs() << llvm::toString(std::move(Err)) << "\n";
  }

  // Reduce phase: combine symbols using the ID as a key.
  auto UniqueSymbols =
      clang::clangd::mergeSymbols(Executor->get()->getToolResults());

  // Output phase: emit YAML for result symbols.
  SymbolsToYAML(UniqueSymbols, llvm::outs());
  return 0;
}
<commit_msg>[clangd] Allow to merge symbols on-the-fly in global-symbol-builder<commit_after>//===--- GlobalSymbolBuilderMain.cpp -----------------------------*- C++-*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// GlobalSymbolBuilder is a tool to generate YAML-format symbols across the
// whole project. This tools is for **experimental** only. Don't use it in
// production code.
//
//===----------------------------------------------------------------------===//

#include "index/CanonicalIncludes.h"
#include "index/Index.h"
#include "index/Merge.h"
#include "index/SymbolCollector.h"
#include "index/SymbolYAML.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Index/IndexDataConsumer.h"
#include "clang/Index/IndexingAction.h"
#include "clang/Tooling/CommonOptionsParser.h"
#include "clang/Tooling/Execution.h"
#include "clang/Tooling/Tooling.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/YAMLTraits.h"

using namespace llvm;
using namespace clang::tooling;
using clang::clangd::SymbolSlab;

namespace clang {
namespace clangd {
namespace {

static llvm::cl::opt<std::string> AssumedHeaderDir(
    "assume-header-dir",
    llvm::cl::desc("The index includes header that a symbol is defined in. "
                   "If the absolute path cannot be determined (e.g. an "
                   "in-memory VFS) then the relative path is resolved against "
                   "this directory, which must be absolute. If this flag is "
                   "not given, such headers will have relative paths."),
    llvm::cl::init(""));

static llvm::cl::opt<bool> MergeOnTheFly(
    "merge-on-the-fly",
    llvm::cl::desc(
        "Merges symbols for each processed translation unit as soon "
        "they become available. This results in a smaller memory "
        "usage and an almost instant reduce stage. Optimal for running as a "
        "standalone tool, but cannot be used with multi-process executors like "
        "MapReduce."),
    llvm::cl::init(true), llvm::cl::Hidden);

/// Responsible for aggregating symbols from each processed file and producing
/// the final results. All methods in this class must be thread-safe,
/// 'consumeSymbols' may be called from multiple threads.
class SymbolsConsumer {
public:
  virtual ~SymbolsConsumer() = default;

  /// Consume a SymbolSlab build for a file.
  virtual void consumeSymbols(SymbolSlab Symbols) = 0;
  /// Produce a resulting symbol slab, by combining  occurrences of the same
  /// symbols across translation units.
  virtual SymbolSlab mergeResults() = 0;
};

class SymbolIndexActionFactory : public tooling::FrontendActionFactory {
public:
  SymbolIndexActionFactory(SymbolsConsumer &Consumer) : Consumer(Consumer) {}

  clang::FrontendAction *create() override {
    // Wraps the index action and reports collected symbols to the execution
    // context at the end of each translation unit.
    class WrappedIndexAction : public WrapperFrontendAction {
    public:
      WrappedIndexAction(std::shared_ptr<SymbolCollector> C,
                         std::unique_ptr<CanonicalIncludes> Includes,
                         const index::IndexingOptions &Opts,
                         SymbolsConsumer &Consumer)
          : WrapperFrontendAction(
                index::createIndexingAction(C, Opts, nullptr)),
            Consumer(Consumer), Collector(C), Includes(std::move(Includes)),
            PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {}

      std::unique_ptr<ASTConsumer>
      CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
        CI.getPreprocessor().addCommentHandler(PragmaHandler.get());
        return WrapperFrontendAction::CreateASTConsumer(CI, InFile);
      }

      bool BeginInvocation(CompilerInstance &CI) override {
        // We want all comments, not just the doxygen ones.
        CI.getLangOpts().CommentOpts.ParseAllComments = true;
        return WrapperFrontendAction::BeginInvocation(CI);
      }

      void EndSourceFileAction() override {
        WrapperFrontendAction::EndSourceFileAction();

        const auto &CI = getCompilerInstance();
        if (CI.hasDiagnostics() &&
            CI.getDiagnostics().hasUncompilableErrorOccurred()) {
          llvm::errs()
              << "Found uncompilable errors in the translation unit. Igoring "
                 "collected symbols...\n";
          return;
        }

        Consumer.consumeSymbols(Collector->takeSymbols());
      }

    private:
      SymbolsConsumer &Consumer;
      std::shared_ptr<SymbolCollector> Collector;
      std::unique_ptr<CanonicalIncludes> Includes;
      std::unique_ptr<CommentHandler> PragmaHandler;
    };

    index::IndexingOptions IndexOpts;
    IndexOpts.SystemSymbolFilter =
        index::IndexingOptions::SystemSymbolFilterKind::All;
    IndexOpts.IndexFunctionLocals = false;
    auto CollectorOpts = SymbolCollector::Options();
    CollectorOpts.FallbackDir = AssumedHeaderDir;
    CollectorOpts.CollectIncludePath = true;
    CollectorOpts.CountReferences = true;
    CollectorOpts.Origin = SymbolOrigin::Static;
    auto Includes = llvm::make_unique<CanonicalIncludes>();
    addSystemHeadersMapping(Includes.get());
    CollectorOpts.Includes = Includes.get();
    return new WrappedIndexAction(
        std::make_shared<SymbolCollector>(std::move(CollectorOpts)),
        std::move(Includes), IndexOpts, Consumer);
  }

  SymbolsConsumer &Consumer;
};

/// Stashes per-file results inside ExecutionContext, merges all of them at the
/// end. Useful for running on MapReduce infrastructure to avoid keeping symbols
/// from multiple files in memory.
class ToolExecutorConsumer : public SymbolsConsumer {
public:
  ToolExecutorConsumer(ToolExecutor &Executor) : Executor(Executor) {}

  void consumeSymbols(SymbolSlab Symbols) override {
    for (const auto &Sym : Symbols)
      Executor.getExecutionContext()->reportResult(Sym.ID.str(),
                                                   SymbolToYAML(Sym));
  }

  SymbolSlab mergeResults() override {
    SymbolSlab::Builder UniqueSymbols;
    llvm::BumpPtrAllocator Arena;
    Symbol::Details Scratch;
    Executor.getToolResults()->forEachResult(
        [&](llvm::StringRef Key, llvm::StringRef Value) {
          Arena.Reset();
          llvm::yaml::Input Yin(Value, &Arena);
          auto Sym = clang::clangd::SymbolFromYAML(Yin, Arena);
          clang::clangd::SymbolID ID;
          Key >> ID;
          if (const auto *Existing = UniqueSymbols.find(ID))
            UniqueSymbols.insert(mergeSymbol(*Existing, Sym, &Scratch));
          else
            UniqueSymbols.insert(Sym);
        });
    return std::move(UniqueSymbols).build();
  }

private:
  ToolExecutor &Executor;
};

/// Merges symbols for each translation unit as soon as the file is processed.
/// Optimal choice for standalone tools.
class OnTheFlyConsumer : public SymbolsConsumer {
public:
  void consumeSymbols(SymbolSlab Symbols) override {
    std::lock_guard<std::mutex> Lock(Mut);
    for (auto &&Sym : Symbols) {
      Symbol::Details Scratch;
      if (const auto *Existing = Result.find(Sym.ID))
        Result.insert(mergeSymbol(*Existing, Sym, &Scratch));
      else
        Result.insert(Sym);
    }
  }

  SymbolSlab mergeResults() override {
    std::lock_guard<std::mutex> Lock(Mut);
    return std::move(Result).build();
  }

private:
  std::mutex Mut;
  SymbolSlab::Builder Result;
};

} // namespace
} // namespace clangd
} // namespace clang

int main(int argc, const char **argv) {
  llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);

  const char *Overview = R"(
  This is an **experimental** tool to generate YAML-format project-wide symbols
  for clangd (global code completion). It would be changed and deprecated
  eventually. Don't use it in production code!

  Example usage for building index for the whole project using CMake compile
  commands:

  $ global-symbol-builder --executor=all-TUs compile_commands.json > index.yaml

  Example usage for file sequence index without flags:

  $ global-symbol-builder File1.cpp File2.cpp ... FileN.cpp > index.yaml

  Note: only symbols from header files will be collected.
  )";

  auto Executor = clang::tooling::createExecutorFromCommandLineArgs(
      argc, argv, cl::GeneralCategory, Overview);

  if (!Executor) {
    llvm::errs() << llvm::toString(Executor.takeError()) << "\n";
    return 1;
  }

  if (!clang::clangd::AssumedHeaderDir.empty() &&
      !llvm::sys::path::is_absolute(clang::clangd::AssumedHeaderDir)) {
    llvm::errs() << "--assume-header-dir must be an absolute path.\n";
    return 1;
  }

  if (clang::clangd::MergeOnTheFly && !Executor->get()->isSingleProcess()) {
    llvm::errs()
        << "Found multi-process executor, forcing the use of intermediate YAML "
           "serialization instead of the on-the-fly merge.\n";
    clang::clangd::MergeOnTheFly = false;
  }

  std::unique_ptr<clang::clangd::SymbolsConsumer> Consumer;
  if (clang::clangd::MergeOnTheFly)
    Consumer = llvm::make_unique<clang::clangd::OnTheFlyConsumer>();
  else
    Consumer =
        llvm::make_unique<clang::clangd::ToolExecutorConsumer>(**Executor);

  // Map phase: emit symbols found in each translation unit.
  auto Err = Executor->get()->execute(
      llvm::make_unique<clang::clangd::SymbolIndexActionFactory>(*Consumer));
  if (Err) {
    llvm::errs() << llvm::toString(std::move(Err)) << "\n";
  }
  // Reduce phase: combine symbols with the same IDs.
  auto UniqueSymbols = Consumer->mergeResults();
  // Output phase: emit YAML for result symbols.
  SymbolsToYAML(UniqueSymbols, llvm::outs());
  return 0;
}
<|endoftext|>


<commit_before>#include <osg/GL>
#include <osg/GLU>

#include <osg/Image>
#include <osg/Notify>

#include <osg/Geode>
#include <osg/GeoSet>
#include <osg/StateSet>
#include <osg/Texture>

using namespace osg;
using namespace std;

Image::Image()
{
    _fileName               = "";
    _s = _t = _r            = 0;
    _internalTextureFormat  = 0;
    _pixelFormat            = (unsigned int)0;
    _dataType               = (unsigned int)0;
    _packing                = 4;

    _data = (unsigned char *)0L;

    _modifiedTag = 0;
}

Image::Image(const Image& image,const CopyOp& copyop):
    Object(image,copyop),
    _fileName(image._fileName),
    _s(image._s), _t(image._t), _r(image._r),
    _internalTextureFormat(image._internalTextureFormat),
    _pixelFormat(image._pixelFormat),
    _dataType(image._dataType),
    _packing(image._packing),
    _data(0L),
    _mipmapData(image._mipmapData),
    _modifiedTag(image._modifiedTag)
{
    if (image._data)
    {
        int num_components =
            _pixelFormat == GL_LUMINANCE ? 1 :
            _pixelFormat == GL_LUMINANCE_ALPHA ? 2 :
            _pixelFormat == GL_RGB ? 3 :
            _pixelFormat == GL_RGBA ? 4 : 4;

        int size = _s*_t*_r*num_components;
        _data = (unsigned char*) malloc(size);
        memcpy(_data,image._data,size);
    }

}

Image::~Image()
{
    if (_data) ::free(_data);
}

void Image::setFileName(const std::string& fileName)
{
    _fileName = fileName;
}

#ifndef GL_VERSION_1_2
    /* 1.2 definitions...*/
    #define GL_BGR					0x80E0
    #define GL_BGRA					0x80E1
    #define GL_UNSIGNED_BYTE_3_3_2			0x8032
    #define GL_UNSIGNED_BYTE_2_3_3_REV		0x8362
    #define GL_UNSIGNED_SHORT_5_6_5			0x8363
    #define GL_UNSIGNED_SHORT_5_6_5_REV		0x8364
    #define GL_UNSIGNED_SHORT_4_4_4_4		0x8033
    #define GL_UNSIGNED_SHORT_4_4_4_4_REV		0x8365
    #define GL_UNSIGNED_SHORT_5_5_5_1		0x8034
    #define GL_UNSIGNED_SHORT_1_5_5_5_REV		0x8366
    #define GL_UNSIGNED_INT_8_8_8_8			0x8035
    #define GL_UNSIGNED_INT_8_8_8_8_REV		0x8367
    #define GL_UNSIGNED_INT_10_10_10_2		0x8036
    #define GL_UNSIGNED_INT_2_10_10_10_REV		0x8368
#endif

const bool Image::isPackedType(GLenum format)
{
    switch(format)
    {
        case(GL_UNSIGNED_BYTE_3_3_2):
        case(GL_UNSIGNED_BYTE_2_3_3_REV):
        case(GL_UNSIGNED_SHORT_5_6_5):
        case(GL_UNSIGNED_SHORT_5_6_5_REV):
        case(GL_UNSIGNED_SHORT_4_4_4_4):
        case(GL_UNSIGNED_SHORT_4_4_4_4_REV):
        case(GL_UNSIGNED_SHORT_5_5_5_1):
        case(GL_UNSIGNED_SHORT_1_5_5_5_REV):
        case(GL_UNSIGNED_INT_8_8_8_8):
        case(GL_UNSIGNED_INT_8_8_8_8_REV):
        case(GL_UNSIGNED_INT_10_10_10_2):
        case(GL_UNSIGNED_INT_2_10_10_10_REV): return true;
        default: return false;
    }
}

const unsigned int Image::computeNumComponents(GLenum type)
{
    switch(type)
    {
        case(GL_COLOR_INDEX): return 1;
        case(GL_STENCIL_INDEX): return 1;
        case(GL_DEPTH_COMPONENT): return 1;
        case(GL_RED): return 1;
        case(GL_GREEN): return 1;
        case(GL_BLUE): return 1;
        case(GL_ALPHA): return 1;
        case(GL_RGB): return 3;
        case(GL_BGR): return 3;
        case(GL_RGBA): return 4;
        case(GL_BGRA): return 4;
        case(GL_LUMINANCE): return 1;
        case(GL_LUMINANCE_ALPHA): return 2;
        default: return 0;
    }
}

const unsigned int Image::computePixelSizeInBits(GLenum format,GLenum type)
{
    switch(type)
    {
        case(GL_BITMAP): return computeNumComponents(format);

        case(GL_BYTE):
        case(GL_UNSIGNED_BYTE): return 8*computeNumComponents(format);

        case(GL_SHORT):
        case(GL_UNSIGNED_SHORT): return 16*computeNumComponents(format);

        case(GL_INT):
        case(GL_UNSIGNED_INT):
        case(GL_FLOAT): return 32*computeNumComponents(format);

        case(GL_UNSIGNED_BYTE_3_3_2):
        case(GL_UNSIGNED_BYTE_2_3_3_REV): return 8;

        case(GL_UNSIGNED_SHORT_5_6_5):
        case(GL_UNSIGNED_SHORT_5_6_5_REV):
        case(GL_UNSIGNED_SHORT_4_4_4_4):
        case(GL_UNSIGNED_SHORT_4_4_4_4_REV):
        case(GL_UNSIGNED_SHORT_5_5_5_1):
        case(GL_UNSIGNED_SHORT_1_5_5_5_REV): return 16;

        case(GL_UNSIGNED_INT_8_8_8_8):
        case(GL_UNSIGNED_INT_8_8_8_8_REV):
        case(GL_UNSIGNED_INT_10_10_10_2):
        case(GL_UNSIGNED_INT_2_10_10_10_REV): return 32;
        default: return 0;
    }

}

const unsigned int Image::computeRowWidthInBytes(int width,GLenum format,GLenum type,int packing)
{
    unsigned int pixelSize = computePixelSizeInBits(format,type);
    int widthInBits = width*pixelSize;
    int packingInBits = packing*8;
    return (widthInBits/packingInBits + ((widthInBits%packingInBits)?1:0))*packing;
}

void Image::setInternalTextureFormat(GLint internalFormat)
{
    // won't do any sanity checking right now, leave it to
    // OpenGL to make the call.
    _internalTextureFormat = internalFormat;
}

void Image::setPixelFormat(const GLenum format)
{
    if (_pixelFormat==format) return; // do nothing if the same.

    if (computeNumComponents(_pixelFormat)==computeNumComponents(format))
    {
       // if the two formats have the same number of componets then
       // we can do a straight swap.
        _pixelFormat = format;
    }
    else
    {
        notify(WARN)<<"Image::setPixelFormat(..) - warning, attempt to reset the pixel format with a different number of components."<<std::endl;
    }
}

void Image::createImage(int s,int t,int r,
                        GLenum format,GLenum type,
                        int packing)
{
    _mipmapData.clear();

    unsigned int previousTotalSize = computeRowWidthInBytes(_s,_pixelFormat,_dataType,_packing)*_t*_r;

    unsigned int newTotalSize = computeRowWidthInBytes(s,format,type,packing)*t*r;

    if (newTotalSize!=previousTotalSize)
    {
        if (_data) ::free(_data);

        if (newTotalSize)
            _data = (unsigned char *)osgMalloc (newTotalSize);
        else
            _data = 0L;
    }

    if (_data)
    {
        _s = s;
        _t = t;
        _r = r;
        _pixelFormat = format;
        _dataType = type;
        _packing = packing;

    }
    else
    {
        // throw exception?? not for now, will simply set values to 0.
        _s = 0;
        _t = 0;
        _r = 0;
        _pixelFormat = 0;
        _dataType = 0;
        _packing = 0;
    }

    ++_modifiedTag;
}

void Image::setImage(int s,int t,int r,
                     GLint internalTextureFormat,
                     GLenum format,GLenum type,
                     unsigned char *data,
                     int packing)
{
    if (_data) ::free(_data);
    _mipmapData.clear();

    _s = s;
    _t = t;
    _r = r;

    _internalTextureFormat = internalTextureFormat;
    _pixelFormat    = format;
    _dataType       = type;

    _data = data;
    _packing = packing;

    // test scaling...
    //    scaleImageTo(16,16,_r);

    ++_modifiedTag;

}

void Image::readPixels(int x,int y,int width,int height,
                       GLenum format,GLenum type)
{
    createImage(width,height,1,format,type);

    glPixelStorei(GL_PACK_ALIGNMENT,_packing);

    glReadPixels(x,y,width,height,format,type,_data);
}

void Image::scaleImage(const int s,const int t,const int r)
{
    if (_data==NULL)
    {
        notify(WARN) << "Error Image::scaleImage() do not succeed : cannot scale NULL image."<<std::endl;
        return;
    }

    if (_r!=1 || r!=1)
    {
        notify(WARN) << "Error Image::scaleImage() do not succeed : scaling of volumes not implemented."<<std::endl;
        return;
    }

    unsigned int newTotalSize = computeRowWidthInBytes(s,_pixelFormat,_dataType,_packing)*t;

    // need to sort out what size to really use...
    unsigned char* newData = (unsigned char *)osgMalloc(newTotalSize);
    if (!newData)
    {
        // should we throw an exception???  Just return for time being.
        notify(FATAL) << "Error Image::scaleImage() do not succeed : out of memory."<<std::endl;
        return;
    }

    glPixelStorei(GL_PACK_ALIGNMENT,_packing);
    glPixelStorei(GL_UNPACK_ALIGNMENT,_packing);

    GLint status = gluScaleImage(_pixelFormat,
        _s,
        _t,
        _dataType,
        _data,
        s,
        t,
        _dataType,
        newData);

    if (status==0)
    {

        // free old image.
        ::free(_data);

        _s = s;
        _t = t;
        _data = newData;
    }
    else
    {
        ::free(newData);

        notify(WARN) << "Error Image::scaleImage() do not succeed : errorString = "<<gluErrorString((GLenum)status)<<std::endl;
    }

    ++_modifiedTag;
}

void Image::ensureValidSizeForTexturing()
{
    int new_s = _s;
    int new_t = _t;

    // check if _s is a power of 2 already.
    if ((_s & (_s-1))!=0)
    {
        // it isn't so lets find the closest power of two.
        // yes, logf and powf are slow, but this code should
        // only be called during scene graph initilization,
        // if at all, so not critical in the greater scheme.
        float p2 = logf((float)_s)/logf(2.0f);
        float rounded_p2 = floorf(p2+0.5f);
        new_s = (int)(powf(2.0f,rounded_p2));
    }

    if ((_t & (_t-1))!=0)
    {
        // it isn't so lets find the closest power of two.
        // yes, logf and powf are slow, but this code should
        // only be called during scene graph initilization,
        // if at all, so not critical in the greater scheme.
        float p2 = logf((float)_t)/logf(2.0f);
        float rounded_p2 = floorf(p2+0.5f);
        new_t = (int)(powf(2.0f,rounded_p2));
    }

    static GLint max_size=256;

    static bool init = true;
    if (init)
    {
        init = false;
        glGetIntegerv(GL_MAX_TEXTURE_SIZE,&max_size);
        notify(INFO) << "Max texture size "<<max_size<<std::endl;
    }

    //max_size = 64;

    if (new_s>max_size) new_s = max_size;
    if (new_t>max_size) new_t = max_size;

    if (new_s!=_s || new_t!=_t)
    {
        if (!_fileName.empty()) notify(NOTICE) << "Scaling image '"<<_fileName<<"' from ("<<_s<<","<<_t<<") to ("<<new_s<<","<<new_t<<")"<<std::endl;
        else notify(NOTICE) << "Scaling image from ("<<_s<<","<<_t<<") to ("<<new_s<<","<<new_t<<")"<<std::endl;

        scaleImage(new_s,new_t,_r);
    }
}

Geode* osg::createGeodeForImage(osg::Image* image)
{
    return createGeodeForImage(image,image->s(),image->t());
}

Geode* osg::createGeodeForImage(osg::Image* image,const float s,const float t)
{
    if (image)
    {
        if (s>0 && t>0)
        {

            float y = 1.0;
            float x = y*(s/t);

            // set up the texture.
            osg::Texture* texture = osgNew osg::Texture;
            texture->setImage(image);

            // set up the drawstate.
            osg::StateSet* dstate = osgNew osg::StateSet;
            dstate->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
            dstate->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
            dstate->setAttributeAndModes(texture,osg::StateAttribute::ON);

            // set up the geoset.
            osg::GeoSet* gset = osgNew osg::GeoSet;
            gset->setStateSet(dstate);

            osg::Vec3* coords = osgNew Vec3[4];
            coords[0].set(-x,0.0f,y);
            coords[1].set(-x,0.0f,-y);
            coords[2].set(x,0.0f,-y);
            coords[3].set(x,0.0f,y);
            gset->setCoords(coords);

            osg::Vec2* tcoords = osgNew Vec2[4];
            tcoords[0].set(0.0f,1.0f);
            tcoords[1].set(0.0f,0.0f);
            tcoords[2].set(1.0f,0.0f);
            tcoords[3].set(1.0f,1.0f);
            gset->setTextureCoords(tcoords);
            gset->setTextureBinding(osg::GeoSet::BIND_PERVERTEX);

            osg::Vec4* colours = osgNew Vec4[1];
            colours->set(1.0f,1.0f,1.0,1.0f);
            gset->setColors(colours);
            gset->setColorBinding(osg::GeoSet::BIND_OVERALL);

            gset->setNumPrims(1);
            gset->setPrimType(osg::GeoSet::QUADS);

            // set up the geode.
            osg::Geode* geode = osgNew osg::Geode;
            geode->addDrawable(gset);

            return geode;

        }
        else
        {
            return NULL;
        }
    }
    else
    {
        return NULL;
    }
}
<commit_msg>Fixed ordering of paramters in osg::Image constructor to remove warning.<commit_after>#include <osg/GL>
#include <osg/GLU>

#include <osg/Image>
#include <osg/Notify>

#include <osg/Geode>
#include <osg/GeoSet>
#include <osg/StateSet>
#include <osg/Texture>

using namespace osg;
using namespace std;

Image::Image()
{
    _fileName               = "";
    _s = _t = _r            = 0;
    _internalTextureFormat  = 0;
    _pixelFormat            = (unsigned int)0;
    _dataType               = (unsigned int)0;
    _packing                = 4;

    _data = (unsigned char *)0L;

    _modifiedTag = 0;
}

Image::Image(const Image& image,const CopyOp& copyop):
    Object(image,copyop),
    _fileName(image._fileName),
    _s(image._s), _t(image._t), _r(image._r),
    _internalTextureFormat(image._internalTextureFormat),
    _pixelFormat(image._pixelFormat),
    _dataType(image._dataType),
    _packing(image._packing),
    _data(0L),
    _modifiedTag(image._modifiedTag),
    _mipmapData(image._mipmapData)
{
    if (image._data)
    {
        int num_components =
            _pixelFormat == GL_LUMINANCE ? 1 :
            _pixelFormat == GL_LUMINANCE_ALPHA ? 2 :
            _pixelFormat == GL_RGB ? 3 :
            _pixelFormat == GL_RGBA ? 4 : 4;

        int size = _s*_t*_r*num_components;
        _data = (unsigned char*) malloc(size);
        memcpy(_data,image._data,size);
    }

}

Image::~Image()
{
    if (_data) ::free(_data);
}

void Image::setFileName(const std::string& fileName)
{
    _fileName = fileName;
}

#ifndef GL_VERSION_1_2
    /* 1.2 definitions...*/
    #define GL_BGR					0x80E0
    #define GL_BGRA					0x80E1
    #define GL_UNSIGNED_BYTE_3_3_2			0x8032
    #define GL_UNSIGNED_BYTE_2_3_3_REV		0x8362
    #define GL_UNSIGNED_SHORT_5_6_5			0x8363
    #define GL_UNSIGNED_SHORT_5_6_5_REV		0x8364
    #define GL_UNSIGNED_SHORT_4_4_4_4		0x8033
    #define GL_UNSIGNED_SHORT_4_4_4_4_REV		0x8365
    #define GL_UNSIGNED_SHORT_5_5_5_1		0x8034
    #define GL_UNSIGNED_SHORT_1_5_5_5_REV		0x8366
    #define GL_UNSIGNED_INT_8_8_8_8			0x8035
    #define GL_UNSIGNED_INT_8_8_8_8_REV		0x8367
    #define GL_UNSIGNED_INT_10_10_10_2		0x8036
    #define GL_UNSIGNED_INT_2_10_10_10_REV		0x8368
#endif

const bool Image::isPackedType(GLenum format)
{
    switch(format)
    {
        case(GL_UNSIGNED_BYTE_3_3_2):
        case(GL_UNSIGNED_BYTE_2_3_3_REV):
        case(GL_UNSIGNED_SHORT_5_6_5):
        case(GL_UNSIGNED_SHORT_5_6_5_REV):
        case(GL_UNSIGNED_SHORT_4_4_4_4):
        case(GL_UNSIGNED_SHORT_4_4_4_4_REV):
        case(GL_UNSIGNED_SHORT_5_5_5_1):
        case(GL_UNSIGNED_SHORT_1_5_5_5_REV):
        case(GL_UNSIGNED_INT_8_8_8_8):
        case(GL_UNSIGNED_INT_8_8_8_8_REV):
        case(GL_UNSIGNED_INT_10_10_10_2):
        case(GL_UNSIGNED_INT_2_10_10_10_REV): return true;
        default: return false;
    }
}

const unsigned int Image::computeNumComponents(GLenum type)
{
    switch(type)
    {
        case(GL_COLOR_INDEX): return 1;
        case(GL_STENCIL_INDEX): return 1;
        case(GL_DEPTH_COMPONENT): return 1;
        case(GL_RED): return 1;
        case(GL_GREEN): return 1;
        case(GL_BLUE): return 1;
        case(GL_ALPHA): return 1;
        case(GL_RGB): return 3;
        case(GL_BGR): return 3;
        case(GL_RGBA): return 4;
        case(GL_BGRA): return 4;
        case(GL_LUMINANCE): return 1;
        case(GL_LUMINANCE_ALPHA): return 2;
        default: return 0;
    }
}

const unsigned int Image::computePixelSizeInBits(GLenum format,GLenum type)
{
    switch(type)
    {
        case(GL_BITMAP): return computeNumComponents(format);

        case(GL_BYTE):
        case(GL_UNSIGNED_BYTE): return 8*computeNumComponents(format);

        case(GL_SHORT):
        case(GL_UNSIGNED_SHORT): return 16*computeNumComponents(format);

        case(GL_INT):
        case(GL_UNSIGNED_INT):
        case(GL_FLOAT): return 32*computeNumComponents(format);

        case(GL_UNSIGNED_BYTE_3_3_2):
        case(GL_UNSIGNED_BYTE_2_3_3_REV): return 8;

        case(GL_UNSIGNED_SHORT_5_6_5):
        case(GL_UNSIGNED_SHORT_5_6_5_REV):
        case(GL_UNSIGNED_SHORT_4_4_4_4):
        case(GL_UNSIGNED_SHORT_4_4_4_4_REV):
        case(GL_UNSIGNED_SHORT_5_5_5_1):
        case(GL_UNSIGNED_SHORT_1_5_5_5_REV): return 16;

        case(GL_UNSIGNED_INT_8_8_8_8):
        case(GL_UNSIGNED_INT_8_8_8_8_REV):
        case(GL_UNSIGNED_INT_10_10_10_2):
        case(GL_UNSIGNED_INT_2_10_10_10_REV): return 32;
        default: return 0;
    }

}

const unsigned int Image::computeRowWidthInBytes(int width,GLenum format,GLenum type,int packing)
{
    unsigned int pixelSize = computePixelSizeInBits(format,type);
    int widthInBits = width*pixelSize;
    int packingInBits = packing*8;
    return (widthInBits/packingInBits + ((widthInBits%packingInBits)?1:0))*packing;
}

void Image::setInternalTextureFormat(GLint internalFormat)
{
    // won't do any sanity checking right now, leave it to
    // OpenGL to make the call.
    _internalTextureFormat = internalFormat;
}

void Image::setPixelFormat(const GLenum format)
{
    if (_pixelFormat==format) return; // do nothing if the same.

    if (computeNumComponents(_pixelFormat)==computeNumComponents(format))
    {
       // if the two formats have the same number of componets then
       // we can do a straight swap.
        _pixelFormat = format;
    }
    else
    {
        notify(WARN)<<"Image::setPixelFormat(..) - warning, attempt to reset the pixel format with a different number of components."<<std::endl;
    }
}

void Image::createImage(int s,int t,int r,
                        GLenum format,GLenum type,
                        int packing)
{
    _mipmapData.clear();

    unsigned int previousTotalSize = computeRowWidthInBytes(_s,_pixelFormat,_dataType,_packing)*_t*_r;

    unsigned int newTotalSize = computeRowWidthInBytes(s,format,type,packing)*t*r;

    if (newTotalSize!=previousTotalSize)
    {
        if (_data) ::free(_data);

        if (newTotalSize)
            _data = (unsigned char *)osgMalloc (newTotalSize);
        else
            _data = 0L;
    }

    if (_data)
    {
        _s = s;
        _t = t;
        _r = r;
        _pixelFormat = format;
        _dataType = type;
        _packing = packing;

    }
    else
    {
        // throw exception?? not for now, will simply set values to 0.
        _s = 0;
        _t = 0;
        _r = 0;
        _pixelFormat = 0;
        _dataType = 0;
        _packing = 0;
    }

    ++_modifiedTag;
}

void Image::setImage(int s,int t,int r,
                     GLint internalTextureFormat,
                     GLenum format,GLenum type,
                     unsigned char *data,
                     int packing)
{
    if (_data) ::free(_data);
    _mipmapData.clear();

    _s = s;
    _t = t;
    _r = r;

    _internalTextureFormat = internalTextureFormat;
    _pixelFormat    = format;
    _dataType       = type;

    _data = data;
    _packing = packing;

    // test scaling...
    //    scaleImageTo(16,16,_r);

    ++_modifiedTag;

}

void Image::readPixels(int x,int y,int width,int height,
                       GLenum format,GLenum type)
{
    createImage(width,height,1,format,type);

    glPixelStorei(GL_PACK_ALIGNMENT,_packing);

    glReadPixels(x,y,width,height,format,type,_data);
}

void Image::scaleImage(const int s,const int t,const int r)
{
    if (_data==NULL)
    {
        notify(WARN) << "Error Image::scaleImage() do not succeed : cannot scale NULL image."<<std::endl;
        return;
    }

    if (_r!=1 || r!=1)
    {
        notify(WARN) << "Error Image::scaleImage() do not succeed : scaling of volumes not implemented."<<std::endl;
        return;
    }

    unsigned int newTotalSize = computeRowWidthInBytes(s,_pixelFormat,_dataType,_packing)*t;

    // need to sort out what size to really use...
    unsigned char* newData = (unsigned char *)osgMalloc(newTotalSize);
    if (!newData)
    {
        // should we throw an exception???  Just return for time being.
        notify(FATAL) << "Error Image::scaleImage() do not succeed : out of memory."<<std::endl;
        return;
    }

    glPixelStorei(GL_PACK_ALIGNMENT,_packing);
    glPixelStorei(GL_UNPACK_ALIGNMENT,_packing);

    GLint status = gluScaleImage(_pixelFormat,
        _s,
        _t,
        _dataType,
        _data,
        s,
        t,
        _dataType,
        newData);

    if (status==0)
    {

        // free old image.
        ::free(_data);

        _s = s;
        _t = t;
        _data = newData;
    }
    else
    {
        ::free(newData);

        notify(WARN) << "Error Image::scaleImage() do not succeed : errorString = "<<gluErrorString((GLenum)status)<<std::endl;
    }

    ++_modifiedTag;
}

void Image::ensureValidSizeForTexturing()
{
    int new_s = _s;
    int new_t = _t;

    // check if _s is a power of 2 already.
    if ((_s & (_s-1))!=0)
    {
        // it isn't so lets find the closest power of two.
        // yes, logf and powf are slow, but this code should
        // only be called during scene graph initilization,
        // if at all, so not critical in the greater scheme.
        float p2 = logf((float)_s)/logf(2.0f);
        float rounded_p2 = floorf(p2+0.5f);
        new_s = (int)(powf(2.0f,rounded_p2));
    }

    if ((_t & (_t-1))!=0)
    {
        // it isn't so lets find the closest power of two.
        // yes, logf and powf are slow, but this code should
        // only be called during scene graph initilization,
        // if at all, so not critical in the greater scheme.
        float p2 = logf((float)_t)/logf(2.0f);
        float rounded_p2 = floorf(p2+0.5f);
        new_t = (int)(powf(2.0f,rounded_p2));
    }

    static GLint max_size=256;

    static bool init = true;
    if (init)
    {
        init = false;
        glGetIntegerv(GL_MAX_TEXTURE_SIZE,&max_size);
        notify(INFO) << "Max texture size "<<max_size<<std::endl;
    }

    //max_size = 64;

    if (new_s>max_size) new_s = max_size;
    if (new_t>max_size) new_t = max_size;

    if (new_s!=_s || new_t!=_t)
    {
        if (!_fileName.empty()) notify(NOTICE) << "Scaling image '"<<_fileName<<"' from ("<<_s<<","<<_t<<") to ("<<new_s<<","<<new_t<<")"<<std::endl;
        else notify(NOTICE) << "Scaling image from ("<<_s<<","<<_t<<") to ("<<new_s<<","<<new_t<<")"<<std::endl;

        scaleImage(new_s,new_t,_r);
    }
}

Geode* osg::createGeodeForImage(osg::Image* image)
{
    return createGeodeForImage(image,image->s(),image->t());
}

Geode* osg::createGeodeForImage(osg::Image* image,const float s,const float t)
{
    if (image)
    {
        if (s>0 && t>0)
        {

            float y = 1.0;
            float x = y*(s/t);

            // set up the texture.
            osg::Texture* texture = osgNew osg::Texture;
            texture->setImage(image);

            // set up the drawstate.
            osg::StateSet* dstate = osgNew osg::StateSet;
            dstate->setMode(GL_CULL_FACE,osg::StateAttribute::OFF);
            dstate->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
            dstate->setAttributeAndModes(texture,osg::StateAttribute::ON);

            // set up the geoset.
            osg::GeoSet* gset = osgNew osg::GeoSet;
            gset->setStateSet(dstate);

            osg::Vec3* coords = osgNew Vec3[4];
            coords[0].set(-x,0.0f,y);
            coords[1].set(-x,0.0f,-y);
            coords[2].set(x,0.0f,-y);
            coords[3].set(x,0.0f,y);
            gset->setCoords(coords);

            osg::Vec2* tcoords = osgNew Vec2[4];
            tcoords[0].set(0.0f,1.0f);
            tcoords[1].set(0.0f,0.0f);
            tcoords[2].set(1.0f,0.0f);
            tcoords[3].set(1.0f,1.0f);
            gset->setTextureCoords(tcoords);
            gset->setTextureBinding(osg::GeoSet::BIND_PERVERTEX);

            osg::Vec4* colours = osgNew Vec4[1];
            colours->set(1.0f,1.0f,1.0,1.0f);
            gset->setColors(colours);
            gset->setColorBinding(osg::GeoSet::BIND_OVERALL);

            gset->setNumPrims(1);
            gset->setPrimType(osg::GeoSet::QUADS);

            // set up the geode.
            osg::Geode* geode = osgNew osg::Geode;
            geode->addDrawable(gset);

            return geode;

        }
        else
        {
            return NULL;
        }
    }
    else
    {
        return NULL;
    }
}
<|endoftext|>


<commit_before>/*
 * SampleAlongProbes.cpp
 * Copyright (C) 2019 by MegaMol Team
 * Alle Rechte vorbehalten.
 */

#include "SampleAlongProbes.h"
#include "CallKDTree.h"
#include "ProbeCalls.h"
#include "adios_plugin/CallADIOSData.h"
#include "mmcore/param/EnumParam.h"
#include "mmcore/param/FlexEnumParam.h"

namespace megamol {
namespace probe {

SampleAlongPobes::SampleAlongPobes()
    : Module()
    , _version(0)
    , _old_datahash(0)
    , _probe_lhs_slot("deployProbe", "")
    , _probe_rhs_slot("getProbe", "")
    , _adios_rhs_slot("getData", "")
    , _full_tree_rhs_slot("getTree", "")
    , _parameter_to_sample_slot("ParameterToSample", "")
    , _num_samples_per_probe_slot("NumSamplesPerProbe", "")
    , _sampling_mode("SamplingMode", "")
    , _vec_param_to_samplex_x("ParameterToSampleX", "")
    , _vec_param_to_samplex_y("ParameterToSampleY", "")
    , _vec_param_to_samplex_z("ParameterToSampleZ", "")
    , _vec_param_to_samplex_w("ParameterToSampleW", "") {

    this->_probe_lhs_slot.SetCallback(CallProbes::ClassName(), CallProbes::FunctionName(0), &SampleAlongPobes::getData);
    this->_probe_lhs_slot.SetCallback(
        CallProbes::ClassName(), CallProbes::FunctionName(1), &SampleAlongPobes::getMetaData);
    this->MakeSlotAvailable(&this->_probe_lhs_slot);

    this->_probe_rhs_slot.SetCompatibleCall<CallProbesDescription>();
    this->MakeSlotAvailable(&this->_probe_rhs_slot);

    this->_adios_rhs_slot.SetCompatibleCall<adios::CallADIOSDataDescription>();
    this->MakeSlotAvailable(&this->_adios_rhs_slot);

    this->_full_tree_rhs_slot.SetCompatibleCall<CallKDTreeDescription>();
    this->MakeSlotAvailable(&this->_full_tree_rhs_slot);

    core::param::FlexEnumParam* paramEnum = new core::param::FlexEnumParam("undef");
    this->_parameter_to_sample_slot << paramEnum;
    this->_parameter_to_sample_slot.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_parameter_to_sample_slot);

    this->_num_samples_per_probe_slot << new core::param::IntParam(10);
    this->_num_samples_per_probe_slot.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_num_samples_per_probe_slot);

    this->_sampling_mode << new megamol::core::param::EnumParam(0);
    this->_sampling_mode.Param<megamol::core::param::EnumParam>()->SetTypePair(0, "Scalar");
    this->_sampling_mode.Param<megamol::core::param::EnumParam>()->SetTypePair(1, "Vector");
    this->MakeSlotAvailable(&this->_sampling_mode);

	core::param::FlexEnumParam* paramEnum_1 = new core::param::FlexEnumParam("undef");
    this->_vec_param_to_samplex_x << paramEnum_1;
    this->_vec_param_to_samplex_x.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_vec_param_to_samplex_x);

	core::param::FlexEnumParam* paramEnum_2 = new core::param::FlexEnumParam("undef");
    this->_vec_param_to_samplex_y << paramEnum_2;
    this->_vec_param_to_samplex_y.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_vec_param_to_samplex_y);

	core::param::FlexEnumParam* paramEnum_3 = new core::param::FlexEnumParam("undef");
    this->_vec_param_to_samplex_z << paramEnum_3;
    this->_vec_param_to_samplex_z.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_vec_param_to_samplex_z);

	core::param::FlexEnumParam* paramEnum_4 = new core::param::FlexEnumParam("undef");
    this->_vec_param_to_samplex_w << paramEnum_4;
    this->_vec_param_to_samplex_w.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_vec_param_to_samplex_w);
}

SampleAlongPobes::~SampleAlongPobes() { this->Release(); }

bool SampleAlongPobes::create() { return true; }

void SampleAlongPobes::release() {}

bool SampleAlongPobes::getData(core::Call& call) {

    auto cp = dynamic_cast<CallProbes*>(&call);
    if (cp == nullptr) return false;

    // query adios data
    auto cd = this->_adios_rhs_slot.CallAs<adios::CallADIOSData>();
    if (cd == nullptr) return false;

    std::vector<std::string> toInq;
    std::string var_str =
        std::string(this->_parameter_to_sample_slot.Param<core::param::FlexEnumParam>()->ValueString());

    std::string x_var_str =
        std::string(this->_vec_param_to_samplex_x.Param<core::param::FlexEnumParam>()->ValueString());
    std::string y_var_str =
        std::string(this->_vec_param_to_samplex_y.Param<core::param::FlexEnumParam>()->ValueString());
    std::string z_var_str =
        std::string(this->_vec_param_to_samplex_z.Param<core::param::FlexEnumParam>()->ValueString());
    std::string w_var_str =
        std::string(this->_vec_param_to_samplex_w.Param<core::param::FlexEnumParam>()->ValueString());

    toInq.clear();
    if (_sampling_mode.Param<core::param::EnumParam>()->Value() == 0) {
        toInq.emplace_back(
            std::string(this->_parameter_to_sample_slot.Param<core::param::FlexEnumParam>()->ValueString()));
    } else {
        toInq.emplace_back(
            std::string(this->_vec_param_to_samplex_x.Param<core::param::FlexEnumParam>()->ValueString()));
        toInq.emplace_back(
            std::string(this->_vec_param_to_samplex_y.Param<core::param::FlexEnumParam>()->ValueString()));
        toInq.emplace_back(
            std::string(this->_vec_param_to_samplex_z.Param<core::param::FlexEnumParam>()->ValueString()));
        toInq.emplace_back(
            std::string(this->_vec_param_to_samplex_w.Param<core::param::FlexEnumParam>()->ValueString()));
    }

    // get data from adios
    for (auto var : toInq) {
        if (!cd->inquire(var)) return false;
    }

    if (cd->getDataHash() != _old_datahash) {
        if (!(*cd)(0)) return false;
    }

    // query kd tree data
    auto ct = this->_full_tree_rhs_slot.CallAs<CallKDTree>();
    if (ct == nullptr) return false;
    if (!(*ct)(0)) return false;

    // query probe data
    auto cprobes = this->_probe_rhs_slot.CallAs<CallProbes>();
    if (cprobes == nullptr) return false;
    if (!(*cprobes)(0)) return false;

    bool something_has_changed = (cd->getDataHash() != _old_datahash) || ct->hasUpdate() || cprobes->hasUpdate();

    auto meta_data = cp->getMetaData();
    auto tree_meta_data = ct->getMetaData();
    auto probes_meta_data = cprobes->getMetaData();

    tree_meta_data = ct->getMetaData();
    probes_meta_data = cprobes->getMetaData();

    if (something_has_changed) {
        ++_version;

		if (_sampling_mode.Param<core::param::EnumParam>()->Value() == 0) {
			// do sampling
			_probes = cprobes->getData();
			auto tree = ct->getData();
			if (cd->getData(var_str)->getType() == "double") {
			    std::vector<double> data = cd->getData(var_str)->GetAsDouble();
			    doSampling(tree, data);

			} else if (cd->getData(var_str)->getType() == "float") {
			    std::vector<float> data = cd->getData(var_str)->GetAsFloat();
			    doSampling(tree, data);
			}
		}
		else
		{
			//vector sampling
            _probes = cprobes->getData();
            auto tree = ct->getData();

			if (cd->getData(x_var_str)->getType() == "double" && cd->getData(y_var_str)->getType() == "double" &&
                cd->getData(z_var_str)->getType() == "double" && cd->getData(w_var_str)->getType() == "double")
			{
                std::vector<double> data_x = cd->getData(x_var_str)->GetAsDouble();
                std::vector<double> data_y = cd->getData(y_var_str)->GetAsDouble();
                std::vector<double> data_z = cd->getData(z_var_str)->GetAsDouble();
                std::vector<double> data_w = cd->getData(w_var_str)->GetAsDouble();
                doVectorSamling(tree, data_x, data_y, data_z, data_w);
			}
			else if (cd->getData(x_var_str)->getType() == "float" && cd->getData(y_var_str)->getType() == "float" &&
                cd->getData(z_var_str)->getType() == "float" && cd->getData(w_var_str)->getType() == "float"	)
			{
                std::vector<float> data_x = cd->getData(x_var_str)->GetAsFloat();
                std::vector<float> data_y = cd->getData(y_var_str)->GetAsFloat();
                std::vector<float> data_z = cd->getData(z_var_str)->GetAsFloat();
                std::vector<float> data_w = cd->getData(w_var_str)->GetAsFloat();
                doVectorSamling(tree, data_x, data_y, data_z, data_w);
			}
		}
    }

    // put data into probes
    if (probes_meta_data.m_bboxs.IsBoundingBoxValid()) {
        meta_data.m_bboxs = probes_meta_data.m_bboxs;
    } else if (tree_meta_data.m_bboxs.IsBoundingBoxValid()) {
        meta_data.m_bboxs = tree_meta_data.m_bboxs;
    }
    cp->setMetaData(meta_data);

    cp->setData(_probes, _version);
    _old_datahash = cd->getDataHash();
    _trigger_recalc = false;

    return true;
}

bool SampleAlongPobes::getMetaData(core::Call& call) {

    auto cp = dynamic_cast<CallProbes*>(&call);
    if (cp == nullptr) return false;

    auto cd = this->_adios_rhs_slot.CallAs<adios::CallADIOSData>();
    if (cd == nullptr) return false;

    auto ct = this->_full_tree_rhs_slot.CallAs<CallKDTree>();
    if (ct == nullptr) return false;

    auto cplaceprobes = this->_probe_rhs_slot.CallAs<CallProbes>();
    if (cplaceprobes == nullptr) return false;

    auto meta_data = cp->getMetaData();
    if (cd->getDataHash() == _old_datahash && meta_data.m_frame_ID == cd->getFrameIDtoLoad() && !_trigger_recalc)
        return true;

    cd->setFrameIDtoLoad(meta_data.m_frame_ID);
    if (!(*cd)(1)) return false;
    if (!(*ct)(1)) return false;
    if (!(*cplaceprobes)(1)) return false;

    // get adios meta data
    auto vars = cd->getAvailableVars();
    for (auto var : vars) {
        this->_parameter_to_sample_slot.Param<core::param::FlexEnumParam>()->AddValue(var);
    }

    // put metadata in mesh call
    meta_data.m_frame_cnt = cd->getFrameCount();
    cp->setMetaData(meta_data);

    return true;
}

bool SampleAlongPobes::paramChanged(core::param::ParamSlot& p) {

    _trigger_recalc = true;
    return true;
}

} // namespace probe
} // namespace megamol
<commit_msg>Add adios var inquiry for vector sampling<commit_after>/*
 * SampleAlongProbes.cpp
 * Copyright (C) 2019 by MegaMol Team
 * Alle Rechte vorbehalten.
 */

#include "SampleAlongProbes.h"
#include "CallKDTree.h"
#include "ProbeCalls.h"
#include "adios_plugin/CallADIOSData.h"
#include "mmcore/param/EnumParam.h"
#include "mmcore/param/FlexEnumParam.h"

namespace megamol {
namespace probe {

SampleAlongPobes::SampleAlongPobes()
    : Module()
    , _version(0)
    , _old_datahash(0)
    , _probe_lhs_slot("deployProbe", "")
    , _probe_rhs_slot("getProbe", "")
    , _adios_rhs_slot("getData", "")
    , _full_tree_rhs_slot("getTree", "")
    , _parameter_to_sample_slot("ParameterToSample", "")
    , _num_samples_per_probe_slot("NumSamplesPerProbe", "")
    , _sampling_mode("SamplingMode", "")
    , _vec_param_to_samplex_x("ParameterToSampleX", "")
    , _vec_param_to_samplex_y("ParameterToSampleY", "")
    , _vec_param_to_samplex_z("ParameterToSampleZ", "")
    , _vec_param_to_samplex_w("ParameterToSampleW", "") {

    this->_probe_lhs_slot.SetCallback(CallProbes::ClassName(), CallProbes::FunctionName(0), &SampleAlongPobes::getData);
    this->_probe_lhs_slot.SetCallback(
        CallProbes::ClassName(), CallProbes::FunctionName(1), &SampleAlongPobes::getMetaData);
    this->MakeSlotAvailable(&this->_probe_lhs_slot);

    this->_probe_rhs_slot.SetCompatibleCall<CallProbesDescription>();
    this->MakeSlotAvailable(&this->_probe_rhs_slot);

    this->_adios_rhs_slot.SetCompatibleCall<adios::CallADIOSDataDescription>();
    this->MakeSlotAvailable(&this->_adios_rhs_slot);

    this->_full_tree_rhs_slot.SetCompatibleCall<CallKDTreeDescription>();
    this->MakeSlotAvailable(&this->_full_tree_rhs_slot);

    core::param::FlexEnumParam* paramEnum = new core::param::FlexEnumParam("undef");
    this->_parameter_to_sample_slot << paramEnum;
    this->_parameter_to_sample_slot.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_parameter_to_sample_slot);

    this->_num_samples_per_probe_slot << new core::param::IntParam(10);
    this->_num_samples_per_probe_slot.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_num_samples_per_probe_slot);

    this->_sampling_mode << new megamol::core::param::EnumParam(0);
    this->_sampling_mode.Param<megamol::core::param::EnumParam>()->SetTypePair(0, "Scalar");
    this->_sampling_mode.Param<megamol::core::param::EnumParam>()->SetTypePair(1, "Vector");
    this->MakeSlotAvailable(&this->_sampling_mode);

	core::param::FlexEnumParam* paramEnum_1 = new core::param::FlexEnumParam("undef");
    this->_vec_param_to_samplex_x << paramEnum_1;
    this->_vec_param_to_samplex_x.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_vec_param_to_samplex_x);

	core::param::FlexEnumParam* paramEnum_2 = new core::param::FlexEnumParam("undef");
    this->_vec_param_to_samplex_y << paramEnum_2;
    this->_vec_param_to_samplex_y.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_vec_param_to_samplex_y);

	core::param::FlexEnumParam* paramEnum_3 = new core::param::FlexEnumParam("undef");
    this->_vec_param_to_samplex_z << paramEnum_3;
    this->_vec_param_to_samplex_z.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_vec_param_to_samplex_z);

	core::param::FlexEnumParam* paramEnum_4 = new core::param::FlexEnumParam("undef");
    this->_vec_param_to_samplex_w << paramEnum_4;
    this->_vec_param_to_samplex_w.SetUpdateCallback(&SampleAlongPobes::paramChanged);
    this->MakeSlotAvailable(&this->_vec_param_to_samplex_w);
}

SampleAlongPobes::~SampleAlongPobes() { this->Release(); }

bool SampleAlongPobes::create() { return true; }

void SampleAlongPobes::release() {}

bool SampleAlongPobes::getData(core::Call& call) {

    auto cp = dynamic_cast<CallProbes*>(&call);
    if (cp == nullptr) return false;

    // query adios data
    auto cd = this->_adios_rhs_slot.CallAs<adios::CallADIOSData>();
    if (cd == nullptr) return false;

    std::vector<std::string> toInq;
    std::string var_str =
        std::string(this->_parameter_to_sample_slot.Param<core::param::FlexEnumParam>()->ValueString());

    std::string x_var_str =
        std::string(this->_vec_param_to_samplex_x.Param<core::param::FlexEnumParam>()->ValueString());
    std::string y_var_str =
        std::string(this->_vec_param_to_samplex_y.Param<core::param::FlexEnumParam>()->ValueString());
    std::string z_var_str =
        std::string(this->_vec_param_to_samplex_z.Param<core::param::FlexEnumParam>()->ValueString());
    std::string w_var_str =
        std::string(this->_vec_param_to_samplex_w.Param<core::param::FlexEnumParam>()->ValueString());

    toInq.clear();
    if (_sampling_mode.Param<core::param::EnumParam>()->Value() == 0) {
        toInq.emplace_back(
            std::string(this->_parameter_to_sample_slot.Param<core::param::FlexEnumParam>()->ValueString()));
    } else {
        toInq.emplace_back(
            std::string(this->_vec_param_to_samplex_x.Param<core::param::FlexEnumParam>()->ValueString()));
        toInq.emplace_back(
            std::string(this->_vec_param_to_samplex_y.Param<core::param::FlexEnumParam>()->ValueString()));
        toInq.emplace_back(
            std::string(this->_vec_param_to_samplex_z.Param<core::param::FlexEnumParam>()->ValueString()));
        toInq.emplace_back(
            std::string(this->_vec_param_to_samplex_w.Param<core::param::FlexEnumParam>()->ValueString()));
    }

    // get data from adios
    for (auto var : toInq) {
        if (!cd->inquire(var)) return false;
    }

    if (cd->getDataHash() != _old_datahash) {
        if (!(*cd)(0)) return false;
    }

    // query kd tree data
    auto ct = this->_full_tree_rhs_slot.CallAs<CallKDTree>();
    if (ct == nullptr) return false;
    if (!(*ct)(0)) return false;

    // query probe data
    auto cprobes = this->_probe_rhs_slot.CallAs<CallProbes>();
    if (cprobes == nullptr) return false;
    if (!(*cprobes)(0)) return false;

    bool something_has_changed = (cd->getDataHash() != _old_datahash) || ct->hasUpdate() || cprobes->hasUpdate();

    auto meta_data = cp->getMetaData();
    auto tree_meta_data = ct->getMetaData();
    auto probes_meta_data = cprobes->getMetaData();

    tree_meta_data = ct->getMetaData();
    probes_meta_data = cprobes->getMetaData();

    if (something_has_changed) {
        ++_version;

		if (_sampling_mode.Param<core::param::EnumParam>()->Value() == 0) {
			// do sampling
			_probes = cprobes->getData();
			auto tree = ct->getData();
			if (cd->getData(var_str)->getType() == "double") {
			    std::vector<double> data = cd->getData(var_str)->GetAsDouble();
			    doSampling(tree, data);

			} else if (cd->getData(var_str)->getType() == "float") {
			    std::vector<float> data = cd->getData(var_str)->GetAsFloat();
			    doSampling(tree, data);
			}
		}
		else
		{
			//vector sampling
            _probes = cprobes->getData();
            auto tree = ct->getData();

			if (cd->getData(x_var_str)->getType() == "double" && cd->getData(y_var_str)->getType() == "double" &&
                cd->getData(z_var_str)->getType() == "double" && cd->getData(w_var_str)->getType() == "double")
			{
                std::vector<double> data_x = cd->getData(x_var_str)->GetAsDouble();
                std::vector<double> data_y = cd->getData(y_var_str)->GetAsDouble();
                std::vector<double> data_z = cd->getData(z_var_str)->GetAsDouble();
                std::vector<double> data_w = cd->getData(w_var_str)->GetAsDouble();
                doVectorSamling(tree, data_x, data_y, data_z, data_w);
			}
			else if (cd->getData(x_var_str)->getType() == "float" && cd->getData(y_var_str)->getType() == "float" &&
                cd->getData(z_var_str)->getType() == "float" && cd->getData(w_var_str)->getType() == "float"	)
			{
                std::vector<float> data_x = cd->getData(x_var_str)->GetAsFloat();
                std::vector<float> data_y = cd->getData(y_var_str)->GetAsFloat();
                std::vector<float> data_z = cd->getData(z_var_str)->GetAsFloat();
                std::vector<float> data_w = cd->getData(w_var_str)->GetAsFloat();
                doVectorSamling(tree, data_x, data_y, data_z, data_w);
			}
		}
    }

    // put data into probes
    if (probes_meta_data.m_bboxs.IsBoundingBoxValid()) {
        meta_data.m_bboxs = probes_meta_data.m_bboxs;
    } else if (tree_meta_data.m_bboxs.IsBoundingBoxValid()) {
        meta_data.m_bboxs = tree_meta_data.m_bboxs;
    }
    cp->setMetaData(meta_data);

    cp->setData(_probes, _version);
    _old_datahash = cd->getDataHash();
    _trigger_recalc = false;

    return true;
}

bool SampleAlongPobes::getMetaData(core::Call& call) {

    auto cp = dynamic_cast<CallProbes*>(&call);
    if (cp == nullptr) return false;

    auto cd = this->_adios_rhs_slot.CallAs<adios::CallADIOSData>();
    if (cd == nullptr) return false;

    auto ct = this->_full_tree_rhs_slot.CallAs<CallKDTree>();
    if (ct == nullptr) return false;

    auto cplaceprobes = this->_probe_rhs_slot.CallAs<CallProbes>();
    if (cplaceprobes == nullptr) return false;

    auto meta_data = cp->getMetaData();
    if (cd->getDataHash() == _old_datahash && meta_data.m_frame_ID == cd->getFrameIDtoLoad() && !_trigger_recalc)
        return true;

    cd->setFrameIDtoLoad(meta_data.m_frame_ID);
    if (!(*cd)(1)) return false;
    if (!(*ct)(1)) return false;
    if (!(*cplaceprobes)(1)) return false;

    // get adios meta data
    auto vars = cd->getAvailableVars();
    for (auto var : vars) {
        this->_parameter_to_sample_slot.Param<core::param::FlexEnumParam>()->AddValue(var);
        this->_vec_param_to_samplex_x.Param<core::param::FlexEnumParam>()->AddValue(var);
        this->_vec_param_to_samplex_y.Param<core::param::FlexEnumParam>()->AddValue(var);
        this->_vec_param_to_samplex_z.Param<core::param::FlexEnumParam>()->AddValue(var);
        this->_vec_param_to_samplex_w.Param<core::param::FlexEnumParam>()->AddValue(var);
    }

    // put metadata in mesh call
    meta_data.m_frame_cnt = cd->getFrameCount();
    cp->setMetaData(meta_data);

    return true;
}

bool SampleAlongPobes::paramChanged(core::param::ParamSlot& p) {

    _trigger_recalc = true;
    return true;
}

} // namespace probe
} // namespace megamol
<|endoftext|>


<commit_before>/*
	Copyright (C) 1998, 1999 by Nathaniel 'NooTe' Saint Martin
	Copyright (C) 1998, 1999 by Jorrit Tyberghein
	Written by Nathaniel 'NooTe' Saint Martin

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Library General Public
	License as published by the Free Software Foundation; either
	version 2 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Library General Public License for more details.

	You should have received a copy of the GNU Library General Public
	License along with this library; if not, write to the Free
	Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <stdio.h>
#include <stdarg.h>

#include "cssysdef.h"
#include "csutil/scf.h"
#include "macsdrv.h"
#include "isystem.h"
#include "isndlstn.h"
#include "isndsrc.h"

static pascal void SoundDoubleBackProc(
	SndChannelPtr		channel,
	SndDoubleBufferPtr	doubleBuffer );

#define kDoubleBufferSize	4096L

IMPLEMENT_FACTORY(csSoundDriverMac);

EXPORT_CLASS_TABLE (snddrv)
	EXPORT_CLASS (csSoundDriverMac, SOUND_DRIVER,
		"Crystal Space Sound driver for Macintosh")
EXPORT_CLASS_TABLE_END

IMPLEMENT_IBASE(csSoundDriverMac)
  IMPLEMENTS_INTERFACE(iSoundDriver)
	IMPLEMENTS_INTERFACE(iPlugIn)
IMPLEMENT_IBASE_END;

csSoundDriverMac::csSoundDriverMac(iBase *piBase)
{
  CONSTRUCT_IBASE(piBase);
  m_piSystem = NULL;
  mStopPlayback = false;
  mSoundDBHeader.dbhDoubleBack = NULL;
  mSoundDBHeader.dbhBufferPtr[0] = NULL;
  mSoundDBHeader.dbhBufferPtr[1] = NULL;
  mSoundChannel = NULL;
  mFramesPerBuffer = 0L;
  mBuffersFilled = 0L;
}

csSoundDriverMac::~csSoundDriverMac()
{
}

bool csSoundDriverMac::Initialize(iSystem *iSys)
{
  m_piSystem = iSys;
  return true;
}

bool csSoundDriverMac::Open(iSoundRender *render, int frequency, bool bit16, bool stereo)
{
  m_piSystem->Printf (MSG_INITIALIZATION, "\nSoundDriver Mac selected\n");

  m_piSoundRender = render;
  OSErr	theError;
  short	outputChannels;

  m_bStereo = stereo;
  m_b16Bits = bit16;
  m_nFrequency = frequency;
  MemorySize = kDoubleBufferSize;

  if ( stereo )
    {
      outputChannels = initStereo;
      mSoundDBHeader.dbhNumChannels = 2;
    }
  else
    {
      outputChannels = initMono;
      mSoundDBHeader.dbhNumChannels = 1;
    }

  theError = SndNewChannel( &mSoundChannel, sampledSynth, outputChannels, NULL );
  if ( theError != noErr ) {
    m_piSystem->Printf( MSG_FATAL_ERROR, "Unable to open a sound channel.");
    return false;
  }

  /*
   *	Fill in the double buffer header
   */

  if ( bit16 ) {
    mSoundDBHeader.dbhSampleSize = 16;
  } else {
    mSoundDBHeader.dbhSampleSize = 8;
  }

  mSoundDBHeader.dbhCompressionID = 0;
  mSoundDBHeader.dbhPacketSize = 0;
  mSoundDBHeader.dbhSampleRate = frequency << 16;
  mSoundDBHeader.dbhDoubleBack = NewSndDoubleBackProc( SoundDoubleBackProc );

  /*
   *	Calculate the number of frames per buffer.
   *	1 frame is = to 2 samples if stereo or 1 sample if mono.
   */
  mFramesPerBuffer = kDoubleBufferSize;
  if ( m_bStereo )
    mFramesPerBuffer /= 2L;
  if ( m_b16Bits )
    mFramesPerBuffer /= 2L;

  /*
   *	Get the space for the first buffer.
   */
  mSoundDBHeader.dbhBufferPtr[0] = (SndDoubleBufferPtr)NewPtr( sizeof( SndDoubleBuffer ) + kDoubleBufferSize );
  if ( mSoundDBHeader.dbhBufferPtr[0] == NULL ) {
    SndDisposeChannel( mSoundChannel, TRUE );
    mSoundChannel = NULL;
    DisposeRoutineDescriptor( mSoundDBHeader.dbhDoubleBack );
    mSoundDBHeader.dbhDoubleBack = NULL;
    m_piSystem->Printf( MSG_FATAL_ERROR, "Unable to get the space for the sound buffer.");
    return false;
  }
  mSoundDBHeader.dbhBufferPtr[0]->dbNumFrames = 0L;
  mSoundDBHeader.dbhBufferPtr[0]->dbFlags = 0;
  mSoundDBHeader.dbhBufferPtr[0]->dbUserInfo[0] = (long)this;

  /*
   *	Fill in this buffer with sounds.
   */
  SndDoubleBackProc( mSoundChannel, mSoundDBHeader.dbhBufferPtr[0] );

  /*
   *	Get the space for the second buffer.
   */
  mSoundDBHeader.dbhBufferPtr[1] = (SndDoubleBufferPtr)NewPtr( sizeof( SndDoubleBuffer ) + kDoubleBufferSize );
  if ( mSoundDBHeader.dbhBufferPtr[1] == NULL ) {
    SndDisposeChannel( mSoundChannel, TRUE );
    mSoundChannel = NULL;
    DisposeRoutineDescriptor( mSoundDBHeader.dbhDoubleBack );
    mSoundDBHeader.dbhDoubleBack = NULL;
    DisposePtr( (Ptr)mSoundDBHeader.dbhBufferPtr[0] );
    mSoundDBHeader.dbhBufferPtr[0] = NULL;
    m_piSystem->Printf( MSG_FATAL_ERROR, "Unable to get the space for the sound buffer.");
    return false;
  }
  mSoundDBHeader.dbhBufferPtr[1]->dbNumFrames = 0L;
  mSoundDBHeader.dbhBufferPtr[1]->dbFlags = 0;
  mSoundDBHeader.dbhBufferPtr[1]->dbUserInfo[0] = (long)this;

  /*
   *	Fill in this buffer with sounds.
   */
  SndDoubleBackProc( mSoundChannel, mSoundDBHeader.dbhBufferPtr[1] );

  /*
   *	Start the sounds playing.
   */
  theError = SndPlayDoubleBuffer( mSoundChannel, &mSoundDBHeader );
  if ( theError != noErr ) {
    SndDisposeChannel( mSoundChannel, TRUE );
    mSoundChannel = NULL;
    DisposeRoutineDescriptor( mSoundDBHeader.dbhDoubleBack );
    mSoundDBHeader.dbhDoubleBack = NULL;
    DisposePtr( (Ptr)mSoundDBHeader.dbhBufferPtr[0] );
    mSoundDBHeader.dbhBufferPtr[0] = NULL;
    DisposePtr( (Ptr)mSoundDBHeader.dbhBufferPtr[1] );
    mSoundDBHeader.dbhBufferPtr[1] = NULL;
    m_piSystem->Printf( MSG_FATAL_ERROR, "Unable to start the sound playing.");
    return false;
  }

  GetDefaultOutputVolume( &mOutputVolume );
  SetDefaultOutputVolume( 255 );

  return true;
}

void csSoundDriverMac::Close()
{
  SCStatus	theStatus;

  /*
   *	Tell the double buffer callback that this is the end.
   */
  mStopPlayback = true;

  if ( mSoundChannel ) {
    /*
     *	Wait until the sound channel is done.
     */
    do {
      SndChannelStatus( mSoundChannel, sizeof( SCStatus ), &theStatus );
    } while ( theStatus.scChannelBusy );

    /*
     *	Get rid of the sound channel.
     */
    SndDisposeChannel( mSoundChannel, TRUE );
  }

  if ( mSoundDBHeader.dbhDoubleBack )
    DisposeRoutineDescriptor( mSoundDBHeader.dbhDoubleBack );

  /*
   *	Get rid of the memory used for the double buffers
   */
  if ( mSoundDBHeader.dbhBufferPtr[0] )
    DisposePtr( (Ptr)mSoundDBHeader.dbhBufferPtr[0] );
  if ( mSoundDBHeader.dbhBufferPtr[1] )
    DisposePtr( (Ptr)mSoundDBHeader.dbhBufferPtr[1] );

  /*
   *	Make sure our superclass does not also go a dispose of the memory
   */
  Memory = NULL;

  SetDefaultOutputVolume( mOutputVolume );
}

void csSoundDriverMac::LockMemory(void **mem, int *memsize)
{
  *mem = Memory;
  *memsize = MemorySize;
}

void csSoundDriverMac::UnlockMemory() {}

bool csSoundDriverMac::IsBackground() { return true; }
bool csSoundDriverMac::Is16Bits() { return m_b16Bits; }
bool csSoundDriverMac::IsStereo() { return m_bStereo; }
int csSoundDriverMac::GetFrequency() { return m_nFrequency; }
bool csSoundDriverMac::IsHandleVoidSound() { return false; }

void csSoundDriverMac::SndDoubleBackProc(
	SndChannelPtr		channel,
	SndDoubleBufferPtr	doubleBuffer )
{
	/*
	 *	Set the location where the sounds will be written into by the
	 *	mixing function to the currently empty buffer.
	 */
	Memory = &(doubleBuffer->dbSoundData[0]);

	/*
	 *	Get more sound samples from the mixing function.
	 */
	m_piSoundRender->MixingFunction();

	/*
	 *	Mark the buffer as ready to be emptied.
	 *	If this object is being closed mark the buffer
	 *	as the last one to be processed.
	 */
	doubleBuffer->dbNumFrames = mFramesPerBuffer;
	doubleBuffer->dbFlags |= dbBufferReady;
	if ( mStopPlayback )
		doubleBuffer->dbFlags |= dbLastBuffer;

	++mBuffersFilled;
}

static pascal void SoundDoubleBackProc(
	SndChannelPtr		channel,
	SndDoubleBufferPtr	doubleBuffer )
{
	csSoundDriverMac *me;

	/*
	 *	Get the address to this object from the double buffer user info.
	 *	It was placed there earlier.
	 */
	me = (csSoundDriverMac *)(doubleBuffer->dbUserInfo[0]);

	/*
	 *	Set the location where the sounds will be written into by the
	 *	mixing function to the currently empty buffer.
	 */
	me->SndDoubleBackProc( channel, doubleBuffer );
}
<commit_msg>Update.<commit_after>/*
	Copyright (C) 1998, 1999 by Nathaniel 'NooTe' Saint Martin
	Copyright (C) 1998, 1999 by Jorrit Tyberghein
	Written by Nathaniel 'NooTe' Saint Martin

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Library General Public
	License as published by the Free Software Foundation; either
	version 2 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Library General Public License for more details.

	You should have received a copy of the GNU Library General Public
	License along with this library; if not, write to the Free
	Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <stdio.h>
#include <stdarg.h>

#include "cssysdef.h"
#include "csutil/scf.h"
#include "macsdrv.h"
#include "isystem.h"
#include "isndlstn.h"
#include "isndsrc.h"

static pascal void SoundDoubleBackProc(
	SndChannelPtr		channel,
	SndDoubleBufferPtr	doubleBuffer );

#define kDoubleBufferSize	4096L

IMPLEMENT_FACTORY(csSoundDriverMac);

EXPORT_CLASS_TABLE (snddrv)
	EXPORT_CLASS (csSoundDriverMac, SOUND_DRIVER,
		"Crystal Space Sound driver for Macintosh")
EXPORT_CLASS_TABLE_END

IMPLEMENT_IBASE(csSoundDriverMac)
  IMPLEMENTS_INTERFACE(iSoundDriver)
	IMPLEMENTS_INTERFACE(iPlugIn)
IMPLEMENT_IBASE_END;

csSoundDriverMac::csSoundDriverMac(iBase *piBase)
{
  CONSTRUCT_IBASE(piBase);
  m_piSystem = NULL;
  mStopPlayback = false;
  mSoundDBHeader.dbhDoubleBack = NULL;
  mSoundDBHeader.dbhBufferPtr[0] = NULL;
  mSoundDBHeader.dbhBufferPtr[1] = NULL;
  mSoundChannel = NULL;
  mFramesPerBuffer = 0L;
  mBuffersFilled = 0L;
}

csSoundDriverMac::~csSoundDriverMac()
{
}

bool csSoundDriverMac::Initialize(iSystem *iSys)
{
  m_piSystem = iSys;
  return true;
}

bool csSoundDriverMac::Open(iSoundRender *render, int frequency, bool bit16, bool stereo)
{
  m_piSystem->Printf (MSG_INITIALIZATION, "\nSoundDriver Mac selected\n");

  m_piSoundRender = render;
  OSErr	theError;
  short	outputChannels;

  m_bStereo = stereo;
  m_b16Bits = bit16;
  m_nFrequency = frequency;
  MemorySize = kDoubleBufferSize;

  if ( stereo )
    {
      outputChannels = initStereo;
      mSoundDBHeader.dbhNumChannels = 2;
    }
  else
    {
      outputChannels = initMono;
      mSoundDBHeader.dbhNumChannels = 1;
    }

  theError = SndNewChannel( &mSoundChannel, sampledSynth, outputChannels, NULL );
  if ( theError != noErr ) {
    m_piSystem->Printf( MSG_FATAL_ERROR, "Unable to open a sound channel.");
    return false;
  }

  /*
   *	Fill in the double buffer header
   */

  if ( bit16 ) {
    mSoundDBHeader.dbhSampleSize = 16;
  } else {
    mSoundDBHeader.dbhSampleSize = 8;
  }

  mSoundDBHeader.dbhCompressionID = 0;
  mSoundDBHeader.dbhPacketSize = 0;
  mSoundDBHeader.dbhSampleRate = frequency << 16;
  mSoundDBHeader.dbhDoubleBack = NewSndDoubleBackProc( SoundDoubleBackProc );

  /*
   *	Calculate the number of frames per buffer.
   *	1 frame is = to 2 samples if stereo or 1 sample if mono.
   */
  mFramesPerBuffer = kDoubleBufferSize;
  if ( m_bStereo )
    mFramesPerBuffer /= 2L;
  if ( m_b16Bits )
    mFramesPerBuffer /= 2L;

  /*
   *	Get the space for the first buffer.
   */
  mSoundDBHeader.dbhBufferPtr[0] = (SndDoubleBufferPtr)NewPtr( sizeof( SndDoubleBuffer ) + kDoubleBufferSize );
  if ( mSoundDBHeader.dbhBufferPtr[0] == NULL ) {
    SndDisposeChannel( mSoundChannel, TRUE );
    mSoundChannel = NULL;
    DisposeRoutineDescriptor( mSoundDBHeader.dbhDoubleBack );
    mSoundDBHeader.dbhDoubleBack = NULL;
    m_piSystem->Printf( MSG_FATAL_ERROR, "Unable to get the space for the sound buffer.");
    return false;
  }
  mSoundDBHeader.dbhBufferPtr[0]->dbNumFrames = 0L;
  mSoundDBHeader.dbhBufferPtr[0]->dbFlags = 0;
  mSoundDBHeader.dbhBufferPtr[0]->dbUserInfo[0] = (long)this;

  /*
   *	Fill in this buffer with sounds.
   */
  SndDoubleBackProc( mSoundChannel, mSoundDBHeader.dbhBufferPtr[0] );

  /*
   *	Get the space for the second buffer.
   */
  mSoundDBHeader.dbhBufferPtr[1] = (SndDoubleBufferPtr)NewPtr( sizeof( SndDoubleBuffer ) + kDoubleBufferSize );
  if ( mSoundDBHeader.dbhBufferPtr[1] == NULL ) {
    SndDisposeChannel( mSoundChannel, TRUE );
    mSoundChannel = NULL;
    DisposeRoutineDescriptor( mSoundDBHeader.dbhDoubleBack );
    mSoundDBHeader.dbhDoubleBack = NULL;
    DisposePtr( (Ptr)mSoundDBHeader.dbhBufferPtr[0] );
    mSoundDBHeader.dbhBufferPtr[0] = NULL;
    m_piSystem->Printf( MSG_FATAL_ERROR, "Unable to get the space for the sound buffer.");
    return false;
  }
  mSoundDBHeader.dbhBufferPtr[1]->dbNumFrames = 0L;
  mSoundDBHeader.dbhBufferPtr[1]->dbFlags = 0;
  mSoundDBHeader.dbhBufferPtr[1]->dbUserInfo[0] = (long)this;

  /*
   *	Fill in this buffer with sounds.
   */
  SndDoubleBackProc( mSoundChannel, mSoundDBHeader.dbhBufferPtr[1] );

  /*
   *	Start the sounds playing.
   */
  theError = SndPlayDoubleBuffer( mSoundChannel, &mSoundDBHeader );
  if ( theError != noErr ) {
    SndDisposeChannel( mSoundChannel, TRUE );
    mSoundChannel = NULL;
    DisposeRoutineDescriptor( mSoundDBHeader.dbhDoubleBack );
    mSoundDBHeader.dbhDoubleBack = NULL;
    DisposePtr( (Ptr)mSoundDBHeader.dbhBufferPtr[0] );
    mSoundDBHeader.dbhBufferPtr[0] = NULL;
    DisposePtr( (Ptr)mSoundDBHeader.dbhBufferPtr[1] );
    mSoundDBHeader.dbhBufferPtr[1] = NULL;
    m_piSystem->Printf( MSG_FATAL_ERROR, "Unable to start the sound playing.");
    return false;
  }

  GetDefaultOutputVolume( &mOutputVolume );
  SetDefaultOutputVolume( 255 );

  return true;
}

void csSoundDriverMac::Close()
{
  SCStatus	theStatus;

  /*
   *	Tell the double buffer callback that this is the end.
   */
  mStopPlayback = true;

  if ( mSoundChannel ) {
    /*
     *	Wait until the sound channel is done.
     */
    do {
      SndChannelStatus( mSoundChannel, sizeof( SCStatus ), &theStatus );
    } while ( theStatus.scChannelBusy );

    /*
     *	Get rid of the sound channel.
     */
    SndDisposeChannel( mSoundChannel, TRUE );
  }

  if ( mSoundDBHeader.dbhDoubleBack )
    DisposeRoutineDescriptor( mSoundDBHeader.dbhDoubleBack );

  /*
   *	Get rid of the memory used for the double buffers
   */
  if ( mSoundDBHeader.dbhBufferPtr[0] )
    DisposePtr( (Ptr)mSoundDBHeader.dbhBufferPtr[0] );
  if ( mSoundDBHeader.dbhBufferPtr[1] )
    DisposePtr( (Ptr)mSoundDBHeader.dbhBufferPtr[1] );

  /*
   *	Make sure our superclass does not also go a dispose of the memory
   */
  Memory = NULL;

  SetDefaultOutputVolume( mOutputVolume );
}

void csSoundDriverMac::LockMemory(void **mem, int *memsize)
{
  *mem = Memory;
  *memsize = MemorySize;
}

void csSoundDriverMac::UnlockMemory() {}

bool csSoundDriverMac::IsBackground() { return true; }
bool csSoundDriverMac::Is16Bits() { return m_b16Bits; }
bool csSoundDriverMac::IsStereo() { return m_bStereo; }
int csSoundDriverMac::GetFrequency() { return m_nFrequency; }
bool csSoundDriverMac::IsHandleVoidSound() { return false; }

void csSoundDriverMac::SndDoubleBackProc(
	SndChannelPtr		channel,
	SndDoubleBufferPtr	doubleBuffer )
{
	/*
	 *	Set the location where the sounds will be written into by the
	 *	mixing function to the currently empty buffer.
	 */
	Memory = &(doubleBuffer->dbSoundData[0]);

	/*
	 *	Get more sound samples from the mixing function.
	 */
// @@@ WHM Commented out for now to compile. Will work on this in the future.
//	m_piSoundRender->MixingFunction();

	/*
	 *	Mark the buffer as ready to be emptied.
	 *	If this object is being closed mark the buffer
	 *	as the last one to be processed.
	 */
	doubleBuffer->dbNumFrames = mFramesPerBuffer;
	doubleBuffer->dbFlags |= dbBufferReady;
	if ( mStopPlayback )
		doubleBuffer->dbFlags |= dbLastBuffer;

	++mBuffersFilled;
}

static pascal void SoundDoubleBackProc(
	SndChannelPtr		channel,
	SndDoubleBufferPtr	doubleBuffer )
{
	csSoundDriverMac *me;

	/*
	 *	Get the address to this object from the double buffer user info.
	 *	It was placed there earlier.
	 */
	me = (csSoundDriverMac *)(doubleBuffer->dbUserInfo[0]);

	/*
	 *	Set the location where the sounds will be written into by the
	 *	mixing function to the currently empty buffer.
	 */
	me->SndDoubleBackProc( channel, doubleBuffer );
}
<|endoftext|>