<commit_before>/**
 * @file
 * @brief Implementation of [LCIOWriter] module
 * @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
 * This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
 * In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

#include "LCIOWriterModule.hpp"

#include <fstream>
#include <string>
#include <utility>
#include <vector>

#include "core/messenger/Messenger.hpp"

#include "core/utils/log.h"

#include <IMPL/LCCollectionVec.h>
#include <IMPL/LCEventImpl.h>
#include <IMPL/LCRunHeaderImpl.h>
#include <IMPL/TrackerDataImpl.h>
#include <IO/LCWriter.h>
#include <IOIMPL/LCFactory.h>
#include <UTIL/CellIDEncoder.h>
#include <lcio.h>

using namespace allpix;
using namespace lcio;

LCIOWriterModule::LCIOWriterModule(Configuration config, Messenger* messenger, GeometryManager* geo)
    : Module(std::move(config)), geo_mgr_(geo) {

    // Bind pixel hits message
    messenger->bindMulti(this, &LCIOWriterModule::pixel_messages_, MsgFlags::REQUIRED);

    // get all detector names and assign id.
    auto detectors = geo_mgr_->getDetectors();
    unsigned int i = 0;
    for(const auto& det : detectors) {
        detectorIDs_[det->getName()] = i;
        LOG(DEBUG) << det->getName() << " has ID " << detectorIDs_[det->getName()];
        i++;
    }

    // Set configuration defaults:
    config_.setDefault("file_name", "output.slcio");
    config_.setDefault("geometry_file", "allpix_squared_gear.xml");
    config_.setDefault("pixel_type", 2);
    config_.setDefault("detector_name", "EUTelescope");
    config_.setDefault("output_collection_name", "zsdata_m26");

    pixelType_ = config_.get<int>("pixel_type");
    DetectorName_ = config_.get<std::string>("detector_name");
    OutputCollectionName_ = config_.get<std::string>("output_collection_name");
}

void LCIOWriterModule::init() {
    // Create the output GEAR file for the detector geometry
    geometry_file_name_ = getOutputPath(config_.get<std::string>("geometry_file"), true);

    // Open LCIO file and write run header
    lcio_file_name_ = getOutputPath(config_.get<std::string>("file_name"));
    lcWriter_ = LCFactory::getInstance()->createLCWriter();
    lcWriter_->open(lcio_file_name_, LCIO::WRITE_NEW);
    auto run = std::make_unique<LCRunHeaderImpl>();
    run->setRunNumber(1);
    run->setDetectorName(DetectorName_);
    lcWriter_->writeRunHeader(run.get());
}

void LCIOWriterModule::run(unsigned int eventNb) {

    auto evt = std::make_unique<LCEventImpl>(); // create the event
    evt->setRunNumber(1);
    evt->setEventNumber(static_cast<int>(eventNb)); // set the event attributes
    evt->parameters().setValue("EventType", 2);

    // Prepare charge vectors
    std::vector<std::vector<float>> charges;
    for(unsigned int i = 0; i < detectorIDs_.size(); i++) {
        std::vector<float> charge;
        charges.push_back(charge);
    }

    // Receive all pixel messages, fill charge vectors
    for(const auto& hit_msg : pixel_messages_) {
        LOG(DEBUG) << hit_msg->getDetector()->getName();
        for(const auto& hitdata : hit_msg->getData()) {
            LOG(DEBUG) << "X: " << hitdata.getPixel().getIndex().x() << ", Y:" << hitdata.getPixel().getIndex().y()
                       << ", Signal: " << hitdata.getSignal();

            unsigned int detectorID = detectorIDs_[hit_msg->getDetector()->getName()];

            switch(pixelType_) {
            case 1: // EUTelSimpleSparsePixel
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().x())); // x
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().y())); // y
                charges[detectorID].push_back(static_cast<float>(hitdata.getSignal()));               // signal
                break;
            case 2:  // EUTelGenericSparsePixel
            default: // EUTelGenericSparsePixel is default
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().x())); // x
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().y())); // y
                charges[detectorID].push_back(static_cast<float>(hitdata.getSignal()));               // signal
                charges[detectorID].push_back(static_cast<float>(0));                                 // time
                break;
            case 5: // EUTelTimepix3SparsePixel
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().x())); // x
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().y())); // y
                charges[detectorID].push_back(static_cast<float>(hitdata.getSignal()));               // signal
                charges[detectorID].push_back(static_cast<float>(0));                                 // time
                charges[detectorID].push_back(static_cast<float>(0));                                 // time
                charges[detectorID].push_back(static_cast<float>(0));                                 // time
                charges[detectorID].push_back(static_cast<float>(0));                                 // time
                break;
            }
        }
    }

    // Prepare hitvector
    LCCollectionVec* hitVec = new LCCollectionVec(LCIO::TRACKERDATA);

    // Fill hitvector with event data
    for(unsigned int detectorID = 0; detectorID < detectorIDs_.size(); detectorID++) {
        auto hit = new TrackerDataImpl();
        CellIDEncoder<TrackerDataImpl> sparseDataEncoder("sensorID:7,sparsePixelType:5", hitVec);
        sparseDataEncoder["sensorID"] = detectorID;
        sparseDataEncoder["sparsePixelType"] = pixelType_;
        sparseDataEncoder.setCellID(hit);
        hit->setChargeValues(charges[detectorID]);
        hitVec->push_back(hit);
    }

    // Add collection to event and write event to LCIO file
    evt->addCollection(hitVec, OutputCollectionName_); // add the collection with a name
    lcWriter_->writeEvent(evt.get());                  // write the event to the file
    write_cnt_++;
}

void LCIOWriterModule::finalize() {
    lcWriter_->close();
    // Print statistics
    LOG(STATUS) << "Wrote " << write_cnt_ << " events to file:" << std::endl << lcio_file_name_;

    // Write geometry:
    std::ofstream geometry_file;
    if(!geometry_file_name_.empty()) {
        geometry_file.open(geometry_file_name_, std::ios_base::out | std::ios_base::trunc);
        if(!geometry_file.good()) {
            throw ModuleError("Cannot write to GEAR geometry file");
        }

        auto detectors = geo_mgr_->getDetectors();
        geometry_file << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl
                      << "<!-- ?xml-stylesheet type=\"text/xsl\" href=\"https://cern.ch/allpix-squared/\"? -->" << std::endl
                      << "<gear>" << std::endl;

        geometry_file << "  <global detectorName=\"" << DetectorName_ << "\"/>" << std::endl;
        geometry_file << "  <detectors>" << std::endl;
        geometry_file << "    <detector name=\"SiPlanes\" geartype=\"SiPlanesParameters\">" << std::endl;
        geometry_file << "      <siplanesType type=\"TelescopeWithDUT\">" << std::endl;
        geometry_file << "      <siplanesNumber number=\"" << detectors.size() << "\">" << std::endl;
        geometry_file << "      <layers>" << std::endl;

        for(auto& detector : detectors) {
            // Write header for the layer:
            geometry_file << "<!-- Allpix Squared Detector: " << detector->getName() << " - type: " << detector->getType()
                          << " -->" << std::endl;
            geometry_file << "        <layer>" << std::endl;

            auto position = detector->getPosition();
            // FIXME auto orientation = detector->getOrientation();

            auto model = detector->getModel();
            auto npixels = model->getNPixels();
            auto pitch = model->getPixelSize();

            auto total_size = model->getSize();
            auto sensitive_size = model->getSensorSize();

            // Write ladder
            geometry_file << "          <ladder ID=\"" << detectorIDs_[detector->getName()] << "\"" << std::endl;
            geometry_file << "            positionX=\"" << position.x() << "\"\tpositionY=\"" << position.y()
                          << "\"\tpositionZ=\"" << position.z() << "\"" << std::endl;
            // FIXME rotation
            geometry_file << "            sizeX=\"" << total_size.x() << "\"\tsizeY=\"" << total_size.y()
                          << "\"\tthickness=\"" << total_size.z() << "\"" << std::endl;
            geometry_file << "            radLength=\"93.65\"" << std::endl;
            geometry_file << "            />" << std::endl;

            // Write sensitive
            geometry_file << "          <sensitive ID=\"" << detectorIDs_[detector->getName()] << "\"" << std::endl;
            geometry_file << "            positionX=\"" << position.x() << "\"\tpositionY=\"" << position.x()
                          << "\"\tpositionZ=\"" << position.x() << "\"" << std::endl;
            geometry_file << "            sizeX=\"" << sensitive_size.x() << "\"\tsizeY=\"" << sensitive_size.y()
                          << "\"\tthickness=\"" << sensitive_size.z() << "\"" << std::endl;
            geometry_file << "            npixelsX=\"" << npixels.x() << "\"\tnpixelsY=\"" << npixels.y() << "\""
                          << std::endl;
            geometry_file << "            pitchX=\"" << pitch.x() << "\"\tpitchY=\"" << pitch.y() << "\"\tresolution=\""
                          << (pitch.x() / std::sqrt(12)) << "\"" << std::endl;
            // FIXME rotation
            geometry_file << "            radLength=\"93.65\"" << std::endl;
            geometry_file << "            />" << std::endl;

            // End the layer:
            geometry_file << "        </layer>" << std::endl;
        }

        // Close XML tree:
        geometry_file << "      </layers>" << std::endl
                      << "    </detector>" << std::endl
                      << "  </detectors>" << std::endl
                      << "</gear>" << std::endl;

        LOG(STATUS) << "Wrote GEAR geometry to file:" << std::endl << geometry_file_name_;
    }
}

LCIOWriterModule::~LCIOWriterModule() {
    delete lcWriter_;
}
<commit_msg>LCIOWriter: half-way implement rotation writing<commit_after>/**
 * @file
 * @brief Implementation of [LCIOWriter] module
 * @copyright Copyright (c) 2017 CERN and the Allpix Squared authors.
 * This software is distributed under the terms of the MIT License, copied verbatim in the file "LICENSE.md".
 * In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an
 * Intergovernmental Organization or submit itself to any jurisdiction.
 */

#include "LCIOWriterModule.hpp"

#include <fstream>
#include <string>
#include <utility>
#include <vector>

#include "core/messenger/Messenger.hpp"

#include "core/utils/log.h"

#include <IMPL/LCCollectionVec.h>
#include <IMPL/LCEventImpl.h>
#include <IMPL/LCRunHeaderImpl.h>
#include <IMPL/TrackerDataImpl.h>
#include <IO/LCWriter.h>
#include <IOIMPL/LCFactory.h>
#include <UTIL/CellIDEncoder.h>
#include <lcio.h>

using namespace allpix;
using namespace lcio;

LCIOWriterModule::LCIOWriterModule(Configuration config, Messenger* messenger, GeometryManager* geo)
    : Module(std::move(config)), geo_mgr_(geo) {

    // Bind pixel hits message
    messenger->bindMulti(this, &LCIOWriterModule::pixel_messages_, MsgFlags::REQUIRED);

    // get all detector names and assign id.
    auto detectors = geo_mgr_->getDetectors();
    unsigned int i = 0;
    for(const auto& det : detectors) {
        detectorIDs_[det->getName()] = i;
        LOG(DEBUG) << det->getName() << " has ID " << detectorIDs_[det->getName()];
        i++;
    }

    // Set configuration defaults:
    config_.setDefault("file_name", "output.slcio");
    config_.setDefault("geometry_file", "allpix_squared_gear.xml");
    config_.setDefault("pixel_type", 2);
    config_.setDefault("detector_name", "EUTelescope");
    config_.setDefault("output_collection_name", "zsdata_m26");

    pixelType_ = config_.get<int>("pixel_type");
    DetectorName_ = config_.get<std::string>("detector_name");
    OutputCollectionName_ = config_.get<std::string>("output_collection_name");
}

void LCIOWriterModule::init() {
    // Create the output GEAR file for the detector geometry
    geometry_file_name_ = getOutputPath(config_.get<std::string>("geometry_file"), true);

    // Open LCIO file and write run header
    lcio_file_name_ = getOutputPath(config_.get<std::string>("file_name"));
    lcWriter_ = LCFactory::getInstance()->createLCWriter();
    lcWriter_->open(lcio_file_name_, LCIO::WRITE_NEW);
    auto run = std::make_unique<LCRunHeaderImpl>();
    run->setRunNumber(1);
    run->setDetectorName(DetectorName_);
    lcWriter_->writeRunHeader(run.get());
}

void LCIOWriterModule::run(unsigned int eventNb) {

    auto evt = std::make_unique<LCEventImpl>(); // create the event
    evt->setRunNumber(1);
    evt->setEventNumber(static_cast<int>(eventNb)); // set the event attributes
    evt->parameters().setValue("EventType", 2);

    // Prepare charge vectors
    std::vector<std::vector<float>> charges;
    for(unsigned int i = 0; i < detectorIDs_.size(); i++) {
        std::vector<float> charge;
        charges.push_back(charge);
    }

    // Receive all pixel messages, fill charge vectors
    for(const auto& hit_msg : pixel_messages_) {
        LOG(DEBUG) << hit_msg->getDetector()->getName();
        for(const auto& hitdata : hit_msg->getData()) {
            LOG(DEBUG) << "X: " << hitdata.getPixel().getIndex().x() << ", Y:" << hitdata.getPixel().getIndex().y()
                       << ", Signal: " << hitdata.getSignal();

            unsigned int detectorID = detectorIDs_[hit_msg->getDetector()->getName()];

            switch(pixelType_) {
            case 1: // EUTelSimpleSparsePixel
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().x())); // x
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().y())); // y
                charges[detectorID].push_back(static_cast<float>(hitdata.getSignal()));               // signal
                break;
            case 2:  // EUTelGenericSparsePixel
            default: // EUTelGenericSparsePixel is default
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().x())); // x
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().y())); // y
                charges[detectorID].push_back(static_cast<float>(hitdata.getSignal()));               // signal
                charges[detectorID].push_back(static_cast<float>(0));                                 // time
                break;
            case 5: // EUTelTimepix3SparsePixel
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().x())); // x
                charges[detectorID].push_back(static_cast<float>(hitdata.getPixel().getIndex().y())); // y
                charges[detectorID].push_back(static_cast<float>(hitdata.getSignal()));               // signal
                charges[detectorID].push_back(static_cast<float>(0));                                 // time
                charges[detectorID].push_back(static_cast<float>(0));                                 // time
                charges[detectorID].push_back(static_cast<float>(0));                                 // time
                charges[detectorID].push_back(static_cast<float>(0));                                 // time
                break;
            }
        }
    }

    // Prepare hitvector
    LCCollectionVec* hitVec = new LCCollectionVec(LCIO::TRACKERDATA);

    // Fill hitvector with event data
    for(unsigned int detectorID = 0; detectorID < detectorIDs_.size(); detectorID++) {
        auto hit = new TrackerDataImpl();
        CellIDEncoder<TrackerDataImpl> sparseDataEncoder("sensorID:7,sparsePixelType:5", hitVec);
        sparseDataEncoder["sensorID"] = detectorID;
        sparseDataEncoder["sparsePixelType"] = pixelType_;
        sparseDataEncoder.setCellID(hit);
        hit->setChargeValues(charges[detectorID]);
        hitVec->push_back(hit);
    }

    // Add collection to event and write event to LCIO file
    evt->addCollection(hitVec, OutputCollectionName_); // add the collection with a name
    lcWriter_->writeEvent(evt.get());                  // write the event to the file
    write_cnt_++;
}

void LCIOWriterModule::finalize() {
    lcWriter_->close();
    // Print statistics
    LOG(STATUS) << "Wrote " << write_cnt_ << " events to file:" << std::endl << lcio_file_name_;

    // Write geometry:
    std::ofstream geometry_file;
    if(!geometry_file_name_.empty()) {
        geometry_file.open(geometry_file_name_, std::ios_base::out | std::ios_base::trunc);
        if(!geometry_file.good()) {
            throw ModuleError("Cannot write to GEAR geometry file");
        }

        auto detectors = geo_mgr_->getDetectors();
        geometry_file << "<?xml version=\"1.0\" encoding=\"utf-8\"?>" << std::endl
                      << "<!-- ?xml-stylesheet type=\"text/xsl\" href=\"https://cern.ch/allpix-squared/\"? -->" << std::endl
                      << "<gear>" << std::endl;

        geometry_file << "  <global detectorName=\"" << DetectorName_ << "\"/>" << std::endl;
        geometry_file << "  <detectors>" << std::endl;
        geometry_file << "    <detector name=\"SiPlanes\" geartype=\"SiPlanesParameters\">" << std::endl;
        geometry_file << "      <siplanesType type=\"TelescopeWithDUT\">" << std::endl;
        geometry_file << "      <siplanesNumber number=\"" << detectors.size() << "\">" << std::endl;
        geometry_file << "      <layers>" << std::endl;

        for(auto& detector : detectors) {
            // Write header for the layer:
            geometry_file << "<!-- Allpix Squared Detector: " << detector->getName() << " - type: " << detector->getType()
                          << " -->" << std::endl;
            geometry_file << "        <layer>" << std::endl;

            auto position = detector->getPosition();
            auto orientation = detector->getOrientation();

            auto model = detector->getModel();
            auto npixels = model->getNPixels();
            auto pitch = model->getPixelSize();

            auto total_size = model->getSize();
            auto sensitive_size = model->getSensorSize();

            // Write ladder
            geometry_file << "          <ladder ID=\"" << detectorIDs_[detector->getName()] << "\"" << std::endl;
            geometry_file << "            positionX=\"" << position.x() << "\"\tpositionY=\"" << position.y()
                          << "\"\tpositionZ=\"" << position.z() << "\"" << std::endl;
            // FIXME rotation
            geometry_file << "            sizeX=\"" << total_size.x() << "\"\tsizeY=\"" << total_size.y()
                          << "\"\tthickness=\"" << total_size.z() << "\"" << std::endl;
            geometry_file << "            radLength=\"93.65\"" << std::endl;
            geometry_file << "            />" << std::endl;

            // Write sensitive
            geometry_file << "          <sensitive ID=\"" << detectorIDs_[detector->getName()] << "\"" << std::endl;
            geometry_file << "            positionX=\"" << position.x() << "\"\tpositionY=\"" << position.x()
                          << "\"\tpositionZ=\"" << position.x() << "\"" << std::endl;
            geometry_file << "            sizeX=\"" << sensitive_size.x() << "\"\tsizeY=\"" << sensitive_size.y()
                          << "\"\tthickness=\"" << sensitive_size.z() << "\"" << std::endl;
            geometry_file << "            npixelsX=\"" << npixels.x() << "\"\tnpixelsY=\"" << npixels.y() << "\""
                          << std::endl;
            geometry_file << "            pitchX=\"" << pitch.x() << "\"\tpitchY=\"" << pitch.y() << "\"\tresolution=\""
                          << (pitch.x() / std::sqrt(12)) << "\"" << std::endl;

            std::vector<double> components(9);
            orientation.GetComponents(components.begin(), components.end());
            geometry_file << "            rotation1=\"" << components.at(0) << "\"\trotation2=\"" << components.at(1) << "\""
                          << std::endl;
            geometry_file << "            rotation3=\"" << components.at(3) << "\"\trotation4=\"" << components.at(4) << "\""
                          << std::endl;
            geometry_file << "            radLength=\"93.65\"" << std::endl;
            geometry_file << "            />" << std::endl;

            // End the layer:
            geometry_file << "        </layer>" << std::endl;
        }

        // Close XML tree:
        geometry_file << "      </layers>" << std::endl
                      << "    </detector>" << std::endl
                      << "  </detectors>" << std::endl
                      << "</gear>" << std::endl;

        LOG(STATUS) << "Wrote GEAR geometry to file:" << std::endl << geometry_file_name_;
    }
}

LCIOWriterModule::~LCIOWriterModule() {
    delete lcWriter_;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2016 - 2020 gtalent2@gmail.com
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <QHeaderView>
#include <QItemDelegate>
#include <QPainter>
#include <QPushButton>
#include <QTableWidget>
#include <QToolBar>

#include <nostalgia/core/gfx.hpp>

#include "util.hpp"
#include "paletteeditor.hpp"

namespace nostalgia::core {

enum class PaletteEditorCommandId {
	AddColor,
	RemoveColor,
	UpdateColor,
	MoveColor,
};

class AddColorCommand: public QUndoCommand {
	private:
		PaletteEditor *m_editor = nullptr;
		Color16 m_color = 0;
		int m_idx = -1;

	public:
		AddColorCommand(PaletteEditor *editor, Color16 color, int idx) {
			m_editor = editor;
			m_color = color;
			m_idx = idx;
		}

		virtual ~AddColorCommand() = default;

		int id() const override {
			return static_cast<int>(PaletteEditorCommandId::AddColor);
		}

		void redo() override {
			m_editor->addColor(m_idx, m_color);
		}

		void undo() override {
			m_editor->rmColor(m_idx);
		}

};

class RemoveColorCommand: public QUndoCommand {
	private:
		PaletteEditor *m_editor = nullptr;
		Color16 m_color = 0;
		int m_idx = -1;

	public:
		RemoveColorCommand(PaletteEditor *editor, Color16 color, int idx) {
			m_editor = editor;
			m_color = color;
			m_idx = idx;
		}

		virtual ~RemoveColorCommand() = default;

		int id() const override {
			return static_cast<int>(PaletteEditorCommandId::RemoveColor);
		}

		void redo() override {
			m_editor->rmColor(m_idx);
		}

		void undo() override {
			m_editor->addColor(m_idx, m_color);
		}

};

class UpdateColorCommand: public QUndoCommand {
	private:
		PaletteEditor *m_editor = nullptr;
		Color16 m_oldColor = 0;
		Color16 m_newColor = 0;
		int m_idx = -1;

	public:
		UpdateColorCommand(PaletteEditor *editor, int idx, Color16 oldColor, Color16 newColor) {
			m_editor = editor;
			m_idx = idx;
			m_oldColor = oldColor;
			m_newColor = newColor;
			setObsolete(m_oldColor == m_newColor);
		}

		virtual ~UpdateColorCommand() = default;

		int id() const override {
			return static_cast<int>(PaletteEditorCommandId::UpdateColor);
		}

		void redo() override {
			m_editor->updateColor(m_idx, m_newColor);
		}

		void undo() override {
			m_editor->updateColor(m_idx, m_oldColor);
		}

};

class MoveColorCommand: public QUndoCommand {
	private:
		PaletteEditor *m_editor = nullptr;
		int m_idx = -1;
		int m_offset = 0;

	public:
		MoveColorCommand(PaletteEditor *editor, int idx, int offset) {
			m_editor = editor;
			m_idx = idx;
			m_offset = offset;
		}

		virtual ~MoveColorCommand() = default;

		int id() const override {
			return static_cast<int>(PaletteEditorCommandId::MoveColor);
		}

		void redo() override {
			m_editor->moveColor(m_idx, m_offset);
		}

		void undo() override {
			m_editor->moveColor(m_idx + m_offset, -m_offset);
		}

};

QWidget *PaletteEditorColorTableDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex &idx) const {
	const auto max = idx.column() != 3 ? 31 : 1;
	auto le = new QLineEdit(parent);
	auto validator = new QIntValidator(0, max, le);
	le->setValidator(validator);
	return le;
}

void PaletteEditorColorTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &idx) const {
	if (idx.column() != 4) {
		QStyledItemDelegate::paint(painter, opt, idx);
	} else {
		auto color = idx.model()->data(idx, Qt::DisplayRole).toString();
		painter->fillRect(opt.rect, QColor(color));
	}
}

static QTableWidgetItem *mkCell(QString v, bool editable = true) {
	auto c = new QTableWidgetItem;
	c->setText(v);
	c->setFont(QFont("monospace"));
	c->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
	if (!editable) {
		c->setFlags(c->flags() & ~Qt::ItemIsEditable);
	}
	return c;
}

static QTableWidgetItem *mkCell(uint8_t v) {
	return mkCell(QString("%1").arg(v), true);
}

PaletteEditor::PaletteEditor(QString path, const studio::Context *ctx, QWidget *parent): studio::Editor(parent) {
	m_ctx = ctx;
	m_itemPath = path;

	auto canvasLyt = new QVBoxLayout(this);
	auto tb = new QToolBar(tr("Tile Sheet Options"));
	m_addBtn = new QPushButton(tr("Add"), tb);
	m_rmBtn = new QPushButton(tr("Remove"), tb);
	m_moveUpBtn = new QPushButton(tr("Move Up"), tb);
	m_moveDownBtn = new QPushButton(tr("Move Down"), tb);
	m_rmBtn->setEnabled(false);
	m_moveUpBtn->setEnabled(false);
	m_moveDownBtn->setEnabled(false);
	tb->addWidget(m_addBtn);
	tb->addWidget(m_rmBtn);
	tb->addWidget(m_moveUpBtn);
	tb->addWidget(m_moveDownBtn);
	canvasLyt->setMenuBar(tb);

	m_table = new QTableWidget(this);
	m_table->setItemDelegate(&m_colorTableDelegate);
	m_table->setColumnCount(5);
	m_table->setSelectionBehavior(QAbstractItemView::SelectRows);
	m_table->setSelectionMode(QAbstractItemView::SingleSelection);
	m_table->setHorizontalHeaderLabels(QStringList() << tr("Red") << tr("Green") << tr("Blue") << tr("Alpha") << tr("Color Preview"));
	m_table->horizontalHeader()->setStretchLastSection(true);
	m_table->verticalHeader()->hide();
	canvasLyt->addWidget(m_table);
	connect(m_table, &QTableWidget::itemSelectionChanged, this, &PaletteEditor::colorSelected);
	connect(m_addBtn, &QPushButton::clicked, this, &PaletteEditor::addColorClicked);
	connect(m_rmBtn, &QPushButton::clicked, this, &PaletteEditor::rmColorClicked);
	connect(m_moveUpBtn, &QPushButton::clicked, this, &PaletteEditor::moveColorUpClicked);
	connect(m_moveDownBtn, &QPushButton::clicked, this, &PaletteEditor::moveColorDownClicked);
	connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
	m_pal = m_ctx->project->loadObj<NostalgiaPalette>(m_itemPath);
	load();
}

QString PaletteEditor::itemName() const {
	return m_itemPath.mid(m_itemPath.lastIndexOf('/'));
}

void PaletteEditor::addTableRow(int i, Color16 c) {
	disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
	m_table->insertRow(i);
	m_table->setItem(i, 0, mkCell(red16(c)));
	m_table->setItem(i, 1, mkCell(green16(c)));
	m_table->setItem(i, 2, mkCell(blue16(c)));
	m_table->setItem(i, 3, mkCell(alpha16(c)));
	m_table->setItem(i, 4, mkCell(toQColor(m_pal->colors[static_cast<std::size_t>(i)]).name(QColor::HexArgb), false));
	connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}

void PaletteEditor::rmTableRow(int idx) {
	disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
	m_table->removeRow(idx);
	connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}

void PaletteEditor::setTableRow(int idx, Color16 c) {
	disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
	m_table->item(idx, 0)->setText(QString::number(red16(c)));
	m_table->item(idx, 1)->setText(QString::number(green16(c)));
	m_table->item(idx, 2)->setText(QString::number(blue16(c)));
	m_table->item(idx, 3)->setText(QString::number(alpha16(c)));
	m_table->item(idx, 4)->setText(toQColor(m_pal->colors[static_cast<std::size_t>(idx)]).name(QColor::HexArgb));
	connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}

void PaletteEditor::addColor(int idx, Color16 c) {
	m_pal->colors.insert(static_cast<std::size_t>(idx), c);
	addTableRow(idx, c);
	setUnsavedChanges(true);
}

void PaletteEditor::rmColor(int idx) {
	rmTableRow(idx);
	m_pal->colors.erase(static_cast<std::size_t>(idx));
	setUnsavedChanges(true);
}

void PaletteEditor::updateColor(int idx, Color16 c) {
	m_pal->colors[static_cast<std::size_t>(idx)] = c;
	setTableRow(idx, c);
	setUnsavedChanges(true);
}

void PaletteEditor::moveColor(int idx, int offset) {
	auto c = m_pal->colors[static_cast<std::size_t>(idx)];
	m_pal->colors.erase(static_cast<std::size_t>(idx));
	m_pal->colors.insert(static_cast<std::size_t>(idx + offset), c);
	rmTableRow(idx);
	addTableRow(idx + offset, c);
	setUnsavedChanges(true);
}

void PaletteEditor::saveItem() {
	m_ctx->project->writeObj(m_itemPath, m_pal.get());
}

void PaletteEditor::load() {
	disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
	for (std::size_t i = 0; i < m_pal->colors.size(); ++i) {
		auto c = m_pal->colors[i];
		addTableRow(i, c);
	}
	connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}

Color16 PaletteEditor::rowColor(int row) const {
	auto r = static_cast<uint8_t>(m_table->item(row, 0)->text().toInt());
	auto g = static_cast<uint8_t>(m_table->item(row, 1)->text().toInt());
	auto b = static_cast<uint8_t>(m_table->item(row, 2)->text().toInt());
	auto a = static_cast<uint8_t>(m_table->item(row, 3)->text().toInt());
	return color16(r, g, b, a);
}

void PaletteEditor::colorSelected() {
	auto row = m_table->currentRow();
	if (row > -1) {
		m_rmBtn->setEnabled(true);
		m_moveUpBtn->setEnabled(row > 0);
		m_moveDownBtn->setEnabled(row < m_table->rowCount() - 1);
	} else {
		m_rmBtn->setEnabled(false);
		m_moveUpBtn->setEnabled(false);
		m_moveDownBtn->setEnabled(false);
	}
}

void PaletteEditor::cellChanged(int row, int) {
	auto oldColor = m_pal->colors[static_cast<std::size_t>(row)];
	auto newColor = rowColor(row);
	undoStack()->push(new UpdateColorCommand(this, row, oldColor, newColor));
}

void PaletteEditor::addColorClicked() {
	auto row = m_table->rowCount();
	undoStack()->push(new AddColorCommand(this, 0, row));
}

void PaletteEditor::rmColorClicked() {
	auto row = m_table->currentRow();
	undoStack()->push(new RemoveColorCommand(this, m_pal->colors[static_cast<std::size_t>(row)], row));
}

void PaletteEditor::moveColorUpClicked() {
	auto row = m_table->currentRow();
	undoStack()->push(new MoveColorCommand(this, row, -1));
}

void PaletteEditor::moveColorDownClicked() {
	auto row = m_table->currentRow();
	undoStack()->push(new MoveColorCommand(this, row, 1));
}

}
<commit_msg>[nostalgia/core/studio] Add 8 bit to 5 bit color conversion optiont to PaletteEditor<commit_after>/*
 * Copyright 2016 - 2020 gtalent2@gmail.com
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <QHeaderView>
#include <QItemDelegate>
#include <QPainter>
#include <QPushButton>
#include <QTableWidget>
#include <QToolBar>

#include <nostalgia/core/gfx.hpp>

#include "util.hpp"
#include "paletteeditor.hpp"

namespace nostalgia::core {

enum class PaletteEditorCommandId {
	AddColor,
	RemoveColor,
	UpdateColor,
	MoveColor,
};

class ColorChannelValidator: public QValidator {

	public:
		ColorChannelValidator(QLineEdit *parent);

		QValidator::State validate(QString &input, int&) const override;

	private:
		QString convert(const QString &input) const;

};

ColorChannelValidator::ColorChannelValidator(QLineEdit *parent): QValidator(parent) {
	connect(parent, &QLineEdit::editingFinished, [this, parent] {
		parent->setText(convert(parent->text()));
	});
}

QString ColorChannelValidator::convert(const QString &input) const {
	int num = 0;
	if (input[0] == '_') {
		num = input.mid(1).toInt() >> 3;
	}
	return QString::number(num);
}

QValidator::State ColorChannelValidator::validate(QString &input, int&) const {
	if (input.size() == 0) {
		return QValidator::State::Intermediate;
	}
	const auto convert = input[0] == '_';
	const auto max = convert ? 255 : 31;
	const auto numTxt = convert ? input.mid(1) : input;
	bool isNumber = false;
	const auto num = numTxt.toInt(&isNumber);
	if (isNumber && num >= 0 && num <= max) {
		return QValidator::State::Acceptable;
	} else if (numTxt == "") {
		return QValidator::State::Intermediate;
	} else {
		return QValidator::State::Invalid;
	}
}

class AddColorCommand: public QUndoCommand {
	private:
		PaletteEditor *m_editor = nullptr;
		Color16 m_color = 0;
		int m_idx = -1;

	public:
		AddColorCommand(PaletteEditor *editor, Color16 color, int idx) {
			m_editor = editor;
			m_color = color;
			m_idx = idx;
		}

		virtual ~AddColorCommand() = default;

		int id() const override {
			return static_cast<int>(PaletteEditorCommandId::AddColor);
		}

		void redo() override {
			m_editor->addColor(m_idx, m_color);
		}

		void undo() override {
			m_editor->rmColor(m_idx);
		}

};

class RemoveColorCommand: public QUndoCommand {
	private:
		PaletteEditor *m_editor = nullptr;
		Color16 m_color = 0;
		int m_idx = -1;

	public:
		RemoveColorCommand(PaletteEditor *editor, Color16 color, int idx) {
			m_editor = editor;
			m_color = color;
			m_idx = idx;
		}

		virtual ~RemoveColorCommand() = default;

		int id() const override {
			return static_cast<int>(PaletteEditorCommandId::RemoveColor);
		}

		void redo() override {
			m_editor->rmColor(m_idx);
		}

		void undo() override {
			m_editor->addColor(m_idx, m_color);
		}

};

class UpdateColorCommand: public QUndoCommand {
	private:
		PaletteEditor *m_editor = nullptr;
		Color16 m_oldColor = 0;
		Color16 m_newColor = 0;
		int m_idx = -1;

	public:
		UpdateColorCommand(PaletteEditor *editor, int idx, Color16 oldColor, Color16 newColor) {
			m_editor = editor;
			m_idx = idx;
			m_oldColor = oldColor;
			m_newColor = newColor;
			setObsolete(m_oldColor == m_newColor);
		}

		virtual ~UpdateColorCommand() = default;

		int id() const override {
			return static_cast<int>(PaletteEditorCommandId::UpdateColor);
		}

		void redo() override {
			m_editor->updateColor(m_idx, m_newColor);
		}

		void undo() override {
			m_editor->updateColor(m_idx, m_oldColor);
		}

};

class MoveColorCommand: public QUndoCommand {
	private:
		PaletteEditor *m_editor = nullptr;
		int m_idx = -1;
		int m_offset = 0;

	public:
		MoveColorCommand(PaletteEditor *editor, int idx, int offset) {
			m_editor = editor;
			m_idx = idx;
			m_offset = offset;
		}

		virtual ~MoveColorCommand() = default;

		int id() const override {
			return static_cast<int>(PaletteEditorCommandId::MoveColor);
		}

		void redo() override {
			m_editor->moveColor(m_idx, m_offset);
		}

		void undo() override {
			m_editor->moveColor(m_idx + m_offset, -m_offset);
		}

};

QWidget *PaletteEditorColorTableDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem&, const QModelIndex &idx) const {
	auto le = new QLineEdit(parent);
	if (idx.column()) {
		auto validator = new ColorChannelValidator(le);
		le->setValidator(validator);
	}
	return le;
}

void PaletteEditorColorTableDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opt, const QModelIndex &idx) const {
	if (idx.column() != 4) {
		QStyledItemDelegate::paint(painter, opt, idx);
	} else {
		auto color = idx.model()->data(idx, Qt::DisplayRole).toString();
		painter->fillRect(opt.rect, QColor(color));
	}
}

static QTableWidgetItem *mkCell(QString v, bool editable = true) {
	auto c = new QTableWidgetItem;
	c->setText(v);
	c->setFont(QFont("monospace"));
	c->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
	if (!editable) {
		c->setFlags(c->flags() & ~Qt::ItemIsEditable);
	}
	return c;
}

static QTableWidgetItem *mkCell(uint8_t v) {
	return mkCell(QString("%1").arg(v), true);
}

PaletteEditor::PaletteEditor(QString path, const studio::Context *ctx, QWidget *parent): studio::Editor(parent) {
	m_ctx = ctx;
	m_itemPath = path;

	auto canvasLyt = new QVBoxLayout(this);
	auto tb = new QToolBar(tr("Tile Sheet Options"));
	m_addBtn = new QPushButton(tr("Add"), tb);
	m_rmBtn = new QPushButton(tr("Remove"), tb);
	m_moveUpBtn = new QPushButton(tr("Move Up"), tb);
	m_moveDownBtn = new QPushButton(tr("Move Down"), tb);
	m_rmBtn->setEnabled(false);
	m_moveUpBtn->setEnabled(false);
	m_moveDownBtn->setEnabled(false);
	tb->addWidget(m_addBtn);
	tb->addWidget(m_rmBtn);
	tb->addWidget(m_moveUpBtn);
	tb->addWidget(m_moveDownBtn);
	canvasLyt->setMenuBar(tb);

	m_table = new QTableWidget(this);
	m_table->setItemDelegate(&m_colorTableDelegate);
	m_table->setColumnCount(5);
	m_table->setSelectionBehavior(QAbstractItemView::SelectRows);
	m_table->setSelectionMode(QAbstractItemView::SingleSelection);
	m_table->setHorizontalHeaderLabels(QStringList() << tr("Red") << tr("Green") << tr("Blue") << tr("Alpha") << tr("Color Preview"));
	m_table->horizontalHeader()->setStretchLastSection(true);
	m_table->verticalHeader()->hide();
	canvasLyt->addWidget(m_table);
	connect(m_table, &QTableWidget::itemSelectionChanged, this, &PaletteEditor::colorSelected);
	connect(m_addBtn, &QPushButton::clicked, this, &PaletteEditor::addColorClicked);
	connect(m_rmBtn, &QPushButton::clicked, this, &PaletteEditor::rmColorClicked);
	connect(m_moveUpBtn, &QPushButton::clicked, this, &PaletteEditor::moveColorUpClicked);
	connect(m_moveDownBtn, &QPushButton::clicked, this, &PaletteEditor::moveColorDownClicked);
	connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
	m_pal = m_ctx->project->loadObj<NostalgiaPalette>(m_itemPath);
	load();
}

QString PaletteEditor::itemName() const {
	return m_itemPath.mid(m_itemPath.lastIndexOf('/'));
}

void PaletteEditor::addTableRow(int i, Color16 c) {
	disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
	m_table->insertRow(i);
	m_table->setItem(i, 0, mkCell(red16(c)));
	m_table->setItem(i, 1, mkCell(green16(c)));
	m_table->setItem(i, 2, mkCell(blue16(c)));
	m_table->setItem(i, 3, mkCell(alpha16(c)));
	m_table->setItem(i, 4, mkCell(toQColor(m_pal->colors[static_cast<std::size_t>(i)]).name(QColor::HexArgb), false));
	connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}

void PaletteEditor::rmTableRow(int idx) {
	disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
	m_table->removeRow(idx);
	connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}

void PaletteEditor::setTableRow(int idx, Color16 c) {
	disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
	m_table->item(idx, 0)->setText(QString::number(red16(c)));
	m_table->item(idx, 1)->setText(QString::number(green16(c)));
	m_table->item(idx, 2)->setText(QString::number(blue16(c)));
	m_table->item(idx, 3)->setText(QString::number(alpha16(c)));
	m_table->item(idx, 4)->setText(toQColor(m_pal->colors[static_cast<std::size_t>(idx)]).name(QColor::HexArgb));
	connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}

void PaletteEditor::addColor(int idx, Color16 c) {
	m_pal->colors.insert(static_cast<std::size_t>(idx), c);
	addTableRow(idx, c);
	setUnsavedChanges(true);
}

void PaletteEditor::rmColor(int idx) {
	rmTableRow(idx);
	m_pal->colors.erase(static_cast<std::size_t>(idx));
	setUnsavedChanges(true);
}

void PaletteEditor::updateColor(int idx, Color16 c) {
	m_pal->colors[static_cast<std::size_t>(idx)] = c;
	setTableRow(idx, c);
	setUnsavedChanges(true);
}

void PaletteEditor::moveColor(int idx, int offset) {
	auto c = m_pal->colors[static_cast<std::size_t>(idx)];
	m_pal->colors.erase(static_cast<std::size_t>(idx));
	m_pal->colors.insert(static_cast<std::size_t>(idx + offset), c);
	rmTableRow(idx);
	addTableRow(idx + offset, c);
	setUnsavedChanges(true);
}

void PaletteEditor::saveItem() {
	m_ctx->project->writeObj(m_itemPath, m_pal.get());
}

void PaletteEditor::load() {
	disconnect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
	for (std::size_t i = 0; i < m_pal->colors.size(); ++i) {
		auto c = m_pal->colors[i];
		addTableRow(i, c);
	}
	connect(m_table, &QTableWidget::cellChanged, this, &PaletteEditor::cellChanged);
}

Color16 PaletteEditor::rowColor(int row) const {
	auto r = static_cast<uint8_t>(m_table->item(row, 0)->text().toInt());
	auto g = static_cast<uint8_t>(m_table->item(row, 1)->text().toInt());
	auto b = static_cast<uint8_t>(m_table->item(row, 2)->text().toInt());
	auto a = static_cast<uint8_t>(m_table->item(row, 3)->text().toInt());
	return color16(r, g, b, a);
}

void PaletteEditor::colorSelected() {
	auto row = m_table->currentRow();
	if (row > -1) {
		m_rmBtn->setEnabled(true);
		m_moveUpBtn->setEnabled(row > 0);
		m_moveDownBtn->setEnabled(row < m_table->rowCount() - 1);
	} else {
		m_rmBtn->setEnabled(false);
		m_moveUpBtn->setEnabled(false);
		m_moveDownBtn->setEnabled(false);
	}
}

void PaletteEditor::cellChanged(int row, int) {
	auto oldColor = m_pal->colors[static_cast<std::size_t>(row)];
	auto newColor = rowColor(row);
	undoStack()->push(new UpdateColorCommand(this, row, oldColor, newColor));
}

void PaletteEditor::addColorClicked() {
	auto row = m_table->rowCount();
	undoStack()->push(new AddColorCommand(this, 0, row));
}

void PaletteEditor::rmColorClicked() {
	auto row = m_table->currentRow();
	undoStack()->push(new RemoveColorCommand(this, m_pal->colors[static_cast<std::size_t>(row)], row));
}

void PaletteEditor::moveColorUpClicked() {
	auto row = m_table->currentRow();
	undoStack()->push(new MoveColorCommand(this, row, -1));
}

void PaletteEditor::moveColorDownClicked() {
	auto row = m_table->currentRow();
	undoStack()->push(new MoveColorCommand(this, row, 1));
}

}
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** 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.
**
****************************************************************************/

#include "settingsdatabase.h"

#include <QDir>
#include <QMap>
#include <QString>
#include <QStringList>
#include <QVariant>

#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QDebug>

/*!
    \class Core::SettingsDatabase
    \brief The SettingsDatabase class offers an alternative to the
    application-wide QSettings that is more
    suitable for storing large amounts of data.

    The settings database is SQLite based, and lazily retrieves data when it
    is asked for. It also does incremental updates of the database rather than
    rewriting the whole file each time one of the settings change.

    The SettingsDatabase API mimics that of QSettings.
*/

using namespace Core;
using namespace Core::Internal;

enum { debug_settings = 0 };

namespace Core {
namespace Internal {

typedef QMap<QString, QVariant> SettingsMap;

class SettingsDatabasePrivate
{
public:
    QString effectiveGroup() const
    {
        return m_groups.join(QString(QLatin1Char('/')));
    }

    QString effectiveKey(const QString &key) const
    {
        QString g = effectiveGroup();
        if (!g.isEmpty() && !key.isEmpty())
            g += QLatin1Char('/');
        g += key;
        return g;
    }

    SettingsMap m_settings;

    QStringList m_groups;
    QStringList m_dirtyKeys;

    QSqlDatabase m_db;
};

} // namespace Internal
} // namespace Core

SettingsDatabase::SettingsDatabase(const QString &path,
                                   const QString &application,
                                   QObject *parent)
    : QObject(parent)
    , d(new SettingsDatabasePrivate)
{
    const QLatin1Char slash('/');

    // TODO: Don't rely on a path, but determine automatically
    QDir pathDir(path);
    if (!pathDir.exists())
        pathDir.mkpath(pathDir.absolutePath());

    QString fileName = path;
    if (!fileName.endsWith(slash))
        fileName += slash;
    fileName += application;
    fileName += QLatin1String(".db");

    d->m_db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("settings"));
    d->m_db.setDatabaseName(fileName);
    if (!d->m_db.open()) {
        qWarning().nospace() << "Warning: Failed to open settings database at " << fileName << " ("
                             << d->m_db.lastError().driverText() << ")";
    } else {
        // Create the settings table if it doesn't exist yet
        QSqlQuery query(d->m_db);
        query.prepare(QLatin1String("CREATE TABLE IF NOT EXISTS settings ("
                                    "key PRIMARY KEY ON CONFLICT REPLACE, "
                                    "value)"));
        if (!query.exec())
            qWarning().nospace() << "Warning: Failed to prepare settings database! ("
                                 << query.lastError().driverText() << ")";

        // Retrieve all available keys (values are retrieved lazily)
        if (query.exec(QLatin1String("SELECT key FROM settings"))) {
            while (query.next()) {
                d->m_settings.insert(query.value(0).toString(), QVariant());
            }
        }
    }
}

SettingsDatabase::~SettingsDatabase()
{
    sync();

    delete d;
    QSqlDatabase::removeDatabase(QLatin1String("settings"));
}

void SettingsDatabase::setValue(const QString &key, const QVariant &value)
{
    const QString effectiveKey = d->effectiveKey(key);

    // Add to cache
    d->m_settings.insert(effectiveKey, value);

    if (!d->m_db.isOpen())
        return;

    // Instant apply (TODO: Delay writing out settings)
    QSqlQuery query(d->m_db);
    query.prepare(QLatin1String("INSERT INTO settings VALUES (?, ?)"));
    query.addBindValue(effectiveKey);
    query.addBindValue(value);
    query.exec();

    if (debug_settings)
        qDebug() << "Stored:" << effectiveKey << "=" << value;
}

QVariant SettingsDatabase::value(const QString &key, const QVariant &defaultValue) const
{
    const QString effectiveKey = d->effectiveKey(key);
    QVariant value = defaultValue;

    SettingsMap::const_iterator i = d->m_settings.constFind(effectiveKey);
    if (i != d->m_settings.constEnd() && i.value().isValid()) {
        value = i.value();
    } else if (d->m_db.isOpen()) {
        // Try to read the value from the database
        QSqlQuery query(d->m_db);
        query.prepare(QLatin1String("SELECT value FROM settings WHERE key = ?"));
        query.addBindValue(effectiveKey);
        query.exec();
        if (query.next()) {
            value = query.value(0);

            if (debug_settings)
                qDebug() << "Retrieved:" << effectiveKey << "=" << value;
        }

        // Cache the result
        d->m_settings.insert(effectiveKey, value);
    }

    return value;
}

bool SettingsDatabase::contains(const QString &key) const
{
    return d->m_settings.contains(d->effectiveKey(key));
}

void SettingsDatabase::remove(const QString &key)
{
    const QString effectiveKey = d->effectiveKey(key);

    // Remove keys from the cache
    foreach (const QString &k, d->m_settings.keys()) {
        // Either it's an exact match, or it matches up to a /
        if (k.startsWith(effectiveKey)
            && (k.length() == effectiveKey.length()
                || k.at(effectiveKey.length()) == QLatin1Char('/')))
        {
            d->m_settings.remove(k);
        }
    }

    if (!d->m_db.isOpen())
        return;

    // Delete keys from the database
    QSqlQuery query(d->m_db);
    query.prepare(QLatin1String("DELETE FROM settings WHERE key = ? OR key LIKE ?"));
    query.addBindValue(effectiveKey);
    query.addBindValue(QString(effectiveKey + QLatin1String("/%")));
    query.exec();
}

void SettingsDatabase::beginGroup(const QString &prefix)
{
    d->m_groups.append(prefix);
}

void SettingsDatabase::endGroup()
{
    d->m_groups.removeLast();
}

QString SettingsDatabase::group() const
{
    return d->effectiveGroup();
}

QStringList SettingsDatabase::childKeys() const
{
    QStringList children;

    const QString g = group();
    QMapIterator<QString, QVariant> i(d->m_settings);
    while (i.hasNext()) {
        const QString &key = i.next().key();
        if (key.startsWith(g) && key.indexOf(QLatin1Char('/'), g.length() + 1) == -1)
            children.append(key.mid(g.length() + 1));
    }

    return children;
}

void SettingsDatabase::beginTransaction()
{
    if (!d->m_db.isOpen())
        return;
    d->m_db.exec(QLatin1String("BEGIN TRANSACTION;"));
}

void SettingsDatabase::endTransaction()
{
    if (!d->m_db.isOpen())
        return;
    d->m_db.exec(QLatin1String("END TRANSACTION;"));
}

void SettingsDatabase::sync()
{
    // TODO: Delay writing of dirty keys and save them here
}
<commit_msg>Settings: Performance optimizations<commit_after>/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** 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.
**
****************************************************************************/

#include "settingsdatabase.h"

#include <QDir>
#include <QMap>
#include <QString>
#include <QStringList>
#include <QVariant>

#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QDebug>

/*!
    \class Core::SettingsDatabase
    \brief The SettingsDatabase class offers an alternative to the
    application-wide QSettings that is more
    suitable for storing large amounts of data.

    The settings database is SQLite based, and lazily retrieves data when it
    is asked for. It also does incremental updates of the database rather than
    rewriting the whole file each time one of the settings change.

    The SettingsDatabase API mimics that of QSettings.
*/

using namespace Core;
using namespace Core::Internal;

enum { debug_settings = 0 };

namespace Core {
namespace Internal {

typedef QMap<QString, QVariant> SettingsMap;

class SettingsDatabasePrivate
{
public:
    QString effectiveGroup() const
    {
        return m_groups.join(QString(QLatin1Char('/')));
    }

    QString effectiveKey(const QString &key) const
    {
        QString g = effectiveGroup();
        if (!g.isEmpty() && !key.isEmpty())
            g += QLatin1Char('/');
        g += key;
        return g;
    }

    SettingsMap m_settings;

    QStringList m_groups;
    QStringList m_dirtyKeys;

    QSqlDatabase m_db;
};

} // namespace Internal
} // namespace Core

SettingsDatabase::SettingsDatabase(const QString &path,
                                   const QString &application,
                                   QObject *parent)
    : QObject(parent)
    , d(new SettingsDatabasePrivate)
{
    const QLatin1Char slash('/');

    // TODO: Don't rely on a path, but determine automatically
    QDir pathDir(path);
    if (!pathDir.exists())
        pathDir.mkpath(pathDir.absolutePath());

    QString fileName = path;
    if (!fileName.endsWith(slash))
        fileName += slash;
    fileName += application;
    fileName += QLatin1String(".db");

    d->m_db = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), QLatin1String("settings"));
    d->m_db.setDatabaseName(fileName);
    if (!d->m_db.open()) {
        qWarning().nospace() << "Warning: Failed to open settings database at " << fileName << " ("
                             << d->m_db.lastError().driverText() << ")";
    } else {
        // Create the settings table if it doesn't exist yet
        QSqlQuery query(d->m_db);
        query.prepare(QLatin1String("CREATE TABLE IF NOT EXISTS settings ("
                                    "key PRIMARY KEY ON CONFLICT REPLACE, "
                                    "value)"));
        if (!query.exec())
            qWarning().nospace() << "Warning: Failed to prepare settings database! ("
                                 << query.lastError().driverText() << ")";

        // Retrieve all available keys (values are retrieved lazily)
        if (query.exec(QLatin1String("SELECT key FROM settings"))) {
            while (query.next()) {
                d->m_settings.insert(query.value(0).toString(), QVariant());
            }
        }

        // syncing can be slow, especially on Linux and Windows
        d->m_db.exec(QLatin1String("PRAGMA synchronous = OFF;"));
    }
}

SettingsDatabase::~SettingsDatabase()
{
    sync();

    delete d;
    QSqlDatabase::removeDatabase(QLatin1String("settings"));
}

void SettingsDatabase::setValue(const QString &key, const QVariant &value)
{
    const QString effectiveKey = d->effectiveKey(key);

    // Add to cache
    d->m_settings.insert(effectiveKey, value);

    if (!d->m_db.isOpen())
        return;

    // Instant apply (TODO: Delay writing out settings)
    QSqlQuery query(d->m_db);
    query.prepare(QLatin1String("INSERT INTO settings VALUES (?, ?)"));
    query.addBindValue(effectiveKey);
    query.addBindValue(value);
    query.exec();

    if (debug_settings)
        qDebug() << "Stored:" << effectiveKey << "=" << value;
}

QVariant SettingsDatabase::value(const QString &key, const QVariant &defaultValue) const
{
    const QString effectiveKey = d->effectiveKey(key);
    QVariant value = defaultValue;

    SettingsMap::const_iterator i = d->m_settings.constFind(effectiveKey);
    if (i != d->m_settings.constEnd() && i.value().isValid()) {
        value = i.value();
    } else if (d->m_db.isOpen()) {
        // Try to read the value from the database
        QSqlQuery query(d->m_db);
        query.prepare(QLatin1String("SELECT value FROM settings WHERE key = ?"));
        query.addBindValue(effectiveKey);
        query.exec();
        if (query.next()) {
            value = query.value(0);

            if (debug_settings)
                qDebug() << "Retrieved:" << effectiveKey << "=" << value;
        }

        // Cache the result
        d->m_settings.insert(effectiveKey, value);
    }

    return value;
}

bool SettingsDatabase::contains(const QString &key) const
{
    return d->m_settings.contains(d->effectiveKey(key));
}

void SettingsDatabase::remove(const QString &key)
{
    const QString effectiveKey = d->effectiveKey(key);

    // Remove keys from the cache
    foreach (const QString &k, d->m_settings.keys()) {
        // Either it's an exact match, or it matches up to a /
        if (k.startsWith(effectiveKey)
            && (k.length() == effectiveKey.length()
                || k.at(effectiveKey.length()) == QLatin1Char('/')))
        {
            d->m_settings.remove(k);
        }
    }

    if (!d->m_db.isOpen())
        return;

    // Delete keys from the database
    QSqlQuery query(d->m_db);
    query.prepare(QLatin1String("DELETE FROM settings WHERE key = ? OR key LIKE ?"));
    query.addBindValue(effectiveKey);
    query.addBindValue(QString(effectiveKey + QLatin1String("/%")));
    query.exec();
}

void SettingsDatabase::beginGroup(const QString &prefix)
{
    d->m_groups.append(prefix);
}

void SettingsDatabase::endGroup()
{
    d->m_groups.removeLast();
}

QString SettingsDatabase::group() const
{
    return d->effectiveGroup();
}

QStringList SettingsDatabase::childKeys() const
{
    QStringList children;

    const QString g = group();
    QMapIterator<QString, QVariant> i(d->m_settings);
    while (i.hasNext()) {
        const QString &key = i.next().key();
        if (key.startsWith(g) && key.indexOf(QLatin1Char('/'), g.length() + 1) == -1)
            children.append(key.mid(g.length() + 1));
    }

    return children;
}

void SettingsDatabase::beginTransaction()
{
    if (!d->m_db.isOpen())
        return;
    d->m_db.exec(QLatin1String("BEGIN TRANSACTION;"));
}

void SettingsDatabase::endTransaction()
{
    if (!d->m_db.isOpen())
        return;
    d->m_db.exec(QLatin1String("END TRANSACTION;"));
}

void SettingsDatabase::sync()
{
    // TODO: Delay writing of dirty keys and save them here
}
<|endoftext|>


<commit_before><commit_msg>Fixed camera rotation when generating billboard<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/sksl/SkSLPipelineStageCodeGenerator.h"

#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLHCodeGenerator.h"

#if !defined(SKSL_STANDALONE) && SK_SUPPORT_GPU

namespace SkSL {

PipelineStageCodeGenerator::PipelineStageCodeGenerator(
                                                  const Context* context,
                                                  const Program* program,
                                                  ErrorReporter* errors,
                                                  OutputStream* out,
                                                  PipelineStageArgs* outArgs)
    : INHERITED(context, program, errors, out)
    , fArgs(outArgs) {}

void PipelineStageCodeGenerator::writeHeader() {
}

bool PipelineStageCodeGenerator::usesPrecisionModifiers() const {
    return false;
}

String PipelineStageCodeGenerator::getTypeName(const Type& type) {
    return type.name();
}

void PipelineStageCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
                                                       Precedence parentPrecedence) {
    if (b.fOperator == Token::PERCENT) {
        // need to use "%%" instead of "%" b/c the code will be inside of a printf
        Precedence precedence = GetBinaryPrecedence(b.fOperator);
        if (precedence >= parentPrecedence) {
            this->write("(");
        }
        this->writeExpression(*b.fLeft, precedence);
        this->write(" %% ");
        this->writeExpression(*b.fRight, precedence);
        if (precedence >= parentPrecedence) {
            this->write(")");
        }
    } else {
        INHERITED::writeBinaryExpression(b, parentPrecedence);
    }
}

void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
    if (c.fFunction.fBuiltin && c.fFunction.fName == "sample" &&
        c.fArguments[0]->fType.kind() != Type::Kind::kSampler_Kind) {
        SkASSERT(c.fArguments.size() == 2);
        SkASSERT("fragmentProcessor"  == c.fArguments[0]->fType.name() ||
                 "fragmentProcessor?" == c.fArguments[0]->fType.name());
        SkASSERT("float2" == c.fArguments[1]->fType.name());
        SkASSERT(Expression::kVariableReference_Kind == c.fArguments[0]->fKind);
        int index = 0;
        bool found = false;
        for (const auto& p : fProgram) {
            if (ProgramElement::kVar_Kind == p.fKind) {
                const VarDeclarations& decls = (const VarDeclarations&) p;
                for (const auto& raw : decls.fVars) {
                    VarDeclaration& decl = (VarDeclaration&) *raw;
                    if (decl.fVar == &((VariableReference&) *c.fArguments[0]).fVariable) {
                        found = true;
                    } else if (decl.fVar->fType == *fContext.fFragmentProcessor_Type) {
                        ++index;
                    }
                }
            }
            if (found) {
                break;
            }
        }
        SkASSERT(found);
        this->write("%s");
        size_t childCallIndex = fArgs->fFormatArgs.size();
        fArgs->fFormatArgs.push_back(
                Compiler::FormatArg(Compiler::FormatArg::Kind::kChildProcessor, index));
        OutputStream* oldOut = fOut;
        StringStream buffer;
        fOut = &buffer;
        this->writeExpression(*c.fArguments[1], kSequence_Precedence);
        fOut = oldOut;
        fArgs->fFormatArgs[childCallIndex].fCoords = buffer.str();
        return;
    }
    if (c.fFunction.fBuiltin) {
        INHERITED::writeFunctionCall(c);
    } else {
        this->write("%s");
        int index = 0;
        for (const auto& e : fProgram) {
            if (e.fKind == ProgramElement::kFunction_Kind) {
                if (&((FunctionDefinition&) e).fDeclaration == &c.fFunction) {
                    break;
                }
                ++index;
            }
        }
        fArgs->fFormatArgs.push_back(
                Compiler::FormatArg(Compiler::FormatArg::Kind::kFunctionName, index));
        this->write("(");
        const char* separator = "";
        for (const auto& arg : c.fArguments) {
            this->write(separator);
            separator = ", ";
            this->writeExpression(*arg, kSequence_Precedence);
        }
        this->write(")");
    }
}

void PipelineStageCodeGenerator::writeIntLiteral(const IntLiteral& i) {
    this->write(to_string((int32_t) i.fValue));
}

void PipelineStageCodeGenerator::writeVariableReference(const VariableReference& ref) {
    switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
        case SK_INCOLOR_BUILTIN:
            this->write("%s");
            fArgs->fFormatArgs.push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kInput));
            break;
        case SK_OUTCOLOR_BUILTIN:
            this->write("%s");
            fArgs->fFormatArgs.push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kOutput));
            break;
        case SK_MAIN_COORDS_BUILTIN:
            this->write("%s");
            fArgs->fFormatArgs.push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kCoords));
            break;
        default: {
            auto varIndexByFlag = [this, &ref](uint32_t flag) {
                int index = 0;
                bool found = false;
                for (const auto& e : fProgram) {
                    if (found) {
                        break;
                    }
                    if (e.fKind == ProgramElement::Kind::kVar_Kind) {
                        const VarDeclarations& decls = (const VarDeclarations&) e;
                        for (const auto& decl : decls.fVars) {
                            const Variable& var = *((VarDeclaration&) *decl).fVar;
                            if (&var == &ref.fVariable) {
                                found = true;
                                break;
                            }
                            if (var.fModifiers.fFlags & flag) {
                                ++index;
                            }
                        }
                    }
                }
                SkASSERT(found);
                return index;
            };

            if (ref.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag) {
                this->write("%s");
                fArgs->fFormatArgs.push_back(
                        Compiler::FormatArg(Compiler::FormatArg::Kind::kUniform,
                                            varIndexByFlag(Modifiers::kUniform_Flag)));
            } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kVarying_Flag) {
                this->write("_vtx_attr_");
                this->write(to_string(varIndexByFlag(Modifiers::kVarying_Flag)));
            } else {
                this->write(ref.fVariable.fName);
            }
        }
    }
}

void PipelineStageCodeGenerator::writeIfStatement(const IfStatement& s) {
    if (s.fIsStatic) {
        this->write("@");
    }
    INHERITED::writeIfStatement(s);
}

void PipelineStageCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
    if (s.fIsStatic) {
        this->write("@");
    }
    INHERITED::writeSwitchStatement(s);
}

static GrSLType glsltype(const Context& context, const Type& type) {
    if (type == *context.fFloat_Type) {
        return GrSLType::kFloat_GrSLType;
    } else if (type == *context.fHalf_Type) {
        return GrSLType::kHalf_GrSLType;
    } else if (type == *context.fFloat2_Type) {
        return GrSLType::kFloat2_GrSLType;
    } else if (type == *context.fHalf2_Type) {
        return GrSLType::kHalf2_GrSLType;
    } else if (type == *context.fFloat3_Type) {
        return GrSLType::kFloat3_GrSLType;
    } else if (type == *context.fHalf3_Type) {
        return GrSLType::kHalf3_GrSLType;
    } else if (type == *context.fFloat4_Type) {
        return GrSLType::kFloat4_GrSLType;
    } else if (type == *context.fHalf4_Type) {
        return GrSLType::kHalf4_GrSLType;
    } else if (type == *context.fFloat4x4_Type) {
        return GrSLType::kFloat4x4_GrSLType;
    } else if (type == *context.fHalf4x4_Type) {
        return GrSLType::kHalf4x4_GrSLType;
    } else if (type == *context.fVoid_Type) {
        return GrSLType::kVoid_GrSLType;
    }
    SkASSERT(false);
    return GrSLType::kVoid_GrSLType;
}

void PipelineStageCodeGenerator::writeFunction(const FunctionDefinition& f) {
    fFunctionHeader = "";
    OutputStream* oldOut = fOut;
    StringStream buffer;
    fOut = &buffer;
    if (f.fDeclaration.fName == "main") {
        this->write("%s = %s;\n");
        fArgs->fFormatArgs.push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kOutput));
        fArgs->fFormatArgs.push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kInput));
        for (const auto& s : ((Block&) *f.fBody).fStatements) {
            this->writeStatement(*s);
            this->writeLine();
        }
        fOut = oldOut;
        this->write(fFunctionHeader);
        this->write(buffer.str());
    } else {
        const FunctionDeclaration& decl = f.fDeclaration;
        Compiler::GLSLFunction result;
        result.fReturnType = glsltype(fContext, decl.fReturnType);
        result.fName = decl.fName;
        for (const Variable* v : decl.fParameters) {
            result.fParameters.emplace_back(v->fName, glsltype(fContext, v->fType));
        }
        for (const auto& s : ((Block&) *f.fBody).fStatements) {
            this->writeStatement(*s);
            this->writeLine();
        }
        fOut = oldOut;
        result.fBody = buffer.str();
        result.fFormatArgs = std::move(fArgs->fFormatArgs);
        fArgs->fFunctions.push_back(result);
    }
}

void PipelineStageCodeGenerator::writeProgramElement(const ProgramElement& p) {
    if (p.fKind == ProgramElement::kSection_Kind) {
        return;
    }
    if (p.fKind == ProgramElement::kVar_Kind) {
        const VarDeclarations& decls = (const VarDeclarations&) p;
        if (!decls.fVars.size()) {
            return;
        }
        const Variable& var = *((VarDeclaration&) *decls.fVars[0]).fVar;
        if (var.fModifiers.fFlags & (Modifiers::kIn_Flag | Modifiers::kUniform_Flag) ||
            -1 != var.fModifiers.fLayout.fBuiltin) {
            return;
        }
    }
    INHERITED::writeProgramElement(p);
}

} // namespace
#endif
<commit_msg>In pipeline stage code generator, skip emitting varying declarations<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/sksl/SkSLPipelineStageCodeGenerator.h"

#include "src/sksl/SkSLCompiler.h"
#include "src/sksl/SkSLHCodeGenerator.h"

#if !defined(SKSL_STANDALONE) && SK_SUPPORT_GPU

namespace SkSL {

PipelineStageCodeGenerator::PipelineStageCodeGenerator(
                                                  const Context* context,
                                                  const Program* program,
                                                  ErrorReporter* errors,
                                                  OutputStream* out,
                                                  PipelineStageArgs* outArgs)
    : INHERITED(context, program, errors, out)
    , fArgs(outArgs) {}

void PipelineStageCodeGenerator::writeHeader() {
}

bool PipelineStageCodeGenerator::usesPrecisionModifiers() const {
    return false;
}

String PipelineStageCodeGenerator::getTypeName(const Type& type) {
    return type.name();
}

void PipelineStageCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
                                                       Precedence parentPrecedence) {
    if (b.fOperator == Token::PERCENT) {
        // need to use "%%" instead of "%" b/c the code will be inside of a printf
        Precedence precedence = GetBinaryPrecedence(b.fOperator);
        if (precedence >= parentPrecedence) {
            this->write("(");
        }
        this->writeExpression(*b.fLeft, precedence);
        this->write(" %% ");
        this->writeExpression(*b.fRight, precedence);
        if (precedence >= parentPrecedence) {
            this->write(")");
        }
    } else {
        INHERITED::writeBinaryExpression(b, parentPrecedence);
    }
}

void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
    if (c.fFunction.fBuiltin && c.fFunction.fName == "sample" &&
        c.fArguments[0]->fType.kind() != Type::Kind::kSampler_Kind) {
        SkASSERT(c.fArguments.size() == 2);
        SkASSERT("fragmentProcessor"  == c.fArguments[0]->fType.name() ||
                 "fragmentProcessor?" == c.fArguments[0]->fType.name());
        SkASSERT("float2" == c.fArguments[1]->fType.name());
        SkASSERT(Expression::kVariableReference_Kind == c.fArguments[0]->fKind);
        int index = 0;
        bool found = false;
        for (const auto& p : fProgram) {
            if (ProgramElement::kVar_Kind == p.fKind) {
                const VarDeclarations& decls = (const VarDeclarations&) p;
                for (const auto& raw : decls.fVars) {
                    VarDeclaration& decl = (VarDeclaration&) *raw;
                    if (decl.fVar == &((VariableReference&) *c.fArguments[0]).fVariable) {
                        found = true;
                    } else if (decl.fVar->fType == *fContext.fFragmentProcessor_Type) {
                        ++index;
                    }
                }
            }
            if (found) {
                break;
            }
        }
        SkASSERT(found);
        this->write("%s");
        size_t childCallIndex = fArgs->fFormatArgs.size();
        fArgs->fFormatArgs.push_back(
                Compiler::FormatArg(Compiler::FormatArg::Kind::kChildProcessor, index));
        OutputStream* oldOut = fOut;
        StringStream buffer;
        fOut = &buffer;
        this->writeExpression(*c.fArguments[1], kSequence_Precedence);
        fOut = oldOut;
        fArgs->fFormatArgs[childCallIndex].fCoords = buffer.str();
        return;
    }
    if (c.fFunction.fBuiltin) {
        INHERITED::writeFunctionCall(c);
    } else {
        this->write("%s");
        int index = 0;
        for (const auto& e : fProgram) {
            if (e.fKind == ProgramElement::kFunction_Kind) {
                if (&((FunctionDefinition&) e).fDeclaration == &c.fFunction) {
                    break;
                }
                ++index;
            }
        }
        fArgs->fFormatArgs.push_back(
                Compiler::FormatArg(Compiler::FormatArg::Kind::kFunctionName, index));
        this->write("(");
        const char* separator = "";
        for (const auto& arg : c.fArguments) {
            this->write(separator);
            separator = ", ";
            this->writeExpression(*arg, kSequence_Precedence);
        }
        this->write(")");
    }
}

void PipelineStageCodeGenerator::writeIntLiteral(const IntLiteral& i) {
    this->write(to_string((int32_t) i.fValue));
}

void PipelineStageCodeGenerator::writeVariableReference(const VariableReference& ref) {
    switch (ref.fVariable.fModifiers.fLayout.fBuiltin) {
        case SK_INCOLOR_BUILTIN:
            this->write("%s");
            fArgs->fFormatArgs.push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kInput));
            break;
        case SK_OUTCOLOR_BUILTIN:
            this->write("%s");
            fArgs->fFormatArgs.push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kOutput));
            break;
        case SK_MAIN_COORDS_BUILTIN:
            this->write("%s");
            fArgs->fFormatArgs.push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kCoords));
            break;
        default: {
            auto varIndexByFlag = [this, &ref](uint32_t flag) {
                int index = 0;
                bool found = false;
                for (const auto& e : fProgram) {
                    if (found) {
                        break;
                    }
                    if (e.fKind == ProgramElement::Kind::kVar_Kind) {
                        const VarDeclarations& decls = (const VarDeclarations&) e;
                        for (const auto& decl : decls.fVars) {
                            const Variable& var = *((VarDeclaration&) *decl).fVar;
                            if (&var == &ref.fVariable) {
                                found = true;
                                break;
                            }
                            if (var.fModifiers.fFlags & flag) {
                                ++index;
                            }
                        }
                    }
                }
                SkASSERT(found);
                return index;
            };

            if (ref.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag) {
                this->write("%s");
                fArgs->fFormatArgs.push_back(
                        Compiler::FormatArg(Compiler::FormatArg::Kind::kUniform,
                                            varIndexByFlag(Modifiers::kUniform_Flag)));
            } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kVarying_Flag) {
                this->write("_vtx_attr_");
                this->write(to_string(varIndexByFlag(Modifiers::kVarying_Flag)));
            } else {
                this->write(ref.fVariable.fName);
            }
        }
    }
}

void PipelineStageCodeGenerator::writeIfStatement(const IfStatement& s) {
    if (s.fIsStatic) {
        this->write("@");
    }
    INHERITED::writeIfStatement(s);
}

void PipelineStageCodeGenerator::writeSwitchStatement(const SwitchStatement& s) {
    if (s.fIsStatic) {
        this->write("@");
    }
    INHERITED::writeSwitchStatement(s);
}

static GrSLType glsltype(const Context& context, const Type& type) {
    if (type == *context.fFloat_Type) {
        return GrSLType::kFloat_GrSLType;
    } else if (type == *context.fHalf_Type) {
        return GrSLType::kHalf_GrSLType;
    } else if (type == *context.fFloat2_Type) {
        return GrSLType::kFloat2_GrSLType;
    } else if (type == *context.fHalf2_Type) {
        return GrSLType::kHalf2_GrSLType;
    } else if (type == *context.fFloat3_Type) {
        return GrSLType::kFloat3_GrSLType;
    } else if (type == *context.fHalf3_Type) {
        return GrSLType::kHalf3_GrSLType;
    } else if (type == *context.fFloat4_Type) {
        return GrSLType::kFloat4_GrSLType;
    } else if (type == *context.fHalf4_Type) {
        return GrSLType::kHalf4_GrSLType;
    } else if (type == *context.fFloat4x4_Type) {
        return GrSLType::kFloat4x4_GrSLType;
    } else if (type == *context.fHalf4x4_Type) {
        return GrSLType::kHalf4x4_GrSLType;
    } else if (type == *context.fVoid_Type) {
        return GrSLType::kVoid_GrSLType;
    }
    SkASSERT(false);
    return GrSLType::kVoid_GrSLType;
}

void PipelineStageCodeGenerator::writeFunction(const FunctionDefinition& f) {
    fFunctionHeader = "";
    OutputStream* oldOut = fOut;
    StringStream buffer;
    fOut = &buffer;
    if (f.fDeclaration.fName == "main") {
        this->write("%s = %s;\n");
        fArgs->fFormatArgs.push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kOutput));
        fArgs->fFormatArgs.push_back(Compiler::FormatArg(Compiler::FormatArg::Kind::kInput));
        for (const auto& s : ((Block&) *f.fBody).fStatements) {
            this->writeStatement(*s);
            this->writeLine();
        }
        fOut = oldOut;
        this->write(fFunctionHeader);
        this->write(buffer.str());
    } else {
        const FunctionDeclaration& decl = f.fDeclaration;
        Compiler::GLSLFunction result;
        result.fReturnType = glsltype(fContext, decl.fReturnType);
        result.fName = decl.fName;
        for (const Variable* v : decl.fParameters) {
            result.fParameters.emplace_back(v->fName, glsltype(fContext, v->fType));
        }
        for (const auto& s : ((Block&) *f.fBody).fStatements) {
            this->writeStatement(*s);
            this->writeLine();
        }
        fOut = oldOut;
        result.fBody = buffer.str();
        result.fFormatArgs = std::move(fArgs->fFormatArgs);
        fArgs->fFunctions.push_back(result);
    }
}

void PipelineStageCodeGenerator::writeProgramElement(const ProgramElement& p) {
    if (p.fKind == ProgramElement::kSection_Kind) {
        return;
    }
    if (p.fKind == ProgramElement::kVar_Kind) {
        const VarDeclarations& decls = (const VarDeclarations&) p;
        if (!decls.fVars.size()) {
            return;
        }
        const Variable& var = *((VarDeclaration&) *decls.fVars[0]).fVar;
        if (var.fModifiers.fFlags &
                    (Modifiers::kIn_Flag | Modifiers::kUniform_Flag | Modifiers::kVarying_Flag) ||
            -1 != var.fModifiers.fLayout.fBuiltin) {
            return;
        }
    }
    INHERITED::writeProgramElement(p);
}

} // namespace
#endif
<|endoftext|>


<commit_before>/*
 * Scanner.cpp
 *
 *  Created on: Mar 24, 2016
 *      Author: James Hong
 */

#include "scan/Scanner.h"

#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <sstream>

#include "ble/helper.h"
#include "Debug.h"

#define EIR_NAME_SHORT		0x08
#define EIR_NAME_COMPLETE  	0x09

Scanner::Scanner() {
	/*
	 * Set these intervals high since we are filtering duplicates manually.
	 */
	scanInterval = 0x0100;	// TODO these should be configurable
	scanWindow = 0x0010;

	running.reset(new std::atomic_flag(true));
}

Scanner::~Scanner() {
	running->clear();
}

static void scanDaemon(std::shared_ptr<std::atomic_flag> terminated, std::vector<DiscoveryHandler> handlers,
		uint16_t scanInterval, uint16_t scanWindow);
void Scanner::start() {
	std::thread t = std::thread(&scanDaemon, running, handlers, scanInterval, scanWindow);
	t.detach();
}

void Scanner::registerHandler(DiscoveryHandler handler) {
	handlers.push_back(handler);
}

static struct hci_filter startScanHelper(int deviceHandle, uint16_t scanInterval, uint16_t scanWindow) {
	int result = hci_le_set_scan_parameters(deviceHandle,
			0,						// type (0 is passive)
			htobs(scanInterval),	// interval
			htobs(scanWindow), 		// window
			0, 						// own_type
			0, 						// filter
			1000);					// to (for timeout)
	if (result < 0) {
		pwarn("failed to set LE scan parameters");
	}

	result = hci_le_set_scan_enable(deviceHandle, 0x01,		// enable
			0, 			// do not filter duplicates
			1000);		// to (see above)
	if (result < 0) {
		pwarn("failed to set scan enable");
	}

	struct hci_filter oldFilter = { 0 };
	socklen_t oldFilterLen = sizeof(oldFilter);
	result = getsockopt(deviceHandle, SOL_HCI, HCI_FILTER, &oldFilter, &oldFilterLen);
	if (result < 0) {
		throw ScannerException("failed to save old hci filter");
	}

	struct hci_filter newFilter;
	hci_filter_clear(&newFilter);
	hci_filter_set_ptype(HCI_EVENT_PKT, &newFilter);
	hci_filter_set_event(EVT_LE_META_EVENT, &newFilter);
	result = setsockopt(deviceHandle, SOL_HCI, HCI_FILTER, &newFilter, sizeof(newFilter));
	if (result < 0) {
		throw ScannerException("failed to set new hci filter");
	}

	return oldFilter;
}

static void stopScanHelper(int deviceHandle, struct hci_filter oldFilter) {
	int result = hci_le_set_scan_enable(deviceHandle,
			0, 		// disable
			1, 		// filter_dup
			1000);	// to
	if (result < 0) {
		pwarn("failed to disable scan");
	}

	result = setsockopt(deviceHandle, SOL_HCI, HCI_FILTER, &oldFilter, sizeof(oldFilter));
	if (result < 0) {
		pwarn("failed to replace old hci filter");
	}
}

static int deviceHandle = -1;
static struct hci_filter oldFilter;
static void scanDaemon(std::shared_ptr<std::atomic_flag> running, std::vector<DiscoveryHandler> handlers,
		uint16_t scanInterval, uint16_t scanWindow) {
	if (debug) {
		pdebug("scanDaemon started");
	}

	int deviceId = hci_get_route(NULL);
	if (deviceId < 0) {
		throw ScannerException("could not get hci device");
	}

	deviceHandle = hci_open_dev(deviceId);
	if (deviceHandle < 0) {
		throw ScannerException("could not get handle to hci device");
	}

 	oldFilter = startScanHelper(deviceHandle, scanInterval, scanWindow);
	startScanHelper(deviceHandle, scanInterval, scanWindow);
	std::atexit([]{
		if (deviceHandle >= 0) {
			if (debug_scan) {
				pdebug("stopping scan and closing device");
			}
			stopScanHelper(deviceHandle, oldFilter);
			hci_close_dev(deviceHandle);
		}
	});

	uint8_t buf[HCI_MAX_EVENT_SIZE];
	while (running->test_and_set()) {
		int n = read(deviceHandle, buf, sizeof(buf));
		if (n <= 0) {
			break;
		} else if (n < (1 + HCI_EVENT_HDR_SIZE)) {
			if (debug_scan) {
				pwarn("read less than hci evt header");
			}
			continue;
		}

		evt_le_meta_event *meta = (evt_le_meta_event *) (buf + (1 + HCI_EVENT_HDR_SIZE));
		switch (meta->subevent) {
		case EVT_LE_ADVERTISING_REPORT: {
			le_advertising_info *info = (le_advertising_info *) (meta->data + 1);

			std::string addr = ba2str_cpp(info->bdaddr);
			LEPeripheral::AddrType addrType = (info->bdaddr_type == LE_PUBLIC_ADDRESS) ?
					LEPeripheral::AddrType::PUBLIC : LEPeripheral::AddrType::RANDOM;
			std::string name = "";
			int i = 0;
			while (i < info->length) {
				int len = info->data[i];
				if (info->data[i + 1] == EIR_NAME_SHORT || info->data[i + 1] == EIR_NAME_COMPLETE) {
					char name_c_str[len];
					name_c_str[len - 1] = '\0';
					memcpy(name_c_str, info->data + i + 2, len - 1);
					name = std::string(name_c_str);
				}
				i += len + 1;
			}

			if (debug_scan) {
				std::stringstream ss;
				ss << "advertisement for " << addr << "\t"
						<< ((addrType == LEPeripheral::AddrType::PUBLIC) ? "public" : "random") << "\t" << name;
				pdebug(ss.str());
			}

			for (auto &cb : handlers) {
				bool isRunning = running->test_and_set();
				if (isRunning) {
					cb(addr, peripheral_info_t { name, info->bdaddr, addrType });
				} else {
					running->clear();
					break;
				}
			}
			break;
		}
		case EVT_LE_CONN_COMPLETE:	// start scanning again
			startScanHelper(deviceHandle, scanInterval, scanWindow);
			break;
		}
	}

	if (debug) {
		pdebug("scanDaemon exited");
	}
}
<commit_msg>Fix duplicate set scan enable.<commit_after>/*
 * Scanner.cpp
 *
 *  Created on: Mar 24, 2016
 *      Author: James Hong
 */

#include "scan/Scanner.h"

#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cassert>
#include <cstring>
#include <sstream>

#include "ble/helper.h"
#include "Debug.h"

#define EIR_NAME_SHORT		0x08
#define EIR_NAME_COMPLETE  	0x09

Scanner::Scanner() {
	/*
	 * Set these intervals high since we are filtering duplicates manually.
	 */
	scanInterval = 0x0100;	// TODO these should be configurable
	scanWindow = 0x0010;

	running.reset(new std::atomic_flag(true));
}

Scanner::~Scanner() {
	running->clear();
}

static void scanDaemon(std::shared_ptr<std::atomic_flag> terminated, std::vector<DiscoveryHandler> handlers,
		uint16_t scanInterval, uint16_t scanWindow);
void Scanner::start() {
	std::thread t = std::thread(&scanDaemon, running, handlers, scanInterval, scanWindow);
	t.detach();
}

void Scanner::registerHandler(DiscoveryHandler handler) {
	handlers.push_back(handler);
}

static struct hci_filter startScanHelper(int deviceHandle, uint16_t scanInterval, uint16_t scanWindow) {
	int result = hci_le_set_scan_parameters(deviceHandle,
			0,						// type (0 is passive)
			htobs(scanInterval),	// interval
			htobs(scanWindow), 		// window
			0, 						// own_type
			0, 						// filter
			1000);					// to (for timeout)
	if (result < 0) {
		pwarn("failed to set LE scan parameters");
	}

	result = hci_le_set_scan_enable(deviceHandle,
			0x01,		// enable
			0, 			// do not filter duplicates
			1000);		// to (see above)
	if (result < 0) {
		pwarn("failed to set scan enable");
	}

	struct hci_filter oldFilter = { 0 };
	socklen_t oldFilterLen = sizeof(oldFilter);
	result = getsockopt(deviceHandle, SOL_HCI, HCI_FILTER, &oldFilter, &oldFilterLen);
	if (result < 0) {
		throw ScannerException("failed to save old hci filter");
	}

	struct hci_filter newFilter;
	hci_filter_clear(&newFilter);
	hci_filter_set_ptype(HCI_EVENT_PKT, &newFilter);
	hci_filter_set_event(EVT_LE_META_EVENT, &newFilter);
	result = setsockopt(deviceHandle, SOL_HCI, HCI_FILTER, &newFilter, sizeof(newFilter));
	if (result < 0) {
		throw ScannerException("failed to set new hci filter");
	}

	return oldFilter;
}

//static void stopScanHelper(int deviceHandle, struct hci_filter oldFilter) {
//	int result = hci_le_set_scan_enable(deviceHandle,
//			0, 		// disable
//			1, 		// filter_dup
//			1000);	// to
//	if (result < 0) {
//		pwarn("failed to disable scan");
//	}
//
//	result = setsockopt(deviceHandle, SOL_HCI, HCI_FILTER, &oldFilter, sizeof(oldFilter));
//	if (result < 0) {
//		pwarn("failed to replace old hci filter");
//	}
//}

static int deviceHandle = -1;
//static struct hci_filter oldFilter;
static void scanDaemon(std::shared_ptr<std::atomic_flag> running, std::vector<DiscoveryHandler> handlers,
		uint16_t scanInterval, uint16_t scanWindow) {
	if (debug) {
		pdebug("scanDaemon started");
	}

	int deviceId = hci_get_route(NULL);
	if (deviceId < 0) {
		throw ScannerException("could not get hci device");
	}

	deviceHandle = hci_open_dev(deviceId);
	if (deviceHandle < 0) {
		throw ScannerException("could not get handle to hci device");
	}

 	startScanHelper(deviceHandle, scanInterval, scanWindow);
	std::atexit([]{
		if (deviceHandle >= 0) {
			if (debug_scan) {
				pdebug("stopping scan and closing device");
			}
//			stopScanHelper(deviceHandle, oldFilter);
			hci_close_dev(deviceHandle);
		}
	});

	uint8_t buf[HCI_MAX_EVENT_SIZE];
	while (running->test_and_set()) {
		int n = read(deviceHandle, buf, sizeof(buf));
		if (n <= 0) {
			break;
		} else if (n < (1 + HCI_EVENT_HDR_SIZE)) {
			if (debug_scan) {
				pwarn("read less than hci evt header");
			}
			continue;
		}

		evt_le_meta_event *meta = (evt_le_meta_event *) (buf + (1 + HCI_EVENT_HDR_SIZE));
		switch (meta->subevent) {
		case EVT_LE_ADVERTISING_REPORT: {
			le_advertising_info *info = (le_advertising_info *) (meta->data + 1);

			std::string addr = ba2str_cpp(info->bdaddr);
			LEPeripheral::AddrType addrType = (info->bdaddr_type == LE_PUBLIC_ADDRESS) ?
					LEPeripheral::AddrType::PUBLIC : LEPeripheral::AddrType::RANDOM;
			std::string name = "";
			int i = 0;
			while (i < info->length) {
				int len = info->data[i];
				if (info->data[i + 1] == EIR_NAME_SHORT || info->data[i + 1] == EIR_NAME_COMPLETE) {
					char name_c_str[len];
					name_c_str[len - 1] = '\0';
					memcpy(name_c_str, info->data + i + 2, len - 1);
					name = std::string(name_c_str);
				}
				i += len + 1;
			}

			if (debug_scan) {
				std::stringstream ss;
				ss << "advertisement for " << addr << "\t"
						<< ((addrType == LEPeripheral::AddrType::PUBLIC) ? "public" : "random") << "\t" << name;
				pdebug(ss.str());
			}

			for (auto &cb : handlers) {
				bool isRunning = running->test_and_set();
				if (isRunning) {
					/*
					 * TODO: not quite memory safe
					 */
					cb(addr, peripheral_info_t { name, info->bdaddr, addrType });
				} else {
					running->clear();
					break;
				}
			}
			break;
		}
		case EVT_LE_CONN_COMPLETE:	// start scanning again
			startScanHelper(deviceHandle, scanInterval, scanWindow);
			break;
		}
	}

	if (debug) {
		pdebug("scanDaemon exited");
	}
}
<|endoftext|>