<commit_before>/*
 * Copyright (C) 2015 Dan Leinir Turthra Jensen <admin@leinir.dk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) version 3, or any
 * later version accepted by the membership of KDE e.V. (or its
 * successor approved by the membership of KDE e.V.), which shall
 * act as a proxy defined in Section 6 of version 3 of the license.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "AcbfPage.h"
#include "AcbfTextlayer.h"
#include "AcbfFrame.h"
#include "AcbfJump.h"
#include "AcbfTextarea.h"

#include <QHash>
#include <QXmlStreamReader>

#include <acbf_debug.h>

using namespace AdvancedComicBookFormat;

class Page::Private
{
public:
    Private()
        : isCoverPage(false)
    {}
    QString bgcolor;
    QString transition;
    QHash<QString, QString> title;
    QString imageHref;
    QHash<QString, Textlayer*> textLayers;
    QList<Frame*> frames;
    QList<Jump*> jumps;
    bool isCoverPage;
};

Page::Page(Document* parent)
    : QObject(parent)
    , d(new Private)
{
    static const int typeId = qRegisterMetaType<Page*>("Page*");
    Q_UNUSED(typeId);
}

Page::~Page() = default;

void Page::toXml(QXmlStreamWriter* writer)
{
    if(d->isCoverPage) {
        writer->writeStartElement(QStringLiteral("coverpage"));
    }
    else {
        writer->writeStartElement(QStringLiteral("page"));
    }

    if(!d->bgcolor.isEmpty()) {
        writer->writeAttribute(QStringLiteral("bgcolor"), d->bgcolor);
    }
    if(!d->transition.isEmpty()) {
        writer->writeAttribute(QStringLiteral("transition"), d->transition);
    }

    QHashIterator<QString, QString> titles(d->title);
    while(titles.hasNext()) {
        titles.next();
        writer->writeStartElement(QStringLiteral("title"));
        writer->writeAttribute(QStringLiteral("lang"), titles.key());
        writer->writeCharacters(titles.value());
        writer->writeEndElement();
    }

    writer->writeStartElement(QStringLiteral("image"));
    writer->writeAttribute(QStringLiteral("href"), d->imageHref);
    writer->writeEndElement();

    for(Textlayer* layer : d->textLayers.values()) {
        layer->toXml(writer);
    }

     for(Frame* frame : d->frames) {
         frame->toXml(writer);
     }

     for(Jump* jump : d->jumps) {
         jump->toXml(writer);
     }

    writer->writeEndElement();
}

bool Page::fromXml(QXmlStreamReader *xmlReader)
{
    setBgcolor(xmlReader->attributes().value(QStringLiteral("bgcolor")).toString());
    setTransition(xmlReader->attributes().value(QStringLiteral("transition")).toString());
    while(xmlReader->readNextStartElement())
    {
        if(xmlReader->name() == QStringLiteral("title"))
        {
            d->title[xmlReader->attributes().value(QStringLiteral("lang")).toString()] = xmlReader->readElementText();
        }
        else if(xmlReader->name() == QStringLiteral("image"))
        {
            /**
             * There are some acbf files out there that have backslashes in their
             * image href. This is probably a mistake from windows users, but not proper XML.
             * We should thus replace those with forward slashes so the image can be loaded.
             */
            QString href = xmlReader->attributes().value(QStringLiteral("href")).toString();
            setImageHref(href.replace(QStringLiteral("\\"), QStringLiteral("/")));
            xmlReader->skipCurrentElement();
        }
        else if(xmlReader->name() == QStringLiteral("text-layer"))
        {
            Textlayer* newLayer = new Textlayer(this);
            if(!newLayer->fromXml(xmlReader)) {
                return false;
            }
            d->textLayers[newLayer->language()] = newLayer;
        }
        else if(xmlReader->name() == QStringLiteral("frame"))
        {
            Frame* newFrame = new Frame(this);
            if(!newFrame->fromXml(xmlReader)) {
                return false;
            }
            d->frames.append(newFrame);

            // Frames have no child elements, so we need to force the reader to go to the next one.
            xmlReader->readNext();
        }
         else if(xmlReader->name() == QStringLiteral("jump"))
         {
             Jump* newJump = new Jump(this);
             if(!newJump->fromXml(xmlReader)) {
                 return false;
             }

             // Jumps have no child elements, so we need to force the reader to go to the next one.
             d->jumps.append(newJump);
             xmlReader->readNext();
         }
        else
        {
            qCWarning(ACBF_LOG) << Q_FUNC_INFO << "currently unsupported subsection:" << xmlReader->name();
            xmlReader->skipCurrentElement();
        }
    }
    if (xmlReader->hasError()) {
        qCWarning(ACBF_LOG) << Q_FUNC_INFO << "Failed to read ACBF XML document at token" << xmlReader->name() << "(" << xmlReader->lineNumber() << ":" << xmlReader->columnNumber() << ") The reported error was:" << xmlReader->errorString();
    }
    qCDebug(ACBF_LOG) << Q_FUNC_INFO << "Created page for image" << d->imageHref;
    return !xmlReader->hasError();
}

QString Page::bgcolor() const
{
    return d->bgcolor;
}

void Page::setBgcolor(const QString& newColor)
{
    d->bgcolor = newColor;
    emit bgcolorChanged();
}

QString Page::transition() const
{
    return d->transition;
}

void Page::setTransition(const QString& transition)
{
    d->transition = transition;
    emit transitionChanged();
}

QStringList Page::availableTransitions()
{
    return {
        QStringLiteral("fade"), // (old page fades out into background, then new page fades in)
        QStringLiteral("blend"), // (new page blends in the image while old page blends out)
        QStringLiteral("scroll_right"), // (screen scrolls to the right to a new page; reversed behavior applies when moving to previous page)
        QStringLiteral("scroll_down"), // (screen scrolls down to a new page; reversed behavior applies when moving to previous page)
        QStringLiteral("none") // (no transition animation happens)
    };
}

QStringList Page::titleForAllLanguages() const
{
    return d->title.values();
}

QString Page::title(const QString& language) const
{
    if (d->title.count()==0) {
        return "";
    }
    if (!d->title.keys().contains(language)) {
        d->title.values().at(0);
    }

    QString title = d->title.value(language);

    if (title.isEmpty()) {
        title = d->title.values().at(0);
    }

    return title;
}

void Page::setTitle(const QString& title, const QString& language)
{
    if(title.isEmpty())
    {
        d->title.remove(language);
    }
    else
    {
        d->title[language] = title;
    }
    emit titlesChanged();
}

QString Page::imageHref() const
{
    return d->imageHref;
}

void Page::setImageHref(const QString& imageHref)
{
    d->imageHref = imageHref;
}

QList<Textlayer *> Page::textLayersForAllLanguages() const
{
    return d->textLayers.values();
}

Textlayer * Page::textLayer(const QString& language) const
{
    if (!d->textLayers.keys().contains("") && language == QString()) {
        return d->textLayers.values().at(0);
    }
    return d->textLayers.value(language);
}

void Page::setTextLayer(Textlayer* textlayer, const QString& language)
{
    if(textlayer)
    {
        d->textLayers[language] = textlayer;
    }
    else
    {
        d->textLayers.remove(language);
    }
    emit textLayerLanguagesChanged();
}

void Page::addTextLayer(const QString &language)
{
    Textlayer* textLayer = new Textlayer(this);
    textLayer->setLanguage(language);
    setTextLayer(textLayer, language);
}

void Page::removeTextLayer(const QString &language)
{
    setTextLayer(nullptr, language);
}

void Page::duplicateTextLayer(const QString &languageFrom, const QString &languageTo)
{
    Textlayer* to = new Textlayer(this);
    to->setLanguage(languageTo);
    if (d->textLayers[languageFrom]) {
        Textlayer* from = d->textLayers[languageFrom];
        to->setBgcolor(from->bgcolor());
        for (int i=0; i<from->textareaPointStrings().size(); i++) {
            to->addTextarea(i);
            to->textarea(i)->setBgcolor(from->textarea(i)->bgcolor());
            to->textarea(i)->setInverted(from->textarea(i)->inverted());
            to->textarea(i)->setTransparent(from->textarea(i)->transparent());
            to->textarea(i)->setTextRotation(from->textarea(i)->textRotation());
            to->textarea(i)->setType(from->textarea(i)->type());
            to->textarea(i)->setParagraphs(from->textarea(i)->paragraphs());
            for (int p=0; p<from->textarea(i)->pointCount(); p++) {
                to->textarea(i)->addPoint(from->textarea(i)->point(p));
            }
        }
    }
    setTextLayer(to);
}

QStringList Page::textLayerLanguages() const
{
    if (d->textLayers.isEmpty()) {
        return QStringList();
    }
    return d->textLayers.keys();
}

QList<Frame *> Page::frames() const
{
    return d->frames;
}

Frame * Page::frame(int index) const
{
    return d->frames.at(index);
}

int Page::frameIndex(Frame* frame) const
{
    return d->frames.indexOf(frame);
}

void Page::addFrame(Frame* frame, int index)
{
    if(index > -1 && d->frames.count() < index) {
        d->frames.insert(index, frame);
    }
    else {
        d->frames.append(frame);
    }
    emit framePointStringsChanged();
}

void Page::removeFrame(Frame* frame)
{
    d->frames.removeAll(frame);
    emit framePointStringsChanged();
}

void Page::removeFrame(int index)
{
    removeFrame(frame(index));
}

void Page::addFrame(int index)
{
    Frame* frame = new Frame(this);
    addFrame(frame, index);
}

bool Page::swapFrames(int swapThis, int withThis)
{
    if(swapThis > -1 && withThis > -1) {
        d->frames.swapItemsAt(swapThis, withThis);
        emit framePointStringsChanged();
        return true;
    }
    return false;
}

QStringList Page::framePointStrings()
{
    QStringList frameList;
    for (int i=0; i<d->frames.size(); i++) {
        QStringList framePoints;
        for (int p=0; p< frame(i)->pointCount(); p++) {
            framePoints.append(QString("%1,%2").arg(frame(i)->point(p).x()).arg(frame(i)->point(p).y()));
        }
        frameList.append(framePoints.join(" "));
    }

    return frameList;
}

QObjectList Page::jumps() const
{
    QObjectList jumpsList;

    for(int i = 0; i < d->jumps.size(); i++) {
        jumpsList.append(d->jumps.at(i));
    }

    return jumpsList;
}

Jump* Page::jump(int index) const
{
    return d->jumps.at(index);
}

int Page::jumpIndex(Jump* jump) const
{
    return d->jumps.indexOf(jump);
}

void Page::addJump(Jump* jump, int index)
{
    if(index > -1 && d->jumps.count() < index) {
        d->jumps.insert(index, jump);
    }
    else {
        d->jumps.append(jump);
    }
    emit jumpsChanged();
}

void Page::addJump(int pageIndex, int index)
{
    Jump* jump = new Jump(this);
    jump->setPageIndex(pageIndex);
    addJump(jump, index);
}

void Page::removeJump(Jump* jump)
{
    d->jumps.removeAll(jump);
    emit jumpsChanged();
}

void Page::removeJump(int index)
{
    removeJump(jump(index));
}

bool Page::swapJumps(int swapThis, int withThis)
{
    if(swapThis > -1 && withThis > -1) {
        d->jumps.swapItemsAt(swapThis, withThis);
        emit jumpsChanged();
        return true;
    }
    return false;
}

bool Page::isCoverPage() const
{
    return d->isCoverPage;
}

void Page::setIsCoverPage(bool isCoverPage)
{
    d->isCoverPage = isCoverPage;
}
<commit_msg>Refactor `addJump` method to listen to `jump` properties<commit_after>/*
 * Copyright (C) 2015 Dan Leinir Turthra Jensen <admin@leinir.dk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) version 3, or any
 * later version accepted by the membership of KDE e.V. (or its
 * successor approved by the membership of KDE e.V.), which shall
 * act as a proxy defined in Section 6 of version 3 of the license.
 *
 * 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "AcbfPage.h"
#include "AcbfTextlayer.h"
#include "AcbfFrame.h"
#include "AcbfJump.h"
#include "AcbfTextarea.h"

#include <QHash>
#include <QXmlStreamReader>
#include <QTimer>

#include <acbf_debug.h>

using namespace AdvancedComicBookFormat;

class Page::Private
{
public:
    Private()
        : isCoverPage(false)
    {
        jumpsUpdateTimer.setSingleShot(true);
        jumpsUpdateTimer.setInterval(0);
    }
    QString bgcolor;
    QString transition;
    QHash<QString, QString> title;
    QString imageHref;
    QHash<QString, Textlayer*> textLayers;
    QList<Frame*> frames;
    QList<Jump*> jumps;
    QTimer jumpsUpdateTimer;
    bool isCoverPage;
};

Page::Page(Document* parent)
    : QObject(parent)
    , d(new Private)
{
    static const int typeId = qRegisterMetaType<Page*>("Page*");
    Q_UNUSED(typeId);

    QObject::connect(&d->jumpsUpdateTimer, &QTimer::timeout, &d->jumpsUpdateTimer, [this]() {
        emit jumpsChanged();
    });
}

Page::~Page() = default;

void Page::toXml(QXmlStreamWriter* writer)
{
    if(d->isCoverPage) {
        writer->writeStartElement(QStringLiteral("coverpage"));
    }
    else {
        writer->writeStartElement(QStringLiteral("page"));
    }

    if(!d->bgcolor.isEmpty()) {
        writer->writeAttribute(QStringLiteral("bgcolor"), d->bgcolor);
    }
    if(!d->transition.isEmpty()) {
        writer->writeAttribute(QStringLiteral("transition"), d->transition);
    }

    QHashIterator<QString, QString> titles(d->title);
    while(titles.hasNext()) {
        titles.next();
        writer->writeStartElement(QStringLiteral("title"));
        writer->writeAttribute(QStringLiteral("lang"), titles.key());
        writer->writeCharacters(titles.value());
        writer->writeEndElement();
    }

    writer->writeStartElement(QStringLiteral("image"));
    writer->writeAttribute(QStringLiteral("href"), d->imageHref);
    writer->writeEndElement();

    for(Textlayer* layer : d->textLayers.values()) {
        layer->toXml(writer);
    }

     for(Frame* frame : d->frames) {
         frame->toXml(writer);
     }

     for(Jump* jump : d->jumps) {
         jump->toXml(writer);
     }

    writer->writeEndElement();
}

bool Page::fromXml(QXmlStreamReader *xmlReader)
{
    setBgcolor(xmlReader->attributes().value(QStringLiteral("bgcolor")).toString());
    setTransition(xmlReader->attributes().value(QStringLiteral("transition")).toString());
    while(xmlReader->readNextStartElement())
    {
        if(xmlReader->name() == QStringLiteral("title"))
        {
            d->title[xmlReader->attributes().value(QStringLiteral("lang")).toString()] = xmlReader->readElementText();
        }
        else if(xmlReader->name() == QStringLiteral("image"))
        {
            /**
             * There are some acbf files out there that have backslashes in their
             * image href. This is probably a mistake from windows users, but not proper XML.
             * We should thus replace those with forward slashes so the image can be loaded.
             */
            QString href = xmlReader->attributes().value(QStringLiteral("href")).toString();
            setImageHref(href.replace(QStringLiteral("\\"), QStringLiteral("/")));
            xmlReader->skipCurrentElement();
        }
        else if(xmlReader->name() == QStringLiteral("text-layer"))
        {
            Textlayer* newLayer = new Textlayer(this);
            if(!newLayer->fromXml(xmlReader)) {
                return false;
            }
            d->textLayers[newLayer->language()] = newLayer;
        }
        else if(xmlReader->name() == QStringLiteral("frame"))
        {
            Frame* newFrame = new Frame(this);
            if(!newFrame->fromXml(xmlReader)) {
                return false;
            }
            d->frames.append(newFrame);

            // Frames have no child elements, so we need to force the reader to go to the next one.
            xmlReader->readNext();
        }
         else if(xmlReader->name() == QStringLiteral("jump"))
         {
             Jump* newJump = new Jump(this);
             if(!newJump->fromXml(xmlReader)) {
                 return false;
             }

             // Jumps have no child elements, so we need to force the reader to go to the next one.
             addJump(newJump, -1);
             xmlReader->readNext();
         }
        else
        {
            qCWarning(ACBF_LOG) << Q_FUNC_INFO << "currently unsupported subsection:" << xmlReader->name();
            xmlReader->skipCurrentElement();
        }
    }
    if (xmlReader->hasError()) {
        qCWarning(ACBF_LOG) << Q_FUNC_INFO << "Failed to read ACBF XML document at token" << xmlReader->name() << "(" << xmlReader->lineNumber() << ":" << xmlReader->columnNumber() << ") The reported error was:" << xmlReader->errorString();
    }
    qCDebug(ACBF_LOG) << Q_FUNC_INFO << "Created page for image" << d->imageHref;
    return !xmlReader->hasError();
}

QString Page::bgcolor() const
{
    return d->bgcolor;
}

void Page::setBgcolor(const QString& newColor)
{
    d->bgcolor = newColor;
    emit bgcolorChanged();
}

QString Page::transition() const
{
    return d->transition;
}

void Page::setTransition(const QString& transition)
{
    d->transition = transition;
    emit transitionChanged();
}

QStringList Page::availableTransitions()
{
    return {
        QStringLiteral("fade"), // (old page fades out into background, then new page fades in)
        QStringLiteral("blend"), // (new page blends in the image while old page blends out)
        QStringLiteral("scroll_right"), // (screen scrolls to the right to a new page; reversed behavior applies when moving to previous page)
        QStringLiteral("scroll_down"), // (screen scrolls down to a new page; reversed behavior applies when moving to previous page)
        QStringLiteral("none") // (no transition animation happens)
    };
}

QStringList Page::titleForAllLanguages() const
{
    return d->title.values();
}

QString Page::title(const QString& language) const
{
    if (d->title.count()==0) {
        return "";
    }
    if (!d->title.keys().contains(language)) {
        d->title.values().at(0);
    }

    QString title = d->title.value(language);

    if (title.isEmpty()) {
        title = d->title.values().at(0);
    }

    return title;
}

void Page::setTitle(const QString& title, const QString& language)
{
    if(title.isEmpty())
    {
        d->title.remove(language);
    }
    else
    {
        d->title[language] = title;
    }
    emit titlesChanged();
}

QString Page::imageHref() const
{
    return d->imageHref;
}

void Page::setImageHref(const QString& imageHref)
{
    d->imageHref = imageHref;
}

QList<Textlayer *> Page::textLayersForAllLanguages() const
{
    return d->textLayers.values();
}

Textlayer * Page::textLayer(const QString& language) const
{
    if (!d->textLayers.keys().contains("") && language == QString()) {
        return d->textLayers.values().at(0);
    }
    return d->textLayers.value(language);
}

void Page::setTextLayer(Textlayer* textlayer, const QString& language)
{
    if(textlayer)
    {
        d->textLayers[language] = textlayer;
    }
    else
    {
        d->textLayers.remove(language);
    }
    emit textLayerLanguagesChanged();
}

void Page::addTextLayer(const QString &language)
{
    Textlayer* textLayer = new Textlayer(this);
    textLayer->setLanguage(language);
    setTextLayer(textLayer, language);
}

void Page::removeTextLayer(const QString &language)
{
    setTextLayer(nullptr, language);
}

void Page::duplicateTextLayer(const QString &languageFrom, const QString &languageTo)
{
    Textlayer* to = new Textlayer(this);
    to->setLanguage(languageTo);
    if (d->textLayers[languageFrom]) {
        Textlayer* from = d->textLayers[languageFrom];
        to->setBgcolor(from->bgcolor());
        for (int i=0; i<from->textareaPointStrings().size(); i++) {
            to->addTextarea(i);
            to->textarea(i)->setBgcolor(from->textarea(i)->bgcolor());
            to->textarea(i)->setInverted(from->textarea(i)->inverted());
            to->textarea(i)->setTransparent(from->textarea(i)->transparent());
            to->textarea(i)->setTextRotation(from->textarea(i)->textRotation());
            to->textarea(i)->setType(from->textarea(i)->type());
            to->textarea(i)->setParagraphs(from->textarea(i)->paragraphs());
            for (int p=0; p<from->textarea(i)->pointCount(); p++) {
                to->textarea(i)->addPoint(from->textarea(i)->point(p));
            }
        }
    }
    setTextLayer(to);
}

QStringList Page::textLayerLanguages() const
{
    if (d->textLayers.isEmpty()) {
        return QStringList();
    }
    return d->textLayers.keys();
}

QList<Frame *> Page::frames() const
{
    return d->frames;
}

Frame * Page::frame(int index) const
{
    return d->frames.at(index);
}

int Page::frameIndex(Frame* frame) const
{
    return d->frames.indexOf(frame);
}

void Page::addFrame(Frame* frame, int index)
{
    if(index > -1 && d->frames.count() < index) {
        d->frames.insert(index, frame);
    }
    else {
        d->frames.append(frame);
    }
    emit framePointStringsChanged();
}

void Page::removeFrame(Frame* frame)
{
    d->frames.removeAll(frame);
    emit framePointStringsChanged();
}

void Page::removeFrame(int index)
{
    removeFrame(frame(index));
}

void Page::addFrame(int index)
{
    Frame* frame = new Frame(this);
    addFrame(frame, index);
}

bool Page::swapFrames(int swapThis, int withThis)
{
    if(swapThis > -1 && withThis > -1) {
        d->frames.swapItemsAt(swapThis, withThis);
        emit framePointStringsChanged();
        return true;
    }
    return false;
}

QStringList Page::framePointStrings()
{
    QStringList frameList;
    for (int i=0; i<d->frames.size(); i++) {
        QStringList framePoints;
        for (int p=0; p< frame(i)->pointCount(); p++) {
            framePoints.append(QString("%1,%2").arg(frame(i)->point(p).x()).arg(frame(i)->point(p).y()));
        }
        frameList.append(framePoints.join(" "));
    }

    return frameList;
}

QObjectList Page::jumps() const
{
    QObjectList jumpsList;

    for(int i = 0; i < d->jumps.size(); i++) {
        jumpsList.append(d->jumps.at(i));
    }

    return jumpsList;
}

Jump* Page::jump(int index) const
{
    return d->jumps.at(index);
}

int Page::jumpIndex(Jump* jump) const
{
    return d->jumps.indexOf(jump);
}

void Page::addJump(Jump* jump, int index)
{
    QObject::connect(jump, &Jump::pointCountChanged, &d->jumpsUpdateTimer, [this]() {
        d->jumpsUpdateTimer.start();
    });
    QObject::connect(jump, &Jump::boundsChanged, &d->jumpsUpdateTimer, [this]() {
        d->jumpsUpdateTimer.start();
    });
    QObject::connect(jump, &Jump::pageIndexChanged, &d->jumpsUpdateTimer, [this]() {
        d->jumpsUpdateTimer.start();
    });
    QObject::connect(jump, &QObject::destroyed, &d->jumpsUpdateTimer, [this]() {
        d->jumpsUpdateTimer.start();
    });

    if(index > -1 && index < d->jumps.count()) {
        d->jumps.insert(index, jump);
    } else {
        d->jumps.append(jump);
    }

    emit jumpsChanged();
}

void Page::addJump(int pageIndex, int index)
{
    Jump* jump = new Jump(this);
    jump->setPageIndex(pageIndex);
    addJump(jump, index);
}

void Page::removeJump(Jump* jump)
{
    d->jumps.removeAll(jump);
    emit jumpsChanged();
}

void Page::removeJump(int index)
{
    removeJump(jump(index));
}

bool Page::swapJumps(int swapThis, int withThis)
{
    if(swapThis > -1 && withThis > -1) {
        d->jumps.swapItemsAt(swapThis, withThis);
        emit jumpsChanged();
        return true;
    }
    return false;
}

bool Page::isCoverPage() const
{
    return d->isCoverPage;
}

void Page::setIsCoverPage(bool isCoverPage)
{
    d->isCoverPage = isCoverPage;
}
<|endoftext|>


<commit_before>#include "parameters.h"
#include <vector>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include "misc.h"
#include <iostream>
#include "parse_json.h"

Parameters parameters;

static struct ParameterDef {
	const char 	*name;
	PType		datatype;
	const char     *defval;
	char		cmdline;
} pdefs[]={
/* network */
	 {"udpMTU", PInteger, "512"}
	,{"NetworkFPS", PDouble, "60"}
	,{"serverPort", PInteger, "1330"}
	,{"clientPort", PInteger, "1320"}
	,{"connectionTimeout", PDouble, "10000"}
	,{"disconnectionTimeout", PDouble, "2000"}
	,{"broadcastPeriod", PDouble, "500"}
	,{"udpPortRange", PInteger, "100"}
	,{"server", PBoolean, "0"}
	,{"join", PString, ""}

/* game */
	,{"missileDelay", PDouble, "200"}
	,{"tankDiameter", PDouble, "128"}
	,{"tankSpriteName", PString, "car"}
	,{"canonSpriteName", PString, "canon"}
	,{"missileSpriteName", PString, "bullet"}
	,{"canonRotationSpeed", PDouble, "0.00523"} /* rad/ms */
	,{"tankRotationSpeed", PDouble, "0.00523"}
	,{"linearTankSpeed", PDouble, "0.3"} /* px per millisecond */
	,{"missileDuration", PDouble, "2000"}
	,{"missileSpeed", PDouble, "0.9"}
	,{"missileDiameter", PDouble, "16"}

/* engine */
	,{"minFPS", PDouble, "15"}
	,{"maxFPS", PDouble, "60"}
	,{"width", PInteger, "0"} /* 0 = desktop size */
	,{"height", PInteger, "0"}

/* file system */
	,{"spritesDirectory", PString, "sprites/"}
	,{"spritesExtension", PString, ".png"}
	,{"defaultFontName", PString, "sans-serif:slant=roman:weight=normal:lang=en_US:scalable=true"}

	,{"fullscreen", PBoolean, "1", 'f'}
	,{"map", PString, "map2.json", 'm'}
	,{"keymap", PString, "keymap.json", 'k'}
	,{"config", PString, "tankfighter.cfg", 'c'}
	,{"help", PBoolean, "0", 'h'}
};

long Parameters::getAsLong (const char *name) {
	long res;
	if (!string2long(values[name].c_str(), &res)) return 0;
	return res;
}
double Parameters::getAsDouble (const char *name) {
	double res;
	if (!string2dbl(values[name].c_str(), &res)) return 0;
	return res;
}
bool Parameters::getAsBoolean (const char *name) {
	long res;
	if (!string2long(values[name].c_str(), &res)) return 0;
	return res;
}
std::string Parameters::getAsString (const char *name) {
	return values[name];
}
bool Parameters::set(const char *name, const std::string &value) {
	const char *valstr = value.c_str();
	VarmapIterator it = variables.find(name);
	if (it == variables.end()) {
		fprintf(stderr, "Variable %s not found\n", name);
		return false;
	}
	PVariable &v = (*it).second;
	if (v.datatype == PInteger || v.datatype == PBoolean) {
		long lv=0;
		if (!string2long(valstr, &lv)) {
			fprintf(stderr, "Value %s invalid for variable %s\n", valstr, name);
			return false;
		}
		if (v.datatype == PBoolean && lv != 1 && lv != 0) return false;
	} else if (v.datatype == PDouble) {
		double dv;
		if (!string2dbl(valstr, &dv)) {
			fprintf(stderr, "Value %s invalid for variable %s\n", valstr, name);
			return false;
		}
	} else if (v.datatype != PString) {
		fprintf(stderr, "Internal error: Variable %s has unknown data type\n", name);
		return false;
	}
	values[name]=value;
	return true;
}
Parameters::Parameters() {
	for(size_t i=0; i < sizeof(pdefs)/sizeof(pdefs[0]); i++) {
		ParameterDef &pdef = pdefs[i];
		PVariable v;
		v.datatype = pdef.datatype;
		v.defval = pdef.defval;
		variables.insert(VarmapVal(pdef.name, v));
		set(pdef.name, pdef.defval);
		/*values.insert(pdef.name, pdef.defval);*/
	}
	parseFile(config().c_str());
}
bool Parameters::parseCmdline (int argc, char **argv) {
	int index = 0;
	std::vector<struct option> options(variables.size());
	size_t i=0;
	for(VarmapIterator it=variables.begin(); it != variables.end(); ++it) {
		VarmapVal v = *it;
		struct option o;
		o.name = v.first.c_str();
		o.has_arg = (v.second.datatype == PBoolean ? optional_argument  : required_argument);
		o.flag = NULL;
		o.val = 0;
		options[i++] = o;
	}
	while (getopt_long(argc, argv, "", &options[0], &index) != -1) {
		if (optarg) {
			set(options[index].name, optarg);
		} else {
			set(options[index].name, "1");
		}
	}
	if (getAsBoolean("help")) {
		outputHelpString(std::cout);
	}
	return true;
}
void Parameters::outputHelpString (std::ostream &out) {
	for(VarmapIterator it=variables.begin(); it != variables.end(); ++it) {
		VarmapVal v = *it;
		PType dt = v.second.datatype;
		std::string type;
		if (dt == PString) type = "string";
		if (dt == PBoolean) type = "boolean";
		if (dt == PInteger) type = "integer";
		if (dt == PDouble) type = "float";
		out << "\t--" << v.first << " " << type << " with default = " << v.second.defval << "\n";
	}
}
bool Parameters::parseFile (const char *config_file) {
	json_value *p = json_parse_file(config_file, parameters.config_magic().c_str());
	if (!p) return false;
	const json_value *map = access_json_hash(p, "parameters");
	if ((!map) || map->type != json_object) {
		json_value_free(p);
		fprintf(stderr, "parameters hash must be present and must be an associative array\n");
		return false;
	}
	for (unsigned i=0; i < map->u.object.length; i++) {
		const char *variable=map->u.object.values[i].name;
		const json_value *val = map->u.object.values[i].value;
		set(variable, val);
	}

	json_value_free(p);
	return true;
}
bool Parameters::set(const char *name, const json_value *value) {
	VarmapIterator it = variables.find(name);
	std::string sval;
	if (it == variables.end()) {
		fprintf(stderr, "Variable %s doesn't exist\n", name);
		return false;
	}
	PVariable &v = (*it).second;
	int d1 = v.datatype, d2 = value->type;
	if (!(d1 == d2 || (d1 == PBoolean && d2 == json_integer) || d2 == json_string)) {
		fprintf(stderr, "Variable assignment type mismatch for %s\n", name);
		return false;
	}
	if (value->type == json_boolean) sval = tostring((int)value->u.boolean);
	if (value->type == json_integer) sval = tostring(value->u.integer);
	if (value->type == json_double)  sval = tostring(value->u.dbl);
	if (value->type == json_string)  {
		char *sp = json_string_to_cstring(value);
		if (sp) sval=sp;
		free(sp);
	}
	return set(name, sval);
}

unsigned Parameters::C2S_Packet_interval_US(void) {return 10000;}
double Parameters::resendPacketAfterMS(void) {return 500;}
double Parameters::clientsCleanupIntervalMS(void) {return 1000;}
double Parameters::maxDupPacketTimeSecs(void) {return 10;}
double Parameters::minWallDistance(void) {return 1e-3;}
std::string Parameters::keymap_magic(void) {return "ktank-ctrl-map";}
std::string Parameters::map_magic(void) {return "ktank-map";}
std::string Parameters::config_magic(void) {return "ktank-config";}
double Parameters::joyTankSpeedCalibration() {return 0.3;}
double Parameters::joyCanonDirectionCalibration() {return 0.15;}
double Parameters::joyDefaultCalibration() {return 0.2;}
int Parameters::serverDiscoveryTimeoutMS() {return 2000;}
int Parameters::serverDiscoveryPeriodMS() {return 500;}
/* constants for networking */
unsigned Parameters::UDP_MTU(void) {return getAsLong("udpMTU");}
unsigned short Parameters::serverPort(void) {return getAsLong("serverPort");}
unsigned short Parameters::clientPort(void) {return getAsLong("clientPort");}
double Parameters::connectionTimeoutInSecs(void) {return getAsDouble("connectionTimeout")*1e-3;}
double Parameters::disconnectionTimeoutMS(void) {return getAsDouble("disconnectionTimeout");}
int Parameters::udpPortRange(void) {return getAsLong("udpPortRange");}

/* constants for game rules */
double Parameters::missileDelayMS(void) {return getAsDouble("missileDelay");}
double Parameters::tankDiameter(void)  {return getAsDouble("tankDiameter");}
std::string Parameters::tankSpriteName(void) {return getAsString("tankSpriteName");}
std::string Parameters::canonSpriteName(void) {return getAsString("canonSpriteName");}
std::string Parameters::missileSpriteName(void) {return getAsString("missileSpriteName");}
double Parameters::canon_rotation_speed(void) {return 1e-3*getAsDouble("canonRotationSpeed");}
double Parameters::tank_rotation_speed(void) {return 1e-3*getAsDouble("tankRotationSpeed");}
double Parameters::linear_tank_speed(void) {return 1e-3*getAsDouble("linearTankSpeed");}
double Parameters::maxMissileLifeDurationMS(void) {return getAsDouble("missileDuration");}
double Parameters::missileSpeed(void) {return 1e-3*getAsDouble("missileSpeed");}
double Parameters::missileDiameter(void) {return getAsDouble("missileDiameter");}

/* constants for game engine */
unsigned Parameters::minFPS(void) {return getAsLong("minFPS");}
unsigned Parameters::maxFPS(void) {return getAsLong("maxFPS");}

/* paths for game data */
std::string Parameters::spritesDirectory(void) {return getAsString("spritesDirectory");}
std::string Parameters::spritesExtension(void) {return getAsString("spritesExtension");}
std::string Parameters::defaultFontName(void) {return getAsString("defaultFontName");}
bool Parameters::fullscreen() {return getAsBoolean("fullscreen");}
std::string Parameters::map() {return getAsString("map");}
std::string Parameters::keymap() {return getAsString("keymap");}
std::string Parameters::config() {return getAsString("config");}
bool Parameters::startServer() {return getAsBoolean("server");}
std::string Parameters::joinAddress() {return getAsString("join");}
long Parameters::screenWidth() {return getAsLong("width");}
long Parameters::screenHeight() {return getAsLong("height");}
sf::VideoMode Parameters::getVideoMode() {
	sf::VideoMode mode;
	long w = screenWidth(), h = screenHeight();
	if (w == 0 && h == 0) return sf::VideoMode::getDesktopMode();
	if (h == 0) {
		if (w >= 1920) h = w*9/16;
		else h = w*3/4;
	}
	return sf::VideoMode(w, h);
}
<commit_msg>Now --help makes application exit immediately.<commit_after>#include "parameters.h"
#include <vector>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include "misc.h"
#include <iostream>
#include "parse_json.h"

Parameters parameters;

static struct ParameterDef {
	const char 	*name;
	PType		datatype;
	const char     *defval;
	char		cmdline;
} pdefs[]={
/* network */
	 {"udpMTU", PInteger, "512"}
	,{"NetworkFPS", PDouble, "60"}
	,{"serverPort", PInteger, "1330"}
	,{"clientPort", PInteger, "1320"}
	,{"connectionTimeout", PDouble, "10000"}
	,{"disconnectionTimeout", PDouble, "2000"}
	,{"broadcastPeriod", PDouble, "500"}
	,{"udpPortRange", PInteger, "100"}
	,{"server", PBoolean, "0"}
	,{"join", PString, ""}

/* game */
	,{"missileDelay", PDouble, "200"}
	,{"tankDiameter", PDouble, "128"}
	,{"tankSpriteName", PString, "car"}
	,{"canonSpriteName", PString, "canon"}
	,{"missileSpriteName", PString, "bullet"}
	,{"canonRotationSpeed", PDouble, "0.00523"} /* rad/ms */
	,{"tankRotationSpeed", PDouble, "0.00523"}
	,{"linearTankSpeed", PDouble, "0.3"} /* px per millisecond */
	,{"missileDuration", PDouble, "2000"}
	,{"missileSpeed", PDouble, "0.9"}
	,{"missileDiameter", PDouble, "16"}

/* engine */
	,{"minFPS", PDouble, "15"}
	,{"maxFPS", PDouble, "60"}
	,{"width", PInteger, "0"} /* 0 = desktop size */
	,{"height", PInteger, "0"}

/* file system */
	,{"spritesDirectory", PString, "sprites/"}
	,{"spritesExtension", PString, ".png"}
	,{"defaultFontName", PString, "sans-serif:slant=roman:weight=normal:lang=en_US:scalable=true"}

	,{"fullscreen", PBoolean, "1", 'f'}
	,{"map", PString, "map2.json", 'm'}
	,{"keymap", PString, "keymap.json", 'k'}
	,{"config", PString, "tankfighter.cfg", 'c'}
	,{"help", PBoolean, "0", 'h'}
};

long Parameters::getAsLong (const char *name) {
	long res;
	if (!string2long(values[name].c_str(), &res)) return 0;
	return res;
}
double Parameters::getAsDouble (const char *name) {
	double res;
	if (!string2dbl(values[name].c_str(), &res)) return 0;
	return res;
}
bool Parameters::getAsBoolean (const char *name) {
	long res;
	if (!string2long(values[name].c_str(), &res)) return 0;
	return res;
}
std::string Parameters::getAsString (const char *name) {
	return values[name];
}
bool Parameters::set(const char *name, const std::string &value) {
	const char *valstr = value.c_str();
	VarmapIterator it = variables.find(name);
	if (it == variables.end()) {
		fprintf(stderr, "Variable %s not found\n", name);
		return false;
	}
	PVariable &v = (*it).second;
	if (v.datatype == PInteger || v.datatype == PBoolean) {
		long lv=0;
		if (!string2long(valstr, &lv)) {
			fprintf(stderr, "Value %s invalid for variable %s\n", valstr, name);
			return false;
		}
		if (v.datatype == PBoolean && lv != 1 && lv != 0) return false;
	} else if (v.datatype == PDouble) {
		double dv;
		if (!string2dbl(valstr, &dv)) {
			fprintf(stderr, "Value %s invalid for variable %s\n", valstr, name);
			return false;
		}
	} else if (v.datatype != PString) {
		fprintf(stderr, "Internal error: Variable %s has unknown data type\n", name);
		return false;
	}
	values[name]=value;
	return true;
}
Parameters::Parameters() {
	for(size_t i=0; i < sizeof(pdefs)/sizeof(pdefs[0]); i++) {
		ParameterDef &pdef = pdefs[i];
		PVariable v;
		v.datatype = pdef.datatype;
		v.defval = pdef.defval;
		variables.insert(VarmapVal(pdef.name, v));
		set(pdef.name, pdef.defval);
		/*values.insert(pdef.name, pdef.defval);*/
	}
	parseFile(config().c_str());
}
bool Parameters::parseCmdline (int argc, char **argv) {
	int index = 0;
	std::vector<struct option> options(variables.size());
	size_t i=0;
	for(VarmapIterator it=variables.begin(); it != variables.end(); ++it) {
		VarmapVal v = *it;
		struct option o;
		o.name = v.first.c_str();
		o.has_arg = (v.second.datatype == PBoolean ? optional_argument  : required_argument);
		o.flag = NULL;
		o.val = 0;
		options[i++] = o;
	}
	while (getopt_long(argc, argv, "", &options[0], &index) != -1) {
		if (optarg) {
			set(options[index].name, optarg);
		} else {
			set(options[index].name, "1");
		}
	}
	if (getAsBoolean("help")) {
		outputHelpString(std::cout);
		return false;
	}
	return true;
}
void Parameters::outputHelpString (std::ostream &out) {
	for(VarmapIterator it=variables.begin(); it != variables.end(); ++it) {
		VarmapVal v = *it;
		PType dt = v.second.datatype;
		std::string type;
		if (dt == PString) type = "string";
		if (dt == PBoolean) type = "boolean";
		if (dt == PInteger) type = "integer";
		if (dt == PDouble) type = "float";
		out << "\t--" << v.first << " " << type << " with default = " << v.second.defval << "\n";
	}
}
bool Parameters::parseFile (const char *config_file) {
	json_value *p = json_parse_file(config_file, parameters.config_magic().c_str());
	if (!p) return false;
	const json_value *map = access_json_hash(p, "parameters");
	if ((!map) || map->type != json_object) {
		json_value_free(p);
		fprintf(stderr, "parameters hash must be present and must be an associative array\n");
		return false;
	}
	for (unsigned i=0; i < map->u.object.length; i++) {
		const char *variable=map->u.object.values[i].name;
		const json_value *val = map->u.object.values[i].value;
		set(variable, val);
	}

	json_value_free(p);
	return true;
}
bool Parameters::set(const char *name, const json_value *value) {
	VarmapIterator it = variables.find(name);
	std::string sval;
	if (it == variables.end()) {
		fprintf(stderr, "Variable %s doesn't exist\n", name);
		return false;
	}
	PVariable &v = (*it).second;
	int d1 = v.datatype, d2 = value->type;
	if (!(d1 == d2 || (d1 == PBoolean && d2 == json_integer) || d2 == json_string)) {
		fprintf(stderr, "Variable assignment type mismatch for %s\n", name);
		return false;
	}
	if (value->type == json_boolean) sval = tostring((int)value->u.boolean);
	if (value->type == json_integer) sval = tostring(value->u.integer);
	if (value->type == json_double)  sval = tostring(value->u.dbl);
	if (value->type == json_string)  {
		char *sp = json_string_to_cstring(value);
		if (sp) sval=sp;
		free(sp);
	}
	return set(name, sval);
}

unsigned Parameters::C2S_Packet_interval_US(void) {return 10000;}
double Parameters::resendPacketAfterMS(void) {return 500;}
double Parameters::clientsCleanupIntervalMS(void) {return 1000;}
double Parameters::maxDupPacketTimeSecs(void) {return 10;}
double Parameters::minWallDistance(void) {return 1e-3;}
std::string Parameters::keymap_magic(void) {return "ktank-ctrl-map";}
std::string Parameters::map_magic(void) {return "ktank-map";}
std::string Parameters::config_magic(void) {return "ktank-config";}
double Parameters::joyTankSpeedCalibration() {return 0.3;}
double Parameters::joyCanonDirectionCalibration() {return 0.15;}
double Parameters::joyDefaultCalibration() {return 0.2;}
int Parameters::serverDiscoveryTimeoutMS() {return 2000;}
int Parameters::serverDiscoveryPeriodMS() {return 500;}
/* constants for networking */
unsigned Parameters::UDP_MTU(void) {return getAsLong("udpMTU");}
unsigned short Parameters::serverPort(void) {return getAsLong("serverPort");}
unsigned short Parameters::clientPort(void) {return getAsLong("clientPort");}
double Parameters::connectionTimeoutInSecs(void) {return getAsDouble("connectionTimeout")*1e-3;}
double Parameters::disconnectionTimeoutMS(void) {return getAsDouble("disconnectionTimeout");}
int Parameters::udpPortRange(void) {return getAsLong("udpPortRange");}

/* constants for game rules */
double Parameters::missileDelayMS(void) {return getAsDouble("missileDelay");}
double Parameters::tankDiameter(void)  {return getAsDouble("tankDiameter");}
std::string Parameters::tankSpriteName(void) {return getAsString("tankSpriteName");}
std::string Parameters::canonSpriteName(void) {return getAsString("canonSpriteName");}
std::string Parameters::missileSpriteName(void) {return getAsString("missileSpriteName");}
double Parameters::canon_rotation_speed(void) {return 1e-3*getAsDouble("canonRotationSpeed");}
double Parameters::tank_rotation_speed(void) {return 1e-3*getAsDouble("tankRotationSpeed");}
double Parameters::linear_tank_speed(void) {return 1e-3*getAsDouble("linearTankSpeed");}
double Parameters::maxMissileLifeDurationMS(void) {return getAsDouble("missileDuration");}
double Parameters::missileSpeed(void) {return 1e-3*getAsDouble("missileSpeed");}
double Parameters::missileDiameter(void) {return getAsDouble("missileDiameter");}

/* constants for game engine */
unsigned Parameters::minFPS(void) {return getAsLong("minFPS");}
unsigned Parameters::maxFPS(void) {return getAsLong("maxFPS");}

/* paths for game data */
std::string Parameters::spritesDirectory(void) {return getAsString("spritesDirectory");}
std::string Parameters::spritesExtension(void) {return getAsString("spritesExtension");}
std::string Parameters::defaultFontName(void) {return getAsString("defaultFontName");}
bool Parameters::fullscreen() {return getAsBoolean("fullscreen");}
std::string Parameters::map() {return getAsString("map");}
std::string Parameters::keymap() {return getAsString("keymap");}
std::string Parameters::config() {return getAsString("config");}
bool Parameters::startServer() {return getAsBoolean("server");}
std::string Parameters::joinAddress() {return getAsString("join");}
long Parameters::screenWidth() {return getAsLong("width");}
long Parameters::screenHeight() {return getAsLong("height");}
sf::VideoMode Parameters::getVideoMode() {
	sf::VideoMode mode;
	long w = screenWidth(), h = screenHeight();
	if (w == 0 && h == 0) return sf::VideoMode::getDesktopMode();
	if (h == 0) {
		if (w >= 1920) h = w*9/16;
		else h = w*3/4;
	}
	return sf::VideoMode(w, h);
}
<|endoftext|>


<commit_before>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * logic-rdm-sniffer.cpp
 * RDM Sniffer software for the Saleae Logic Device.
 * Copyright (C) 2013 Simon Newton
 */

#if HAVE_CONFIG_H
#  include <config.h>
#endif

#ifdef HAVE_SALEAEDEVICEAPI_H
#include <SaleaeDeviceApi.h>
#endif

#include <string.h>
#include <time.h>

#include <ola/Logging.h>
#include <ola/base/Flags.h>
#include <ola/io/SelectServer.h>

#include <ola/BaseTypes.h>
#include <ola/Callback.h>
#include <ola/base/SysExits.h>
#include <ola/Clock.h>
#include <ola/network/NetworkUtils.h>
#include <ola/rdm/CommandPrinter.h>
#include <ola/rdm/PidStoreHelper.h>
#include <ola/rdm/RDMCommand.h>
#include <ola/rdm/RDMEnums.h>
#include <ola/rdm/RDMHelper.h>
#include <ola/rdm/RDMResponseCodes.h>
#include <ola/rdm/UID.h>

#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <vector>

using std::auto_ptr;
using std::cerr;
using std::cout;
using std::endl;
using std::string;
using std::stringstream;
using std::vector;
using ola::io::SelectServer;
using ola::messaging::Descriptor;
using ola::messaging::Message;
using ola::rdm::CommandPrinter;
using ola::rdm::PidStoreHelper;
using ola::rdm::RDMCommand;
using ola::rdm::UID;

using ola::thread::Mutex;
using ola::thread::MutexLocker;

DEFINE_bool(display_asc, false,
              "Display non-RDM alternate start code frames");
DEFINE_s_bool(display_dmx, d, false, "Display DMX Frames");
DEFINE_s_bool(full_rdm, r, true, "Display the full RDM frame");
DEFINE_bool(timestamp, false, "Include timestamps");

DEFINE_uint16(dmx_slot_limit, DMX_UNIVERSE_SIZE,
              "Only display the first N DMX slots");

void OnReadData(U64 device_id, uint8_t *data, uint32_t data_length,
                void *user_data) {
  std::cout << "Read " << data_length << " bytes, starting with 0x"
            << std::hex << static_cast<int>(*data) << std::dec << std::endl;
  DevicesManagerInterface::DeleteU8ArrayPtr(data);
  (void) device_id;
  (void) user_data;
}

void OnError(U64 device_id, void *user_data) {
  OLA_INFO << "A device reported an Error.";
  (void) device_id;
  (void) user_data;
}

class LogicReader {
  public:
    explicit LogicReader(SelectServer *ss)
      : m_device_id(0),
        m_logic(NULL),
        m_ss(ss) {
    }

    void DeviceConnected(U64 device, GenericInterface *interface);
    void DeviceDisconnected(U64 device);

    void Stop();

  private:
    U64 m_device_id;  // GUARDED_BY(mu_);
    LogicInterface *m_logic;  // GUARDED_BY(mu_);
    Mutex m_mu;

    SelectServer *m_ss;

    static const uint32_t SAMPLE_HZ = 4000000;
};

void LogicReader::DeviceConnected(U64 device,
                                  GenericInterface *interface) {
  OLA_INFO << "Device " << device << " connected";
  MutexLocker lock(&m_mu);
  if (m_logic != NULL) {
    OLA_WARN << "More than one device is connected";
    return;
  }

  LogicInterface *logic = dynamic_cast<LogicInterface*>(interface);  // NOLINT
  if (logic == NULL) {
    OLA_WARN << "Only the Logic is supported for now";
    return;
  }

  m_device_id = device;
  m_logic = logic;

  // do stuff here
  m_logic->RegisterOnReadData(&OnReadData);
  m_logic->RegisterOnError(&OnError);

  m_logic->SetSampleRateHz(SAMPLE_HZ);
  m_logic->ReadStart();
}

void LogicReader::DeviceDisconnected(U64 device) {
  OLA_INFO << "Device " << device << " disconnected";

  MutexLocker lock(&m_mu);
  if (device != m_device_id) {
    return;
  }
  m_device_id = 0;
  m_logic = NULL;

  m_ss->Terminate();

  //
}

void LogicReader::Stop() {
  MutexLocker lock(&m_mu);
  if (m_logic) {
    m_logic->Stop();
  }
}

void OnConnect(U64 device_id, GenericInterface* device_interface,
               void* user_data) {
  if (!user_data)
    return;

  LogicReader *reader = (LogicReader*) user_data;  // NOLINT
  reader->DeviceConnected(device_id, device_interface);
}

void OnDisconnect(U64 device_id, void* user_data) {
  if (!user_data)
    return;

  LogicReader *reader = (LogicReader*) user_data;  // NOLINT
  reader->DeviceDisconnected(device_id);
}

/*
 * Main.
 */
int main(int argc, char *argv[]) {
  ola::SetHelpString("[options]",
                     "Decode DMX/RDM data from a Saleae Logic device");
  ola::ParseFlags(&argc, argv);
  ola::InitLoggingFromFlags();

  SelectServer ss;
  LogicReader reader(&ss);

  DevicesManagerInterface::RegisterOnConnect(&OnConnect, &reader);
  DevicesManagerInterface::RegisterOnDisconnect(&OnDisconnect, &reader);
  DevicesManagerInterface::BeginConnect();

  OLA_INFO << "Running...";
  ss.Run();
  reader.Stop();
  return ola::EXIT_OK;
}
<commit_msg>* Style fix<commit_after>/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 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 Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * logic-rdm-sniffer.cpp
 * RDM Sniffer software for the Saleae Logic Device.
 * Copyright (C) 2013 Simon Newton
 */

#if HAVE_CONFIG_H
#  include <config.h>
#endif

#ifdef HAVE_SALEAEDEVICEAPI_H
#include <SaleaeDeviceApi.h>
#endif

#include <string.h>
#include <time.h>

#include <ola/Logging.h>
#include <ola/base/Flags.h>
#include <ola/io/SelectServer.h>

#include <ola/BaseTypes.h>
#include <ola/Callback.h>
#include <ola/base/SysExits.h>
#include <ola/Clock.h>
#include <ola/network/NetworkUtils.h>
#include <ola/rdm/CommandPrinter.h>
#include <ola/rdm/PidStoreHelper.h>
#include <ola/rdm/RDMCommand.h>
#include <ola/rdm/RDMEnums.h>
#include <ola/rdm/RDMHelper.h>
#include <ola/rdm/RDMResponseCodes.h>
#include <ola/rdm/UID.h>

#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <vector>

using std::auto_ptr;
using std::cerr;
using std::cout;
using std::endl;
using std::string;
using std::stringstream;
using std::vector;
using ola::io::SelectServer;
using ola::messaging::Descriptor;
using ola::messaging::Message;
using ola::rdm::CommandPrinter;
using ola::rdm::PidStoreHelper;
using ola::rdm::RDMCommand;
using ola::rdm::UID;

using ola::thread::Mutex;
using ola::thread::MutexLocker;

DEFINE_bool(display_asc, false,
              "Display non-RDM alternate start code frames");
DEFINE_s_bool(display_dmx, d, false, "Display DMX Frames");
DEFINE_s_bool(full_rdm, r, true, "Display the full RDM frame");
DEFINE_bool(timestamp, false, "Include timestamps");

DEFINE_uint16(dmx_slot_limit, DMX_UNIVERSE_SIZE,
              "Only display the first N DMX slots");

void OnReadData(U64 device_id, uint8_t *data, uint32_t data_length,
                void *user_data) {
  std::cout << "Read " << data_length << " bytes, starting with 0x"
            << std::hex << static_cast<int>(*data) << std::dec << std::endl;
  DevicesManagerInterface::DeleteU8ArrayPtr(data);
  (void) device_id;
  (void) user_data;
}

void OnError(U64 device_id, void *user_data) {
  OLA_INFO << "A device reported an Error.";
  (void) device_id;
  (void) user_data;
}

class LogicReader {
  public:
    explicit LogicReader(SelectServer *ss)
      : m_device_id(0),
        m_logic(NULL),
        m_ss(ss) {
    }

    void DeviceConnected(U64 device, GenericInterface *interface);
    void DeviceDisconnected(U64 device);

    void Stop();

  private:
    U64 m_device_id;  // GUARDED_BY(mu_);
    LogicInterface *m_logic;  // GUARDED_BY(mu_);
    Mutex m_mu;

    SelectServer *m_ss;

    static const uint32_t SAMPLE_HZ = 4000000;
};

void LogicReader::DeviceConnected(U64 device,
                                  GenericInterface *interface) {
  OLA_INFO << "Device " << device << " connected";
  MutexLocker lock(&m_mu);
  if (m_logic != NULL) {
    OLA_WARN << "More than one device is connected";
    return;
  }

  LogicInterface *logic = dynamic_cast<LogicInterface*>(interface);  // NOLINT
  if (logic == NULL) {
    OLA_WARN << "Only the Logic is supported for now";
    return;
  }

  m_device_id = device;
  m_logic = logic;

  // do stuff here
  m_logic->RegisterOnReadData(&OnReadData);
  m_logic->RegisterOnError(&OnError);

  m_logic->SetSampleRateHz(SAMPLE_HZ);
  m_logic->ReadStart();
}

void LogicReader::DeviceDisconnected(U64 device) {
  OLA_INFO << "Device " << device << " disconnected";

  MutexLocker lock(&m_mu);
  if (device != m_device_id) {
    return;
  }
  m_device_id = 0;
  m_logic = NULL;

  m_ss->Terminate();

  //
}

void LogicReader::Stop() {
  MutexLocker lock(&m_mu);
  if (m_logic) {
    m_logic->Stop();
  }
}

void OnConnect(U64 device_id, GenericInterface* device_interface,
               void* user_data) {
  if (!user_data)
    return;

  LogicReader *reader = (LogicReader*) user_data;  // NOLINT
  reader->DeviceConnected(device_id, device_interface);
}

void OnDisconnect(U64 device_id, void *user_data) {
  if (!user_data)
    return;

  LogicReader *reader = (LogicReader*) user_data;  // NOLINT
  reader->DeviceDisconnected(device_id);
}

/*
 * Main.
 */
int main(int argc, char *argv[]) {
  ola::SetHelpString("[options]",
                     "Decode DMX/RDM data from a Saleae Logic device");
  ola::ParseFlags(&argc, argv);
  ola::InitLoggingFromFlags();

  SelectServer ss;
  LogicReader reader(&ss);

  DevicesManagerInterface::RegisterOnConnect(&OnConnect, &reader);
  DevicesManagerInterface::RegisterOnDisconnect(&OnDisconnect, &reader);
  DevicesManagerInterface::BeginConnect();

  OLA_INFO << "Running...";
  ss.Run();
  reader.Stop();
  return ola::EXIT_OK;
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
//
// 	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 <MTAllocator.h>
#include <MTTools.h>
#include <xmmintrin.h>

#ifdef _WIN32
#include <windows.h>
#else
#endif

namespace MT
{

	void* Memory::Alloc(size_t size, size_t align)
	{
		return _mm_malloc(size, align);
	}

	void Memory::Free(void* p)
	{
		_mm_free(p);
	}

	Memory::StackDesc Memory::AllocStack(size_t size)
	{
		StackDesc desc;

#ifdef _WIN32

		SYSTEM_INFO systemInfo;
		GetSystemInfo(&systemInfo);

		int pageSize = (int)systemInfo.dwPageSize;
		int pagesCount = (int)size / pageSize;

		//need additional page for stack guard
		if ((size % pageSize) > 0)
		{
			pagesCount++;
		}

		//protected guard page
		pagesCount++;

		desc.stackMemoryBytesCount = pagesCount * pageSize;
		desc.stackMemory = (char*)VirtualAlloc(NULL, desc.stackMemoryBytesCount, MEM_COMMIT, PAGE_READWRITE);
		MT_ASSERT(desc.stackMemory != NULL, "Can't allocate memory");

		desc.stackBottom = desc.stackMemory + pageSize;
		desc.stackTop = desc.stackMemory + desc.stackMemoryBytesCount;

		DWORD oldProtect = 0;
		BOOL res = VirtualProtect(desc.stackMemory, pageSize, PAGE_NOACCESS, &oldProtect);
		MT_ASSERT(res != 0, "Can't protect memory");

#else

			int pageSize = sysconf(_SC_PAGE_SIZE);
			int pagesCount = size / pageSize;

			//need additional page for stack tail
			if ((size % pageSize) > 0)
			{
				pagesCount++;
			}

			//protected guard page
			pagesCount++;

			desc.stackMemoryBytesCount = pagesCount * pageSize;
			desc.stackMemory = (char*)mmap(NULL, desc.stackMemoryBytesCount, PROT_READ | PROT_WRITE,  MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);

			MT_ASSERT((void *)desc.stackMemory != (void *)-1, "Can't allocate memory");

			desc.stackBottom = desc.stackMemory + pageSize;
			desc.stackTop = desc.stackMemory + desc.stackMemoryBytesCount;

			int res = mprotect(desc.stackMemory, pageSize, PROT_NONE);
			MT_ASSERT(res == 0, "Can't protect memory");

#endif

		return desc;
	}

	void Memory::FreeStack(const Memory::StackDesc & desc)
	{
#ifdef _WIN32

		int res = VirtualFree(desc.stackMemory, desc.stackMemoryBytesCount, MEM_RELEASE);
		MT_ASSERT(res == 0, "Can't free memory");

#else

		int res = munmap(desc.stackMemory, desc.stackMemoryBytesCount);
		MT_ASSERT(res == 0, "Can't free memory");

#endif
	}

}
<commit_msg>Fixed VirtualFree params (memory leak fixed)<commit_after>// The MIT License (MIT)
//
// 	Copyright (c) 2015 Sergey Makeev, Vadim Slyusarev
//
// 	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 <MTAllocator.h>
#include <MTTools.h>
#include <xmmintrin.h>

#ifdef _WIN32
#include <windows.h>
#else
#endif

namespace MT
{

	void* Memory::Alloc(size_t size, size_t align)
	{
		return _mm_malloc(size, align);
	}

	void Memory::Free(void* p)
	{
		_mm_free(p);
	}

	Memory::StackDesc Memory::AllocStack(size_t size)
	{
		StackDesc desc;

#ifdef _WIN32

		SYSTEM_INFO systemInfo;
		GetSystemInfo(&systemInfo);

		int pageSize = (int)systemInfo.dwPageSize;
		int pagesCount = (int)size / pageSize;

		//need additional page for stack guard
		if ((size % pageSize) > 0)
		{
			pagesCount++;
		}

		//protected guard page
		pagesCount++;

		desc.stackMemoryBytesCount = pagesCount * pageSize;
		desc.stackMemory = (char*)VirtualAlloc(NULL, desc.stackMemoryBytesCount, MEM_COMMIT, PAGE_READWRITE);
		MT_ASSERT(desc.stackMemory != NULL, "Can't allocate memory");

		desc.stackBottom = desc.stackMemory + pageSize;
		desc.stackTop = desc.stackMemory + desc.stackMemoryBytesCount;

		DWORD oldProtect = 0;
		BOOL res = VirtualProtect(desc.stackMemory, pageSize, PAGE_NOACCESS, &oldProtect);
		MT_ASSERT(res != 0, "Can't protect memory");

#else

			int pageSize = sysconf(_SC_PAGE_SIZE);
			int pagesCount = size / pageSize;

			//need additional page for stack tail
			if ((size % pageSize) > 0)
			{
				pagesCount++;
			}

			//protected guard page
			pagesCount++;

			desc.stackMemoryBytesCount = pagesCount * pageSize;
			desc.stackMemory = (char*)mmap(NULL, desc.stackMemoryBytesCount, PROT_READ | PROT_WRITE,  MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);

			MT_ASSERT((void *)desc.stackMemory != (void *)-1, "Can't allocate memory");

			desc.stackBottom = desc.stackMemory + pageSize;
			desc.stackTop = desc.stackMemory + desc.stackMemoryBytesCount;

			int res = mprotect(desc.stackMemory, pageSize, PROT_NONE);
			MT_ASSERT(res == 0, "Can't protect memory");

#endif

		return desc;
	}

	void Memory::FreeStack(const Memory::StackDesc & desc)
	{
#ifdef _WIN32

		int res = VirtualFree(desc.stackMemory, 0, MEM_RELEASE);
		MT_ASSERT(res != 0, "Can't free memory");

#else

		int res = munmap(desc.stackMemory, desc.stackMemoryBytesCount);
		MT_ASSERT(res == 0, "Can't free memory");

#endif
	}

}
<|endoftext|>


<commit_before>/*******************************************************
* Copyright (c) 2014, ArrayFire
* All rights reserved.
*
* This file is distributed under 3-clause BSD license.
* The complete license agreement can be obtained at:
* http://arrayfire.com/licenses/BSD-3-Clause
********************************************************/

#include <af/defines.h>
#include <af/dim4.hpp>
#include <af/image.h>
#include <handle.hpp>
#include <err_common.hpp>
#include <backend.hpp>
#include <hsv_rgb.hpp>

using af::dim4;
using namespace detail;

template<typename T, bool isHSV2RGB>
static af_array convert(const af_array& in)
{
    const Array<T> input = getArray<T>(in);
    if (isHSV2RGB) {
        return getHandle<T>(hsv2rgb<T>(input));
    }
    else {
        return getHandle<T>(rgb2hsv<T>(input));
    }
}

template<bool isHSV2RGB>
af_err convert(af_array* out, const af_array& in)
{
    try {
        ArrayInfo info = getInfo(in);
        af_dtype iType = info.getType();
        af::dim4 inputDims = info.dims();

        ARG_ASSERT(1, (inputDims.ndims() == 3));

        af_array output = 0;
        switch (iType) {
            case f64: output = convert<double, isHSV2RGB>(in); break;
            case f32: output = convert<float , isHSV2RGB>(in); break;
            default: TYPE_ERROR(1, iType); break;
        }
        std::swap(*out, output);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_hsv2rgb(af_array* out, const af_array in)
{
    return convert<true>(out, in);
}

af_err af_rgb2hsv(af_array* out, const af_array in)
{
    return convert<false>(out, in);
}
<commit_msg>Enabled 4th dim batch support for hsv-rgb conversions<commit_after>/*******************************************************
* Copyright (c) 2014, ArrayFire
* All rights reserved.
*
* This file is distributed under 3-clause BSD license.
* The complete license agreement can be obtained at:
* http://arrayfire.com/licenses/BSD-3-Clause
********************************************************/

#include <af/defines.h>
#include <af/dim4.hpp>
#include <af/image.h>
#include <handle.hpp>
#include <err_common.hpp>
#include <backend.hpp>
#include <hsv_rgb.hpp>

using af::dim4;
using namespace detail;

template<typename T, bool isHSV2RGB>
static af_array convert(const af_array& in)
{
    const Array<T> input = getArray<T>(in);
    if (isHSV2RGB) {
        return getHandle<T>(hsv2rgb<T>(input));
    }
    else {
        return getHandle<T>(rgb2hsv<T>(input));
    }
}

template<bool isHSV2RGB>
af_err convert(af_array* out, const af_array& in)
{
    try {
        ArrayInfo info = getInfo(in);
        af_dtype iType = info.getType();
        af::dim4 inputDims = info.dims();

        ARG_ASSERT(1, (inputDims.ndims() >= 3));

        af_array output = 0;
        switch (iType) {
            case f64: output = convert<double, isHSV2RGB>(in); break;
            case f32: output = convert<float , isHSV2RGB>(in); break;
            default: TYPE_ERROR(1, iType); break;
        }
        std::swap(*out, output);
    }
    CATCHALL;
    return AF_SUCCESS;
}

af_err af_hsv2rgb(af_array* out, const af_array in)
{
    return convert<true>(out, in);
}

af_err af_rgb2hsv(af_array* out, const af_array in)
{
    return convert<false>(out, in);
}
<|endoftext|>


<commit_before>//=================================================================================================
// Copyright 2013-2015 Dirk Lemstra <https://magick.codeplex.com/>
//
// Licensed under the ImageMagick License (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.imagemagick.org/script/license.php
//
// 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 "Stdafx.h"
#include "ExifWriter.h"

using namespace System::Text;

namespace ImageMagick
{
	//==============================================================================================
	int ExifWriter::GetIndex(List<int>^ indexes, ExifTag tag)
	{
		for each (int index in indexes)
		{
			if (_Values[index]->Tag == tag)
				return index;
		}

		int index = _Values->Count;
		indexes->Add(index);
		_Values->Add(ExifValue::Create(tag, nullptr));
		return index;
	}
	//==============================================================================================
	List<int>^ ExifWriter::GetIndexes(ExifParts part, array<ExifTag>^ tags)
	{
		if (((int)_AllowedParts & (int)part) == 0)
			return gcnew List<int>(0);

		List<int>^ result = gcnew List<int>();
		for (int i=0; i < _Values->Count; i++)
		{
			ExifValue^ value = _Values[i];

			if (!value->HasValue)
				continue;

			int index = Array::IndexOf(tags, value->Tag);
			if (index > -1)
				result->Add(i);
		}

		return result;
	}
	//==============================================================================================
	int ExifWriter::GetLength(IEnumerable<int>^ indexes)
	{
		int length = 0;

		for each (int index in indexes)
		{
			int valueLength = _Values[index]->Length;

			if (valueLength > 4)
				length += 12 + valueLength;
			else
				length += 12;
		}

		return length;
	}
	//==============================================================================================
	template<typename TDataType>
	int ExifWriter::Write(TDataType value, array<Byte>^ destination, int offset)
	{
		array<Byte>^ bytes = BitConverter::GetBytes(value);
		Buffer::BlockCopy(bytes, 0, destination, offset, bytes->Length);

		return offset + bytes->Length;
	}
	//==============================================================================================
	int ExifWriter::WriteArray(ExifValue^ value, array<Byte>^ destination, int offset)
	{
		if (value->DataType == ExifDataType::Ascii)
			return WriteValue(ExifDataType::Ascii, value->Value, destination, offset);

		int newOffset = offset;
		for each(Object^ obj in (Array^)value->Value)
			newOffset = WriteValue(value->DataType, obj, destination, newOffset);

		return newOffset;
	}
	//==============================================================================================
	int ExifWriter::WriteData(List<int>^ indexes, array<Byte>^ destination, int offset)
	{
		if (_DataOffsets->Count == 0)
			return offset;

		int newOffset = offset;

		int i = 0;
		for each(int index in indexes)
		{
			ExifValue^ value = _Values[index];
			if (value->Length > 4)
			{
				Write(newOffset - _StartIndex, destination, _DataOffsets[i++]);
				newOffset = WriteValue(value, destination, newOffset);
			}
		}

		return newOffset;
	}
	//==============================================================================================
	int ExifWriter::WriteHeaders(List<int>^ indexes, array<Byte>^ destination, int offset)
	{
		_DataOffsets = gcnew List<int>();

		int newOffset = Write((unsigned short)indexes->Count, destination, offset);

		if (indexes->Count == 0)
			return newOffset;

		for each(int index in indexes)
		{
			ExifValue^ value = _Values[index];
			newOffset = Write((unsigned short)value->Tag, destination, newOffset);
			newOffset = Write((unsigned short)value->DataType, destination, newOffset);
			newOffset = Write((unsigned int)value->NumberOfComponents, destination, newOffset);

			if (value->Length > 4)
				_DataOffsets->Add(newOffset);
			else
				WriteValue(value, destination, newOffset);

			newOffset += 4;
		}

		return newOffset;
	}
	//==============================================================================================
	int ExifWriter::WriteRational(double value, array<Byte>^ destination, int offset)
	{
		unsigned int numerator = 1;
		unsigned int denominator = 1;

		if (double::IsPositiveInfinity(value))
			denominator = 0;
		else if (double::IsNegativeInfinity(value))
			denominator = 0;
		else
		{
			double val = abs(value);
			double df = numerator / denominator;

			while (abs(df - val) > .000001)
			{
				if (df < val)
				{
					numerator++;
				}
				else
				{
					denominator++;
					numerator = (unsigned int)(val * denominator);
				}

				df = numerator / (double)denominator;
			}
		}

		Write(numerator, destination, offset);
		Write(denominator, destination, offset + 4);

		return offset + 8;
	}
	//==============================================================================================
	int ExifWriter::WriteSignedRational(double value, array<Byte>^ destination, int offset)
	{
		int numerator = 1;
		int denominator = 1;

		if (double::IsPositiveInfinity(value))
			denominator = 0;
		else if (double::IsNegativeInfinity(value))
			denominator = 0;
		else
		{
			double val = abs(value);
			double df = numerator / denominator;
			while (abs(df - val) > .000001)
			{
				if (df < val)
				{
					numerator++;
				}
				else
				{
					denominator++;
					numerator = (int)(val * denominator);
				}

				df = numerator / (double)denominator;
			}
		}

		Write(numerator * (value < 0.0 ? -1 : 1), destination, offset);
		Write(denominator, destination, offset + 4);

		return offset + 8;
	}
	//==============================================================================================
	int ExifWriter::WriteValue(ExifDataType dataType, Object^ value, array<Byte>^ destination, int offset)
	{
		switch(dataType)
		{
		case ExifDataType::Ascii:
			{
				array<Byte>^ bytes = Encoding::UTF8->GetBytes((String^) value);
				Buffer::BlockCopy(bytes, 0, destination, offset, bytes->Length);
				return offset + bytes->Length;
			}
		case ExifDataType::Byte:
		case ExifDataType::Undefined:
			destination[offset] = (Byte)value;
			return offset + 1;
		case ExifDataType::DoubleFloat:
			return Write((double)value, destination, offset);
		case ExifDataType::Short:
			return Write((unsigned short)value, destination, offset);
		case ExifDataType::Long:
			return Write((unsigned int)value, destination, offset);
		case ExifDataType::Rational:
			return WriteRational((double)value, destination, offset);
		case ExifDataType::SignedByte:
			destination[offset] = (SByte)value;
			return offset + 1;
		case ExifDataType::SignedLong:
			return Write((int)value, destination, offset);
		case ExifDataType::SignedShort:
			return Write((short)value, destination, offset);
		case ExifDataType::SignedRational:
			return WriteSignedRational((double)value, destination, offset);
		case ExifDataType::SingleFloat:
			return Write((Single)value, destination, offset);
		default:
			throw gcnew NotImplementedException();
		}
	}
	//==============================================================================================
	int ExifWriter::WriteValue(ExifValue^ value, array<Byte>^ destination, int offset)
	{
		if (value->IsArray && value->DataType != ExifDataType::Ascii)
			return WriteArray(value, destination, offset);
		else
			return WriteValue(value->DataType, value->Value, destination, offset);
	}
	//==============================================================================================
	ExifWriter::ExifWriter(List<ExifValue^>^ values, ExifParts allowedParts)
	{
		_Values = values;
		_AllowedParts = allowedParts;

		_IfdIndexes = GetIndexes(ExifParts::IfdTags, _IfdTags);
		_ExifIndexes = GetIndexes(ExifParts::ExifTags, _ExifTags);
		_GPSIndexes = GetIndexes(ExifParts::GPSTags, _GPSTags);
	}
	//==============================================================================================
	array<Byte>^ ExifWriter::GetData()
	{
		unsigned int length = 0;
		int exifIndex = -1;
		int gpsIndex = -1;

		if (_ExifIndexes->Count > 0)
			exifIndex = (int)GetIndex(_IfdIndexes, ExifTag::SubIFDOffset);

		if (_GPSIndexes->Count > 0)
			gpsIndex = (int)GetIndex(_IfdIndexes, ExifTag::GPSIFDOffset);

		unsigned int ifdLength = 2 + GetLength(_IfdIndexes) + 4;
		unsigned int exifLength = GetLength(_ExifIndexes);
		unsigned int gpsLength = GetLength(_GPSIndexes);

		if (exifLength > 0)
			exifLength += 2;

		if (gpsLength > 0)
			gpsLength += 2;

		length = ifdLength + exifLength + gpsLength;

		if (length == 6)
			return nullptr;

		length += 10 + 4 + 2;

		array<Byte>^ result = gcnew array<Byte>(length);
		result[0] = 'E';
		result[1] = 'x';
		result[2] = 'i';
		result[3] = 'f';
		result[4] = 0x00;
		result[5] = 0x00;
		result[6] = 'I';
		result[7] = 'I';
		result[8] = 0x2A;
		result[9] = 0x00;

		unsigned int i = 10;
		unsigned int ifdOffset = (i - _StartIndex) + 4;
		unsigned int thumbnailOffset = ifdOffset + ifdLength + exifLength + gpsLength;

		if (exifLength > 0)
			_Values[exifIndex]->Value = (unsigned int)(ifdOffset + ifdLength);

		if (gpsLength > 0)
			_Values[gpsIndex]->Value = (unsigned int)(ifdOffset + ifdLength + exifLength);

		i = Write(ifdOffset, result, i);
		i = WriteHeaders(_IfdIndexes, result, i);
		i = Write(thumbnailOffset, result, i);
		i = WriteData(_IfdIndexes, result, i);

		if (exifLength > 0)
		{
			i = WriteHeaders(_ExifIndexes, result, i);
			i = WriteData(_ExifIndexes, result, i);
		}

		if (gpsLength > 0)
		{
			i = WriteHeaders(_GPSIndexes, result, i);
			i = WriteData(_GPSIndexes, result, i);
		}

		Write((unsigned short)0, result, i);

		return result;
	}
	//==============================================================================================
}<commit_msg>Replaced call to abs with fabs.<commit_after>//=================================================================================================
// Copyright 2013-2015 Dirk Lemstra <https://magick.codeplex.com/>
//
// Licensed under the ImageMagick License (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.imagemagick.org/script/license.php
//
// 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 "Stdafx.h"
#include "ExifWriter.h"
#include <cmath>

using namespace System::Text;

namespace ImageMagick
{
	//==============================================================================================
	int ExifWriter::GetIndex(List<int>^ indexes, ExifTag tag)
	{
		for each (int index in indexes)
		{
			if (_Values[index]->Tag == tag)
				return index;
		}

		int index = _Values->Count;
		indexes->Add(index);
		_Values->Add(ExifValue::Create(tag, nullptr));
		return index;
	}
	//==============================================================================================
	List<int>^ ExifWriter::GetIndexes(ExifParts part, array<ExifTag>^ tags)
	{
		if (((int)_AllowedParts & (int)part) == 0)
			return gcnew List<int>(0);

		List<int>^ result = gcnew List<int>();
		for (int i=0; i < _Values->Count; i++)
		{
			ExifValue^ value = _Values[i];

			if (!value->HasValue)
				continue;

			int index = Array::IndexOf(tags, value->Tag);
			if (index > -1)
				result->Add(i);
		}

		return result;
	}
	//==============================================================================================
	int ExifWriter::GetLength(IEnumerable<int>^ indexes)
	{
		int length = 0;

		for each (int index in indexes)
		{
			int valueLength = _Values[index]->Length;

			if (valueLength > 4)
				length += 12 + valueLength;
			else
				length += 12;
		}

		return length;
	}
	//==============================================================================================
	template<typename TDataType>
	int ExifWriter::Write(TDataType value, array<Byte>^ destination, int offset)
	{
		array<Byte>^ bytes = BitConverter::GetBytes(value);
		Buffer::BlockCopy(bytes, 0, destination, offset, bytes->Length);

		return offset + bytes->Length;
	}
	//==============================================================================================
	int ExifWriter::WriteArray(ExifValue^ value, array<Byte>^ destination, int offset)
	{
		if (value->DataType == ExifDataType::Ascii)
			return WriteValue(ExifDataType::Ascii, value->Value, destination, offset);

		int newOffset = offset;
		for each(Object^ obj in (Array^)value->Value)
			newOffset = WriteValue(value->DataType, obj, destination, newOffset);

		return newOffset;
	}
	//==============================================================================================
	int ExifWriter::WriteData(List<int>^ indexes, array<Byte>^ destination, int offset)
	{
		if (_DataOffsets->Count == 0)
			return offset;

		int newOffset = offset;

		int i = 0;
		for each(int index in indexes)
		{
			ExifValue^ value = _Values[index];
			if (value->Length > 4)
			{
				Write(newOffset - _StartIndex, destination, _DataOffsets[i++]);
				newOffset = WriteValue(value, destination, newOffset);
			}
		}

		return newOffset;
	}
	//==============================================================================================
	int ExifWriter::WriteHeaders(List<int>^ indexes, array<Byte>^ destination, int offset)
	{
		_DataOffsets = gcnew List<int>();

		int newOffset = Write((unsigned short)indexes->Count, destination, offset);

		if (indexes->Count == 0)
			return newOffset;

		for each(int index in indexes)
		{
			ExifValue^ value = _Values[index];
			newOffset = Write((unsigned short)value->Tag, destination, newOffset);
			newOffset = Write((unsigned short)value->DataType, destination, newOffset);
			newOffset = Write((unsigned int)value->NumberOfComponents, destination, newOffset);

			if (value->Length > 4)
				_DataOffsets->Add(newOffset);
			else
				WriteValue(value, destination, newOffset);

			newOffset += 4;
		}

		return newOffset;
	}
	//==============================================================================================
	int ExifWriter::WriteRational(double value, array<Byte>^ destination, int offset)
	{
		unsigned int numerator = 1;
		unsigned int denominator = 1;

		if (double::IsPositiveInfinity(value))
			denominator = 0;
		else if (double::IsNegativeInfinity(value))
			denominator = 0;
		else
		{
			double val = fabs(value);
			double df = numerator / denominator;

			while (fabs(df - val) > .000001)
			{
				if (df < val)
				{
					numerator++;
				}
				else
				{
					denominator++;
					numerator = (unsigned int)(val * denominator);
				}

				df = numerator / (double)denominator;
			}
		}

		Write(numerator, destination, offset);
		Write(denominator, destination, offset + 4);

		return offset + 8;
	}
	//==============================================================================================
	int ExifWriter::WriteSignedRational(double value, array<Byte>^ destination, int offset)
	{
		int numerator = 1;
		int denominator = 1;

		if (double::IsPositiveInfinity(value))
			denominator = 0;
		else if (double::IsNegativeInfinity(value))
			denominator = 0;
		else
		{
			double val = fabs(value);
			double df = numerator / denominator;
			while (fabs(df - val) > .000001)
			{
				if (df < val)
				{
					numerator++;
				}
				else
				{
					denominator++;
					numerator = (int)(val * denominator);
				}

				df = numerator / (double)denominator;
			}
		}

		Write(numerator * (value < 0.0 ? -1 : 1), destination, offset);
		Write(denominator, destination, offset + 4);

		return offset + 8;
	}
	//==============================================================================================
	int ExifWriter::WriteValue(ExifDataType dataType, Object^ value, array<Byte>^ destination, int offset)
	{
		switch(dataType)
		{
		case ExifDataType::Ascii:
			{
				array<Byte>^ bytes = Encoding::UTF8->GetBytes((String^) value);
				Buffer::BlockCopy(bytes, 0, destination, offset, bytes->Length);
				return offset + bytes->Length;
			}
		case ExifDataType::Byte:
		case ExifDataType::Undefined:
			destination[offset] = (Byte)value;
			return offset + 1;
		case ExifDataType::DoubleFloat:
			return Write((double)value, destination, offset);
		case ExifDataType::Short:
			return Write((unsigned short)value, destination, offset);
		case ExifDataType::Long:
			return Write((unsigned int)value, destination, offset);
		case ExifDataType::Rational:
			return WriteRational((double)value, destination, offset);
		case ExifDataType::SignedByte:
			destination[offset] = (SByte)value;
			return offset + 1;
		case ExifDataType::SignedLong:
			return Write((int)value, destination, offset);
		case ExifDataType::SignedShort:
			return Write((short)value, destination, offset);
		case ExifDataType::SignedRational:
			return WriteSignedRational((double)value, destination, offset);
		case ExifDataType::SingleFloat:
			return Write((Single)value, destination, offset);
		default:
			throw gcnew NotImplementedException();
		}
	}
	//==============================================================================================
	int ExifWriter::WriteValue(ExifValue^ value, array<Byte>^ destination, int offset)
	{
		if (value->IsArray && value->DataType != ExifDataType::Ascii)
			return WriteArray(value, destination, offset);
		else
			return WriteValue(value->DataType, value->Value, destination, offset);
	}
	//==============================================================================================
	ExifWriter::ExifWriter(List<ExifValue^>^ values, ExifParts allowedParts)
	{
		_Values = values;
		_AllowedParts = allowedParts;

		_IfdIndexes = GetIndexes(ExifParts::IfdTags, _IfdTags);
		_ExifIndexes = GetIndexes(ExifParts::ExifTags, _ExifTags);
		_GPSIndexes = GetIndexes(ExifParts::GPSTags, _GPSTags);
	}
	//==============================================================================================
	array<Byte>^ ExifWriter::GetData()
	{
		unsigned int length = 0;
		int exifIndex = -1;
		int gpsIndex = -1;

		if (_ExifIndexes->Count > 0)
			exifIndex = (int)GetIndex(_IfdIndexes, ExifTag::SubIFDOffset);

		if (_GPSIndexes->Count > 0)
			gpsIndex = (int)GetIndex(_IfdIndexes, ExifTag::GPSIFDOffset);

		unsigned int ifdLength = 2 + GetLength(_IfdIndexes) + 4;
		unsigned int exifLength = GetLength(_ExifIndexes);
		unsigned int gpsLength = GetLength(_GPSIndexes);

		if (exifLength > 0)
			exifLength += 2;

		if (gpsLength > 0)
			gpsLength += 2;

		length = ifdLength + exifLength + gpsLength;

		if (length == 6)
			return nullptr;

		length += 10 + 4 + 2;

		array<Byte>^ result = gcnew array<Byte>(length);
		result[0] = 'E';
		result[1] = 'x';
		result[2] = 'i';
		result[3] = 'f';
		result[4] = 0x00;
		result[5] = 0x00;
		result[6] = 'I';
		result[7] = 'I';
		result[8] = 0x2A;
		result[9] = 0x00;

		unsigned int i = 10;
		unsigned int ifdOffset = (i - _StartIndex) + 4;
		unsigned int thumbnailOffset = ifdOffset + ifdLength + exifLength + gpsLength;

		if (exifLength > 0)
			_Values[exifIndex]->Value = (unsigned int)(ifdOffset + ifdLength);

		if (gpsLength > 0)
			_Values[gpsIndex]->Value = (unsigned int)(ifdOffset + ifdLength + exifLength);

		i = Write(ifdOffset, result, i);
		i = WriteHeaders(_IfdIndexes, result, i);
		i = Write(thumbnailOffset, result, i);
		i = WriteData(_IfdIndexes, result, i);

		if (exifLength > 0)
		{
			i = WriteHeaders(_ExifIndexes, result, i);
			i = WriteData(_ExifIndexes, result, i);
		}

		if (gpsLength > 0)
		{
			i = WriteHeaders(_GPSIndexes, result, i);
			i = WriteData(_GPSIndexes, result, i);
		}

		Write((unsigned short)0, result, i);

		return result;
	}
	//==============================================================================================
}<|endoftext|>


<commit_before>//
// showinfJNI.cpp
//

/*
OME Bio-Formats C++ bindings for native access to Bio-Formats Java library.
Copyright (C) 2008-@year@ UW-Madison LOCI and Glencoe Software, Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

// An example of how to call Bio-Formats from C++ using raw JNI
// (i.e., without the C++ bindings).

/*
Compile on Mac OS X with:
  c++ showinfJNI.cpp -o showinfJNI -framework JavaVM

Compile on 32-bit Linux with:
  c++ showinfJNI.cpp -o showinfJNI \
    -I $JAVA_HOME/include -I $JAVA_HOME/include/linux \
    -L $JAVA_HOME/jre/lib/i386/client -ljvm

Compile on 64-bit Linux with:
  c++ showinfJNI.cpp -o showinfJNI \
    -I $JAVA_HOME/include -I $JAVA_HOME/include/linux \
    -L $JAVA_HOME/jre/lib/amd64/server -ljvm

Then copy loci_tools.jar to the same folder and run:
  ./showinfJNI
*/

#include <jni.h>

#include <iostream>
using namespace std;

int main(int argc, char* argv[]) {
  JavaVM *jvm;
  JNIEnv *env;

  // NB: This program requires loci_tools.jar in the same directory.
  string classpath = "-Djava.class.path";
  const int numJars = 1;
  string jars[numJars] = {
    "loci_tools.jar"
  };
  for (int i=0; i<numJars; i++) {
    classpath += i == 0 ? "=" : ":";
    classpath += jars[i];
  }
  cout << "Classpath = " + classpath << endl;

  // get the default initialization arguments and set the class path
  JavaVMInitArgs vm_args;
  JNI_GetDefaultJavaVMInitArgs(&vm_args);
  const int numOptions = 1;
  JavaVMOption options[numOptions];
  options[0].optionString = (char*) classpath.c_str();
  //options[1].optionString = "-verbose:jni";
  vm_args.version = JNI_VERSION_1_4; // VM version 1.4
  vm_args.options = options;
  vm_args.nOptions = numOptions;

  // load and initialize a Java VM, return a JNI interface pointer in env
  if (JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args)) {
    cout << "Failed to create the JVM" << endl;
    exit(1);
  }

  // invoke the ImageInfo.main(String[]) method using the JNI
  jclass imageInfoClass = env->FindClass("loci/formats/tools/ImageInfo");
  cout << "Got ImageInfo class: " << imageInfoClass << endl;
  jmethodID mid = env->GetStaticMethodID(imageInfoClass,
    "main", "([Ljava/lang/String;)V");
  cout << "Got main method: " << mid << endl;
  jclass stringClass = env->FindClass("java/lang/String");
  cout << "Got String class: " << stringClass << endl;

  jobjectArray args = env->NewObjectArray(argc - 1, stringClass, 0);
  for (int i=1; i<argc; i++) {
    jstring arg = env->NewStringUTF(argv[i]);
    env->SetObjectArrayElement(args, i - 1, arg);
  }
  cout << "Got object array: " << args << endl;

  env->CallStaticVoidMethod(imageInfoClass, mid, args);

  jvm->DestroyJavaVM();
}
<commit_msg>Tweaks to the showinfJNI standalone example:   * Include stdlib.h for exit function;   * Include instructions on setting LD_LIBARY_PATH if necessary.<commit_after>//
// showinfJNI.cpp
//

/*
OME Bio-Formats C++ bindings for native access to Bio-Formats Java library.
Copyright (C) 2008-@year@ UW-Madison LOCI and Glencoe Software, Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

// An example of how to call Bio-Formats from C++ using raw JNI
// (i.e., without the C++ bindings).

/*
Compile on Mac OS X with:
  c++ showinfJNI.cpp -o showinfJNI -framework JavaVM

Compile on 32-bit Linux with:
  c++ showinfJNI.cpp -o showinfJNI \
    -I $JAVA_HOME/include -I $JAVA_HOME/include/linux \
    -L $JAVA_HOME/jre/lib/i386/client -ljvm

Depending on your flavor of 32-bit Linux, you may also need to execute:
  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/i386/client

Compile on 64-bit Linux with:
  c++ showinfJNI.cpp -o showinfJNI \
    -I $JAVA_HOME/include -I $JAVA_HOME/include/linux \
    -L $JAVA_HOME/jre/lib/amd64/server -ljvm

Depending on your flavor of 64-bit Linux, you may also need to execute:
  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$JAVA_HOME/jre/lib/amd64/server

Then copy loci_tools.jar to the same folder and run:
  ./showinfJNI
*/

#include <iostream>
using namespace std;

#include <stdlib.h>

#include <jni.h>

int main(int argc, char* argv[]) {
  JavaVM *jvm;
  JNIEnv *env;

  // NB: This program requires loci_tools.jar in the same directory.
  string classpath = "-Djava.class.path";
  const int numJars = 1;
  string jars[numJars] = {
    "loci_tools.jar"
  };
  for (int i=0; i<numJars; i++) {
    classpath += i == 0 ? "=" : ":";
    classpath += jars[i];
  }
  cout << "Classpath = " + classpath << endl;

  // get the default initialization arguments and set the class path
  JavaVMInitArgs vm_args;
  JNI_GetDefaultJavaVMInitArgs(&vm_args);
  const int numOptions = 1;
  JavaVMOption options[numOptions];
  options[0].optionString = (char*) classpath.c_str();
  //options[1].optionString = "-verbose:jni";
  vm_args.version = JNI_VERSION_1_4; // VM version 1.4
  vm_args.options = options;
  vm_args.nOptions = numOptions;

  // load and initialize a Java VM, return a JNI interface pointer in env
  if (JNI_CreateJavaVM(&jvm, (void**) &env, &vm_args)) {
    cout << "Failed to create the JVM" << endl;
    exit(1);
  }

  // invoke the ImageInfo.main(String[]) method using the JNI
  jclass imageInfoClass = env->FindClass("loci/formats/tools/ImageInfo");
  cout << "Got ImageInfo class: " << imageInfoClass << endl;
  jmethodID mid = env->GetStaticMethodID(imageInfoClass,
    "main", "([Ljava/lang/String;)V");
  cout << "Got main method: " << mid << endl;
  jclass stringClass = env->FindClass("java/lang/String");
  cout << "Got String class: " << stringClass << endl;

  jobjectArray args = env->NewObjectArray(argc - 1, stringClass, 0);
  for (int i=1; i<argc; i++) {
    jstring arg = env->NewStringUTF(argv[i]);
    env->SetObjectArrayElement(args, i - 1, arg);
  }
  cout << "Got object array: " << args << endl;

  env->CallStaticVoidMethod(imageInfoClass, mid, args);

  jvm->DestroyJavaVM();
}
<|endoftext|>