<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mimagewidgetview.h"
#include "mimagewidgetview_p.h"

#include <QPixmap>

#include "mimagewidget.h"
#include "mimagewidget_p.h"
#include "mviewcreator.h"
#include "private/mwidgetview_p.h"

MImageWidgetViewPrivate::MImageWidgetViewPrivate()
    : controller(NULL),
    cachedPixmapSize(0, 0),
    borderOpacity(1.0),
    brush(Qt::transparent),
    leftBorder(0.0),
    topBorder(0.0),
    rightBorder(0.0),
    bottomBorder(0.0)
{
}

MImageWidgetViewPrivate::~MImageWidgetViewPrivate()
{
}

void MImageWidgetViewPrivate::calculateDrawRect(const QSizeF &imageSize)
{
    Q_Q(MImageWidgetView);

    // no image, return
    if (imageSize.isEmpty())
        return;

    // get target size, bounded by widget size
    QSizeF widgetSize = q->size();
    QSizeF targetSize = widgetSize;
    QSizeF t;

    // get the image display size
    qreal fx, fy;
    controller->zoomFactor(&fx, &fy);

    t.setWidth(imageSize.width()*fx);
    t.setHeight(imageSize.height()*fy);

    // limited by target size
    t = targetSize.boundedTo(t);

    // calculate the rectangle of draw
    qreal dx = (widgetSize.width() - t.width()) / 2.0;
    qreal dy = (widgetSize.height() - t.height()) / 2.0;

    // calculate draw rect
    drawRect.setRect(dx, dy, t.width(), t.height());
}

QSizeF MImageWidgetViewPrivate::calculateSourceSize(const QSizeF &imageSize)
{
    QSizeF sourceSize = imageSize;
    QSizeF targetSize = controller->size();

    // protection codes
    if (sourceSize.width() < 1.0)
        sourceSize.setWidth(1.0);
    if (sourceSize.height() < 1.0)
        sourceSize.setHeight(1.0);

    QSizeF t;

    // get the image display size
    qreal fx, fy;
    controller->zoomFactor(&fx, &fy);

    t.setWidth(imageSize.width()*fx);
    t.setHeight(imageSize.height()*fy);

    // update sourceSize for crop section by compare with targetSize, simulate zoom effect
    qreal value;
    if (t.width() > targetSize.width()) {
        value = sourceSize.width();
        sourceSize.setWidth(qRound(value * targetSize.width() / t.width()));
    }
    if (t.height() > targetSize.height()) {
        value = sourceSize.height();
        sourceSize.setHeight(qRound(value * targetSize.height() / t.height()));
    }

    return sourceSize;
}

void MImageWidgetViewPrivate::calculateSourceRect(const QSizeF &imageSize)
{
    QPointF topLeft = controller->crop().topLeft();
    QSizeF originalSize = controller->imageSize();
    QSizeF sourceSize = calculateSourceSize(imageSize);

    if (topLeft == QPointF(-1.0, -1.0)) {
        // calculate default crop section
        qreal dx = (originalSize.width() - sourceSize.width()) / 2.0;
        qreal dy = (originalSize.height() - sourceSize.height()) / 2.0;

        sourceRect = QRectF(dx, dy, sourceSize.width(), sourceSize.height());
    } else {
        // calculate crop section by given topLeft corner
        qreal dx = topLeft.x();
        qreal dy = topLeft.y();

        sourceRect = QRectF(dx, dy, qMin(dx + sourceSize.width(), originalSize.width()),
                            qMin(dy + sourceSize.height(), originalSize.height()));
    }
}

void MImageWidgetViewPrivate::checkPixmapSize()
{
    Q_Q(MImageWidgetView);

    const QPixmap *pixmap = controller->pixmap();
    if (pixmap->size() != cachedPixmapSize) {
        cachedPixmapSize = pixmap->size();
        q->updateGeometry();
    }
}

void MImageWidgetViewPrivate::drawBorders(QPainter *painter, const QRectF &drawRect) const
{
    qreal w = leftBorder + rightBorder;
    qreal h = topBorder + bottomBorder;

    if (w > 0 || h > 0) {
        QRectF border;

        qreal oldOpacity = painter->opacity();
        painter->setOpacity(borderOpacity);

        qreal dx = drawRect.x() - leftBorder;
        qreal dy = drawRect.y() - topBorder;

        if (w > 0 && h == 0) {
            // only have horizontal border
            border = QRectF(dx, drawRect.y(), leftBorder, drawRect.height());
            painter->fillRect(border, brush);

            border = QRectF(drawRect.x() + drawRect.width(), drawRect.y(), rightBorder, drawRect.height());
            painter->fillRect(border, brush);
        } else if (w == 0 && h > 0) {
            // only have vertical border
            border = QRectF(drawRect.x(), dy, drawRect.width(), topBorder);
            painter->fillRect(border, brush);

            border = QRectF(drawRect.x(), drawRect.y() + drawRect.height(), drawRect.width(), bottomBorder);
            painter->fillRect(border, brush);
        } else {
            // draw top border
            border = QRectF(dx, dy, drawRect.width() + w, topBorder);
            painter->fillRect(border, brush);

            // draw left border
            border = QRectF(dx, drawRect.y(), leftBorder, drawRect.height());
            painter->fillRect(border, brush);

            // draw right border
            border = QRectF(drawRect.x() + drawRect.width(), drawRect.y(), rightBorder, drawRect.height());
            painter->fillRect(border, brush);

            // draw bottom border
            border = QRectF(dx, drawRect.y() + drawRect.height(), drawRect.width() + w, bottomBorder);
            painter->fillRect(border, brush);
        }
        painter->setOpacity(oldOpacity);
    }

}

void MImageWidgetViewPrivate::applyStyle()
{
    Q_Q(MImageWidgetView);

    borderOpacity = q->style()->borderOpacity();
    brush.setColor(q->style()->borderColor());

    leftBorder = q->style()->borderLeft();
    topBorder = q->style()->borderTop();
    rightBorder = q->style()->borderRight();
    bottomBorder = q->style()->borderBottom();
}

MImageWidgetView::MImageWidgetView(MImageWidget *controller) :
    MWidgetView(* new MImageWidgetViewPrivate, controller)
{
    Q_D(MImageWidgetView);
    d->controller = controller;
}

MImageWidgetView::MImageWidgetView(MImageWidgetViewPrivate &dd, MImageWidget *controller) :
    MWidgetView(dd, controller)
{
    Q_D(MImageWidgetView);
    d->controller = controller;
}

MImageWidgetView::~MImageWidgetView()
{
}

void MImageWidgetView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    Q_UNUSED(option);

    Q_D(const MImageWidgetView);

    const QPixmap *pixmap = d->controller->pixmap();

    d->drawBorders(painter, d->drawRect);

    if (pixmap) {
        const_cast<MImageWidgetViewPrivate*>(d)->checkPixmapSize();
        painter->drawPixmap(d->drawRect, *pixmap, d->sourceRect);
    } else {
        QImage image = d->controller->d_func()->image;
        painter->drawImage(d->drawRect, image, d->sourceRect);
    }
}

void MImageWidgetView::resizeEvent(QGraphicsSceneResizeEvent *event)
{
    MWidgetView::resizeEvent(event);
    update();
}

void MImageWidgetView::setGeometry(const QRectF &rect)
{
    Q_D(MImageWidgetView);
    MWidgetView::setGeometry(rect);

    QSizeF imageSize = d->controller->d_func()->imageDataSize();
    d->calculateDrawRect(imageSize);
    d->calculateSourceRect(imageSize);
}

void MImageWidgetView::applyStyle()
{
    Q_D(MImageWidgetView);
    MWidgetView::applyStyle();

    d->applyStyle();
}

void MImageWidgetView::updateData(const QList<const char *> &modifications)
{
    MWidgetView::updateData(modifications);

    const char *member;
    for (int i = 0; i < modifications.count(); i++) {
        member = modifications[i];
        if (member == MImageWidgetModel::ZoomFactorX || member == MImageWidgetModel::ZoomFactorY || member == MImageWidgetModel::Crop) {
            updateGeometry();
        }
    }
}

QSizeF MImageWidgetView::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    Q_D(const MImageWidgetView);

    QSizeF s = MWidgetView::sizeHint(which, constraint);
    if(s.isValid())
        return s;

    QSizeF s2;
    if(which == Qt::MinimumSize)
        s2 = QSizeF(0,0);
    else if(which == Qt::MaximumSize)
        s2 = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
    else
        s2 = d->controller->imageSize();

    if(s.height() < 0)
        s.setHeight(s2.height());
    if(s.width() < 0)
        s.setWidth(s2.width());
    return s;
}

M_REGISTER_VIEW_NEW(MImageWidgetView, MImageWidget)

<commit_msg>Changes: MImageWidgetView checks if widget has pixmap directly from private object.<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mimagewidgetview.h"
#include "mimagewidgetview_p.h"

#include <QPixmap>

#include "mimagewidget.h"
#include "mimagewidget_p.h"
#include "mviewcreator.h"
#include "private/mwidgetview_p.h"

MImageWidgetViewPrivate::MImageWidgetViewPrivate()
    : controller(NULL),
    cachedPixmapSize(0, 0),
    borderOpacity(1.0),
    brush(Qt::transparent),
    leftBorder(0.0),
    topBorder(0.0),
    rightBorder(0.0),
    bottomBorder(0.0)
{
}

MImageWidgetViewPrivate::~MImageWidgetViewPrivate()
{
}

void MImageWidgetViewPrivate::calculateDrawRect(const QSizeF &imageSize)
{
    Q_Q(MImageWidgetView);

    // no image, return
    if (imageSize.isEmpty())
        return;

    // get target size, bounded by widget size
    QSizeF widgetSize = q->size();
    QSizeF targetSize = widgetSize;
    QSizeF t;

    // get the image display size
    qreal fx, fy;
    controller->zoomFactor(&fx, &fy);

    t.setWidth(imageSize.width()*fx);
    t.setHeight(imageSize.height()*fy);

    // limited by target size
    t = targetSize.boundedTo(t);

    // calculate the rectangle of draw
    qreal dx = (widgetSize.width() - t.width()) / 2.0;
    qreal dy = (widgetSize.height() - t.height()) / 2.0;

    // calculate draw rect
    drawRect.setRect(dx, dy, t.width(), t.height());
}

QSizeF MImageWidgetViewPrivate::calculateSourceSize(const QSizeF &imageSize)
{
    QSizeF sourceSize = imageSize;
    QSizeF targetSize = controller->size();

    // protection codes
    if (sourceSize.width() < 1.0)
        sourceSize.setWidth(1.0);
    if (sourceSize.height() < 1.0)
        sourceSize.setHeight(1.0);

    QSizeF t;

    // get the image display size
    qreal fx, fy;
    controller->zoomFactor(&fx, &fy);

    t.setWidth(imageSize.width()*fx);
    t.setHeight(imageSize.height()*fy);

    // update sourceSize for crop section by compare with targetSize, simulate zoom effect
    qreal value;
    if (t.width() > targetSize.width()) {
        value = sourceSize.width();
        sourceSize.setWidth(qRound(value * targetSize.width() / t.width()));
    }
    if (t.height() > targetSize.height()) {
        value = sourceSize.height();
        sourceSize.setHeight(qRound(value * targetSize.height() / t.height()));
    }

    return sourceSize;
}

void MImageWidgetViewPrivate::calculateSourceRect(const QSizeF &imageSize)
{
    QPointF topLeft = controller->crop().topLeft();
    QSizeF originalSize = controller->imageSize();
    QSizeF sourceSize = calculateSourceSize(imageSize);

    if (topLeft == QPointF(-1.0, -1.0)) {
        // calculate default crop section
        qreal dx = (originalSize.width() - sourceSize.width()) / 2.0;
        qreal dy = (originalSize.height() - sourceSize.height()) / 2.0;

        sourceRect = QRectF(dx, dy, sourceSize.width(), sourceSize.height());
    } else {
        // calculate crop section by given topLeft corner
        qreal dx = topLeft.x();
        qreal dy = topLeft.y();

        sourceRect = QRectF(dx, dy, qMin(dx + sourceSize.width(), originalSize.width()),
                            qMin(dy + sourceSize.height(), originalSize.height()));
    }
}

void MImageWidgetViewPrivate::checkPixmapSize()
{
    Q_Q(MImageWidgetView);

    const QPixmap *pixmap = controller->pixmap();
    if (pixmap->size() != cachedPixmapSize) {
        cachedPixmapSize = pixmap->size();
        q->updateGeometry();
    }
}

void MImageWidgetViewPrivate::drawBorders(QPainter *painter, const QRectF &drawRect) const
{
    qreal w = leftBorder + rightBorder;
    qreal h = topBorder + bottomBorder;

    if (w > 0 || h > 0) {
        QRectF border;

        qreal oldOpacity = painter->opacity();
        painter->setOpacity(borderOpacity);

        qreal dx = drawRect.x() - leftBorder;
        qreal dy = drawRect.y() - topBorder;

        if (w > 0 && h == 0) {
            // only have horizontal border
            border = QRectF(dx, drawRect.y(), leftBorder, drawRect.height());
            painter->fillRect(border, brush);

            border = QRectF(drawRect.x() + drawRect.width(), drawRect.y(), rightBorder, drawRect.height());
            painter->fillRect(border, brush);
        } else if (w == 0 && h > 0) {
            // only have vertical border
            border = QRectF(drawRect.x(), dy, drawRect.width(), topBorder);
            painter->fillRect(border, brush);

            border = QRectF(drawRect.x(), drawRect.y() + drawRect.height(), drawRect.width(), bottomBorder);
            painter->fillRect(border, brush);
        } else {
            // draw top border
            border = QRectF(dx, dy, drawRect.width() + w, topBorder);
            painter->fillRect(border, brush);

            // draw left border
            border = QRectF(dx, drawRect.y(), leftBorder, drawRect.height());
            painter->fillRect(border, brush);

            // draw right border
            border = QRectF(drawRect.x() + drawRect.width(), drawRect.y(), rightBorder, drawRect.height());
            painter->fillRect(border, brush);

            // draw bottom border
            border = QRectF(dx, drawRect.y() + drawRect.height(), drawRect.width() + w, bottomBorder);
            painter->fillRect(border, brush);
        }
        painter->setOpacity(oldOpacity);
    }

}

void MImageWidgetViewPrivate::applyStyle()
{
    Q_Q(MImageWidgetView);

    borderOpacity = q->style()->borderOpacity();
    brush.setColor(q->style()->borderColor());

    leftBorder = q->style()->borderLeft();
    topBorder = q->style()->borderTop();
    rightBorder = q->style()->borderRight();
    bottomBorder = q->style()->borderBottom();
}

MImageWidgetView::MImageWidgetView(MImageWidget *controller) :
    MWidgetView(* new MImageWidgetViewPrivate, controller)
{
    Q_D(MImageWidgetView);
    d->controller = controller;
}

MImageWidgetView::MImageWidgetView(MImageWidgetViewPrivate &dd, MImageWidget *controller) :
    MWidgetView(dd, controller)
{
    Q_D(MImageWidgetView);
    d->controller = controller;
}

MImageWidgetView::~MImageWidgetView()
{
}

void MImageWidgetView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    Q_UNUSED(option);

    Q_D(const MImageWidgetView);

    const QPixmap *pixmap = d->controller->d_func()->pixmap;

    d->drawBorders(painter, d->drawRect);

    if (pixmap) {
        const_cast<MImageWidgetViewPrivate*>(d)->checkPixmapSize();
        painter->drawPixmap(d->drawRect, *pixmap, d->sourceRect);
    } else {
        QImage image = d->controller->d_func()->image;
        painter->drawImage(d->drawRect, image, d->sourceRect);
    }
}

void MImageWidgetView::resizeEvent(QGraphicsSceneResizeEvent *event)
{
    MWidgetView::resizeEvent(event);
    update();
}

void MImageWidgetView::setGeometry(const QRectF &rect)
{
    Q_D(MImageWidgetView);
    MWidgetView::setGeometry(rect);

    QSizeF imageSize = d->controller->d_func()->imageDataSize();
    d->calculateDrawRect(imageSize);
    d->calculateSourceRect(imageSize);
}

void MImageWidgetView::applyStyle()
{
    Q_D(MImageWidgetView);
    MWidgetView::applyStyle();

    d->applyStyle();
}

void MImageWidgetView::updateData(const QList<const char *> &modifications)
{
    MWidgetView::updateData(modifications);

    const char *member;
    for (int i = 0; i < modifications.count(); i++) {
        member = modifications[i];
        if (member == MImageWidgetModel::ZoomFactorX || member == MImageWidgetModel::ZoomFactorY || member == MImageWidgetModel::Crop) {
            updateGeometry();
        }
    }
}

QSizeF MImageWidgetView::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    Q_D(const MImageWidgetView);

    QSizeF s = MWidgetView::sizeHint(which, constraint);
    if(s.isValid())
        return s;

    QSizeF s2;
    if(which == Qt::MinimumSize)
        s2 = QSizeF(0,0);
    else if(which == Qt::MaximumSize)
        s2 = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
    else
        s2 = d->controller->imageSize();

    if(s.height() < 0)
        s.setHeight(s2.height());
    if(s.width() < 0)
        s.setWidth(s2.width());
    return s;
}

M_REGISTER_VIEW_NEW(MImageWidgetView, MImageWidget)

<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mimagewidgetview.h"
#include "mimagewidgetview_p.h"

#include <QPixmap>

#include "mimagewidget.h"
#include "mimagewidget_p.h"
#include "mviewcreator.h"
#include "private/mwidgetview_p.h"

MImageWidgetViewPrivate::MImageWidgetViewPrivate()
    : controller(NULL),
    cachedPixmapSize(0, 0),
    borderOpacity(1.0),
    brush(Qt::transparent),
    leftBorder(0.0),
    topBorder(0.0),
    rightBorder(0.0),
    bottomBorder(0.0)
{
}

MImageWidgetViewPrivate::~MImageWidgetViewPrivate()
{
}

void MImageWidgetViewPrivate::calculateDrawRect(const QSizeF &imageSize)
{
    Q_Q(MImageWidgetView);

    // no image, return
    if (imageSize.isEmpty())
        return;

    // get target size, bounded by widget size
    QSizeF widgetSize = q->size();
    QSizeF targetSize = widgetSize;
    QSizeF t;

    // get the image display size
    qreal fx, fy;
    controller->zoomFactor(&fx, &fy);

    t.setWidth(imageSize.width()*fx);
    t.setHeight(imageSize.height()*fy);

    // limited by target size
    t = targetSize.boundedTo(t);

    // calculate the rectangle of draw
    qreal dx = (widgetSize.width() - t.width()) / 2.f;
    qreal dy = (widgetSize.height() - t.height()) / 2.f;

    // calculate draw rect
    drawRect.setRect(dx, dy, t.width(), t.height());
}

QSizeF MImageWidgetViewPrivate::calculateSourceSize(const QSizeF &imageSize)
{
    QSizeF sourceSize = imageSize;
    QSizeF targetSize = controller->size();

    // protection codes
    if (sourceSize.width() < 1.0)
        sourceSize.setWidth(1.f);
    if (sourceSize.height() < 1.0)
        sourceSize.setHeight(1.f);

    QSizeF t;

    // get the image display size
    qreal fx, fy;
    controller->zoomFactor(&fx, &fy);

    t.setWidth(imageSize.width()*fx);
    t.setHeight(imageSize.height()*fy);

    // update sourceSize for crop section by compare with targetSize, simulate zoom effect
    qreal value;
    if (t.width() > targetSize.width()) {
        value = sourceSize.width();
        sourceSize.setWidth(qRound(value * targetSize.width() / t.width()));
    }
    if (t.height() > targetSize.height()) {
        value = sourceSize.height();
        sourceSize.setHeight(qRound(value * targetSize.height() / t.height()));
    }

    return sourceSize;
}

void MImageWidgetViewPrivate::calculateSourceRect(const QSizeF &imageSize)
{
    QPointF topLeft = controller->crop().topLeft();
    QSizeF originalSize = controller->imageSize();
    QSizeF sourceSize = calculateSourceSize(imageSize);

    if (topLeft == QPointF(-1.0, -1.0)) {
        // calculate default crop section
        qreal dx = (originalSize.width() - sourceSize.width()) / 2.f;
        qreal dy = (originalSize.height() - sourceSize.height()) / 2.f;

        sourceRect = QRectF(dx, dy, sourceSize.width(), sourceSize.height());
    } else {
        // calculate crop section by given topLeft corner
        qreal dx = topLeft.x();
        qreal dy = topLeft.y();

        sourceRect = QRectF(dx, dy, qMin(dx + sourceSize.width(), originalSize.width()),
                            qMin(dy + sourceSize.height(), originalSize.height()));
    }
}

void MImageWidgetViewPrivate::checkPixmapSize()
{
    Q_Q(MImageWidgetView);

    const QPixmap *pixmap = controller->pixmap();
    if (pixmap->size() != cachedPixmapSize) {
        cachedPixmapSize = pixmap->size();
        q->updateGeometry();
    }
}

void MImageWidgetViewPrivate::drawBorders(QPainter *painter, const QRectF &drawRect) const
{
    qreal w = leftBorder + rightBorder;
    qreal h = topBorder + bottomBorder;

    if (w > 0 || h > 0) {
        QRectF border;

        qreal oldOpacity = painter->opacity();
        painter->setOpacity(borderOpacity);

        qreal dx = drawRect.x() - leftBorder;
        qreal dy = drawRect.y() - topBorder;

        if (w > 0 && h == 0) {
            // only have horizontal border
            border = QRectF(dx, drawRect.y(), leftBorder, drawRect.height());
            painter->fillRect(border, brush);

            border = QRectF(drawRect.x() + drawRect.width(), drawRect.y(), rightBorder, drawRect.height());
            painter->fillRect(border, brush);
        } else if (w == 0 && h > 0) {
            // only have vertical border
            border = QRectF(drawRect.x(), dy, drawRect.width(), topBorder);
            painter->fillRect(border, brush);

            border = QRectF(drawRect.x(), drawRect.y() + drawRect.height(), drawRect.width(), bottomBorder);
            painter->fillRect(border, brush);
        } else {
            // draw top border
            border = QRectF(dx, dy, drawRect.width() + w, topBorder);
            painter->fillRect(border, brush);

            // draw left border
            border = QRectF(dx, drawRect.y(), leftBorder, drawRect.height());
            painter->fillRect(border, brush);

            // draw right border
            border = QRectF(drawRect.x() + drawRect.width(), drawRect.y(), rightBorder, drawRect.height());
            painter->fillRect(border, brush);

            // draw bottom border
            border = QRectF(dx, drawRect.y() + drawRect.height(), drawRect.width() + w, bottomBorder);
            painter->fillRect(border, brush);
        }
        painter->setOpacity(oldOpacity);
    }

}

void MImageWidgetViewPrivate::applyStyle()
{
    Q_Q(MImageWidgetView);

    borderOpacity = q->style()->borderOpacity();
    brush.setColor(q->style()->borderColor());

    leftBorder = q->style()->borderLeft();
    topBorder = q->style()->borderTop();
    rightBorder = q->style()->borderRight();
    bottomBorder = q->style()->borderBottom();
}

const QPixmap *MImageWidgetViewPrivate::createPixmapFromTheme()
{
    QSize imageSize = controller->model()->imageSize();
    QString imageId = controller->imageId();

    if (!imageSize.isValid() || imageSize.isNull())
        imageSize = preferredImageSize();

    if (!imageSize.isValid())
        return MTheme::pixmap(imageId);

    return MTheme::pixmap(imageId, imageSize);
}

QSize MImageWidgetViewPrivate::preferredImageSize()
{
    Q_Q(MImageWidgetView);

    QSize imageSize = controller->preferredSize().toSize();
    imageSize.setWidth(imageSize.width() - (q->marginLeft() + q->marginRight()));
    imageSize.setHeight(imageSize.height() - (q->marginTop() + q->marginBottom()));

    return imageSize;
}

void MImageWidgetViewPrivate::updateImageGeometry()
{
    Q_Q(MImageWidgetView);
    QSizeF imageSize = q->imageDataSize();

    calculateDrawRect(imageSize);
    calculateSourceRect(imageSize);
}

MImageWidgetView::MImageWidgetView(MImageWidget *controller) :
    MWidgetView(* new MImageWidgetViewPrivate, controller)
{
    Q_D(MImageWidgetView);
    d->controller = controller;
}

MImageWidgetView::MImageWidgetView(MImageWidgetViewPrivate &dd, MImageWidget *controller) :
    MWidgetView(dd, controller)
{
    Q_D(MImageWidgetView);
    d->controller = controller;
}

MImageWidgetView::~MImageWidgetView()
{
}

void MImageWidgetView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    Q_UNUSED(option);

    Q_D(const MImageWidgetView);

    const QPixmap *pixmap = d->controller->d_func()->getPixmap();

    d->drawBorders(painter, d->drawRect);

    if (pixmap) {
        const_cast<MImageWidgetViewPrivate*>(d)->checkPixmapSize();
        painter->drawPixmap(d->drawRect, *pixmap, d->sourceRect);
    } else
        painter->drawImage(d->drawRect, d->controller->d_func()->getImage(), d->sourceRect);
}

void MImageWidgetView::resizeEvent(QGraphicsSceneResizeEvent *event)
{
    MWidgetView::resizeEvent(event);
    update();
}

void MImageWidgetView::setGeometry(const QRectF &rect)
{
    Q_D(MImageWidgetView);
    MWidgetView::setGeometry(rect);

    d->updateImageGeometry();
}

void MImageWidgetView::updateGeometry()
{
    Q_D(MImageWidgetView);

    MWidgetView::updateGeometry();

    d->updateImageGeometry();
}

void MImageWidgetView::applyStyle()
{
    Q_D(MImageWidgetView);
    MWidgetView::applyStyle();

    d->applyStyle();

    if (!model()->imageId().isEmpty())
        d->controller->d_func()->setPixmap(d->createPixmapFromTheme(), false);
}

void MImageWidgetView::updateData(const QList<const char *> &modifications)
{
    Q_D(MImageWidgetView);
    MWidgetView::updateData(modifications);

    bool needsGeometryUpdate = false;
    bool needsPixmap = false;
    const char *member;
    for (int i = 0; i < modifications.count(); i++) {
        member = modifications[i];
        if (member == MImageWidgetModel::ZoomFactorX ||
            member == MImageWidgetModel::ZoomFactorY ||
            member == MImageWidgetModel::Crop ||
            member == MImageWidgetModel::AspectRatioMode) {
            needsGeometryUpdate = true;
        } else if (member == MImageWidgetModel::ImageId ||
                   member == MImageWidgetModel::ImageSize) {
            needsPixmap = true;
        }
    }

    if (needsPixmap && !model()->imageId().isEmpty())
        d->controller->d_func()->setPixmap(d->createPixmapFromTheme(), false);

    if (needsGeometryUpdate || needsPixmap)
        updateGeometry();
}

QSizeF MImageWidgetView::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    Q_D(const MImageWidgetView);

    if (which == Qt::MinimumSize || which == Qt::MaximumSize)
        return QSizeF(-1,-1);

    QSizeF size = d->controller->imageSize();

    // an empty QSize() translates to (-1,-1) which would result in an incorrect size hint
    if (!size.isValid())
        size = QSizeF(0, 0);

    if (constraint.width() >= 0 && constraint.width() < size.width())
        size.scale(QSizeF(constraint.width(), QWIDGETSIZE_MAX), Qt::KeepAspectRatio);
    else if (constraint.height() >= 0 && constraint.height() < size.height())
        size.scale(QSizeF(QWIDGETSIZE_MAX, constraint.height()), Qt::KeepAspectRatio);
    return size;
}

QSize MImageWidgetView::imageDataSize()
{
    Q_D(MImageWidgetView);

    return d->controller->d_func()->imageDataSize(model()->crop()).toSize();
}

M_REGISTER_VIEW_NEW(MImageWidgetView, MImageWidget)

<commit_msg>Fixes: NB#299022 - MImageWidget does not render properly the images when a QPixmap is used Changes: enable smooth transformation in MImageWidget for QPixmap RevBy: Heikki Holstila, Vesa Halttunen Details: when the setPixmap method is used in MImageWidget, the QPixmap is provided without mthedaemon interaction, thus, MScalableImage is not called, and it is a requirement to draw the QPixmap with the same transformation as MThemedaemon does, to avoid any blurriness<commit_after>/***************************************************************************
**
** Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "mimagewidgetview.h"
#include "mimagewidgetview_p.h"

#include <QPixmap>

#include "mimagewidget.h"
#include "mimagewidget_p.h"
#include "mviewcreator.h"
#include "private/mwidgetview_p.h"

MImageWidgetViewPrivate::MImageWidgetViewPrivate()
    : controller(NULL),
    cachedPixmapSize(0, 0),
    borderOpacity(1.0),
    brush(Qt::transparent),
    leftBorder(0.0),
    topBorder(0.0),
    rightBorder(0.0),
    bottomBorder(0.0)
{
}

MImageWidgetViewPrivate::~MImageWidgetViewPrivate()
{
}

void MImageWidgetViewPrivate::calculateDrawRect(const QSizeF &imageSize)
{
    Q_Q(MImageWidgetView);

    // no image, return
    if (imageSize.isEmpty())
        return;

    // get target size, bounded by widget size
    QSizeF widgetSize = q->size();
    QSizeF targetSize = widgetSize;
    QSizeF t;

    // get the image display size
    qreal fx, fy;
    controller->zoomFactor(&fx, &fy);

    t.setWidth(imageSize.width()*fx);
    t.setHeight(imageSize.height()*fy);

    // limited by target size
    t = targetSize.boundedTo(t);

    // calculate the rectangle of draw
    qreal dx = (widgetSize.width() - t.width()) / 2.f;
    qreal dy = (widgetSize.height() - t.height()) / 2.f;

    // calculate draw rect
    drawRect.setRect(dx, dy, t.width(), t.height());
}

QSizeF MImageWidgetViewPrivate::calculateSourceSize(const QSizeF &imageSize)
{
    QSizeF sourceSize = imageSize;
    QSizeF targetSize = controller->size();

    // protection codes
    if (sourceSize.width() < 1.0)
        sourceSize.setWidth(1.f);
    if (sourceSize.height() < 1.0)
        sourceSize.setHeight(1.f);

    QSizeF t;

    // get the image display size
    qreal fx, fy;
    controller->zoomFactor(&fx, &fy);

    t.setWidth(imageSize.width()*fx);
    t.setHeight(imageSize.height()*fy);

    // update sourceSize for crop section by compare with targetSize, simulate zoom effect
    qreal value;
    if (t.width() > targetSize.width()) {
        value = sourceSize.width();
        sourceSize.setWidth(qRound(value * targetSize.width() / t.width()));
    }
    if (t.height() > targetSize.height()) {
        value = sourceSize.height();
        sourceSize.setHeight(qRound(value * targetSize.height() / t.height()));
    }

    return sourceSize;
}

void MImageWidgetViewPrivate::calculateSourceRect(const QSizeF &imageSize)
{
    QPointF topLeft = controller->crop().topLeft();
    QSizeF originalSize = controller->imageSize();
    QSizeF sourceSize = calculateSourceSize(imageSize);

    if (topLeft == QPointF(-1.0, -1.0)) {
        // calculate default crop section
        qreal dx = (originalSize.width() - sourceSize.width()) / 2.f;
        qreal dy = (originalSize.height() - sourceSize.height()) / 2.f;

        sourceRect = QRectF(dx, dy, sourceSize.width(), sourceSize.height());
    } else {
        // calculate crop section by given topLeft corner
        qreal dx = topLeft.x();
        qreal dy = topLeft.y();

        sourceRect = QRectF(dx, dy, qMin(dx + sourceSize.width(), originalSize.width()),
                            qMin(dy + sourceSize.height(), originalSize.height()));
    }
}

void MImageWidgetViewPrivate::checkPixmapSize()
{
    Q_Q(MImageWidgetView);

    const QPixmap *pixmap = controller->pixmap();
    if (pixmap->size() != cachedPixmapSize) {
        cachedPixmapSize = pixmap->size();
        q->updateGeometry();
    }
}

void MImageWidgetViewPrivate::drawBorders(QPainter *painter, const QRectF &drawRect) const
{
    qreal w = leftBorder + rightBorder;
    qreal h = topBorder + bottomBorder;

    if (w > 0 || h > 0) {
        QRectF border;

        qreal oldOpacity = painter->opacity();
        painter->setOpacity(borderOpacity);

        qreal dx = drawRect.x() - leftBorder;
        qreal dy = drawRect.y() - topBorder;

        if (w > 0 && h == 0) {
            // only have horizontal border
            border = QRectF(dx, drawRect.y(), leftBorder, drawRect.height());
            painter->fillRect(border, brush);

            border = QRectF(drawRect.x() + drawRect.width(), drawRect.y(), rightBorder, drawRect.height());
            painter->fillRect(border, brush);
        } else if (w == 0 && h > 0) {
            // only have vertical border
            border = QRectF(drawRect.x(), dy, drawRect.width(), topBorder);
            painter->fillRect(border, brush);

            border = QRectF(drawRect.x(), drawRect.y() + drawRect.height(), drawRect.width(), bottomBorder);
            painter->fillRect(border, brush);
        } else {
            // draw top border
            border = QRectF(dx, dy, drawRect.width() + w, topBorder);
            painter->fillRect(border, brush);

            // draw left border
            border = QRectF(dx, drawRect.y(), leftBorder, drawRect.height());
            painter->fillRect(border, brush);

            // draw right border
            border = QRectF(drawRect.x() + drawRect.width(), drawRect.y(), rightBorder, drawRect.height());
            painter->fillRect(border, brush);

            // draw bottom border
            border = QRectF(dx, drawRect.y() + drawRect.height(), drawRect.width() + w, bottomBorder);
            painter->fillRect(border, brush);
        }
        painter->setOpacity(oldOpacity);
    }

}

void MImageWidgetViewPrivate::applyStyle()
{
    Q_Q(MImageWidgetView);

    borderOpacity = q->style()->borderOpacity();
    brush.setColor(q->style()->borderColor());

    leftBorder = q->style()->borderLeft();
    topBorder = q->style()->borderTop();
    rightBorder = q->style()->borderRight();
    bottomBorder = q->style()->borderBottom();
}

const QPixmap *MImageWidgetViewPrivate::createPixmapFromTheme()
{
    QSize imageSize = controller->model()->imageSize();
    QString imageId = controller->imageId();

    if (!imageSize.isValid() || imageSize.isNull())
        imageSize = preferredImageSize();

    if (!imageSize.isValid())
        return MTheme::pixmap(imageId);

    return MTheme::pixmap(imageId, imageSize);
}

QSize MImageWidgetViewPrivate::preferredImageSize()
{
    Q_Q(MImageWidgetView);

    QSize imageSize = controller->preferredSize().toSize();
    imageSize.setWidth(imageSize.width() - (q->marginLeft() + q->marginRight()));
    imageSize.setHeight(imageSize.height() - (q->marginTop() + q->marginBottom()));

    return imageSize;
}

void MImageWidgetViewPrivate::updateImageGeometry()
{
    Q_Q(MImageWidgetView);
    QSizeF imageSize = q->imageDataSize();

    calculateDrawRect(imageSize);
    calculateSourceRect(imageSize);
}

MImageWidgetView::MImageWidgetView(MImageWidget *controller) :
    MWidgetView(* new MImageWidgetViewPrivate, controller)
{
    Q_D(MImageWidgetView);
    d->controller = controller;
}

MImageWidgetView::MImageWidgetView(MImageWidgetViewPrivate &dd, MImageWidget *controller) :
    MWidgetView(dd, controller)
{
    Q_D(MImageWidgetView);
    d->controller = controller;
}

MImageWidgetView::~MImageWidgetView()
{
}

void MImageWidgetView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
{
    Q_UNUSED(option);

    Q_D(const MImageWidgetView);

    const QPixmap *pixmap = d->controller->d_func()->getPixmap();

    d->drawBorders(painter, d->drawRect);

    if (pixmap) {
        const_cast<MImageWidgetViewPrivate*>(d)->checkPixmapSize();
        // Because we are providing the pixmap directly without themedaemon
        // mscalableimage it is not called, thus, in order to have a smooth
        // scalation of the pixmap we need to draw it with a smooth transformation
        static bool enabled = painter->renderHints() & QPainter::SmoothPixmapTransform;
        painter->setRenderHint(QPainter::SmoothPixmapTransform);
        painter->drawPixmap(d->drawRect, *pixmap, d->sourceRect);
        painter->setRenderHint(QPainter::SmoothPixmapTransform, enabled);
    } else
        painter->drawImage(d->drawRect, d->controller->d_func()->getImage(), d->sourceRect);
}

void MImageWidgetView::resizeEvent(QGraphicsSceneResizeEvent *event)
{
    MWidgetView::resizeEvent(event);
    update();
}

void MImageWidgetView::setGeometry(const QRectF &rect)
{
    Q_D(MImageWidgetView);
    MWidgetView::setGeometry(rect);

    d->updateImageGeometry();
}

void MImageWidgetView::updateGeometry()
{
    Q_D(MImageWidgetView);

    MWidgetView::updateGeometry();

    d->updateImageGeometry();
}

void MImageWidgetView::applyStyle()
{
    Q_D(MImageWidgetView);
    MWidgetView::applyStyle();

    d->applyStyle();

    if (!model()->imageId().isEmpty())
        d->controller->d_func()->setPixmap(d->createPixmapFromTheme(), false);
}

void MImageWidgetView::updateData(const QList<const char *> &modifications)
{
    Q_D(MImageWidgetView);
    MWidgetView::updateData(modifications);

    bool needsGeometryUpdate = false;
    bool needsPixmap = false;
    const char *member;
    for (int i = 0; i < modifications.count(); i++) {
        member = modifications[i];
        if (member == MImageWidgetModel::ZoomFactorX ||
            member == MImageWidgetModel::ZoomFactorY ||
            member == MImageWidgetModel::Crop ||
            member == MImageWidgetModel::AspectRatioMode) {
            needsGeometryUpdate = true;
        } else if (member == MImageWidgetModel::ImageId ||
                   member == MImageWidgetModel::ImageSize) {
            needsPixmap = true;
        }
    }

    if (needsPixmap && !model()->imageId().isEmpty())
        d->controller->d_func()->setPixmap(d->createPixmapFromTheme(), false);

    if (needsGeometryUpdate || needsPixmap)
        updateGeometry();
}

QSizeF MImageWidgetView::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    Q_D(const MImageWidgetView);

    if (which == Qt::MinimumSize || which == Qt::MaximumSize)
        return QSizeF(-1,-1);

    QSizeF size = d->controller->imageSize();

    // an empty QSize() translates to (-1,-1) which would result in an incorrect size hint
    if (!size.isValid())
        size = QSizeF(0, 0);

    if (constraint.width() >= 0 && constraint.width() < size.width())
        size.scale(QSizeF(constraint.width(), QWIDGETSIZE_MAX), Qt::KeepAspectRatio);
    else if (constraint.height() >= 0 && constraint.height() < size.height())
        size.scale(QSizeF(QWIDGETSIZE_MAX, constraint.height()), Qt::KeepAspectRatio);
    return size;
}

QSize MImageWidgetView::imageDataSize()
{
    Q_D(MImageWidgetView);

    return d->controller->d_func()->imageDataSize(model()->crop()).toSize();
}

M_REGISTER_VIEW_NEW(MImageWidgetView, MImageWidget)

<|endoftext|>


<commit_before>//sg: this is going to be the primary vision node for qubo (or future robots, whatever)
#include "vision_node.h"

using namespace std;
using namespace ros;
using namespace AVT::VmbAPI;

//you need to pass in a node handle, and a camera feed, which should be a file path either to a physical device or to a video
VisionNode::VisionNode(NodeHandle n, NodeHandle np, string feed)
	:	m_buoy_server(n, "buoy_action", boost::bind(&VisionNode::findBuoy, this, _1, &m_buoy_server), false),
		m_gate_server(n, "gate_action", boost::bind(&VisionNode::findGate, this, _1, &m_gate_server), false)
{

	// isdigit makes sure checks if we're dealing with a number (like if want to open the default camera by passing a 0). If we are we convert our string to an int (VideoCapture won't correctly open the camera with the string in this case);
	//this could give us problems if we pass something like "0followed_by_string" but just don't do that.

	//sgillen@20170429-07:04 we really don't even need this... we can just pass in /dev/video0 if we want the web camera... I'll leave it for now though
	if(feed == "IP") {
		// use the mako if feed isn't given
		//start the camera
		auto vmb_err = [](const int func_call, const string err_msg) {
			if (func_call != VmbErrorSuccess){
				ROS_ERROR("%s, code: %i", err_msg.c_str(), func_call);
				return func_call;
			}
			return func_call;
		};

		ROS_ERROR("getting Vimba");
		auto& m_vimba_sys = VimbaSystem::GetInstance();
		if( vmb_err(m_vimba_sys.Startup(), "Unable to start the Vimba System") ) { exit(1); }

		ROS_ERROR("finding cameras");
		CameraPtrVector c_vec;
		if( vmb_err(m_vimba_sys.GetCameras( c_vec ), "Unable to find cameras") ) {exit(1); }
		// Since we only have one camera currently, we can just use the first thing returned when getting all the cameras
		m_gige_camera = c_vec[0];
		string camera_id;
		if (vmb_err(m_gige_camera->GetID(camera_id), "Unable to get a camera ID from Vimba") ){ exit(1); }
		while( !vmb_err(m_gige_camera->Open(VmbAccessModeFull), "Error opening a camera with Vimba") ) {}

		// We need to height, width and pixel format of the camera to convert the images to something OpenCV likes
		FeaturePtr feat;
		if(!vmb_err(m_gige_camera->GetFeatureByName( "Width", feat), ("Error getting the camera width" ))){
			VmbInt64_t width;
			if (!vmb_err(feat->GetValue(width), "Error getting width")) {
				ROS_ERROR("Width: %lld", width);
				m_width = width;
			}
		}
		if(!vmb_err(m_gige_camera->GetFeatureByName( "Height", feat), ("Error getting the camera height" ))){
			VmbInt64_t height;
			if (!vmb_err(feat->GetValue(height), "Error getting height")) {
				ROS_ERROR("Height: %lld", height);
				m_height = height;
			}
		}
		if(!vmb_err(m_gige_camera->GetFeatureByName("PixelFormat", feat), "Error getting pixel format")){
			vmb_err(feat->GetValue(m_pixel_format), "Error getting format");
		}
	} else {

		if(isdigit(feed.c_str()[0])){
			m_cap = cv::VideoCapture(atoi(feed.c_str()));
		}
		else{
			m_cap = cv::VideoCapture(feed);
		}

		//make sure we have something valid
		if(!m_cap.isOpened()){
			ROS_ERROR("couldn't open file/camera  %s\n now exiting" ,feed.c_str());
			exit(0);
		}
		m_width = m_cap.get(CV_CAP_PROP_FRAME_WIDTH);
		m_height = m_cap.get(CV_CAP_PROP_FRAME_HEIGHT);
	}

	//TODO give option to user to specify the name of the video
	//TODO make sure this doesn't fail when specifying a directory that does not yet exist

	string output_dir;
	np.param<string>("output_dir", output_dir, ""); //this line will populate the output_dir variable if it's specified in the launch file

	//TODO change variable names
	if(!output_dir.empty()){

		stringstream output_ss;
		auto t = time(nullptr);
		auto tm = *localtime(&t);

		output_ss << output_dir;
		output_ss << put_time(&tm, "%Y%m%d_%H-%M-%S");
		output_ss << ".avi";

		string output_str = output_ss.str();

		int ex = static_cast<int>(m_cap.get(CV_CAP_PROP_FOURCC));

		cv::Size S = cv::Size((int) m_width,    // Acquire input size
							  (int) m_height);

		//sgillen@20172107-06:21 I found more problems trying to keep the extension (by passing ex as the second argument) than I did by forcing the output to be CV_FOURCC('M','J','P','G')
		//m_output_video.open(output_str, ex, m_cap.get(CV_CAP_PROP_FPS), S, true);
		m_output_video.open(output_str, CV_FOURCC('M','J','P','G'), 20/* m_cap.get(CV_CAP_PROP_FPS) */, S, true);

		if(!m_output_video.isOpened()){
			ROS_ERROR("problem opening output video! we tried saving it as %s, now exiting" ,output_str.c_str());
			exit(0);
		}

		ROS_INFO("output video being saved as %s" , output_str.c_str());
	}

	//register all services here
	//------------------------------------------------------------------------------
	m_test_srv = n.advertiseService("service_test", &VisionNode::serviceTest, this);

	//start your action servers here
	//------------------------------------------------------------------------------
	m_buoy_server.start();
	m_gate_server.start();
	ROS_INFO("servers started");

}

VisionNode::~VisionNode(){
	//sg: may need to close the cameras here not sure..
	auto& m_vimba_sys = VimbaSystem::GetInstance();
	m_gige_camera->Close();
	m_vimba_sys.Shutdown();
}

void VisionNode::update(){

	// Use the mako if its present
	if (m_gige_camera == nullptr){
		ROS_ERROR("Get vimba frame");
		getVmbFrame(m_img);
	} else {
		m_cap >> m_img;
	}
	//if one of our frames was empty it means we ran out of footage, should only happen with test feeds or if a camera breaks I guess
	if(m_img.empty()){
		// ROS_ERROR("ran out of video (one of the frames was empty) exiting node now");
		// exit(0);
		ROS_ERROR("Empty Frame");
	}

	//if the user didn't specify a directory this will not be open
	if(m_output_video.isOpened()){
		ROS_ERROR("writing image!");
		m_output_video << m_img;
	}

	spinOnce();
}

void VisionNode::getVmbFrame(cv::Mat& cv_frame){
	if(m_gige_camera == nullptr) {
		ROS_ERROR("Attempted to get a frame from a null Vimba camera pointer");
		return;
	}

	// This is a wrapper for the Vimba error functions
	// call a function in it and give it an error message to print
	// if the function fails
	// it also returns the error code, so you can still fail if you need to
	auto vmb_err = [](const int func_call, const string err_msg) {
		if (func_call != VmbErrorSuccess){
			ROS_ERROR("%s, code: %i", err_msg.c_str(), func_call);
			return func_call;
		}
		return func_call;
	};
	FramePtr vmb_frame;
	if( vmb_err( m_gige_camera->AcquireSingleImage(vmb_frame, 2), "Error getting single frame" ) ) { return; }
	VmbFrameStatusType status;
	vmb_frame->GetReceiveStatus(status);
	if(status != VmbFrameStatusComplete) {
		ROS_ERROR("Malformed frame received");
		return;
	}
	VmbUint32_t size;
	vmb_frame->GetImageSize(size);
	unsigned char* img_buf;
	if( vmb_err( vmb_frame->GetImage(img_buf), "Error getting image buffer")) { return; }

	VmbImage img_src, img_dest;
	img_src.Size = sizeof( img_src );
	img_dest.Size = sizeof( img_dest );

	VmbSetImageInfoFromPixelFormat( m_pixel_format, m_width, m_height, &img_src);
	VmbSetImageInfoFromPixelFormat(VmbPixelFormatBgr8, m_width, m_height, &img_dest);

	img_src.Data = img_buf;
	img_src.Data = cv_frame.data;

	vmb_err( VmbImageTransform(&img_src, &img_dest, NULL, 0), "Error transforming image" );
}

/*
* Past this point is a collection of services and
* actions that will be able to called from any other node
* =================================================================================================================
*/
bool VisionNode::serviceTest(ram_msgs::bool_bool::Request &req, ram_msgs::bool_bool::Response &res){
	ROS_ERROR("service called successfully");
}

//There are the definitions for all of our actionlib actions, may be moved to it's own class not sure yet.
//=================================================================================================================
void VisionNode::testExecute(const ram_msgs::VisionNavGoalConstPtr& goal, actionlib::SimpleActionServer<ram_msgs::VisionNavAction> *as){
	//    goal->test_feedback = 5;
	ROS_ERROR("You called the action well done!");
	as->setSucceeded();
}

//if a buoy is found on frame finds where it is and returns the center offset
void VisionNode::findBuoy(const ram_msgs::VisionNavGoalConstPtr& goal,  actionlib::SimpleActionServer<ram_msgs::VisionNavAction> *as){

	BuoyAction action = BuoyAction(as);

	while(true){
		action.updateAction(m_img); //this will also publish the feedback
	}

	as->setSucceeded();
}

void VisionNode::findGate(const ram_msgs::VisionNavGoalConstPtr& goal,  actionlib::SimpleActionServer<ram_msgs::VisionNavAction> *as){

	GateAction action = GateAction();

	while(true){
		ROS_ERROR("updating action");
		action.updateAction(m_img);
	}

	as->setSucceeded();
}
<commit_msg>tweaks<commit_after>//sg: this is going to be the primary vision node for qubo (or future robots, whatever)
#include "vision_node.h"

using namespace std;
using namespace ros;
using namespace AVT::VmbAPI;

//you need to pass in a node handle, and a camera feed, which should be a file path either to a physical device or to a video
VisionNode::VisionNode(NodeHandle n, NodeHandle np, string feed)
	:	m_buoy_server(n, "buoy_action", boost::bind(&VisionNode::findBuoy, this, _1, &m_buoy_server), false),
		m_gate_server(n, "gate_action", boost::bind(&VisionNode::findGate, this, _1, &m_gate_server), false)
{

	// isdigit makes sure checks if we're dealing with a number (like if want to open the default camera by passing a 0). If we are we convert our string to an int (VideoCapture won't correctly open the camera with the string in this case);
	//this could give us problems if we pass something like "0followed_by_string" but just don't do that.

	//sgillen@20170429-07:04 we really don't even need this... we can just pass in /dev/video0 if we want the web camera... I'll leave it for now though
	if(feed == "IP") {
		// use the mako if feed isn't given
		//start the camera
		auto vmb_err = [](const int func_call, const string err_msg) {
			if (func_call != VmbErrorSuccess){
				ROS_ERROR("%s, code: %i", err_msg.c_str(), func_call);
				return func_call;
			}
			return func_call;
		};

		ROS_ERROR("getting Vimba");
		auto& m_vimba_sys = VimbaSystem::GetInstance();
		if( vmb_err(m_vimba_sys.Startup(), "Unable to start the Vimba System") ) { exit(1); }

		ROS_ERROR("finding cameras");
		CameraPtrVector c_vec;
		if( vmb_err(m_vimba_sys.GetCameras( c_vec ), "Unable to find cameras") ) {exit(1); }
		// Since we only have one camera currently, we can just use the first thing returned when getting all the cameras
		m_gige_camera = c_vec[0];
		string camera_id;
		if (vmb_err(m_gige_camera->GetID(camera_id), "Unable to get a camera ID from Vimba") ){ exit(1); }
		while( !vmb_err(m_gige_camera->Open(VmbAccessModeFull), "Error opening a camera with Vimba") ) {}

		// We need to height, width and pixel format of the camera to convert the images to something OpenCV likes
		FeaturePtr feat;
		if(!vmb_err(m_gige_camera->GetFeatureByName( "Width", feat), ("Error getting the camera width" ))){
			VmbInt64_t width;
			if (!vmb_err(feat->GetValue(width), "Error getting width")) {
				ROS_ERROR("Width: %lld", width);
				m_width = width;
			}
		}
		if(!vmb_err(m_gige_camera->GetFeatureByName( "Height", feat), ("Error getting the camera height" ))){
			VmbInt64_t height;
			if (!vmb_err(feat->GetValue(height), "Error getting height")) {
				ROS_ERROR("Height: %lld", height);
				m_height = height;
			}
		}
		if(!vmb_err(m_gige_camera->GetFeatureByName("PixelFormat", feat), "Error getting pixel format")){
			vmb_err(feat->GetValue(m_pixel_format), "Error getting format");
		}
	} else {

		if(isdigit(feed.c_str()[0])){
			m_cap = cv::VideoCapture(atoi(feed.c_str()));
		}
		else{
			m_cap = cv::VideoCapture(feed);
		}

		//make sure we have something valid
		if(!m_cap.isOpened()){
			ROS_ERROR("couldn't open file/camera  %s\n now exiting" ,feed.c_str());
			exit(0);
		}
		m_width = m_cap.get(CV_CAP_PROP_FRAME_WIDTH);
		m_height = m_cap.get(CV_CAP_PROP_FRAME_HEIGHT);
	}

	//TODO give option to user to specify the name of the video
	//TODO make sure this doesn't fail when specifying a directory that does not yet exist

	string output_dir;
	np.param<string>("output_dir", output_dir, ""); //this line will populate the output_dir variable if it's specified in the launch file

	//TODO change variable names
	if(!output_dir.empty()){

		stringstream output_ss;
		auto t = time(nullptr);
		auto tm = *localtime(&t);

		output_ss << output_dir;
		output_ss << put_time(&tm, "%Y%m%d_%H-%M-%S");
		output_ss << ".avi";

		string output_str = output_ss.str();

		int ex = static_cast<int>(m_cap.get(CV_CAP_PROP_FOURCC));

		cv::Size S = cv::Size((int) m_width,    // Acquire input size
							  (int) m_height);

		//sgillen@20172107-06:21 I found more problems trying to keep the extension (by passing ex as the second argument) than I did by forcing the output to be CV_FOURCC('M','J','P','G')
		//m_output_video.open(output_str, ex, m_cap.get(CV_CAP_PROP_FPS), S, true);
		m_output_video.open(output_str, CV_FOURCC('M','J','P','G'), 20/* m_cap.get(CV_CAP_PROP_FPS) */, S, true);

		if(!m_output_video.isOpened()){
			ROS_ERROR("problem opening output video! we tried saving it as %s, now exiting" ,output_str.c_str());
			exit(0);
		}

		ROS_INFO("output video being saved as %s" , output_str.c_str());
	}

	//register all services here
	//------------------------------------------------------------------------------
	m_test_srv = n.advertiseService("service_test", &VisionNode::serviceTest, this);

	//start your action servers here
	//------------------------------------------------------------------------------
	m_buoy_server.start();
	m_gate_server.start();
	ROS_INFO("servers started");

}

VisionNode::~VisionNode(){
	//sg: may need to close the cameras here not sure..
	auto& m_vimba_sys = VimbaSystem::GetInstance();
	m_gige_camera->Close();
	m_vimba_sys.Shutdown();
}

void VisionNode::update(){

	// Use the mako if its present
	if (m_gige_camera != nullptr){
		ROS_ERROR("Get vimba frame");
		getVmbFrame(m_img);
		ROS_ERROR("%s", m_img.data);
	} else {
		m_cap >> m_img;
	}
	//if one of our frames was empty it means we ran out of footage, should only happen with test feeds or if a camera breaks I guess
	if(m_img.empty()){
		// ROS_ERROR("ran out of video (one of the frames was empty) exiting node now");
		// exit(0);
		ROS_ERROR("Empty Frame");
	}

	//if the user didn't specify a directory this will not be open
	if(m_output_video.isOpened()){
		ROS_ERROR("writing image!");
		m_output_video << m_img;
	}

	spinOnce();
}

void VisionNode::getVmbFrame(cv::Mat& cv_frame){
	if(m_gige_camera == nullptr) {
		ROS_ERROR("Attempted to get a frame from a null Vimba camera pointer");
		return;
	}

	// This is a wrapper for the Vimba error functions
	// call a function in it and give it an error message to print
	// if the function fails
	// it also returns the error code, so you can still fail if you need to
	auto vmb_err = [](const int func_call, const string err_msg) {
		if (func_call != VmbErrorSuccess){
			ROS_ERROR("%s, code: %i", err_msg.c_str(), func_call);
			return func_call;
		}
		return func_call;
	};
	FramePtr vmb_frame;
	if( vmb_err( m_gige_camera->AcquireSingleImage(vmb_frame, 2), "Error getting single frame" ) ) { return; }
	VmbFrameStatusType status;
	vmb_frame->GetReceiveStatus(status);
	if(status != VmbFrameStatusComplete) {
		ROS_ERROR("Malformed frame received");
		return;
	}
	VmbUint32_t size;
	vmb_frame->GetImageSize(size);
	unsigned char* img_buf;
	if( vmb_err( vmb_frame->GetImage(img_buf), "Error getting image buffer")) { return; }

	VmbImage img_src, img_dest;
	img_src.Size = sizeof( img_src );
	img_dest.Size = sizeof( img_dest );

	VmbSetImageInfoFromPixelFormat( m_pixel_format, m_width, m_height, &img_src);
	VmbSetImageInfoFromPixelFormat(VmbPixelFormatBgr8, m_width, m_height, &img_dest);

	img_src.Data = img_buf;
	img_src.Data = cv_frame.data;

	vmb_err( VmbImageTransform(&img_src, &img_dest, NULL, 0), "Error transforming image" );
}

/*
* Past this point is a collection of services and
* actions that will be able to called from any other node
* =================================================================================================================
*/
bool VisionNode::serviceTest(ram_msgs::bool_bool::Request &req, ram_msgs::bool_bool::Response &res){
	ROS_ERROR("service called successfully");
}

//There are the definitions for all of our actionlib actions, may be moved to it's own class not sure yet.
//=================================================================================================================
void VisionNode::testExecute(const ram_msgs::VisionNavGoalConstPtr& goal, actionlib::SimpleActionServer<ram_msgs::VisionNavAction> *as){
	//    goal->test_feedback = 5;
	ROS_ERROR("You called the action well done!");
	as->setSucceeded();
}

//if a buoy is found on frame finds where it is and returns the center offset
void VisionNode::findBuoy(const ram_msgs::VisionNavGoalConstPtr& goal,  actionlib::SimpleActionServer<ram_msgs::VisionNavAction> *as){

	BuoyAction action = BuoyAction(as);

	while(true){
		action.updateAction(m_img); //this will also publish the feedback
	}

	as->setSucceeded();
}

void VisionNode::findGate(const ram_msgs::VisionNavGoalConstPtr& goal,  actionlib::SimpleActionServer<ram_msgs::VisionNavAction> *as){

	GateAction action = GateAction();

	while(true){
		ROS_ERROR("updating action");
		action.updateAction(m_img);
	}

	as->setSucceeded();
}
<|endoftext|>


<commit_before>
#include <functional>
#include <random>
#include <ncurses.h>
#include <string.h>
#include <vector>
#include "data.h"
#include "maze.h"
#include "menu.h"
#include "story.h"
#include "windows.h"

using namespace std;

// forward declarations
menu_state game_ui(WINDOW *menu_win, game_data *d, menu_state state);
static void get_new_dims(int& nrows, int& ncols, int level);

/**
 *   The game maze GUI.
 *
 *   Use arrow keys to navigate the maze or type "q" to quit.
 */
menu_state game_ui(WINDOW *win, game_data *d, menu_state state)
{
    maze_data *maze = &d->maze;
    player_data *player = &d->player;
    int c;
    int win_y(15);
    int win_x(15);
    int last_win_y, last_win_x;

    // init window at current resolution
    if (maze->level == -1) {
        intro_splash(win);
    }
    init_maze_window(win);
    getmaxyx(stdscr, win_y, win_x);
    last_win_y = win_y;
    last_win_x = win_x;

    // generate a new maze, if necessary
    if (maze->level == -1) {
        // TODO: Move to data.cpp
        backtracking_maze_gen(maze);
        gen_entrances_opposites(maze);
        std::fill_n(player->visited, maze->max_size * maze->max_size / 2, false);
        player->visited[maze->finish[0] * maze->max_size + maze->finish[1]] = true;
        player->loc[0] = maze->start[0];
        player->loc[1] = maze->start[1];
        maze->level = 0;
        maze->difficulty = state;
    }

    // select the appropriate print function
    function<void(WINDOW*, const maze_data, const int[], const bool[])> maze_print = \
        state == game_easy ? maze_print_easy : (state == game_medium ? maze_print_medium : maze_print_hard);

    // game loop
    while (true) {
        player->visited[player->loc[0] * maze->max_size + player->loc[1]] = true;
        maze_print(win, *maze, player->loc, player->visited);

        // input and update
        c = wgetch(win);
        switch (c) {
            case KEY_UP:
                if (maze_valid_move(*maze, player->loc[0] - 1, player->loc[1])) {
                    player->loc[0] -= 1;
                }
                break;
            case KEY_DOWN:
                if (maze_valid_move(*maze, player->loc[0] + 1, player->loc[1])) {
                    player->loc[0] += 1;
                }
                break;
            case KEY_LEFT:
                if (maze_valid_move(*maze, player->loc[0], player->loc[1] - 1)) {
                    player->loc[1] -= 1;
                }
                break;
            case KEY_RIGHT:
                if (maze_valid_move(*maze, player->loc[0], player->loc[1] + 1)) {
                    player->loc[1] += 1;
                }
                break;
            case 113:  // q
                return menu_cont;
            case KEY_RESIZE:
                getmaxyx(stdscr, win_y, win_x);
                if (last_win_x != win_x || last_win_y != win_y) {
                    last_win_y = win_y;
                    last_win_x = win_x;
                    wresize(win, win_y, win_x);
                    wclear(win);
                    box(win, 0, 0);
                    refresh();
                    wrefresh(win);
                }
                break;
            // no default actions to be taken
        }

        // TODO: How to better check for value equality in arrays?
        // If you reach the end, start over in a new maze
        if (player->loc[0] == maze->finish[0] && player->loc[1] == maze->finish[1]) {
            success_splash(win, maze->level + 2);
            wclear(win);
            clear();
            box(win, 0, 0);
            refresh();
            wrefresh(win);
            get_new_dims(maze->nrows, maze->ncols, maze->level);

            // generate a new maze
            std::fill_n(player->visited, maze->max_size * maze->max_size / 2, false);
            backtracking_maze_gen(maze);
            gen_entrances_opposites(maze);
            player->loc[0] = maze->start[0];
            player->loc[1] = maze->start[1];

            maze->level += 1;
        }
    }
}

/**
 *   Randomly generate maze dimensions.
 */
static void get_new_dims(int& nrows, int& ncols, int level) {
    level %= 20;

    const int bottom_y = 15;
    nrows = bottom_y + level / 2 + (rand() % (int)(level / 2 + 1));
    if (nrows % 2 == 0) { nrows += 1; }

    const int bottom_x = 31;
    ncols = bottom_x + level + (rand() % (int)((level) + 1));
    if (ncols % 2 == 0) { ncols += 1; }
}
<commit_msg>minor cleanup<commit_after>
#include <functional>
#include <random>
#include <ncurses.h>
#include <string.h>
#include <vector>
#include "data.h"
#include "maze.h"
#include "menu.h"
#include "story.h"
#include "windows.h"

using namespace std;

// forward declarations
menu_state game_ui(WINDOW *menu_win, game_data *d, menu_state state);
static void get_new_dims(int& nrows, int& ncols, int level);

/**
 *   The game maze GUI.
 *
 *   Use arrow keys to navigate the maze or type "q" to quit.
 */
menu_state game_ui(WINDOW *win, game_data *d, menu_state state)
{
    maze_data *maze = &d->maze;
    player_data *player = &d->player;
    int c;
    int win_y(15);
    int win_x(15);
    int last_win_y, last_win_x;

    // init window at current resolution
    if (maze->level == -1) {
        intro_splash(win);
    }
    init_maze_window(win);
    getmaxyx(stdscr, win_y, win_x);
    last_win_y = win_y;
    last_win_x = win_x;

    // generate a new maze, if necessary
    if (maze->level == -1) {
        // TODO: Move to data.cpp
        backtracking_maze_gen(maze);
        gen_entrances_opposites(maze);
        std::fill_n(player->visited, maze->max_size * maze->max_size / 2, false);
        player->visited[maze->finish[0] * maze->max_size + maze->finish[1]] = true;
        player->loc[0] = maze->start[0];
        player->loc[1] = maze->start[1];
        maze->level = 0;
        maze->difficulty = state;
    }

    // select the appropriate print function
    function<void(WINDOW*, const maze_data, const int[], const bool[])> maze_print = \
        state == game_easy ? maze_print_easy : (state == game_medium ? maze_print_medium : maze_print_hard);

    // game loop
    while (true) {
        player->visited[player->loc[0] * maze->max_size + player->loc[1]] = true;
        maze_print(win, *maze, player->loc, player->visited);

        // input and update
        c = wgetch(win);
        switch (c) {
            case KEY_UP:
                if (maze_valid_move(*maze, player->loc[0] - 1, player->loc[1])) {
                    player->loc[0] -= 1;
                }
                break;
            case KEY_DOWN:
                if (maze_valid_move(*maze, player->loc[0] + 1, player->loc[1])) {
                    player->loc[0] += 1;
                }
                break;
            case KEY_LEFT:
                if (maze_valid_move(*maze, player->loc[0], player->loc[1] - 1)) {
                    player->loc[1] -= 1;
                }
                break;
            case KEY_RIGHT:
                if (maze_valid_move(*maze, player->loc[0], player->loc[1] + 1)) {
                    player->loc[1] += 1;
                }
                break;
            case 113:  // q
                return menu_cont;
            case KEY_RESIZE:
                getmaxyx(stdscr, win_y, win_x);
                if (last_win_x != win_x || last_win_y != win_y) {
                    last_win_y = win_y;
                    last_win_x = win_x;
                    wresize(win, win_y, win_x);
                    wclear(win);
                    box(win, 0, 0);
                    refresh();
                    wrefresh(win);
                }
                break;
            // no default actions to be taken
        }

        // If you reach the end, start over in a new maze
        if (player->loc[0] == maze->finish[0] && player->loc[1] == maze->finish[1]) {
            success_splash(win, maze->level + 2);
            wclear(win);
            clear();
            box(win, 0, 0);
            refresh();
            wrefresh(win);
            get_new_dims(maze->nrows, maze->ncols, maze->level);

            // generate a new maze
            std::fill_n(player->visited, maze->max_size * maze->max_size / 2, false);
            backtracking_maze_gen(maze);
            gen_entrances_opposites(maze);
            player->loc[0] = maze->start[0];
            player->loc[1] = maze->start[1];

            maze->level += 1;
        }
    }
}

/**
 *   Randomly generate maze dimensions.
 */
static void get_new_dims(int& nrows, int& ncols, int level) {
    level %= 20;

    const int bottom_y = 15;
    nrows = bottom_y + level / 2 + (rand() % (int)(level / 2 + 1));
    if (nrows % 2 == 0) { nrows += 1; }

    const int bottom_x = 31;
    ncols = bottom_x + level + (rand() % (int)((level) + 1));
    if (ncols % 2 == 0) { ncols += 1; }
}
<|endoftext|>


<commit_before>/* Copyright 2015 Peter Goodman (peter@trailofbits.com), all rights reserved. */

#include <gflags/gflags.h>
#include <glog/logging.h>

#include <functional>
#include <set>
#include <string>
#include <sstream>

#include <llvm/ADT/SmallVector.h>

#include <llvm/IR/Constants.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/Type.h>

#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Utils/Cloning.h>
#include <llvm/Transforms/Utils/ValueMapper.h>

#include "mcsema/Arch/Arch.h"
#include "mcsema/BC/IntrinsicTable.h"
#include "mcsema/BC/Translator.h"
#include "mcsema/CFG/CFG.h"
#include "mcsema/OS/OS.h"

DEFINE_int32(max_dataflow_analysis_iterations, 0,
             "Maximum number of iterations of a data flow pass to perform "
             "over the control-flow graph being lifted.");

DEFINE_bool(aggressive_dataflow_analysis, false,
            "Should data-flow analysis be conservative in their conclusions? "
            "If not then the analysis will be really aggressive and make a lot "
            "of assumptions about function call behavior.");

namespace llvm {
class ReturnInst;
}  // namespace

namespace mcsema {
namespace {

// Name of some meta-data that we use to distinguish between external symbols
// and private symbols.
static std::string NamedSymbolMetaId(std::string func_name) {
  return "mcsema_external:" + func_name;
}

// Returns the ID for this binary. We prefix every basic block function added to
// a module with the ID of the binary, where the ID is a number that increments
// linearly.
static int GetBinaryId(llvm::Module *M) {
  for (auto i = 1; ; ++i) {
    std::string id = "mcsema_binary:" + std::to_string(i);
    if (!M->getNamedMetadata(id)) {
      M->getOrInsertNamedMetadata(id);
      return i;
    }
  }
  __builtin_unreachable();
}

}  // namespace

Translator::Translator(const Arch *arch_, llvm::Module *module_)
    : arch(arch_),
      module(module_),
      blocks(),
      functions(),
      symbols(),
      binary_id(GetBinaryId(module)),
      basic_block(FindFunction(module, "__mcsema_basic_block")),
      intrinsics(new IntrinsicTable(module)) {

  EnableDeferredInlining();
  IdentifyExistingSymbols();
}

namespace {

static void DisableInlining(llvm::Function *F) {
  F->removeFnAttr(llvm::Attribute::AlwaysInline);
  F->removeFnAttr(llvm::Attribute::InlineHint);
  F->addFnAttr(llvm::Attribute::NoInline);
}

}  // namespace

// Enable deferred inlining. The goal is to support better dead-store
// elimination for flags.
void Translator::EnableDeferredInlining(void) {
  if (!intrinsics->defer_inlining) return;
  DisableInlining(intrinsics->defer_inlining);

  for (auto U : intrinsics->defer_inlining->users()) {
    if (auto C = llvm::dyn_cast_or_null<llvm::CallInst>(U)) {
      auto B = C->getParent();
      auto F = B->getParent();
      DisableInlining(F);
    }
  }
}

// Find existing exported functions and variables. This is for the sake of
// linking functions of the same names across CFG files.
void Translator::IdentifyExistingSymbols(void) {
  for (auto &F : module->functions()) {
    std::string name = F.getName();
    if (module->getNamedMetadata(NamedSymbolMetaId(name))) {
      functions[name] = &F;
    }
  }

  for (auto &V : module->globals()) {
    std::string name = V.getName();
    if (module->getNamedMetadata(NamedSymbolMetaId(name))) {
      symbols[name] = &V;
    }
  }
}

namespace {

void InitBlockFuncAttributes(llvm::Function *BF, const llvm::Function *TF) {
  BF->setAttributes(TF->getAttributes());
  InitFunctionAttributes(BF);
  auto args = BF->arg_begin();
  (args++)->setName("state");
  args->setName("pc");
}

llvm::Function *GetBlockFunction(llvm::Module *M,
                                 const llvm::Function *TF,
                                 std::string name) {
  auto func_type = TF->getFunctionType();
  auto BF = llvm::dyn_cast<llvm::Function>(
      M->getOrInsertFunction(name, func_type));
  InitBlockFuncAttributes(BF, TF);
  BF->setLinkage(llvm::GlobalValue::PrivateLinkage);
  return BF;
}

}  // namespace

// Create functions for every block in the CFG.
void Translator::CreateFunctionsForBlocks(const cfg::Module *cfg) {
  std::set<uint64_t> indirect_blocks;
  for (const auto &block : cfg->indirect_blocks()) {
    indirect_blocks.insert(block.address());
  }

  for (const auto &block : cfg->blocks()) {
    auto &BF = blocks[block.address()];
    if (!BF) {
      std::stringstream ss;
      ss << "__lifted_block_" << binary_id << "_0x"
         << std::hex << block.address();

      BF = GetBlockFunction(module, basic_block, ss.str());

      // This block is externally visible so change its linkage and make a new
      // private block to which other blocks will refer.
      if (indirect_blocks.count(block.address())) {
        std::stringstream ss2;
        ss2 << "__extern_block_" << binary_id << "_0x"
           << std::hex << block.address();

        auto EF = GetBlockFunction(module, basic_block, ss2.str());
        EF->setLinkage(llvm::GlobalValue::ExternalLinkage);
        AddTerminatingTailCall(EF, BF, block.address());
      }
    }
  }
}

namespace {

// On Mac this strips off leading the underscore on symbol names.
//
// TODO(pag): This is really ugly and is probably incorrect. The expectation is
//            that the leading underscore will be re-added when this code is
//            compiled.
std::string CanonicalName(OSName os_name, const std::string &name) {
  if (kOSMacOSX == os_name && name.length() && '_' == name[0]) {
    return name.substr(1);
  } else {
    return name;
  }
}

}  // namespace

// Create functions for every function in the CFG.
void Translator::CreateExternalFunctions(const cfg::Module *cfg) {
  for (const auto &func : cfg->functions()) {
    if (!func.is_exported() && !func.is_imported()) continue;

    auto func_name = CanonicalName(arch->os_name, func.name());
    CHECK(!func_name.empty())
        << "Module contains unnamed function at address " << func.address();

    CHECK(!(func.is_exported() && func.is_imported()))
        << "Function " << func_name << " can't be imported and exported.";

    llvm::Function *&F = functions[func_name];
    if (!F) {
      F = GetBlockFunction(module, basic_block, func_name);

      // To get around some issues that `opt` has.
      F->addFnAttr(llvm::Attribute::NoBuiltin);
      F->addFnAttr(llvm::Attribute::OptimizeNone);
      F->addFnAttr(llvm::Attribute::NoInline);
      F->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
    }

    // Mark this symbol as external. We do this so that we can pick up on it
    // if we merge another CFG into this bitcode module.
    module->getOrInsertNamedMetadata(NamedSymbolMetaId(func_name));
  }
}

// Link together functions and basic blocks.
void Translator::LinkExternalFunctionsToBlocks(const cfg::Module *cfg) {
  for (const auto &func : cfg->functions()) {
    if (!func.is_exported() && !func.is_imported()) continue;
    if (!func.address()) continue;

    auto func_name = CanonicalName(arch->os_name, func.name());
    auto F = functions[func_name];
    auto &BF = blocks[func.address()];

    // In the case of an exported function, redirect the function's
    // implementation to a locally defined block.
    if (func.is_exported()) {

      CHECK(nullptr != BF)
          << "Exported function " << func_name << " has no address!";

      CHECK(F->isDeclaration())
          << "Function " << func_name << " is already defined!";

      AddTerminatingTailCall(F, BF, func.address());

    // In the case of ELF binaries, we tend to see a call to something like
    // `malloc@plt` that is responsible for finding and invoking the actual
    // `malloc` implementation. In this case, we want to treat the `malloc@plt`
    // block address as synonymous with the function.
    //
    // TODO(pag): What about versioned ELF symbols?
    } else if (func.is_imported()) {
      if (!BF) {
        BF = F;
      } else {
        AddTerminatingTailCall(BF, F);
      }
    }
  }
}

namespace {

// Clone the block method template `TF` into a specific method `BF` that
// will contain lifted code.
static std::vector<llvm::BasicBlock *> InitBlockFunction(
    llvm::Function *BF, const llvm::Function *TF, const cfg::Block &block) {
  llvm::ValueToValueMapTy var_map;
  auto targs = TF->arg_begin();
  auto bargs = BF->arg_begin();
  for (; targs != TF->arg_end(); ++targs, ++bargs) {
    var_map[&*targs] = &*bargs;
  }

  llvm::SmallVector<llvm::ReturnInst *, 1> returns;
  llvm::CloneFunctionInto(BF, TF, var_map, false, returns);

  InitBlockFuncAttributes(BF, TF);

  auto R = returns[0];
  R->removeFromParent();
  delete R;

  std::vector<llvm::BasicBlock *> instruction_blocks;
  instruction_blocks.reserve(block.instructions_size());
  instruction_blocks.push_back(&(BF->back()));
  for (const auto &instr : block.instructions()) {

    // Name the block according to the instruction's address.
    std::stringstream ss;
    ss << "0x" << std::hex << instr.address();

    // Create a block for this instruction.
    auto B = llvm::BasicBlock::Create(BF->getContext(), ss.str(), BF);
    instruction_blocks.push_back(B);
  }
  return instruction_blocks;
}

// Fall-through PC for a block.
static uint64_t FallThroughPC(const cfg::Block &block) {
  if (0 < block.instructions_size()) {
    const auto &instr = block.instructions(block.instructions_size() - 1);
    return instr.address() + instr.size();

  } else {
    LOG(ERROR) << "Using block address as fall-through address.";
    return block.address();
  }
}

}  // namespace

// Add a fall-through terminator to the block method just in case one is
// missing.
void Translator::TerminateBlockMethod(const cfg::Block &block,
                                      llvm::Function *BF) {
  auto &B = BF->back();
  if (B.getTerminator()) {
    return;
  }
  if (block.instructions_size()) {
    auto next_pc = FallThroughPC(block);
    AddTerminatingTailCall(BF, GetLiftedBlockForPC(next_pc), next_pc);
  } else {
    AddTerminatingTailCall(BF, intrinsics->missing_block, block.address());
  }
}

// Lift code contained in blocks into the block methods.
void Translator::LiftBlocks(const cfg::Module *cfg) {
  llvm::legacy::FunctionPassManager FPM(module);
  FPM.add(llvm::createDeadCodeEliminationPass());
  FPM.add(llvm::createCFGSimplificationPass());
  FPM.add(llvm::createPromoteMemoryToRegisterPass());
  FPM.add(llvm::createReassociatePass());
  FPM.add(llvm::createInstructionCombiningPass());
  FPM.add(llvm::createDeadStoreEliminationPass());

  for (const auto &block : cfg->blocks()) {
    LOG_IF(WARNING, !block.instructions_size())
        << "Block at " << block.address() << " has no instructions!";

    auto BF = GetLiftedBlockForPC(block.address());
    if (!BF->isDeclaration()) {
      LOG(WARNING) << "Ignoring already lifted block at " << block.address();
      continue;
    }

    const auto instruction_blocks = InitBlockFunction(BF, basic_block, block);

    // Lift the instructions into the block in reverse order. This helps for
    // using the results of backward data-flow analyses.
    for (auto i = block.instructions_size(); i--; ) {
      const auto &instr = block.instructions(i);
      const auto bb = instruction_blocks[i + 1];
      LiftInstructionIntoBlock(block, instr, bb);
    }

    // Connect the internal blocks together with branches.
    if (instruction_blocks.size()) {
      for (auto i = 1; i < instruction_blocks.size(); ++i) {
        llvm::IRBuilder<> ir(instruction_blocks[i - 1]);
        ir.CreateBr(instruction_blocks[i]);
      }
    }

    TerminateBlockMethod(block, BF);

    // Perform simple, incremental optimizations on the block functions to
    // avoid OOMs.
    FPM.run(*BF);
  }
}

// Lift an instruction into a basic block. This does some minor sanity checks
// then dispatches to the arch-specific translator.
void Translator::LiftInstructionIntoBlock(
    const cfg::Block &block, const cfg::Instr &instr, llvm::BasicBlock *B) {

  CHECK(0 < instr.size())
      << "Can't decode zero-sized instruction at " << instr.address();

  CHECK(instr.size() == instr.bytes().length())
      << "Instruction size mismatch for instruction at " << instr.address();

  arch->LiftInstructionIntoBlock(*this, block, instr, B);
}

void Translator::LiftCFG(const cfg::Module *cfg) {
  blocks.clear();  // Just in case we call `LiftCFG` multiple times.
  CreateFunctionsForBlocks(cfg);
  CreateExternalFunctions(cfg);
  LinkExternalFunctionsToBlocks(cfg);
  AnalyzeCFG(cfg);
  LiftBlocks(cfg);
}

// Run an architecture-specific data-flow analysis on the module.
void Translator::AnalyzeCFG(const cfg::Module *cfg) {
  if (!FLAGS_max_dataflow_analysis_iterations) return;

  AnalysisWorkList wl;
  auto &analysis = arch->CFGAnalyzer();

  for (const auto &block : cfg->blocks()) {
    analysis.AddBlock(block);
  }

  for (const auto &func : cfg->functions()) {
    analysis.AddFunction(func);
  }

  analysis.InitWorkList(wl);
  for (auto i = 0;
       wl.size() && i < FLAGS_max_dataflow_analysis_iterations; ++i) {
    AnalysisWorkList next_wl;
    for (auto item : wl) {
      analysis.AnalyzeBlock(item, next_wl);
    }
    wl.swap(next_wl);
  }
  analysis.Finalize();
}

llvm::Function *Translator::GetLiftedBlockForPC(uintptr_t pc) const {
  auto F = blocks[pc];
  if (!F) {
    LOG(ERROR) << "Could not find lifted block for PC " << pc;
    F = intrinsics->missing_block;
  }
  return F;
}

}  // namespace mcsema
<commit_msg>Add in address information to lifted basic block functions in the form of metadata.<commit_after>/* Copyright 2015 Peter Goodman (peter@trailofbits.com), all rights reserved. */

#include <gflags/gflags.h>
#include <glog/logging.h>

#include <functional>
#include <set>
#include <string>
#include <sstream>

#include <llvm/ADT/SmallVector.h>

#include <llvm/IR/Constants.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Metadata.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/IR/Type.h>

#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Utils/Cloning.h>
#include <llvm/Transforms/Utils/ValueMapper.h>

#include "mcsema/Arch/Arch.h"
#include "mcsema/BC/IntrinsicTable.h"
#include "mcsema/BC/Translator.h"
#include "mcsema/CFG/CFG.h"
#include "mcsema/OS/OS.h"

DEFINE_int32(max_dataflow_analysis_iterations, 0,
             "Maximum number of iterations of a data flow pass to perform "
             "over the control-flow graph being lifted.");

DEFINE_bool(aggressive_dataflow_analysis, false,
            "Should data-flow analysis be conservative in their conclusions? "
            "If not then the analysis will be really aggressive and make a lot "
            "of assumptions about function call behavior.");

namespace llvm {
class ReturnInst;
}  // namespace

namespace mcsema {
namespace {

// Name of some meta-data that we use to distinguish between external symbols
// and private symbols.
static std::string NamedSymbolMetaId(std::string func_name) {
  return "mcsema_external:" + func_name;
}

// Returns the ID for this binary. We prefix every basic block function added to
// a module with the ID of the binary, where the ID is a number that increments
// linearly.
static int GetBinaryId(llvm::Module *M) {
  for (auto i = 1; ; ++i) {
    std::string id = "mcsema_binary:" + std::to_string(i);
    if (!M->getNamedMetadata(id)) {
      M->getOrInsertNamedMetadata(id);
      return i;
    }
  }
  __builtin_unreachable();
}

}  // namespace

Translator::Translator(const Arch *arch_, llvm::Module *module_)
    : arch(arch_),
      module(module_),
      blocks(),
      functions(),
      symbols(),
      binary_id(GetBinaryId(module)),
      basic_block(FindFunction(module, "__mcsema_basic_block")),
      intrinsics(new IntrinsicTable(module)) {

  EnableDeferredInlining();
  IdentifyExistingSymbols();
}

namespace {

static void DisableInlining(llvm::Function *F) {
  F->removeFnAttr(llvm::Attribute::AlwaysInline);
  F->removeFnAttr(llvm::Attribute::InlineHint);
  F->addFnAttr(llvm::Attribute::NoInline);
}

}  // namespace

// Enable deferred inlining. The goal is to support better dead-store
// elimination for flags.
void Translator::EnableDeferredInlining(void) {
  if (!intrinsics->defer_inlining) return;
  DisableInlining(intrinsics->defer_inlining);

  for (auto U : intrinsics->defer_inlining->users()) {
    if (auto C = llvm::dyn_cast_or_null<llvm::CallInst>(U)) {
      auto B = C->getParent();
      auto F = B->getParent();
      DisableInlining(F);
    }
  }
}

// Find existing exported functions and variables. This is for the sake of
// linking functions of the same names across CFG files.
void Translator::IdentifyExistingSymbols(void) {
  for (auto &F : module->functions()) {
    std::string name = F.getName();
    if (module->getNamedMetadata(NamedSymbolMetaId(name))) {
      functions[name] = &F;
    }
  }

  for (auto &V : module->globals()) {
    std::string name = V.getName();
    if (module->getNamedMetadata(NamedSymbolMetaId(name))) {
      symbols[name] = &V;
    }
  }
}

namespace {

void InitBlockFuncAttributes(llvm::Function *BF, const llvm::Function *TF) {
  BF->setAttributes(TF->getAttributes());
  InitFunctionAttributes(BF);
  auto args = BF->arg_begin();
  (args++)->setName("state");
  args->setName("pc");
}

llvm::Function *GetBlockFunction(llvm::Module *M,
                                 const llvm::Function *TF,
                                 std::string name) {
  auto func_type = TF->getFunctionType();
  auto BF = llvm::dyn_cast<llvm::Function>(
      M->getOrInsertFunction(name, func_type));
  InitBlockFuncAttributes(BF, TF);
  BF->setLinkage(llvm::GlobalValue::PrivateLinkage);
  return BF;
}

// Add the address of the block as a special meta-data flag.
static void SetMetaDataAddress(llvm::Module *M, const std::string &block_name,
                               uint64_t block_address) {
  std::stringstream ss;
  ss << "mcsema_address:" << block_name;
  auto &C = M->getContext();
  auto Int64Ty = llvm::Type::getInt64Ty(C);
  auto CI = llvm::ConstantInt::get(Int64Ty, block_address, false);
  auto CM = llvm::ConstantAsMetadata::get(CI);
  M->addModuleFlag(llvm::Module::Warning, ss.str(), CM);
}

}  // namespace

// Create functions for every block in the CFG.
void Translator::CreateFunctionsForBlocks(const cfg::Module *cfg) {
  std::set<uint64_t> indirect_blocks;
  for (const auto &block : cfg->indirect_blocks()) {
    indirect_blocks.insert(block.address());
  }

  for (const auto &block : cfg->blocks()) {
    auto &BF = blocks[block.address()];
    if (!BF) {
      std::stringstream ss;
      ss << "__lifted_block_" << binary_id << "_0x"
         << std::hex << block.address();

      BF = GetBlockFunction(module, basic_block, ss.str());
      SetMetaDataAddress(module, ss.str(), block.address());

      // This block is externally visible so change its linkage and make a new
      // private block to which other blocks will refer.
      if (indirect_blocks.count(block.address())) {
        std::stringstream ss2;
        ss2 << "__extern_block_" << binary_id << "_0x"
           << std::hex << block.address();

        auto EF = GetBlockFunction(module, basic_block, ss2.str());
        EF->setLinkage(llvm::GlobalValue::ExternalLinkage);
        AddTerminatingTailCall(EF, BF, block.address());
      }
    }
  }
}

namespace {

// On Mac this strips off leading the underscore on symbol names.
//
// TODO(pag): This is really ugly and is probably incorrect. The expectation is
//            that the leading underscore will be re-added when this code is
//            compiled.
std::string CanonicalName(OSName os_name, const std::string &name) {
  if (kOSMacOSX == os_name && name.length() && '_' == name[0]) {
    return name.substr(1);
  } else {
    return name;
  }
}

}  // namespace

// Create functions for every function in the CFG.
void Translator::CreateExternalFunctions(const cfg::Module *cfg) {
  for (const auto &func : cfg->functions()) {
    if (!func.is_exported() && !func.is_imported()) continue;

    auto func_name = CanonicalName(arch->os_name, func.name());
    CHECK(!func_name.empty())
        << "Module contains unnamed function at address " << func.address();

    CHECK(!(func.is_exported() && func.is_imported()))
        << "Function " << func_name << " can't be imported and exported.";

    llvm::Function *&F = functions[func_name];
    if (!F) {
      F = GetBlockFunction(module, basic_block, func_name);

      // To get around some issues that `opt` has.
      F->addFnAttr(llvm::Attribute::NoBuiltin);
      F->addFnAttr(llvm::Attribute::OptimizeNone);
      F->addFnAttr(llvm::Attribute::NoInline);
      F->setLinkage(llvm::GlobalValue::ExternalWeakLinkage);
    }

    // Mark this symbol as external. We do this so that we can pick up on it
    // if we merge another CFG into this bitcode module.
    module->getOrInsertNamedMetadata(NamedSymbolMetaId(func_name));
  }
}

// Link together functions and basic blocks.
void Translator::LinkExternalFunctionsToBlocks(const cfg::Module *cfg) {
  for (const auto &func : cfg->functions()) {
    if (!func.is_exported() && !func.is_imported()) continue;
    if (!func.address()) continue;

    auto func_name = CanonicalName(arch->os_name, func.name());
    auto F = functions[func_name];
    auto &BF = blocks[func.address()];

    // In the case of an exported function, redirect the function's
    // implementation to a locally defined block.
    if (func.is_exported()) {

      CHECK(nullptr != BF)
          << "Exported function " << func_name << " has no address!";

      CHECK(F->isDeclaration())
          << "Function " << func_name << " is already defined!";

      AddTerminatingTailCall(F, BF, func.address());

    // In the case of ELF binaries, we tend to see a call to something like
    // `malloc@plt` that is responsible for finding and invoking the actual
    // `malloc` implementation. In this case, we want to treat the `malloc@plt`
    // block address as synonymous with the function.
    //
    // TODO(pag): What about versioned ELF symbols?
    } else if (func.is_imported()) {
      if (!BF) {
        BF = F;
      } else {
        AddTerminatingTailCall(BF, F);
      }
    }
  }
}

namespace {

// Clone the block method template `TF` into a specific method `BF` that
// will contain lifted code.
static std::vector<llvm::BasicBlock *> InitBlockFunction(
    llvm::Function *BF, const llvm::Function *TF, const cfg::Block &block) {
  llvm::ValueToValueMapTy var_map;
  auto targs = TF->arg_begin();
  auto bargs = BF->arg_begin();
  for (; targs != TF->arg_end(); ++targs, ++bargs) {
    var_map[&*targs] = &*bargs;
  }

  llvm::SmallVector<llvm::ReturnInst *, 1> returns;
  llvm::CloneFunctionInto(BF, TF, var_map, false, returns);

  InitBlockFuncAttributes(BF, TF);

  auto R = returns[0];
  R->removeFromParent();
  delete R;

  std::vector<llvm::BasicBlock *> instruction_blocks;
  instruction_blocks.reserve(block.instructions_size());
  instruction_blocks.push_back(&(BF->back()));
  for (const auto &instr : block.instructions()) {

    // Name the block according to the instruction's address.
    std::stringstream ss;
    ss << "0x" << std::hex << instr.address();

    // Create a block for this instruction.
    auto B = llvm::BasicBlock::Create(BF->getContext(), ss.str(), BF);
    instruction_blocks.push_back(B);
  }
  return instruction_blocks;
}

// Fall-through PC for a block.
static uint64_t FallThroughPC(const cfg::Block &block) {
  if (0 < block.instructions_size()) {
    const auto &instr = block.instructions(block.instructions_size() - 1);
    return instr.address() + instr.size();

  } else {
    LOG(ERROR) << "Using block address as fall-through address.";
    return block.address();
  }
}

}  // namespace

// Add a fall-through terminator to the block method just in case one is
// missing.
void Translator::TerminateBlockMethod(const cfg::Block &block,
                                      llvm::Function *BF) {
  auto &B = BF->back();
  if (B.getTerminator()) {
    return;
  }
  if (block.instructions_size()) {
    auto next_pc = FallThroughPC(block);
    AddTerminatingTailCall(BF, GetLiftedBlockForPC(next_pc), next_pc);
  } else {
    AddTerminatingTailCall(BF, intrinsics->missing_block, block.address());
  }
}

// Lift code contained in blocks into the block methods.
void Translator::LiftBlocks(const cfg::Module *cfg) {
  llvm::legacy::FunctionPassManager FPM(module);
  FPM.add(llvm::createDeadCodeEliminationPass());
  FPM.add(llvm::createCFGSimplificationPass());
  FPM.add(llvm::createPromoteMemoryToRegisterPass());
  FPM.add(llvm::createReassociatePass());
  FPM.add(llvm::createInstructionCombiningPass());
  FPM.add(llvm::createDeadStoreEliminationPass());

  for (const auto &block : cfg->blocks()) {
    LOG_IF(WARNING, !block.instructions_size())
        << "Block at " << block.address() << " has no instructions!";

    auto BF = GetLiftedBlockForPC(block.address());
    if (!BF->isDeclaration()) {
      LOG(WARNING) << "Ignoring already lifted block at " << block.address();
      continue;
    }

    const auto instruction_blocks = InitBlockFunction(BF, basic_block, block);

    // Lift the instructions into the block in reverse order. This helps for
    // using the results of backward data-flow analyses.
    for (auto i = block.instructions_size(); i--; ) {
      const auto &instr = block.instructions(i);
      const auto bb = instruction_blocks[i + 1];
      LiftInstructionIntoBlock(block, instr, bb);
    }

    // Connect the internal blocks together with branches.
    if (instruction_blocks.size()) {
      for (auto i = 1; i < instruction_blocks.size(); ++i) {
        llvm::IRBuilder<> ir(instruction_blocks[i - 1]);
        ir.CreateBr(instruction_blocks[i]);
      }
    }

    TerminateBlockMethod(block, BF);

    // Perform simple, incremental optimizations on the block functions to
    // avoid OOMs.
    FPM.run(*BF);
  }
}

// Lift an instruction into a basic block. This does some minor sanity checks
// then dispatches to the arch-specific translator.
void Translator::LiftInstructionIntoBlock(
    const cfg::Block &block, const cfg::Instr &instr, llvm::BasicBlock *B) {

  CHECK(0 < instr.size())
      << "Can't decode zero-sized instruction at " << instr.address();

  CHECK(instr.size() == instr.bytes().length())
      << "Instruction size mismatch for instruction at " << instr.address();

  arch->LiftInstructionIntoBlock(*this, block, instr, B);
}

void Translator::LiftCFG(const cfg::Module *cfg) {
  blocks.clear();  // Just in case we call `LiftCFG` multiple times.
  CreateFunctionsForBlocks(cfg);
  CreateExternalFunctions(cfg);
  LinkExternalFunctionsToBlocks(cfg);
  AnalyzeCFG(cfg);
  LiftBlocks(cfg);
}

// Run an architecture-specific data-flow analysis on the module.
void Translator::AnalyzeCFG(const cfg::Module *cfg) {
  if (!FLAGS_max_dataflow_analysis_iterations) return;

  AnalysisWorkList wl;
  auto &analysis = arch->CFGAnalyzer();

  for (const auto &block : cfg->blocks()) {
    analysis.AddBlock(block);
  }

  for (const auto &func : cfg->functions()) {
    analysis.AddFunction(func);
  }

  analysis.InitWorkList(wl);
  for (auto i = 0;
       wl.size() && i < FLAGS_max_dataflow_analysis_iterations; ++i) {
    AnalysisWorkList next_wl;
    for (auto item : wl) {
      analysis.AnalyzeBlock(item, next_wl);
    }
    wl.swap(next_wl);
  }
  analysis.Finalize();
}

llvm::Function *Translator::GetLiftedBlockForPC(uintptr_t pc) const {
  auto F = blocks[pc];
  if (!F) {
    LOG(ERROR) << "Could not find lifted block for PC " << pc;
    F = intrinsics->missing_block;
  }
  return F;
}

}  // namespace mcsema
<|endoftext|>


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

#include "FreeRTOS.h"
#include "task.h"

#include <lib/shell/shell.h>

#include <lib/core/CHIPCore.h>
#include <lib/support/Base64.h>
#include <lib/support/CHIPArgParser.hpp>
#include <lib/support/CodeUtils.h>
#include <lib/support/RandUtils.h>
#include <support/logging/CHIPLogging.h>

#include <ChipShellCollection.h>

#include "qvCHIP.h"

#if CHIP_ENABLE_OPENTHREAD
extern "C" {
#include <openthread/platform/platform-softdevice.h>
}
#endif // CHIP_ENABLE_OPENTHREAD

using namespace chip;
using namespace chip::Shell;

namespace {

const size_t kShellTaskStackSize = 2048;
const int kShellTaskPriority     = 1;
TaskHandle_t sShellTaskHandle;

void ShellCLIMain(void * pvParameter)
{
    // Initialize the default streamer that was linked.
    const int rc = streamer_init(streamer_get());

    if (rc != 0)
    {
        ChipLogError(Shell, "Streamer initialization failed: %d", rc);
        return;
    }

    ChipLogDetail(Shell, "Initializing CHIP shell commands", rc);

    cmd_device_init();
    cmd_base64_init();
    cmd_misc_init();
    cmd_btp_init();
    cmd_otcli_init();

    ChipLogDetail(Shell, "Run CHIP shell Task", rc);

    shell_task(nullptr);
}

} // namespace

int StartShellTask(void)
{
    int ret = 0;

    // Start Shell task.
    if (xTaskCreate(ShellCLIMain, "SHELL", kShellTaskStackSize / sizeof(StackType_t), NULL, kShellTaskPriority,
                    &sShellTaskHandle) != pdPASS)
    {
        ret = -1;
    }

    return ret;
}

int main(void)
{
    /* Initialize platform */
    qvCHIP_init();

    /* Launch shell task */
    StartShellTask();

    /* Start FreeRTOS */
    vTaskStartScheduler();
    return 0;
}
<commit_msg>CodeQL: Too many arguments to formatting function in examples/shell/qpg6100/main.cpp (#3775)<commit_after>/*
 *
 *    Copyright (c) 2020 Project CHIP Authors
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

#include "FreeRTOS.h"
#include "task.h"

#include <lib/shell/shell.h>

#include <lib/core/CHIPCore.h>
#include <lib/support/Base64.h>
#include <lib/support/CHIPArgParser.hpp>
#include <lib/support/CodeUtils.h>
#include <lib/support/RandUtils.h>
#include <support/logging/CHIPLogging.h>

#include <ChipShellCollection.h>

#include "qvCHIP.h"

#if CHIP_ENABLE_OPENTHREAD
extern "C" {
#include <openthread/platform/platform-softdevice.h>
}
#endif // CHIP_ENABLE_OPENTHREAD

using namespace chip;
using namespace chip::Shell;

namespace {

const size_t kShellTaskStackSize = 2048;
const int kShellTaskPriority     = 1;
TaskHandle_t sShellTaskHandle;

void ShellCLIMain(void * pvParameter)
{
    // Initialize the default streamer that was linked.
    const int rc = streamer_init(streamer_get());

    if (rc != 0)
    {
        ChipLogError(Shell, "Streamer initialization failed: %d", rc);
        return;
    }

    ChipLogDetail(Shell, "Initializing CHIP shell commands: %d", rc);

    cmd_device_init();
    cmd_base64_init();
    cmd_misc_init();
    cmd_btp_init();
    cmd_otcli_init();

    ChipLogDetail(Shell, "Run CHIP shell Task: %d", rc);

    shell_task(nullptr);
}

} // namespace

int StartShellTask(void)
{
    int ret = 0;

    // Start Shell task.
    if (xTaskCreate(ShellCLIMain, "SHELL", kShellTaskStackSize / sizeof(StackType_t), NULL, kShellTaskPriority,
                    &sShellTaskHandle) != pdPASS)
    {
        ret = -1;
    }

    return ret;
}

int main(void)
{
    /* Initialize platform */
    qvCHIP_init();

    /* Launch shell task */
    StartShellTask();

    /* Start FreeRTOS */
    vTaskStartScheduler();
    return 0;
}
<|endoftext|>