<commit_before>#include "window/Patcher.h"

/*
Copyright (C) 2016 AGC.

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

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

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <assert.h>
#include <iostream>
#include <sstream>
#include <string>
#include <boost/filesystem.hpp>
#include <rapidjson/document.h>
#include <RakNetTypes.h>
#include "Database.h"

using namespace x801::game;

const char* x801::game::PATCHER_DIR = "gamedat/";

x801::game::Patcher::Patcher(std::string u) {
  if (!boost::filesystem::is_directory(PATCHER_DIR)) {
    std::cout <<
      "Warning: overwriting " << PATCHER_DIR <<
      " because it is not a directory\n";
    boost::filesystem::remove(PATCHER_DIR);
    boost::filesystem::create_directories(PATCHER_DIR);
  }
  curl = curl_easy_init();
  if (curl == nullptr) throw "Could not initialise CURL";
#ifndef NDEBUG
  curl_easy_setopt(curl, CURLOPT_VERBOSE, true);
#endif
  std::string urishadow = u;
  std::cout << urishadow.c_str() << '\n';
  size_t index = urishadow.find("://") + 3;
  urishadow[urishadow.find(':', index)] = '|';
  RakNet::SystemAddress addr;
  bool stat = addr.FromString(urishadow.c_str() + index, '|');
  char aname[64];
  addr.ToString(true, aname, '_');
  if (!stat) throw "Address of connected server unknown";
  std::stringstream dfnamein;
  dfnamein << PATCHER_DIR << "gd_" << aname << ".dat";
  // This is to be a filename as such:
  // gd_127.0.0.1_9001.dat
  std::string dfname = dfnamein.str();
  open(conn, dfname.c_str());
  createFileTable();
  uri = u;
}

x801::game::Patcher::~Patcher() {
  done = true;
  fetchThread.join();
  int stat = sqlite3_close(conn);
  curl_easy_cleanup(curl);
  conn = nullptr;
  (void) stat;
  assert(stat == SQLITE_OK);
}

void x801::game::Patcher::open(sqlite3*& handle, const char* path) {
  int status = sqlite3_open(path, &handle);
  if (handle == nullptr) throw "Can't open the database.";
  if (status != SQLITE_OK) throw sqlite3_errmsg(handle);
}

static const char* CREATE_FILE_TABLE_QUERY =
  "CREATE TABLE IF NOT EXISTS "
  "Files("
  "  fileID INTEGER PRIMARY KEY ASC,"
  "  fname STRING UNIQUE NOT NULL,"
  "  version INTEGER NOT NULL,"
  "  contents BLOB"
  ");"
  "CREATE INDEX IF NOT EXISTS "
  "  filesByName ON Files (fname);"
  ;

static const char* DEFAULT_ERROR = "patcher default error";

void x801::game::Patcher::createFileTable() {
  const char* errMessage = DEFAULT_ERROR;
  int stat = sqlite3_exec(
    conn,
    CREATE_FILE_TABLE_QUERY,
    nullptr, nullptr, // don't callback
    (char**) &errMessage
  );
  if (stat != SQLITE_OK) throw errMessage;
}

static const char* CREATE_FILE_ENTRY_QUERY =
  "INSERT OR REPLACE INTO Files (fname, version, contents) VALUES (?, ?, ?)"
  ;

void x801::game::Patcher::createFileEntry(
    const char* fname,
    uint32_t version,
    uint32_t contentLength,
    const uint8_t* contents) {
  sqlite3_stmt* statement;
  int stat = sqlite3_prepare_v2(
    conn,
    CREATE_FILE_ENTRY_QUERY, -1,
    &statement,
    nullptr
  );
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_text(statement, 1, fname, -1, SQLITE_STATIC);
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_int(statement, 2, version);
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_blob(
    statement, 3, static_cast<const void*>(contents), contentLength,
    SQLITE_STATIC
  );
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stepBlock(statement, conn);
  sqlite3_finalize(statement);
}

static const char* UPDATE_FILE_VERSION_QUERY =
  "BEGIN TRANSACTION;"
  "INSERT OR IGNORE INTO Files"
  "  (fname, version, contents) VALUES (?1, ?2, NULL);"
  "UPDATE Files"
  "  SET version = ?2"
  "  WHERE fname = ?1;"
  "COMMIT;"
  ;

void x801::game::Patcher::updateFileVersion(
    const char* fname,
    uint32_t version) {
  sqlite3_stmt* statement;
  int stat = sqlite3_prepare_v2(
    conn,
    UPDATE_FILE_VERSION_QUERY, -1,
    &statement,
    nullptr
  );
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_text(statement, 1, fname, -1, SQLITE_STATIC);
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_int(statement, 2, version);
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stepBlock(statement, conn);
  sqlite3_finalize(statement);
}

static const char* FIND_FILE_BY_NAME_QUERY =
  "SELECT * from Files"
  "  WHERE fname = ?;"
  ;

bool x801::game::Patcher::getFileEntry(
    const char* fname,
    uint32_t& version,
    uint32_t& contentLength,
    uint8_t*& contents) {
  sqlite3_stmt* statement;
  int stat = sqlite3_prepare_v2(
    conn,
    FIND_FILE_BY_NAME_QUERY, -1,
    &statement,
    nullptr
  );
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_text(statement, 1, fname, -1, SQLITE_STATIC);
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = stepBlock(statement, conn);
  if (stat != SQLITE_ROW) {
    sqlite3_finalize(statement);
    return false;
  }
  version = sqlite3_column_int(statement, 2);
  int size = sqlite3_column_bytes(statement, 3);
  contentLength = (uint32_t) size;
  uint8_t* bytes = new uint8_t[size];
  memcpy(bytes, sqlite3_column_blob(statement, 3), size);
  contents = bytes;
  sqlite3_finalize(statement);
  return true;
}

void x801::game::Patcher::updateEntry(
    const char* fname,
    uint32_t version,
    uint32_t contentLength,
    const uint8_t* contents) {
  if (fname == nullptr || contents == nullptr)
    throw "x801::game::Patcher::createFileEntry: "
      "fname and contents must not be null";
  if (version > latestVersion) latestVersion = version;
  if (contentLength == 0) updateFileVersion(fname, version);
  else createFileEntry(fname, version, contentLength, contents);
}

static size_t patcherWriteFunction(char *ptr, size_t size, size_t nmemb, void *userdata) {
  std::stringstream* output = (std::stringstream*) userdata;
  output->write(ptr, size * nmemb);
  return size * nmemb;
}

void x801::game::Patcher::refetchFile(const char* fname, uint32_t version) {
  std::lock_guard<std::mutex> lock(mutex);
  std::stringstream ss;
  curl_easy_setopt(curl, CURLOPT_URL, (uri + "/content?fname=" + fname).c_str());
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, patcherWriteFunction);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &ss);
  CURLcode res = curl_easy_perform(curl);
  if (res != CURLE_OK) return;
  std::string s = ss.str();
  createFileEntry(fname, version, s.length(), (const uint8_t*) s.c_str());
}

uint32_t x801::game::Patcher::getVersionFromServer(const char* fname) {
  std::lock_guard<std::mutex> lock(mutex);
  std::stringstream ss;
  curl_easy_setopt(curl, CURLOPT_URL, (uri + "/version?fname=" + fname).c_str());
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, patcherWriteFunction);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &ss);
  CURLcode res = curl_easy_perform(curl);
  if (res != CURLE_OK) return -1;
  long responseCode;
  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
  if (responseCode != 200) return -1;
  //std::cerr << "*** File " << fname << " ***\n";
  //std::cerr << "Response code: " << responseCode << "\n";
  std::string s = ss.str();
  //std::cerr << "Contents: " << s << "\n";
  return std::stoul(s);
}

void x801::game::Patcher::fetchFile(const char* fname) {
  uint32_t lversion;
  uint32_t contentLength;
  uint8_t* contents;
  getFileEntry(fname, lversion, contentLength, contents);
  uint32_t sversion = getVersionFromServer(fname);
  if (sversion != lversion) refetchFile(fname, sversion);
}

void x801::game::Patcher::requestFile(const char* fname) {
  files.enqueue(std::string(fname));
}

void x801::game::Patcher::startFetchThread() {
  auto cback = [this]() {
    while (!this->done) {
      std::string fname;
      bool stat = files.wait_dequeue_timed(fname, 5000);
      if (stat) {
        std::cerr << "Fetching file " << fname << "!\n";
        fetchFile(fname.c_str());
        std::cerr << "Done fetching file " << fname << "!\n";
      }
    }
  };
  fetchThread = std::thread(cback);
}

bool x801::game::Patcher::fetchIndex(std::stringstream& ss) {
  std::lock_guard<std::mutex> lock(mutex);
  curl_easy_setopt(curl, CURLOPT_URL, "/v0tgil-sucks");
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, patcherWriteFunction);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &ss);
  CURLcode res = curl_easy_perform(curl);
  if (res != CURLE_OK) return false;
  long responseCode;
  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
  if (responseCode != 200) return false;
  return true;
}

bool x801::game::Patcher::updateAllFiles() {
  std::stringstream ss;
  bool stat = fetchIndex(ss);
  if (!stat) return false;
  std::string indexStr = ss.str();
  rapidjson::Document index;
  if (index.Parse(indexStr.c_str()).HasParseError())
    return false;
  if (!index.IsObject()) return false;
  rapidjson::Value::ConstMemberIterator begin = index.MemberBegin();
  rapidjson::Value::ConstMemberIterator end = index.MemberEnd();
  bool ok = true;
  for (auto it = begin; it != end; ++it) {
    auto fname = it->name.GetString();
    const rapidjson::Value& props = it->value;
    if (!props.IsObject()) {
      ok = false;
      continue;
    }
    auto verNode = props.FindMember("version");
    auto endNode = props.MemberEnd();
    if (verNode == endNode || !verNode->value.IsInt()) {
      ok = false;
      continue;
    }
    uint32_t version = verNode->value.GetInt();
    uint32_t oldVersion, oldLength;
    uint8_t* oldContents;
    bool fileExists = getFileEntry(fname, oldVersion, oldLength, oldContents);
    if (fileExists) delete[] oldContents;
    bool needsToUpdate = !fileExists || (oldVersion < version);
    if (needsToUpdate) {
      refetchFile(fname, version);
    }
  }
  return ok;
}<commit_msg>Patcher now updates files when starting up. This is currently blocking, and this might need to be changed if the game data gets big enough.<commit_after>#include "window/Patcher.h"

/*
Copyright (C) 2016 AGC.

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

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

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <assert.h>
#include <iostream>
#include <sstream>
#include <string>
#include <boost/filesystem.hpp>
#include <rapidjson/document.h>
#include <RakNetTypes.h>
#include "Database.h"

using namespace x801::game;

const char* x801::game::PATCHER_DIR = "gamedat/";

x801::game::Patcher::Patcher(std::string u) {
  if (!boost::filesystem::is_directory(PATCHER_DIR)) {
    std::cout <<
      "Warning: overwriting " << PATCHER_DIR <<
      " because it is not a directory\n";
    boost::filesystem::remove(PATCHER_DIR);
    boost::filesystem::create_directories(PATCHER_DIR);
  }
  curl = curl_easy_init();
  if (curl == nullptr) throw "Could not initialise CURL";
#ifndef NDEBUG
  curl_easy_setopt(curl, CURLOPT_VERBOSE, true);
#endif
  std::string urishadow = u;
  std::cout << urishadow.c_str() << '\n';
  size_t index = urishadow.find("://") + 3;
  urishadow[urishadow.find(':', index)] = '|';
  RakNet::SystemAddress addr;
  bool stat = addr.FromString(urishadow.c_str() + index, '|');
  char aname[64];
  addr.ToString(true, aname, '_');
  if (!stat) throw "Address of connected server unknown";
  std::stringstream dfnamein;
  dfnamein << PATCHER_DIR << "gd_" << aname << ".dat";
  // This is to be a filename as such:
  // gd_127.0.0.1_9001.dat
  std::string dfname = dfnamein.str();
  open(conn, dfname.c_str());
  createFileTable();
  uri = u;
  // XXX this is blocking
  updateAllFiles();
}

x801::game::Patcher::~Patcher() {
  done = true;
  fetchThread.join();
  int stat = sqlite3_close(conn);
  curl_easy_cleanup(curl);
  conn = nullptr;
  (void) stat;
  assert(stat == SQLITE_OK);
}

void x801::game::Patcher::open(sqlite3*& handle, const char* path) {
  int status = sqlite3_open(path, &handle);
  if (handle == nullptr) throw "Can't open the database.";
  if (status != SQLITE_OK) throw sqlite3_errmsg(handle);
}

static const char* CREATE_FILE_TABLE_QUERY =
  "CREATE TABLE IF NOT EXISTS "
  "Files("
  "  fileID INTEGER PRIMARY KEY ASC,"
  "  fname STRING UNIQUE NOT NULL,"
  "  version INTEGER NOT NULL,"
  "  contents BLOB"
  ");"
  "CREATE INDEX IF NOT EXISTS "
  "  filesByName ON Files (fname);"
  ;

static const char* DEFAULT_ERROR = "patcher default error";

void x801::game::Patcher::createFileTable() {
  const char* errMessage = DEFAULT_ERROR;
  int stat = sqlite3_exec(
    conn,
    CREATE_FILE_TABLE_QUERY,
    nullptr, nullptr, // don't callback
    (char**) &errMessage
  );
  if (stat != SQLITE_OK) throw errMessage;
}

static const char* CREATE_FILE_ENTRY_QUERY =
  "INSERT OR REPLACE INTO Files (fname, version, contents) VALUES (?, ?, ?)"
  ;

void x801::game::Patcher::createFileEntry(
    const char* fname,
    uint32_t version,
    uint32_t contentLength,
    const uint8_t* contents) {
  sqlite3_stmt* statement;
  int stat = sqlite3_prepare_v2(
    conn,
    CREATE_FILE_ENTRY_QUERY, -1,
    &statement,
    nullptr
  );
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_text(statement, 1, fname, -1, SQLITE_STATIC);
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_int(statement, 2, version);
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_blob(
    statement, 3, static_cast<const void*>(contents), contentLength,
    SQLITE_STATIC
  );
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stepBlock(statement, conn);
  sqlite3_finalize(statement);
}

static const char* UPDATE_FILE_VERSION_QUERY =
  "BEGIN TRANSACTION;"
  "INSERT OR IGNORE INTO Files"
  "  (fname, version, contents) VALUES (?1, ?2, NULL);"
  "UPDATE Files"
  "  SET version = ?2"
  "  WHERE fname = ?1;"
  "COMMIT;"
  ;

void x801::game::Patcher::updateFileVersion(
    const char* fname,
    uint32_t version) {
  sqlite3_stmt* statement;
  int stat = sqlite3_prepare_v2(
    conn,
    UPDATE_FILE_VERSION_QUERY, -1,
    &statement,
    nullptr
  );
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_text(statement, 1, fname, -1, SQLITE_STATIC);
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_int(statement, 2, version);
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stepBlock(statement, conn);
  sqlite3_finalize(statement);
}

static const char* FIND_FILE_BY_NAME_QUERY =
  "SELECT * from Files"
  "  WHERE fname = ?;"
  ;

bool x801::game::Patcher::getFileEntry(
    const char* fname,
    uint32_t& version,
    uint32_t& contentLength,
    uint8_t*& contents) {
  sqlite3_stmt* statement;
  int stat = sqlite3_prepare_v2(
    conn,
    FIND_FILE_BY_NAME_QUERY, -1,
    &statement,
    nullptr
  );
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = sqlite3_bind_text(statement, 1, fname, -1, SQLITE_STATIC);
  if (stat != SQLITE_OK) throw sqlite3_errmsg(conn);
  stat = stepBlock(statement, conn);
  if (stat != SQLITE_ROW) {
    sqlite3_finalize(statement);
    return false;
  }
  version = sqlite3_column_int(statement, 2);
  int size = sqlite3_column_bytes(statement, 3);
  contentLength = (uint32_t) size;
  uint8_t* bytes = new uint8_t[size];
  memcpy(bytes, sqlite3_column_blob(statement, 3), size);
  contents = bytes;
  sqlite3_finalize(statement);
  return true;
}

void x801::game::Patcher::updateEntry(
    const char* fname,
    uint32_t version,
    uint32_t contentLength,
    const uint8_t* contents) {
  if (fname == nullptr || contents == nullptr)
    throw "x801::game::Patcher::createFileEntry: "
      "fname and contents must not be null";
  if (version > latestVersion) latestVersion = version;
  if (contentLength == 0) updateFileVersion(fname, version);
  else createFileEntry(fname, version, contentLength, contents);
}

static size_t patcherWriteFunction(char *ptr, size_t size, size_t nmemb, void *userdata) {
  std::stringstream* output = (std::stringstream*) userdata;
  output->write(ptr, size * nmemb);
  return size * nmemb;
}

void x801::game::Patcher::refetchFile(const char* fname, uint32_t version) {
  std::lock_guard<std::mutex> lock(mutex);
  std::stringstream ss;
  curl_easy_setopt(curl, CURLOPT_URL, (uri + "/content?fname=" + fname).c_str());
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, patcherWriteFunction);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &ss);
  CURLcode res = curl_easy_perform(curl);
  if (res != CURLE_OK) return;
  std::string s = ss.str();
  createFileEntry(fname, version, s.length(), (const uint8_t*) s.c_str());
}

uint32_t x801::game::Patcher::getVersionFromServer(const char* fname) {
  std::lock_guard<std::mutex> lock(mutex);
  std::stringstream ss;
  curl_easy_setopt(curl, CURLOPT_URL, (uri + "/version?fname=" + fname).c_str());
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, patcherWriteFunction);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &ss);
  CURLcode res = curl_easy_perform(curl);
  if (res != CURLE_OK) return -1;
  long responseCode;
  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
  if (responseCode != 200) return -1;
  //std::cerr << "*** File " << fname << " ***\n";
  //std::cerr << "Response code: " << responseCode << "\n";
  std::string s = ss.str();
  //std::cerr << "Contents: " << s << "\n";
  return std::stoul(s);
}

void x801::game::Patcher::fetchFile(const char* fname) {
  uint32_t lversion;
  uint32_t contentLength;
  uint8_t* contents;
  getFileEntry(fname, lversion, contentLength, contents);
  uint32_t sversion = getVersionFromServer(fname);
  if (sversion != lversion) refetchFile(fname, sversion);
}

void x801::game::Patcher::requestFile(const char* fname) {
  files.enqueue(std::string(fname));
}

void x801::game::Patcher::startFetchThread() {
  auto cback = [this]() {
    while (!this->done) {
      std::string fname;
      bool stat = files.wait_dequeue_timed(fname, 5000);
      if (stat) {
        std::cerr << "Fetching file " << fname << "!\n";
        fetchFile(fname.c_str());
        std::cerr << "Done fetching file " << fname << "!\n";
      }
    }
  };
  fetchThread = std::thread(cback);
}

bool x801::game::Patcher::fetchIndex(std::stringstream& ss) {
  std::lock_guard<std::mutex> lock(mutex);
  curl_easy_setopt(curl, CURLOPT_URL, (uri + "/v0tgil-sucks").c_str());
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, patcherWriteFunction);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*) &ss);
  CURLcode res = curl_easy_perform(curl);
  if (res != CURLE_OK) return false;
  long responseCode;
  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
  if (responseCode != 200) return false;
  return true;
}

bool x801::game::Patcher::updateAllFiles() {
  std::stringstream ss;
  bool stat = fetchIndex(ss);
  if (!stat) return false;
  std::string indexStr = ss.str();
  rapidjson::Document index;
  if (index.Parse(indexStr.c_str()).HasParseError())
    return false;
  if (!index.IsObject()) return false;
  rapidjson::Value::ConstMemberIterator begin = index.MemberBegin();
  rapidjson::Value::ConstMemberIterator end = index.MemberEnd();
  bool ok = true;
  for (auto it = begin; it != end; ++it) {
    auto fname = it->name.GetString();
    const rapidjson::Value& props = it->value;
    if (!props.IsObject()) {
      ok = false;
      continue;
    }
    auto verNode = props.FindMember("version");
    auto endNode = props.MemberEnd();
    if (verNode == endNode || !verNode->value.IsInt()) {
      ok = false;
      continue;
    }
    uint32_t version = verNode->value.GetInt();
    uint32_t oldVersion, oldLength;
    uint8_t* oldContents;
    bool fileExists = getFileEntry(fname, oldVersion, oldLength, oldContents);
    bool needsToUpdate = !fileExists || (oldVersion < version);
    if (needsToUpdate) {
#ifndef NDEBUG
      std::cerr << "Updating file " << fname << "\n";
#endif
      refetchFile(fname, version);
    } else {
#ifndef NDEBUG
      std::cerr << fname << " doesn't need to update\n";
#endif
    }
    if (fileExists) delete[] oldContents;
  }
  return ok;
}<|endoftext|>


<commit_before>/* libs/graphics/effects/SkBlurMask.cpp
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include "SkBlurMask.h"
#include "SkTemplates.h"

static void build_sum_buffer(uint32_t dst[], int w, int h, const uint8_t src[], int srcRB)
{
    SkASSERT(srcRB >= w);
    // mod srcRB so we can apply it after each row
    srcRB -= w;

    int x, y;

    // special case first row
    uint32_t X = 0;
    for (x = w - 1; x >= 0; --x)
    {
        X = *src++ + X;
        *dst++ = X;
    }
    src += srcRB;

    // now do the rest of the rows
    for (y = h - 1; y > 0; --y)
    {
        uint32_t L = 0;
        uint32_t C = 0;
        for (x = w - 1; x >= 0; --x)
        {
            uint32_t T = dst[-w];
            X = *src++ + L + T - C;
            *dst++ = X;
            L = X;
            C = T;
        }
        src += srcRB;
    }
}

static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t src[], int sw, int sh)
{
    uint32_t scale = (1 << 24) / ((2*rx + 1)*(2*ry + 1));

    int rowBytes = sw;

    int dw = sw + 2*rx;
    int dh = sh + 2*ry;

    sw -= 1;    // now it is max_x
    sh -= 1;    // now it is max_y

    int prev_y = -ry - 1    -ry;
    int next_y = ry         -ry;

    for (int y = 0; y < dh; y++)
    {
        int py = SkClampPos(prev_y) * rowBytes;
        int ny = SkFastMin32(next_y, sh) * rowBytes;

        int prev_x = -rx - 1    -rx;
        int next_x = rx         -rx;

        for (int x = 0; x < dw; x++)
        {
            int px = SkClampPos(prev_x);
            int nx = SkFastMin32(next_x, sw);

            uint32_t sum = src[px+py] + src[nx+ny] - src[nx+py] - src[px+ny];
            *dst++ = SkToU8(sum * scale >> 24);

            prev_x += 1;
            next_x += 1;
        }
        prev_y += 1;
        next_y += 1;
    }
}

static void apply_kernel_interp(uint8_t dst[], int rx, int ry, const uint32_t src[], int sw, int sh, U8CPU outer_weight)
{
    SkASSERT(rx > 0 && ry > 0);
    SkASSERT(outer_weight <= 255);

    int inner_weight = 255 - outer_weight;

    // round these guys up if they're bigger than 127
    outer_weight += outer_weight >> 7;
    inner_weight += inner_weight >> 7;

    uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
    uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));

    int rowBytes = sw;

    int dw = sw + 2*rx;
    int dh = sh + 2*ry;

    sw -= 1;    // now it is max_x
    sh -= 1;    // now it is max_y

    int prev_y = -ry - 1    -ry;
    int next_y = ry         -ry;

    for (int y = 0; y < dh; y++)
    {
        int py = SkClampPos(prev_y) * rowBytes;
        int ny = SkFastMin32(next_y, sh) * rowBytes;

        int ipy = SkClampPos(prev_y + 1) * rowBytes;
        int iny = SkClampMax(next_y - 1, sh) * rowBytes;

        int prev_x = -rx - 1    -rx;
        int next_x = rx         -rx;

        for (int x = 0; x < dw; x++)
        {
            int px = SkClampPos(prev_x);
            int nx = SkFastMin32(next_x, sw);

            int ipx = SkClampPos(prev_x + 1);
            int inx = SkClampMax(next_x - 1, sw);

            uint32_t outer_sum = src[px+py] + src[nx+ny] - src[nx+py] - src[px+ny];
            uint32_t inner_sum = src[ipx+ipy] + src[inx+iny] - src[inx+ipy] - src[ipx+iny];
            *dst++ = SkToU8((outer_sum * outer_scale + inner_sum * inner_scale) >> 24);

            prev_x += 1;
            next_x += 1;
        }
        prev_y += 1;
        next_y += 1;
    }
}

#include "SkColorPriv.h"

static void merge_src_with_blur(uint8_t dst[],
                                const uint8_t src[], int sw, int sh,
                                const uint8_t blur[], int blurRowBytes)
{
    while (--sh >= 0)
    {
        for (int x = sw - 1; x >= 0; --x)
        {
            *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
            dst += 1;
            src += 1;
            blur += 1;
        }
        blur += blurRowBytes - sw;
    }
}

static void clamp_with_orig(uint8_t dst[], int dstRowBytes,
                            const uint8_t src[], int sw, int sh,
                            SkBlurMask::Style style)
{
    int x;
    while (--sh >= 0)
    {
        switch (style) {
        case SkBlurMask::kSolid_Style:
            for (x = sw - 1; x >= 0; --x)
            {
                *dst = SkToU8(*src + SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
                dst += 1;
                src += 1;
            }
            break;
        case SkBlurMask::kOuter_Style:
            for (x = sw - 1; x >= 0; --x)
            {
                if (*src)
                    *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
                dst += 1;
                src += 1;
            }
            break;
        default:
            SkASSERT(!"Unexpected blur style here");
            break;
        }
        dst += dstRowBytes - sw;
    }
}

////////////////////////////////////////////////////////////////////////

// we use a local funciton to wrap the class static method to work around
// a bug in gcc98
void SkMask_FreeImage(uint8_t* image);
void SkMask_FreeImage(uint8_t* image)
{
    SkMask::FreeImage(image);
}

bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
                      SkScalar radius, Style style)
{
    if (src.fFormat != SkMask::kA8_Format)
        return false;

    int rx = SkScalarCeil(radius);
    int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - radius) * 255);

    SkASSERT(rx >= 0);
    SkASSERT((unsigned)outer_weight <= 255);

    if (rx == 0)
        return false;

    int ry = rx;    // only do square blur for now

    dst->fBounds.set(src.fBounds.fLeft - rx, src.fBounds.fTop - ry,
                        src.fBounds.fRight + rx, src.fBounds.fBottom + ry);
    dst->fRowBytes = dst->fBounds.width();
    dst->fFormat = SkMask::kA8_Format;
    dst->fImage = NULL;

    if (src.fImage)
    {
        size_t dstSize = dst->computeImageSize();
        if (0 == dstSize) {
            return false;   // too big to allocate, abort
        }

        int             sw = src.fBounds.width();
        int             sh = src.fBounds.height();
        const uint8_t*  sp = src.fImage;
        uint8_t*        dp = SkMask::AllocImage(dstSize);

        SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);

        // build the blurry destination
        {
            SkAutoTMalloc<uint32_t> storage(sw * sh);
            uint32_t*               sumBuffer = storage.get();

            build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes);
            if (outer_weight == 255)
                apply_kernel(dp, rx, ry, sumBuffer, sw, sh);
            else
                apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
        }

        dst->fImage = dp;
        // if need be, alloc the "real" dst (same size as src) and copy/merge
        // the blur into it (applying the src)
        if (style == kInner_Style)
        {
            size_t srcSize = src.computeImageSize();
            if (0 == srcSize) {
                return false;   // too big to allocate, abort
            }
            dst->fImage = SkMask::AllocImage(srcSize);
            merge_src_with_blur(dst->fImage, sp, sw, sh,
                                dp + rx + ry*dst->fBounds.width(),
                                dst->fBounds.width());
            SkMask::FreeImage(dp);
        }
        else if (style != kNormal_Style)
        {
            clamp_with_orig(dp + rx + ry*dst->fBounds.width(),
                            dst->fBounds.width(),
                            sp, sw, sh,
                            style);
        }
        (void)autoCall.detach();
    }

    if (style == kInner_Style)
    {
        dst->fBounds = src.fBounds; // restore trimmed bounds
        dst->fRowBytes = dst->fBounds.width();
    }

#if 0
    if (gamma && dst->fImage)
    {
        uint8_t*    image = dst->fImage;
        uint8_t*    stop = image + dst->computeImageSize();

        for (; image < stop; image += 1)
            *image = gamma[*image];
    }
#endif
    return true;
}

#if 0
void SkBlurMask::BuildSqrtGamma(uint8_t gamma[256], SkScalar percent)
{
    SkASSERT(gamma);
    SkASSERT(percent >= 0 && percent <= SK_Scalar1);

    int scale = SkScalarRound(percent * 256);

    for (int i = 0; i < 256; i++)
    {
        SkFixed n = i * 257;
        n += n >> 15;
        SkASSERT(n >= 0 && n <= SK_Fixed1);
        n = SkFixedSqrt(n);
        n = n * 255 >> 16;
        n = SkAlphaBlend(n, i, scale);
        gamma[i] = SkToU8(n);
    }
}

void SkBlurMask::BuildSqrGamma(uint8_t gamma[256], SkScalar percent)
{
    SkASSERT(gamma);
    SkASSERT(percent >= 0 && percent <= SK_Scalar1);

    int     scale = SkScalarRound(percent * 256);
    SkFixed div255 = SK_Fixed1 / 255;

    for (int i = 0; i < 256; i++)
    {
        int square = i * i;
        int linear = i * 255;
        int n = SkAlphaBlend(square, linear, scale);
        gamma[i] = SkToU8(n * div255 >> 16);
    }
}
#endif
<commit_msg>use rowbytes instead of bounds.width()<commit_after>/* libs/graphics/effects/SkBlurMask.cpp
**
** Copyright 2006, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include "SkBlurMask.h"
#include "SkTemplates.h"

static void build_sum_buffer(uint32_t dst[], int w, int h, const uint8_t src[], int srcRB)
{
    SkASSERT(srcRB >= w);
    // mod srcRB so we can apply it after each row
    srcRB -= w;

    int x, y;

    // special case first row
    uint32_t X = 0;
    for (x = w - 1; x >= 0; --x)
    {
        X = *src++ + X;
        *dst++ = X;
    }
    src += srcRB;

    // now do the rest of the rows
    for (y = h - 1; y > 0; --y)
    {
        uint32_t L = 0;
        uint32_t C = 0;
        for (x = w - 1; x >= 0; --x)
        {
            uint32_t T = dst[-w];
            X = *src++ + L + T - C;
            *dst++ = X;
            L = X;
            C = T;
        }
        src += srcRB;
    }
}

static void apply_kernel(uint8_t dst[], int rx, int ry, const uint32_t src[], int sw, int sh)
{
    uint32_t scale = (1 << 24) / ((2*rx + 1)*(2*ry + 1));

    int rowBytes = sw;

    int dw = sw + 2*rx;
    int dh = sh + 2*ry;

    sw -= 1;    // now it is max_x
    sh -= 1;    // now it is max_y

    int prev_y = -ry - 1    -ry;
    int next_y = ry         -ry;

    for (int y = 0; y < dh; y++)
    {
        int py = SkClampPos(prev_y) * rowBytes;
        int ny = SkFastMin32(next_y, sh) * rowBytes;

        int prev_x = -rx - 1    -rx;
        int next_x = rx         -rx;

        for (int x = 0; x < dw; x++)
        {
            int px = SkClampPos(prev_x);
            int nx = SkFastMin32(next_x, sw);

            uint32_t sum = src[px+py] + src[nx+ny] - src[nx+py] - src[px+ny];
            *dst++ = SkToU8(sum * scale >> 24);

            prev_x += 1;
            next_x += 1;
        }
        prev_y += 1;
        next_y += 1;
    }
}

static void apply_kernel_interp(uint8_t dst[], int rx, int ry, const uint32_t src[], int sw, int sh, U8CPU outer_weight)
{
    SkASSERT(rx > 0 && ry > 0);
    SkASSERT(outer_weight <= 255);

    int inner_weight = 255 - outer_weight;

    // round these guys up if they're bigger than 127
    outer_weight += outer_weight >> 7;
    inner_weight += inner_weight >> 7;

    uint32_t outer_scale = (outer_weight << 16) / ((2*rx + 1)*(2*ry + 1));
    uint32_t inner_scale = (inner_weight << 16) / ((2*rx - 1)*(2*ry - 1));

    int rowBytes = sw;

    int dw = sw + 2*rx;
    int dh = sh + 2*ry;

    sw -= 1;    // now it is max_x
    sh -= 1;    // now it is max_y

    int prev_y = -ry - 1    -ry;
    int next_y = ry         -ry;

    for (int y = 0; y < dh; y++)
    {
        int py = SkClampPos(prev_y) * rowBytes;
        int ny = SkFastMin32(next_y, sh) * rowBytes;

        int ipy = SkClampPos(prev_y + 1) * rowBytes;
        int iny = SkClampMax(next_y - 1, sh) * rowBytes;

        int prev_x = -rx - 1    -rx;
        int next_x = rx         -rx;

        for (int x = 0; x < dw; x++)
        {
            int px = SkClampPos(prev_x);
            int nx = SkFastMin32(next_x, sw);

            int ipx = SkClampPos(prev_x + 1);
            int inx = SkClampMax(next_x - 1, sw);

            uint32_t outer_sum = src[px+py] + src[nx+ny] - src[nx+py] - src[px+ny];
            uint32_t inner_sum = src[ipx+ipy] + src[inx+iny] - src[inx+ipy] - src[ipx+iny];
            *dst++ = SkToU8((outer_sum * outer_scale + inner_sum * inner_scale) >> 24);

            prev_x += 1;
            next_x += 1;
        }
        prev_y += 1;
        next_y += 1;
    }
}

#include "SkColorPriv.h"

static void merge_src_with_blur(uint8_t dst[], int dstRB,
                                const uint8_t src[], int srcRB,
                                const uint8_t blur[], int blurRB,
                                int sw, int sh) {
    dstRB -= sw;
    srcRB -= sw;
    blurRB -= sw;
    while (--sh >= 0) {
        for (int x = sw - 1; x >= 0; --x) {
            *dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
            dst += 1;
            src += 1;
            blur += 1;
        }
        dst += dstRB;
        src += srcRB;
        blur += blurRB;
    }
}

static void clamp_with_orig(uint8_t dst[], int dstRowBytes,
                            const uint8_t src[], int srcRowBytes,
                            int sw, int sh,
                            SkBlurMask::Style style)
{
    int x;
    while (--sh >= 0) {
        switch (style) {
        case SkBlurMask::kSolid_Style:
            for (x = sw - 1; x >= 0; --x) {
                int s = *src;
                int d = *dst;
                *dst = SkToU8(s + d - SkMulDiv255Round(s, d));
                dst += 1;
                src += 1;
            }
            break;
        case SkBlurMask::kOuter_Style:
            for (x = sw - 1; x >= 0; --x) {
                if (*src) {
                    *dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
                }
                dst += 1;
                src += 1;
            }
            break;
        default:
            SkASSERT(!"Unexpected blur style here");
            break;
        }
        dst += dstRowBytes - sw;
        src += srcRowBytes - sw;
    }
}

////////////////////////////////////////////////////////////////////////

// we use a local funciton to wrap the class static method to work around
// a bug in gcc98
void SkMask_FreeImage(uint8_t* image);
void SkMask_FreeImage(uint8_t* image)
{
    SkMask::FreeImage(image);
}

bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
                      SkScalar radius, Style style)
{
    if (src.fFormat != SkMask::kA8_Format)
        return false;

    int rx = SkScalarCeil(radius);
    int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - radius) * 255);

    SkASSERT(rx >= 0);
    SkASSERT((unsigned)outer_weight <= 255);
    if (rx <= 0) {
        return false;
    }

    int ry = rx;    // only do square blur for now

    dst->fBounds.set(src.fBounds.fLeft - rx, src.fBounds.fTop - ry,
                        src.fBounds.fRight + rx, src.fBounds.fBottom + ry);
    dst->fRowBytes = dst->fBounds.width();
    dst->fFormat = SkMask::kA8_Format;
    dst->fImage = NULL;

    if (src.fImage) {
        size_t dstSize = dst->computeImageSize();
        if (0 == dstSize) {
            return false;   // too big to allocate, abort
        }

        int             sw = src.fBounds.width();
        int             sh = src.fBounds.height();
        const uint8_t*  sp = src.fImage;
        uint8_t*        dp = SkMask::AllocImage(dstSize);

        SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);

        // build the blurry destination
        {
            SkAutoTMalloc<uint32_t> storage(sw * sh);
            uint32_t*               sumBuffer = storage.get();

            build_sum_buffer(sumBuffer, sw, sh, sp, src.fRowBytes);
            if (outer_weight == 255)
                apply_kernel(dp, rx, ry, sumBuffer, sw, sh);
            else
                apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
        }

        dst->fImage = dp;
        // if need be, alloc the "real" dst (same size as src) and copy/merge
        // the blur into it (applying the src)
        if (style == kInner_Style) {
            // now we allocate the "real" dst, mirror the size of src
            size_t srcSize = src.computeImageSize();
            if (0 == srcSize) {
                return false;   // too big to allocate, abort
            }
            dst->fImage = SkMask::AllocImage(srcSize);
            merge_src_with_blur(dst->fImage, src.fRowBytes,
                                sp, src.fRowBytes,
                                dp + rx + ry*dst->fRowBytes, dst->fRowBytes,
                                sw, sh);
            SkMask::FreeImage(dp);
        } else if (style != kNormal_Style) {
            clamp_with_orig(dp + rx + ry*dst->fRowBytes, dst->fRowBytes,
                            sp, src.fRowBytes, sw, sh,
                            style);
        }
        (void)autoCall.detach();
    }

    if (style == kInner_Style) {
        dst->fBounds = src.fBounds; // restore trimmed bounds
        dst->fRowBytes = src.fRowBytes;
    }

#if 0
    if (gamma && dst->fImage) {
        uint8_t*    image = dst->fImage;
        uint8_t*    stop = image + dst->computeImageSize();

        for (; image < stop; image += 1) {
            *image = gamma[*image];
        }
    }
#endif
    return true;
}

#if 0
void SkBlurMask::BuildSqrtGamma(uint8_t gamma[256], SkScalar percent)
{
    SkASSERT(gamma);
    SkASSERT(percent >= 0 && percent <= SK_Scalar1);

    int scale = SkScalarRound(percent * 256);

    for (int i = 0; i < 256; i++)
    {
        SkFixed n = i * 257;
        n += n >> 15;
        SkASSERT(n >= 0 && n <= SK_Fixed1);
        n = SkFixedSqrt(n);
        n = n * 255 >> 16;
        n = SkAlphaBlend(n, i, scale);
        gamma[i] = SkToU8(n);
    }
}

void SkBlurMask::BuildSqrGamma(uint8_t gamma[256], SkScalar percent)
{
    SkASSERT(gamma);
    SkASSERT(percent >= 0 && percent <= SK_Scalar1);

    int     scale = SkScalarRound(percent * 256);
    SkFixed div255 = SK_Fixed1 / 255;

    for (int i = 0; i < 256; i++)
    {
        int square = i * i;
        int linear = i * 255;
        int n = SkAlphaBlend(square, linear, scale);
        gamma[i] = SkToU8(n * div255 >> 16);
    }
}
#endif
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkConfidenceConnectedImageFilterTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include <fstream>
#include "itkConfidenceConnectedImageFilter.h"
#include "itkImageFileReader.h"
#include "itkPNGImageIOFactory.h"
#include "itkTextOutput.h"
#include "itkImageRegionIterator.h"

int itkConfidenceConnectedImageFilterTest(int ac, char** av)
{
  // Comment the following if you want to use the itk text output window
  itk::OutputWindow::SetInstance(itk::TextOutput::New());

  if(ac < 3)
    {
    std::cerr << "Usage: " << av[0] << " InputImage BaselineImage\n";
    return -1;
    }

  // Register one Factory of PNG readers
  itk::PNGImageIOFactory::RegisterOneFactory();

  typedef unsigned char PixelType;
  typedef itk::Image<PixelType, 2> myImage;
  itk::ImageFileReader<myImage>::Pointer input
    = itk::ImageFileReader<myImage>::New();
  input->SetFileName(av[1]);

  // Create a filter
  typedef itk::ConfidenceConnectedImageFilter<myImage,myImage> FilterType;

  FilterType::Pointer filter = FilterType::New();
  filter->SetInput(input->GetOutput());

  FilterType::IndexType seed; seed[0] = 165; seed[1] = 90;
  //  FilterType::IndexType seed; seed[0] = 56; seed[1] = 90;
  //  FilterType::IndexType seed; seed[0] = 96; seed[1] = 214;
  filter->SetSeed(seed);
  filter->SetMultiplier(2.5);
  filter->SetReplaceValue(255);
  filter->SetNumberOfIterations(10);
  try
    {
    input->Update();
    filter->Update();
    }
  catch (itk::ExceptionObject& e)
    {
    std::cerr << "Exception detected: "  << e.GetDescription();
    return -1;
    }

  // now read the regression image
  itk::ImageFileReader<myImage>::Pointer baseline
    = itk::ImageFileReader<myImage>::New();
    baseline->SetFileName(av[2]);

  try
    {
    baseline->Update();
    }
  catch (itk::ImageFileReaderException& e)
    {
    std::cerr << "Exception in file reader: "  << e.GetDescription() << std::endl;
    return -1;
    }

  // compare the two images
  itk::ImageRegionIterator<myImage> it(filter->GetOutput(),filter->GetOutput()->GetBufferedRegion());
  itk::ImageRegionIterator<myImage> rit(baseline->GetOutput(),baseline->GetOutput()->GetBufferedRegion());
  int status = 0;
  while (!it.IsAtEnd())
    {
    if (it.Get() != rit.Get())
      {
      status++;
      }
    ++it;
    ++rit;
    }
  return status;
}
<commit_msg>ENH: SetInitialNeighborhoodRadius() method exercised.<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkConfidenceConnectedImageFilterTest.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) 2002 Insight Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include <fstream>
#include "itkConfidenceConnectedImageFilter.h"
#include "itkImageFileReader.h"
#include "itkPNGImageIOFactory.h"
#include "itkTextOutput.h"
#include "itkImageRegionIterator.h"

int itkConfidenceConnectedImageFilterTest(int ac, char** av)
{
  // Comment the following if you want to use the itk text output window
  itk::OutputWindow::SetInstance(itk::TextOutput::New());

  if(ac < 3)
    {
    std::cerr << "Usage: " << av[0] << " InputImage BaselineImage\n";
    return -1;
    }

  // Register one Factory of PNG readers
  itk::PNGImageIOFactory::RegisterOneFactory();

  typedef unsigned char PixelType;
  typedef itk::Image<PixelType, 2> myImage;
  itk::ImageFileReader<myImage>::Pointer input
    = itk::ImageFileReader<myImage>::New();
  input->SetFileName(av[1]);

  // Create a filter
  typedef itk::ConfidenceConnectedImageFilter<myImage,myImage> FilterType;

  FilterType::Pointer filter = FilterType::New();
  filter->SetInput(input->GetOutput());
  filter->SetInitialNeighborhoodRadius( 3 ); // measured in pixels

  FilterType::IndexType seed; seed[0] = 165; seed[1] = 90;
  //  FilterType::IndexType seed; seed[0] = 56; seed[1] = 90;
  //  FilterType::IndexType seed; seed[0] = 96; seed[1] = 214;
  filter->SetSeed(seed);
  filter->SetMultiplier(2.5);
  filter->SetReplaceValue(255);
  filter->SetNumberOfIterations(10);
  try
    {
    input->Update();
    filter->Update();
    }
  catch (itk::ExceptionObject& e)
    {
    std::cerr << "Exception detected: "  << e.GetDescription();
    return -1;
    }

  // now read the regression image
  itk::ImageFileReader<myImage>::Pointer baseline
    = itk::ImageFileReader<myImage>::New();
    baseline->SetFileName(av[2]);

  try
    {
    baseline->Update();
    }
  catch (itk::ImageFileReaderException& e)
    {
    std::cerr << "Exception in file reader: "  << e.GetDescription() << std::endl;
    return -1;
    }

  // compare the two images
  itk::ImageRegionIterator<myImage> it(filter->GetOutput(),filter->GetOutput()->GetBufferedRegion());
  itk::ImageRegionIterator<myImage> rit(baseline->GetOutput(),baseline->GetOutput()->GetBufferedRegion());
  int status = 0;
  while (!it.IsAtEnd())
    {
    if (it.Get() != rit.Get())
      {
      status++;
      }
    ++it;
    ++rit;
    }
  return status;
}
<|endoftext|>


<commit_before>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013, 2014
              Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include <Corrade/PluginManager/Manager.h>
#include <Corrade/Utility/Arguments.h>
#include <Corrade/Utility/Directory.h>

#include "Magnum/Text/AbstractFont.h"
#include "Magnum/Text/AbstractFontConverter.h"
#include "Magnum/Text/DistanceFieldGlyphCache.h"
#include "Magnum/Trade/AbstractImageConverter.h"

#ifdef CORRADE_TARGET_APPLE
#include "Magnum/Platform/WindowlessCglApplication.h"
#elif defined(CORRADE_TARGET_UNIX)
#include "Magnum/Platform/WindowlessGlxApplication.h"
#elif defined(CORRADE_TARGET_WINDOWS)
#include "Magnum/Platform/WindowlessWglApplication.h"
#else
#error No windowless application available on this platform
#endif

#include "fontconverterConfigure.h"

namespace Magnum { namespace Text {

class FontConverter: public Platform::WindowlessApplication {
    public:
        explicit FontConverter(const Arguments& arguments);

        int exec() override;

    private:
        Utility::Arguments args;
};

FontConverter::FontConverter(const Arguments& arguments): Platform::WindowlessApplication(arguments, nullptr) {
    args.addArgument("input").setHelp("input", "input font")
        .addArgument("output").setHelp("output", "output filename prefix")
        .addNamedArgument("font").setHelp("font", "font plugin")
        .addNamedArgument("converter").setHelp("converter", "font converter plugin")
        .addOption("plugin-dir", MAGNUM_PLUGINS_DIR).setHelpKey("plugin-dir", "DIR").setHelp("plugin-dir", "base plugin dir")
        .addOption("characters", "abcdefghijklmnopqrstuvwxyz"
                                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                 "0123456789?!:,. ").setHelp("characters", "characters to include in the output")
        .addOption("font-size", "128").setHelpKey("font-size", "N").setHelp("font-size", "TTF font size")
        .addOption("atlas-size", "2048 2048").setHelpKey("atlas-size", "\"X Y\"").setHelp("atlas-size", "glyph atlas size")
        .addOption("output-size", "256 256").setHelpKey("output-size", "\"X Y\"").setHelp("output-size", "output atlas size. If set to zero size, distance field computation will not be used.")
        .addOption("radius", "24").setHelpKey("radius", "N").setHelp("radius", "distance field computation radius")
        .setHelp("Converts font to raster one of given atlas size.")
        .parse(arguments.argc, arguments.argv);

    createContext();
}

int FontConverter::exec() {
    /* Font converter dependencies */
    PluginManager::Manager<Trade::AbstractImageConverter> imageConverterManager(Utility::Directory::join(MAGNUM_PLUGINS_DIR, "imageconverters/"));

    /* Load font */
    PluginManager::Manager<Text::AbstractFont> fontManager(Utility::Directory::join(MAGNUM_PLUGINS_DIR, "fonts/"));
    if(!(fontManager.load(args.value("font")) & PluginManager::LoadState::Loaded))
        std::exit(1);
    std::unique_ptr<Text::AbstractFont> font = fontManager.instance(args.value("font"));

    /* Load font converter */
    PluginManager::Manager<Text::AbstractFontConverter> converterManager(Utility::Directory::join(MAGNUM_PLUGINS_DIR, "fontconverters/"));
    if(!(converterManager.load(args.value("converter")) & PluginManager::LoadState::Loaded))
        std::exit(1);
    std::unique_ptr<Text::AbstractFontConverter> converter = converterManager.instance(args.value("converter"));

    /* Open font */
    if(!font->openFile(args.value("input"), args.value<Float>("font-size"))) {
        Error() << "Cannot open font" << args.value("input");
        std::exit(1);
    }

    /* Create distance field glyph cache if radius is specified */
    std::unique_ptr<Text::GlyphCache> cache;
    if(!args.value<Vector2i>("output-size").isZero()) {
        Debug() << "Populating distance field glyph cache...";

        cache.reset(new Text::DistanceFieldGlyphCache(
            args.value<Vector2i>("atlas-size"),
            args.value<Vector2i>("output-size"),
            args.value<Int>("radius")));

    /* Otherwise use normal cache */
    } else {
        Debug() << "Zero-size distance field output specified, populating normal glyph cache...";

        cache.reset(new Text::GlyphCache(args.value<Vector2i>("atlas-size")));
    }

    /* Fill the cache */
    font->fillGlyphCache(*cache, args.value("characters"));

    Debug() << "Converting font...";

    /* Convert the font */
    if(!converter->exportFontToFile(*font, *cache, args.value("output"), args.value("characters"))) {
        Error() << "Cannot export font to" << args.value("output");
        std::exit(1);
    }

    Debug() << "Done.";

    return 0;
}

}}

MAGNUM_WINDOWLESSAPPLICATION_MAIN(Magnum::Text::FontConverter)
<commit_msg>Text: actually make use of plugin-dir parameter in fontconverter utility.<commit_after>/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013, 2014
              Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#include <Corrade/PluginManager/Manager.h>
#include <Corrade/Utility/Arguments.h>
#include <Corrade/Utility/Directory.h>

#include "Magnum/Text/AbstractFont.h"
#include "Magnum/Text/AbstractFontConverter.h"
#include "Magnum/Text/DistanceFieldGlyphCache.h"
#include "Magnum/Trade/AbstractImageConverter.h"

#ifdef CORRADE_TARGET_APPLE
#include "Magnum/Platform/WindowlessCglApplication.h"
#elif defined(CORRADE_TARGET_UNIX)
#include "Magnum/Platform/WindowlessGlxApplication.h"
#elif defined(CORRADE_TARGET_WINDOWS)
#include "Magnum/Platform/WindowlessWglApplication.h"
#else
#error No windowless application available on this platform
#endif

#include "fontconverterConfigure.h"

namespace Magnum { namespace Text {

class FontConverter: public Platform::WindowlessApplication {
    public:
        explicit FontConverter(const Arguments& arguments);

        int exec() override;

    private:
        Utility::Arguments args;
};

FontConverter::FontConverter(const Arguments& arguments): Platform::WindowlessApplication(arguments, nullptr) {
    args.addArgument("input").setHelp("input", "input font")
        .addArgument("output").setHelp("output", "output filename prefix")
        .addNamedArgument("font").setHelp("font", "font plugin")
        .addNamedArgument("converter").setHelp("converter", "font converter plugin")
        .addOption("plugin-dir", MAGNUM_PLUGINS_DIR).setHelpKey("plugin-dir", "DIR").setHelp("plugin-dir", "base plugin dir")
        .addOption("characters", "abcdefghijklmnopqrstuvwxyz"
                                 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                 "0123456789?!:,. ").setHelp("characters", "characters to include in the output")
        .addOption("font-size", "128").setHelpKey("font-size", "N").setHelp("font-size", "TTF font size")
        .addOption("atlas-size", "2048 2048").setHelpKey("atlas-size", "\"X Y\"").setHelp("atlas-size", "glyph atlas size")
        .addOption("output-size", "256 256").setHelpKey("output-size", "\"X Y\"").setHelp("output-size", "output atlas size. If set to zero size, distance field computation will not be used.")
        .addOption("radius", "24").setHelpKey("radius", "N").setHelp("radius", "distance field computation radius")
        .setHelp("Converts font to raster one of given atlas size.")
        .parse(arguments.argc, arguments.argv);

    createContext();
}

int FontConverter::exec() {
    /* Font converter dependencies */
    PluginManager::Manager<Trade::AbstractImageConverter> imageConverterManager(Utility::Directory::join(args.value("plugin-dir"), "imageconverters/"));

    /* Load font */
    PluginManager::Manager<Text::AbstractFont> fontManager(Utility::Directory::join(args.value("plugin-dir"), "fonts/"));
    if(!(fontManager.load(args.value("font")) & PluginManager::LoadState::Loaded))
        std::exit(1);
    std::unique_ptr<Text::AbstractFont> font = fontManager.instance(args.value("font"));

    /* Load font converter */
    PluginManager::Manager<Text::AbstractFontConverter> converterManager(Utility::Directory::join(args.value("plugin-dir"), "fontconverters/"));
    if(!(converterManager.load(args.value("converter")) & PluginManager::LoadState::Loaded))
        std::exit(1);
    std::unique_ptr<Text::AbstractFontConverter> converter = converterManager.instance(args.value("converter"));

    /* Open font */
    if(!font->openFile(args.value("input"), args.value<Float>("font-size"))) {
        Error() << "Cannot open font" << args.value("input");
        std::exit(1);
    }

    /* Create distance field glyph cache if radius is specified */
    std::unique_ptr<Text::GlyphCache> cache;
    if(!args.value<Vector2i>("output-size").isZero()) {
        Debug() << "Populating distance field glyph cache...";

        cache.reset(new Text::DistanceFieldGlyphCache(
            args.value<Vector2i>("atlas-size"),
            args.value<Vector2i>("output-size"),
            args.value<Int>("radius")));

    /* Otherwise use normal cache */
    } else {
        Debug() << "Zero-size distance field output specified, populating normal glyph cache...";

        cache.reset(new Text::GlyphCache(args.value<Vector2i>("atlas-size")));
    }

    /* Fill the cache */
    font->fillGlyphCache(*cache, args.value("characters"));

    Debug() << "Converting font...";

    /* Convert the font */
    if(!converter->exportFontToFile(*font, *cache, args.value("output"), args.value("characters"))) {
        Error() << "Cannot export font to" << args.value("output");
        std::exit(1);
    }

    Debug() << "Done.";

    return 0;
}

}}

MAGNUM_WINDOWLESSAPPLICATION_MAIN(Magnum::Text::FontConverter)
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qwaylandglcontext.h"

#include "qwaylanddisplay.h"
#include "qwaylandwindow.h"
#include "qwaylandeglwindow.h"
#include "qwaylanddecoration.h"

#include <QDebug>
#include <QtPlatformSupport/private/qeglconvenience_p.h>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtGui/private/qopengltexturecache_p.h>

#include <qpa/qplatformopenglcontext.h>
#include <QtGui/QSurfaceFormat>
#include <QtGui/QOpenGLShaderProgram>

QT_USE_NAMESPACE

QWaylandGLContext::QWaylandGLContext(EGLDisplay eglDisplay, const QSurfaceFormat &format, QPlatformOpenGLContext *share)
    : QPlatformOpenGLContext()
    , m_eglDisplay(eglDisplay)
    , m_config(q_configFromGLFormat(m_eglDisplay, format, true))
    , m_format(q_glFormatFromConfig(m_eglDisplay, m_config))
    , m_blitProgram(0)
    , m_textureCache(0)
    , m_currentOnSurface(0)
{
    m_shareEGLContext = share ? static_cast<QWaylandGLContext *>(share)->eglContext() : EGL_NO_CONTEXT;

    eglBindAPI(EGL_OPENGL_ES_API);

    QVector<EGLint> eglContextAttrs;
    eglContextAttrs.append(EGL_CONTEXT_CLIENT_VERSION);
    eglContextAttrs.append(format.majorVersion() == 1 ? 1 : 2);
    eglContextAttrs.append(EGL_NONE);

    m_context = eglCreateContext(m_eglDisplay, m_config, m_shareEGLContext, eglContextAttrs.constData());

    if (m_context == EGL_NO_CONTEXT) {
        m_context = eglCreateContext(m_eglDisplay, m_config, EGL_NO_CONTEXT, eglContextAttrs.constData());
        m_shareEGLContext = EGL_NO_CONTEXT;
    }
}

QWaylandGLContext::~QWaylandGLContext()
{
    delete m_blitProgram;
    delete m_textureCache;
    eglDestroyContext(m_eglDisplay, m_context);
}

bool QWaylandGLContext::makeCurrent(QPlatformSurface *surface)
{
    QWaylandEglWindow *window = static_cast<QWaylandEglWindow *>(surface);
    if (m_currentOnSurface != window) {
        if (m_currentOnSurface) {
            QWaylandWindow *oldWindow = m_currentOnSurface;
            m_currentOnSurface = 0;
            oldWindow->resizeMutex()->unlock();
        }
        window->resizeMutex()->lock();
        m_currentOnSurface = window;
    }

    EGLSurface eglSurface = window->eglSurface();
    if (!eglMakeCurrent(m_eglDisplay, eglSurface, eglSurface, m_context)) {
        qWarning("QEGLPlatformContext::makeCurrent: eglError: %x, this: %p \n", eglGetError(), this);
        m_currentOnSurface->resizeMutex()->unlock();
        return false;
    }

    window->bindContentFBO();

    return true;
}

void QWaylandGLContext::doneCurrent()
{
    eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    if (m_currentOnSurface) {
        QWaylandWindow *window = m_currentOnSurface;
        m_currentOnSurface = 0;
        window->resizeMutex()->unlock();
    }
}

void QWaylandGLContext::swapBuffers(QPlatformSurface *surface)
{
    QWaylandEglWindow *window = static_cast<QWaylandEglWindow *>(surface);

    EGLSurface eglSurface = window->eglSurface();

    if (window->decoration()) {
        makeCurrent(surface);
        if (!m_blitProgram) {
            m_blitProgram = new QOpenGLShaderProgram();
            m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec4 position;\n\
                                                                        attribute vec4 texCoords;\n\
                                                                        varying vec2 outTexCoords;\n\
                                                                        void main()\n\
                                                                        {\n\
                                                                            gl_Position = position;\n\
                                                                            outTexCoords = texCoords.xy;\n\
                                                                        }");
            m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, "varying vec2 outTexCoords;\n\
                                                                            uniform sampler2D texture;\n\
                                                                            void main()\n\
                                                                            {\n\
                                                                                gl_FragColor = texture2D(texture, outTexCoords);\n\
                                                                            }");

            if (!m_blitProgram->link()) {
                qDebug() << "Shader Program link failed.";
                qDebug() << m_blitProgram->log();
            }
        }

        if (!m_textureCache) {
            m_textureCache = new QOpenGLTextureCache(this->context());
        }

        glDisable(GL_DEPTH_TEST);
        glDisable(GL_BLEND);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        static const GLfloat squareVertices[] = {
            -1.f, -1.f,
            1.0f, -1.f,
            -1.f,  1.0f,
            1.0f,  1.0f
        };

        static const GLfloat inverseSquareVertices[] = {
            -1.f, 1.f,
            1.f, 1.f,
            -1.f, -1.f,
            1.f, -1.f
        };

        static const GLfloat textureVertices[] = {
            0.0f,  0.0f,
            1.0f,  0.0f,
            0.0f,  1.0f,
            1.0f,  1.0f,
        };

        m_blitProgram->setUniformValue("texture", 0);

        m_blitProgram->enableAttributeArray("position");
        m_blitProgram->enableAttributeArray("texCoords");
        m_blitProgram->setAttributeArray("texCoords", textureVertices, 2);

        m_blitProgram->bind();
        glActiveTexture(GL_TEXTURE0);

        //Draw Decoration
        m_blitProgram->setAttributeArray("position", inverseSquareVertices, 2);
        QImage decorationImage = window->decoration()->contentImage();
        m_textureCache->bindTexture(context(), decorationImage);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        QRect windowRect = window->window()->frameGeometry();
        glViewport(0, 0, windowRect.width(), windowRect.height());
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        //Draw Content
        m_blitProgram->setAttributeArray("position", squareVertices, 2);
        glBindTexture(GL_TEXTURE_2D, window->contentTexture());
        QRect r = window->contentsRect();
        glViewport(r.x(), r.y(), r.width(), r.height());
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        //Cleanup
        m_blitProgram->disableAttributeArray("position");
        m_blitProgram->disableAttributeArray("texCoords");
        m_blitProgram->release();
    }

    eglSwapBuffers(m_eglDisplay, eglSurface);
    if (m_currentOnSurface == window) {
        m_currentOnSurface = 0;
        window->resizeMutex()->unlock();
    }

}

GLuint QWaylandGLContext::defaultFramebufferObject(QPlatformSurface *surface) const
{
    return static_cast<QWaylandEglWindow *>(surface)->contentFBO();
}

bool QWaylandGLContext::isSharing() const
{
    return m_shareEGLContext != EGL_NO_CONTEXT;
}

bool QWaylandGLContext::isValid() const
{
    return m_context != EGL_NO_CONTEXT;
}

void (*QWaylandGLContext::getProcAddress(const QByteArray &procName)) ()
{
    return eglGetProcAddress(procName.constData());
}

EGLConfig QWaylandGLContext::eglConfig() const
{
    return m_config;
}

<commit_msg>Fix shader precision qualifier<commit_after>/****************************************************************************
**
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qwaylandglcontext.h"

#include "qwaylanddisplay.h"
#include "qwaylandwindow.h"
#include "qwaylandeglwindow.h"
#include "qwaylanddecoration.h"

#include <QDebug>
#include <QtPlatformSupport/private/qeglconvenience_p.h>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtGui/private/qopengltexturecache_p.h>

#include <qpa/qplatformopenglcontext.h>
#include <QtGui/QSurfaceFormat>
#include <QtGui/QOpenGLShaderProgram>

QT_USE_NAMESPACE

QWaylandGLContext::QWaylandGLContext(EGLDisplay eglDisplay, const QSurfaceFormat &format, QPlatformOpenGLContext *share)
    : QPlatformOpenGLContext()
    , m_eglDisplay(eglDisplay)
    , m_config(q_configFromGLFormat(m_eglDisplay, format, true))
    , m_format(q_glFormatFromConfig(m_eglDisplay, m_config))
    , m_blitProgram(0)
    , m_textureCache(0)
    , m_currentOnSurface(0)
{
    m_shareEGLContext = share ? static_cast<QWaylandGLContext *>(share)->eglContext() : EGL_NO_CONTEXT;

    eglBindAPI(EGL_OPENGL_ES_API);

    QVector<EGLint> eglContextAttrs;
    eglContextAttrs.append(EGL_CONTEXT_CLIENT_VERSION);
    eglContextAttrs.append(format.majorVersion() == 1 ? 1 : 2);
    eglContextAttrs.append(EGL_NONE);

    m_context = eglCreateContext(m_eglDisplay, m_config, m_shareEGLContext, eglContextAttrs.constData());

    if (m_context == EGL_NO_CONTEXT) {
        m_context = eglCreateContext(m_eglDisplay, m_config, EGL_NO_CONTEXT, eglContextAttrs.constData());
        m_shareEGLContext = EGL_NO_CONTEXT;
    }
}

QWaylandGLContext::~QWaylandGLContext()
{
    delete m_blitProgram;
    delete m_textureCache;
    eglDestroyContext(m_eglDisplay, m_context);
}

bool QWaylandGLContext::makeCurrent(QPlatformSurface *surface)
{
    QWaylandEglWindow *window = static_cast<QWaylandEglWindow *>(surface);
    if (m_currentOnSurface != window) {
        if (m_currentOnSurface) {
            QWaylandWindow *oldWindow = m_currentOnSurface;
            m_currentOnSurface = 0;
            oldWindow->resizeMutex()->unlock();
        }
        window->resizeMutex()->lock();
        m_currentOnSurface = window;
    }

    EGLSurface eglSurface = window->eglSurface();
    if (!eglMakeCurrent(m_eglDisplay, eglSurface, eglSurface, m_context)) {
        qWarning("QEGLPlatformContext::makeCurrent: eglError: %x, this: %p \n", eglGetError(), this);
        m_currentOnSurface->resizeMutex()->unlock();
        return false;
    }

    window->bindContentFBO();

    return true;
}

void QWaylandGLContext::doneCurrent()
{
    eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    if (m_currentOnSurface) {
        QWaylandWindow *window = m_currentOnSurface;
        m_currentOnSurface = 0;
        window->resizeMutex()->unlock();
    }
}

void QWaylandGLContext::swapBuffers(QPlatformSurface *surface)
{
    QWaylandEglWindow *window = static_cast<QWaylandEglWindow *>(surface);

    EGLSurface eglSurface = window->eglSurface();

    if (window->decoration()) {
        makeCurrent(surface);
        if (!m_blitProgram) {
            m_blitProgram = new QOpenGLShaderProgram();
            m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec4 position;\n\
                                                                        attribute vec4 texCoords;\n\
                                                                        varying vec2 outTexCoords;\n\
                                                                        void main()\n\
                                                                        {\n\
                                                                            gl_Position = position;\n\
                                                                            outTexCoords = texCoords.xy;\n\
                                                                        }");
            m_blitProgram->addShaderFromSourceCode(QOpenGLShader::Fragment, "varying highp vec2 outTexCoords;\n\
                                                                            uniform sampler2D texture;\n\
                                                                            void main()\n\
                                                                            {\n\
                                                                                gl_FragColor = texture2D(texture, outTexCoords);\n\
                                                                            }");

            if (!m_blitProgram->link()) {
                qDebug() << "Shader Program link failed.";
                qDebug() << m_blitProgram->log();
            }
        }

        if (!m_textureCache) {
            m_textureCache = new QOpenGLTextureCache(this->context());
        }

        glDisable(GL_DEPTH_TEST);
        glDisable(GL_BLEND);
        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        static const GLfloat squareVertices[] = {
            -1.f, -1.f,
            1.0f, -1.f,
            -1.f,  1.0f,
            1.0f,  1.0f
        };

        static const GLfloat inverseSquareVertices[] = {
            -1.f, 1.f,
            1.f, 1.f,
            -1.f, -1.f,
            1.f, -1.f
        };

        static const GLfloat textureVertices[] = {
            0.0f,  0.0f,
            1.0f,  0.0f,
            0.0f,  1.0f,
            1.0f,  1.0f,
        };

        m_blitProgram->setUniformValue("texture", 0);

        m_blitProgram->enableAttributeArray("position");
        m_blitProgram->enableAttributeArray("texCoords");
        m_blitProgram->setAttributeArray("texCoords", textureVertices, 2);

        m_blitProgram->bind();
        glActiveTexture(GL_TEXTURE0);

        //Draw Decoration
        m_blitProgram->setAttributeArray("position", inverseSquareVertices, 2);
        QImage decorationImage = window->decoration()->contentImage();
        m_textureCache->bindTexture(context(), decorationImage);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        QRect windowRect = window->window()->frameGeometry();
        glViewport(0, 0, windowRect.width(), windowRect.height());
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        //Draw Content
        m_blitProgram->setAttributeArray("position", squareVertices, 2);
        glBindTexture(GL_TEXTURE_2D, window->contentTexture());
        QRect r = window->contentsRect();
        glViewport(r.x(), r.y(), r.width(), r.height());
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        //Cleanup
        m_blitProgram->disableAttributeArray("position");
        m_blitProgram->disableAttributeArray("texCoords");
        m_blitProgram->release();
    }

    eglSwapBuffers(m_eglDisplay, eglSurface);
    if (m_currentOnSurface == window) {
        m_currentOnSurface = 0;
        window->resizeMutex()->unlock();
    }

}

GLuint QWaylandGLContext::defaultFramebufferObject(QPlatformSurface *surface) const
{
    return static_cast<QWaylandEglWindow *>(surface)->contentFBO();
}

bool QWaylandGLContext::isSharing() const
{
    return m_shareEGLContext != EGL_NO_CONTEXT;
}

bool QWaylandGLContext::isValid() const
{
    return m_context != EGL_NO_CONTEXT;
}

void (*QWaylandGLContext::getProcAddress(const QByteArray &procName)) ()
{
    return eglGetProcAddress(procName.constData());
}

EGLConfig QWaylandGLContext::eglConfig() const
{
    return m_config;
}

<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (c) 2013 Luke Parry <l.parry@warwick.ac.uk>                 *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   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; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
#include <assert.h>
#include <QGraphicsScene>
#include <QMenu>
#include <QMouseEvent>
#include <QGraphicsSceneHoverEvent>
#include <QStyleOptionGraphicsItem>
#include <QPainterPathStroker>
#include <QPainter>
#endif

#include <App/Application.h>
#include <Base/Parameter.h>
#include <Base/Console.h>

#include "Rez.h"
#include "QGIArrow.h"

using namespace TechDrawGui;

QGIArrow::QGIArrow() :
    m_fill(Qt::SolidPattern),
    m_size(5.0),
    m_style(0),
    m_dirMode(false),
    m_dir(Base::Vector3d(1.0,0.0,0.0))
{
    isFlipped = false;
    m_brush.setStyle(m_fill);

    setCacheMode(QGraphicsItem::NoCache);
    setAcceptHoverEvents(false);
    setFlag(QGraphicsItem::ItemIsSelectable, false);
    setFlag(QGraphicsItem::ItemIsMovable, false);
}

void QGIArrow::flip(bool state) {
    isFlipped = state;
}

void QGIArrow::draw() {
    QPainterPath path;
    if (m_style == 0) {
        if (m_dirMode) {
            path = makeFilledTriangle(getDirection(), m_size,m_size/6.0);
        } else {
            path = makeFilledTriangle(m_size,m_size/6.0,isFlipped);     //"arrow l/w sb 3/1" ??
        }
    } else if (m_style == 1) {
        if (m_dirMode) {
            path = makeOpenArrow(getDirection(), m_size,m_size/3.0);          //broad arrow?
        } else {
            path = makeOpenArrow(m_size,m_size/3.0,isFlipped);
        }
    } else if (m_style == 2) {
        if (m_dirMode) {
            path = makeHashMark(getDirection(), m_size/2.0,m_size/2.0);       //big enough?
        } else {
            path = makeHashMark(m_size/2.0,m_size/2.0,isFlipped);       //big enough?
        }
    } else if (m_style == 3) {
        path = makeDot(m_size/2.0,m_size/2.0,isFlipped);
    } else if (m_style == 4) {
        path = makeOpenDot(m_size/2.0,m_size/2.0,isFlipped);
    } else if (m_style == 5) {
        if (m_dirMode) {
            path = makeForkArrow(getDirection(), m_size/2.0,m_size/2.0);       //big enough?
        } else {
            path = makeForkArrow(m_size/2.0,m_size/2.0,isFlipped);       //big enough?
        }
    } else {
        path = makeFilledTriangle(m_size,m_size/6.0,isFlipped);     //sb a question mark or ???
    }
    setPath(path);
}

void QGIArrow::setSize(double s)
{
    m_size = s;
}

QPainterPath QGIArrow::makeFilledTriangle(double length, double width, bool flipped)
{
//(0,0) is tip of arrow
    if (!flipped) {
        length *= -1;
    }

    QPainterPath path;
    path.moveTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(length),Rez::guiX(-width)));
    path.lineTo(QPointF(Rez::guiX(length),Rez::guiX(width)));
    path.closeSubpath();
    m_fill = Qt::SolidPattern;
    return path;
}

QPainterPath QGIArrow::makeFilledTriangle(Base::Vector3d dir, double length, double width)
{
//(0,0) is tip of arrow
// dir is direction arrow points
    Base::Vector3d negDir = -dir;
    negDir.Normalize();
    Base::Vector3d perp(-negDir.y,negDir.x, 0.0);
    Base::Vector3d barb1 = negDir * length + perp * width;
    Base::Vector3d barb2 = negDir * length - perp * width;

    QPainterPath path;
    path.moveTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(barb1.x),Rez::guiX(barb1.y)));
    path.lineTo(QPointF(Rez::guiX(barb2.x),Rez::guiX(barb2.y)));
    path.closeSubpath();
    m_fill = Qt::SolidPattern;
    return path;
}

QPainterPath QGIArrow::makeOpenArrow(double length, double width, bool flipped)
{
//(0,0) is tip of arrow
    if (!flipped) {
        length *= -1;
    }

    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(length),Rez::guiX(-width)));
    path.lineTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(length),Rez::guiX(width)));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeOpenArrow(Base::Vector3d dir, double length, double width)
{
//(0,0) is tip of arrow
    Base::Vector3d negDir = -dir;
    negDir.Normalize();
    Base::Vector3d perp(-negDir.y,negDir.x, 0.0);
    Base::Vector3d barb1 = negDir * length + perp * width;
    Base::Vector3d barb2 = negDir * length - perp * width;

    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(barb1.x),Rez::guiX(barb1.y)));
    path.lineTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(barb2.x),Rez::guiX(barb2.y)));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeHashMark(double length, double width, bool flipped)   //Arch tick
{
    double adjWidth = 1.0;
//(0,0) is tip of arrow
    if (!flipped) {
        length *= -1;
        adjWidth *= -1;
    }
    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(length),Rez::guiX(adjWidth * (-width))));
    path.lineTo(QPointF(Rez::guiX(-length),Rez::guiX(adjWidth * width)));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeHashMark(Base::Vector3d dir, double length, double width)   //Arch tick
{
    double adjWidth = 1.0;
    Base::Vector3d negDir = -dir;
    negDir.Normalize();
    Base::Vector3d perp(-negDir.y,negDir.x, 0.0);
    Base::Vector3d barb1 = negDir * length - perp * (adjWidth * width);
    Base::Vector3d barb2 = dir * length + perp * (adjWidth * width);

    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(barb1.x),Rez::guiX(barb1.y)));
    path.lineTo(QPointF(Rez::guiX(barb2.x),Rez::guiX(barb2.y)));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeDot(double length, double width, bool flipped)   //closed dot
{
    Q_UNUSED(flipped);
    QPainterPath path;
    path.moveTo(0.0,0.0);                                  ////(0,0) is Center of dot
    path.addEllipse(Rez::guiX(-length/2.0), Rez::guiX(-width/2.0), Rez::guiX(length), Rez::guiX(width));
    m_fill = Qt::SolidPattern;
    return path;
}

QPainterPath QGIArrow::makeOpenDot(double length, double width, bool flipped)
{
    Q_UNUSED(flipped);
    QPainterPath path;
    path.moveTo(0.0,0.0);                                  ////(0,0) is Center of dot
    path.addEllipse(Rez::guiX(-length/2.0), Rez::guiX(-width/2.0), Rez::guiX(length), Rez::guiX(width));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeForkArrow(double length, double width, bool flipped)
{
//(0,0) is tip of arrow
    if (flipped) {
        length *= -1;
    }

    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(length),Rez::guiX(-width)));
    path.lineTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(length),Rez::guiX(width)));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeForkArrow(Base::Vector3d dir, double length, double width)
{
//(0,0) is tip of arrow
    Base::Vector3d negDir = -dir;
    Base::Vector3d normDir = dir;
    negDir.Normalize();
    normDir.Normalize();
    Base::Vector3d perp(-normDir.y,normDir.x, 0.0);
    Base::Vector3d barb1 = normDir * length + perp * width;
    Base::Vector3d barb2 = normDir * length - perp * width;

    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(barb1.x),Rez::guiX(barb1.y)));
    path.lineTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(barb2.x),Rez::guiX(barb2.y)));
    m_fill = Qt::NoBrush;
    return path;
}

int QGIArrow::getPrefArrowStyle()
{
    Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter().
                                         GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions");
    int style = hGrp->GetInt("ArrowStyle", 0);
    return style;
}

double QGIArrow::getPrefArrowSize()
{
    Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter().
                                         GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions");
    double style = hGrp->GetFloat("ArrowSize", 3.5);
    return style;
}

void QGIArrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QStyleOptionGraphicsItem myOption(*option);
    myOption.state &= ~QStyle::State_Selected;

    setPen(m_pen);
    m_brush.setColor(m_colCurrent);
    m_brush.setStyle(m_fill);
    setBrush(m_brush);
    QGIPrimPath::paint (painter, &myOption, widget);
}
<commit_msg>Fix "tick" arrow style in directional mode<commit_after>/***************************************************************************
 *   Copyright (c) 2013 Luke Parry <l.parry@warwick.ac.uk>                 *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   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; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/

#include "PreCompiled.h"
#ifndef _PreComp_
#include <assert.h>
#include <QGraphicsScene>
#include <QMenu>
#include <QMouseEvent>
#include <QGraphicsSceneHoverEvent>
#include <QStyleOptionGraphicsItem>
#include <QPainterPathStroker>
#include <QPainter>
#endif

#include <App/Application.h>
#include <Base/Parameter.h>
#include <Base/Console.h>

#include "Rez.h"
#include "QGIArrow.h"

using namespace TechDrawGui;

QGIArrow::QGIArrow() :
    m_fill(Qt::SolidPattern),
    m_size(5.0),
    m_style(0),
    m_dirMode(false),
    m_dir(Base::Vector3d(1.0,0.0,0.0))
{
    isFlipped = false;
    m_brush.setStyle(m_fill);

    setCacheMode(QGraphicsItem::NoCache);
    setAcceptHoverEvents(false);
    setFlag(QGraphicsItem::ItemIsSelectable, false);
    setFlag(QGraphicsItem::ItemIsMovable, false);
}

void QGIArrow::flip(bool state) {
    isFlipped = state;
}

void QGIArrow::draw() {
    QPainterPath path;
    if (m_style == 0) {
        if (m_dirMode) {
            path = makeFilledTriangle(getDirection(), m_size,m_size/6.0);
        } else {
            path = makeFilledTriangle(m_size,m_size/6.0,isFlipped);     //"arrow l/w sb 3/1" ??
        }
    } else if (m_style == 1) {
        if (m_dirMode) {
            path = makeOpenArrow(getDirection(), m_size,m_size/3.0);          //broad arrow?
        } else {
            path = makeOpenArrow(m_size,m_size/3.0,isFlipped);
        }
    } else if (m_style == 2) {
        if (m_dirMode) {
            path = makeHashMark(getDirection(), m_size/2.0,m_size/2.0);       //big enough?
        } else {
            path = makeHashMark(m_size/2.0,m_size/2.0,isFlipped);       //big enough?
        }
    } else if (m_style == 3) {
        path = makeDot(m_size/2.0,m_size/2.0,isFlipped);
    } else if (m_style == 4) {
        path = makeOpenDot(m_size/2.0,m_size/2.0,isFlipped);
    } else if (m_style == 5) {
        if (m_dirMode) {
            path = makeForkArrow(getDirection(), m_size/2.0,m_size/2.0);       //big enough?
        } else {
            path = makeForkArrow(m_size/2.0,m_size/2.0,isFlipped);       //big enough?
        }
    } else {
        path = makeFilledTriangle(m_size,m_size/6.0,isFlipped);     //sb a question mark or ???
    }
    setPath(path);
}

void QGIArrow::setSize(double s)
{
    m_size = s;
}

QPainterPath QGIArrow::makeFilledTriangle(double length, double width, bool flipped)
{
//(0,0) is tip of arrow
    if (!flipped) {
        length *= -1;
    }

    QPainterPath path;
    path.moveTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(length),Rez::guiX(-width)));
    path.lineTo(QPointF(Rez::guiX(length),Rez::guiX(width)));
    path.closeSubpath();
    m_fill = Qt::SolidPattern;
    return path;
}

QPainterPath QGIArrow::makeFilledTriangle(Base::Vector3d dir, double length, double width)
{
//(0,0) is tip of arrow
// dir is direction arrow points
    Base::Vector3d negDir = -dir;
    negDir.Normalize();
    Base::Vector3d perp(-negDir.y,negDir.x, 0.0);
    Base::Vector3d barb1 = negDir * length + perp * width;
    Base::Vector3d barb2 = negDir * length - perp * width;

    QPainterPath path;
    path.moveTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(barb1.x),Rez::guiX(barb1.y)));
    path.lineTo(QPointF(Rez::guiX(barb2.x),Rez::guiX(barb2.y)));
    path.closeSubpath();
    m_fill = Qt::SolidPattern;
    return path;
}

QPainterPath QGIArrow::makeOpenArrow(double length, double width, bool flipped)
{
//(0,0) is tip of arrow
    if (!flipped) {
        length *= -1;
    }

    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(length),Rez::guiX(-width)));
    path.lineTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(length),Rez::guiX(width)));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeOpenArrow(Base::Vector3d dir, double length, double width)
{
//(0,0) is tip of arrow
    Base::Vector3d negDir = -dir;
    negDir.Normalize();
    Base::Vector3d perp(-negDir.y,negDir.x, 0.0);
    Base::Vector3d barb1 = negDir * length + perp * width;
    Base::Vector3d barb2 = negDir * length - perp * width;

    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(barb1.x),Rez::guiX(barb1.y)));
    path.lineTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(barb2.x),Rez::guiX(barb2.y)));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeHashMark(double length, double width, bool flipped)   //Arch tick
{
    double adjWidth = 1.0;
//(0,0) is tip of arrow
    if (!flipped) {
        length *= -1;
        adjWidth *= -1;
    }
    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(length),Rez::guiX(adjWidth * (-width))));
    path.lineTo(QPointF(Rez::guiX(-length),Rez::guiX(adjWidth * width)));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeHashMark(Base::Vector3d dir, double length, double width)   //Arch tick
{
    double adjWidth = 1.0;
    Base::Vector3d negDir = -dir;
    Base::Vector3d normDir = dir;
    negDir.Normalize();
    normDir.Normalize();
    Base::Vector3d perp(-negDir.y,negDir.x, 0.0);
    Base::Vector3d barb1 = negDir * length - perp * (adjWidth * width);
    Base::Vector3d barb2 = normDir * length + perp * (adjWidth * width);

    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(barb1.x),Rez::guiX(barb1.y)));
    path.lineTo(QPointF(Rez::guiX(barb2.x),Rez::guiX(barb2.y)));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeDot(double length, double width, bool flipped)   //closed dot
{
    Q_UNUSED(flipped);
    QPainterPath path;
    path.moveTo(0.0,0.0);                                  ////(0,0) is Center of dot
    path.addEllipse(Rez::guiX(-length/2.0), Rez::guiX(-width/2.0), Rez::guiX(length), Rez::guiX(width));
    m_fill = Qt::SolidPattern;
    return path;
}

QPainterPath QGIArrow::makeOpenDot(double length, double width, bool flipped)
{
    Q_UNUSED(flipped);
    QPainterPath path;
    path.moveTo(0.0,0.0);                                  ////(0,0) is Center of dot
    path.addEllipse(Rez::guiX(-length/2.0), Rez::guiX(-width/2.0), Rez::guiX(length), Rez::guiX(width));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeForkArrow(double length, double width, bool flipped)
{
//(0,0) is tip of arrow
    if (flipped) {
        length *= -1;
    }

    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(length),Rez::guiX(-width)));
    path.lineTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(length),Rez::guiX(width)));
    m_fill = Qt::NoBrush;
    return path;
}

QPainterPath QGIArrow::makeForkArrow(Base::Vector3d dir, double length, double width)
{
//(0,0) is tip of arrow
    Base::Vector3d negDir = -dir;
    Base::Vector3d normDir = dir;
    negDir.Normalize();
    normDir.Normalize();
    Base::Vector3d perp(-normDir.y,normDir.x, 0.0);
    Base::Vector3d barb1 = normDir * length + perp * width;
    Base::Vector3d barb2 = normDir * length - perp * width;

    QPainterPath path;
    path.moveTo(QPointF(Rez::guiX(barb1.x),Rez::guiX(barb1.y)));
    path.lineTo(QPointF(0.,0.));
    path.lineTo(QPointF(Rez::guiX(barb2.x),Rez::guiX(barb2.y)));
    m_fill = Qt::NoBrush;
    return path;
}

int QGIArrow::getPrefArrowStyle()
{
    Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter().
                                         GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions");
    int style = hGrp->GetInt("ArrowStyle", 0);
    return style;
}

double QGIArrow::getPrefArrowSize()
{
    Base::Reference<ParameterGrp> hGrp = App::GetApplication().GetUserParameter().
                                         GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/Dimensions");
    double style = hGrp->GetFloat("ArrowSize", 3.5);
    return style;
}

void QGIArrow::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QStyleOptionGraphicsItem myOption(*option);
    myOption.state &= ~QStyle::State_Selected;

    setPen(m_pen);
    m_brush.setColor(m_colCurrent);
    m_brush.setStyle(m_fill);
    setBrush(m_brush);
    QGIPrimPath::paint (painter, &myOption, widget);
}
<|endoftext|>