<commit_before>#include "MainWindow.h"
#include "CodeView/CodeView.h"
#include "RecentExecutables.h"

#include "AmigaUAE/AmigaUAE.h"
#include "Backend/BackendRequests.h"
#include "Backend/BackendSession.h"
#include "BreakpointModel.h"
#include "CodeViews.h"
#include "Config/AmigaUAEConfig.h"
#include "MemoryView/MemoryView.h"
#include "RegisterView/RegisterView.h"
#include "ViewHandler.h"

#include <QDebug>
#include <QFileDialog>
#include <QMainWindow>
#include <QThread>
#include <QTimer>
#include <QtCore/QSettings>
#include <QtWidgets/QDockWidget>

namespace prodbg {

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

MainWindow::MainWindow()
    : m_memoryView(new MemoryView(this))
    , m_registerView(new RegisterView(this))
    , m_statusbar(new QStatusBar(this))
    , m_backend(nullptr)
//, m_currentSession(nullptr)
//, m_amigaUae(nullptr)
{
    qRegisterMetaType<uint16_t>("uint16_t");
    qRegisterMetaType<uint32_t>("uint32_t");
    qRegisterMetaType<uint64_t>("uint64_t");
    qRegisterMetaType<IBackendRequests::ProgramCounterChange>("IBackendRequests::ProgramCounterChange");

    m_viewHandler = new ViewHandler(this);

    //m_viewHandler->addView(new MemoryView(this));
    //m_viewHandler->addView(new RegisterView(this));

    m_recentExecutables = new RecentExecutables;
    m_amigaUae = new AmigaUAE(this);

    m_ui.setupUi(this);

    m_breakpoints = new BreakpointModel;

    m_codeViews = new CodeViews(m_breakpoints, this);
    // m_codeViews->openFile(QStringLiteral("src/prodbg/main.cpp"), true);
    // m_codeViews->openFile(QStringLiteral("src/prodbg/main.cpp"), m_breakpoints);
    // m_codeViews->openFile(QStringLiteral("src/prodbg/main.cpp"), m_breakpoints);

    setCentralWidget(m_codeViews);

    setWindowTitle(QStringLiteral("ProDBG"));

    // Setup docking for MemoryView

    {
        QDockWidget* dock = new QDockWidget(QStringLiteral("MemoryView"), this);
        dock->setAllowedAreas(Qt::AllDockWidgetAreas);
        dock->setObjectName(QStringLiteral("MemoryViewDock"));
        dock->setWidget(m_memoryView);
        addDockWidget(Qt::RightDockWidgetArea, dock);
    }

    // Setup docking for RegisterView

    {
        QDockWidget* dock = new QDockWidget(QStringLiteral("RegisterView"), this);
        dock->setAllowedAreas(Qt::AllDockWidgetAreas);
        dock->setObjectName(QStringLiteral("RegisterViewDock"));
        dock->setWidget(m_registerView);
        addDockWidget(Qt::BottomDockWidgetArea, dock);
    }

    m_statusbar->showMessage(tr("Ready."));

    setStatusBar(m_statusbar);

    startDummyBackend();

    initActions();
    readSettings();

    initRecentFileActions();
}

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

MainWindow::~MainWindow()
{
    delete m_recentExecutables;

    closeCurrentBackend();
}

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

void MainWindow::initActions()
{
    connect(m_ui.actionStart, &QAction::triggered, this, &MainWindow::startDebug);
    connect(m_ui.actionReloadCurrentFile, &QAction::triggered, this, &MainWindow::reloadCurrentFile);
    connect(m_ui.actionStep_Over, &QAction::triggered, this, &MainWindow::stepOver);
    connect(m_ui.actionStep_In, &QAction::triggered, this, &MainWindow::stepIn);
    connect(m_ui.actionAmiga_UAE, &QAction::triggered, this, &MainWindow::amigaUAEConfig);
    connect(m_ui.actionDebugAmigaExe, &QAction::triggered, this, &MainWindow::debugAmigaExe);
    connect(m_ui.actionToggleBreakpoint, &QAction::triggered, this, &MainWindow::toggleBreakpoint);
    connect(m_ui.actionOpen, &QAction::triggered, this, &MainWindow::openSourceFile);
    connect(m_ui.actionBreak, &QAction::triggered, this, &MainWindow::breakDebug);
    connect(m_ui.actionStop, &QAction::triggered, this, &MainWindow::stop);
    connect(m_ui.actionToggleSourceAsm, &QAction::triggered, m_codeViews, &CodeViews::toggleSourceAsm);
    connect(m_ui.actionMemoryView, &QAction::triggered, this, &MainWindow::newMemoryView);
    connect(m_ui.actionRegisterView, &QAction::triggered, this, &MainWindow::newRegisterView);
}

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

static QAction* findMenu(QMenu* menu, const QString& menuName)
{
    QList<QAction*> menus = menu->actions();

    for (auto& menu : menus) {
        if (menu->text() == menuName) {
            return menu;
        }
    }

    return nullptr;
}

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

void MainWindow::initRecentFileActions()
{
    QMenu* menu = m_ui.menuRecentExecutables;

    //QAction* recentFiles = m_ui.menuFile->actionRecentExecutables;

    for (int i = 0; i < RecentExecutables::MaxFiles_Count; ++i) {
        QAction* recentFileAction = new QAction(this);
        recentFileAction->setVisible(false);
        connect(recentFileAction, &QAction::triggered, this, &MainWindow::openRecentExe);
        m_recentFileActions.append(recentFileAction);
        menu->addAction(recentFileAction);
    }

    m_recentExecutables->updateActionList(m_recentFileActions);
}

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

void MainWindow::openSourceFile()
{
    QString path = QFileDialog::getOpenFileName(nullptr, QStringLiteral("Open Source File"));

    if (path.isEmpty()) {
        return;
    }

    m_codeViews->openFile(path, true);
}

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

void MainWindow::start()
{
    printf("MainWindow::start\n");

    const QVector<BreakpointModel::FileLineBreakpoint>& fileLineBreakpoints = m_breakpoints->getFileLineBreakpoints();
    const QVector<uint64_t>& addressBreakpoints = m_breakpoints->getAddressBreakpoints();

    // add the breakpoints before we start

    for (auto& bp : fileLineBreakpoints) {
        m_backendRequests->beginAddFileLineBreakpoint(bp.filename, bp.line);
    }

    for (auto& bp : addressBreakpoints) {
        m_backendRequests->beginAddAddressBreakpoint(bp);
    }

    startBackend();
}

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

void MainWindow::breakDebug()
{
    breakBackend();
}

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

void MainWindow::amigaUAEConfig()
{
    AmigaUAEConfig cfg(this);
    cfg.setModal(true);
    cfg.exec();
}

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

void MainWindow::uaeStarted()
{
    startAmigaUAEBackend();
}

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

void MainWindow::debugAmigaExe()
{
    if (!m_amigaUae->openFile()) {
        return;
    }

    m_lastAmigaExe = m_amigaUae->m_localExeToRun;

    m_recentExecutables->setFile(m_recentFileActions, m_lastAmigaExe, BackendType_AmigaUAE);

    internalStartAmigaExe();
}

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

void MainWindow::openRecentExe()
{
    QAction* action = qobject_cast<QAction*>(sender());
    if (action) {
        QString filename = action->data().toString();
        m_lastAmigaExe = filename;
        m_amigaUae->m_localExeToRun = filename;

        m_recentExecutables->putFileOnTop(m_recentFileActions, filename);

        internalStartAmigaExe();
    }
}

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

void MainWindow::startDebug()
{
    if (m_currentBackend == Amiga && !m_lastAmigaExe.isEmpty()) {
        internalStartAmigaExe();
    }
}

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

void MainWindow::reloadCurrentFile()
{
    m_codeViews->reloadCurrentFile();
}

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

void MainWindow::internalStartAmigaExe()
{
    m_amigaUae->m_localExeToRun = m_lastAmigaExe;

    if (!m_amigaUae->validateSettings()) {
        return;
    }

    m_lastAmigaExe = m_amigaUae->m_localExeToRun;

    if (!m_amigaUae->m_skipUAELaunch) {
        connect(m_amigaUae->m_uaeProcess, &QProcess::started, this, &MainWindow::uaeStarted);
        connect(m_amigaUae->m_uaeProcess, static_cast<void(QProcess::*)(int)>(&QProcess::finished), this,
                &MainWindow::processEnded);

        printf("BackendSession::destroyPluginData\n");

        m_amigaUae->launchUAE();
    } else {
        startAmigaUAEBackend();
    }
}

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

void MainWindow::processEnded(int)
{
    stopInternal();
}

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

void MainWindow::setupBackendConnections()
{
    connect(this, &MainWindow::stepInBackend, m_backend, &BackendSession::stepIn);
    connect(this, &MainWindow::stepOverBackend, m_backend, &BackendSession::stepOver);
    connect(this, &MainWindow::startBackend, m_backend, &BackendSession::start);
    connect(this, &MainWindow::breakBackend, m_backend, &BackendSession::breakDebug);
    connect(this, &MainWindow::stopBackend, m_backend, &BackendSession::breakDebug);

    connect(m_backendThread, &QThread::finished, m_backend, &BackendSession::threadFinished);
}

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

void MainWindow::startDummyBackend()
{
    BackendSession* backend = BackendSession::createBackendSession(QStringLiteral("Dummy Backend"));

    if (!backend) {
        qDebug() << "Unable to create Dummy Backend";
        return;
    }

    setupBackend(backend);
}

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

void MainWindow::closeCurrentBackend()
{
    if (m_backendThread) {
        m_backendThread->quit();
        m_backendThread->wait();
    }

    delete m_backendThread;
    delete m_backendRequests;

    // m_registerView->setBackendInterface(nullptr);
    // m_codeViews->setBackendInterface(nullptr);
    // m_memoryView->setBackendInterface(nullptr);

    m_backend = nullptr;
    m_backendThread = nullptr;
    m_backendRequests = nullptr;
}

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

void MainWindow::startAmigaUAEBackend()
{
    closeCurrentBackend();

    BackendSession* backend = BackendSession::createBackendSession(QStringLiteral("Amiga UAE Debugger"));

    if (!backend) {
        printf("Unable to create Amiga UAE Debugger backend\n");
        return;
    }

    setupBackend(backend);

    m_backendRequests->sendCustomString(m_amigaUae->m_setFileId, m_amigaUae->m_fileToRun);
    m_backendRequests->sendCustomString(m_amigaUae->m_setHddPathId, m_amigaUae->m_dh0Path);

    // TODO: This is really temporary
    QTimer::singleShot(2000, this, &MainWindow::start);

    m_currentBackend = Amiga;
}

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

void MainWindow::setupBackend(BackendSession* backend)
{
    m_backend = backend;

    m_backendThread = new QThread;

    m_backend->moveToThread(m_backendThread);
    m_backendRequests = new BackendRequests(m_backend);

    m_registerView->setBackendInterface(m_backendRequests);
    m_codeViews->setBackendInterface(m_backendRequests);
    m_memoryView->setBackendInterface(m_backendRequests);

    m_backendThread->start();

    setupBackendConnections();
}

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

void MainWindow::stopInternal()
{
    stopBackend();
    closeCurrentBackend();

    m_statusbar->showMessage(QStringLiteral("Ready."));
}

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

void MainWindow::stop()
{
    if (m_currentBackend == Amiga) {
        m_amigaUae->killProcess();
    } else {
        stopInternal();
    }
}

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

void MainWindow::stepIn()
{
    stepInBackend();
}

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

void MainWindow::stepOver()
{
    stepOverBackend();

    printf("stepOver\n");

}

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

void MainWindow::toggleBreakpoint()
{
    m_codeViews->toggleBreakpoint();
}

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

void MainWindow::newMemoryView()
{
    MemoryView* mv = new MemoryView(this);
    mv->setBackendInterface(m_backendRequests);
    QDockWidget* dock = new QDockWidget(QStringLiteral("MemoryView"), this);
    dock->setAllowedAreas(Qt::AllDockWidgetAreas);
    dock->setObjectName(QStringLiteral("MemoryViewDock"));
    dock->setWidget(mv);
    addDockWidget(Qt::TopDockWidgetArea, dock);
    dock->setFloating(true);

    m_viewHandler->addView(mv);
}

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

Q_SLOT void MainWindow::newRegisterView()
{
    RegisterView* rg = new RegisterView(this);
    rg->setBackendInterface(m_backendRequests);
    QDockWidget* dock = new QDockWidget(QStringLiteral("RegisteView"), this);
    dock->setAllowedAreas(Qt::AllDockWidgetAreas);
    dock->setObjectName(QStringLiteral("RegisteViewDock"));
    dock->setWidget(rg);
    addDockWidget(Qt::TopDockWidgetArea, dock);
    dock->setFloating(true);

    m_viewHandler->addView(rg);
}

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

void MainWindow::statusUpdate(const QString& status)
{
    m_statusbar->showMessage(status);
}

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

void MainWindow::closeEvent(QCloseEvent* event)
{
    QSettings settings(QStringLiteral("TBL"), QStringLiteral("ProDBG"));

    writeSettings();
    event->accept();
}

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

void MainWindow::writeSettings()
{
    QSettings settings(QStringLiteral("TBL"), QStringLiteral("ProDBG"));

    settings.beginGroup(QStringLiteral("MainWindow"));

    qDebug() << "filename " << settings.fileName();

    /*
    settings.beginWriteArray(QStringLiteral("views"));

    int index = 0;

    QList<QDockWidget*> docs = findChildren<QDockWidget*>(QString());
    for (auto& doc : docs) {
        View* view = qobject_cast<View*>(doc->widget());

        if (view && view->isVisible()) {
            // TODO: Write settings here
            settings.setArrayIndex(index);
            settings.setValue(QStringLiteral("type"), QString::fromUtf8(view->metaObject()->className()));
            index += 1;
        }
    }

    settings.endArray();
    */

    settings.setValue(QStringLiteral("size"), size());
    settings.setValue(QStringLiteral("pos"), pos());
    settings.setValue(QStringLiteral("geometry"), saveGeometry());
    settings.setValue(QStringLiteral("windowState"), saveState());

    settings.endGroup();
}

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

void MainWindow::readSettings()
{
    QSettings settings(QStringLiteral("TBL"), QStringLiteral("ProDBG"));

    settings.beginGroup(QStringLiteral("MainWindow"));

    /*
    int size = settings.beginReadArray(QStringLiteral("views"));

    for (int i = 0; i < size; ++i) {
        settings.setArrayIndex(i);
        QString viewType = settings.value(QStringLiteral("type")).toString();

        View* view = nullptr;

        // hack for now
        if (viewType == QStringLiteral("prodbg::MemoryView")) {
            view = new MemoryView(this);
        } else if (viewType == QStringLiteral("prodbg::RegisterView")) {
            view = new RegisterView(this);
        } else {
            Q_ASSERT(view);
        }

        QDockWidget* dock = new QDockWidget(viewType, this);
        dock->setAllowedAreas(Qt::AllDockWidgetAreas);
        dock->setObjectName(viewType + QStringLiteral("Dock"));
        dock->setWidget(view);
        addDockWidget(Qt::RightDockWidgetArea, dock);
    }

    settings.endArray();
    */

    restoreGeometry(settings.value(QStringLiteral("geometry")).toByteArray());
    restoreState(settings.value(QStringLiteral("windowState")).toByteArray());
    resize(settings.value(QStringLiteral("size"), QSize(800, 600)).toSize());
    move(settings.value(QStringLiteral("pos"), QPoint(100, 100)).toPoint());
    settings.endGroup();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
<commit_msg>Removed unsued function<commit_after>#include "MainWindow.h"
#include "CodeView/CodeView.h"
#include "RecentExecutables.h"

#include "AmigaUAE/AmigaUAE.h"
#include "Backend/BackendRequests.h"
#include "Backend/BackendSession.h"
#include "BreakpointModel.h"
#include "CodeViews.h"
#include "Config/AmigaUAEConfig.h"
#include "MemoryView/MemoryView.h"
#include "RegisterView/RegisterView.h"
#include "ViewHandler.h"

#include <QDebug>
#include <QFileDialog>
#include <QMainWindow>
#include <QThread>
#include <QTimer>
#include <QtCore/QSettings>
#include <QtWidgets/QDockWidget>

namespace prodbg {

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

MainWindow::MainWindow()
    : m_memoryView(new MemoryView(this))
    , m_registerView(new RegisterView(this))
    , m_statusbar(new QStatusBar(this))
    , m_backend(nullptr)
//, m_currentSession(nullptr)
//, m_amigaUae(nullptr)
{
    qRegisterMetaType<uint16_t>("uint16_t");
    qRegisterMetaType<uint32_t>("uint32_t");
    qRegisterMetaType<uint64_t>("uint64_t");
    qRegisterMetaType<IBackendRequests::ProgramCounterChange>("IBackendRequests::ProgramCounterChange");

    m_viewHandler = new ViewHandler(this);

    //m_viewHandler->addView(new MemoryView(this));
    //m_viewHandler->addView(new RegisterView(this));

    m_recentExecutables = new RecentExecutables;
    m_amigaUae = new AmigaUAE(this);

    m_ui.setupUi(this);

    m_breakpoints = new BreakpointModel;

    m_codeViews = new CodeViews(m_breakpoints, this);
    // m_codeViews->openFile(QStringLiteral("src/prodbg/main.cpp"), true);
    // m_codeViews->openFile(QStringLiteral("src/prodbg/main.cpp"), m_breakpoints);
    // m_codeViews->openFile(QStringLiteral("src/prodbg/main.cpp"), m_breakpoints);

    setCentralWidget(m_codeViews);

    setWindowTitle(QStringLiteral("ProDBG"));

    // Setup docking for MemoryView

    {
        QDockWidget* dock = new QDockWidget(QStringLiteral("MemoryView"), this);
        dock->setAllowedAreas(Qt::AllDockWidgetAreas);
        dock->setObjectName(QStringLiteral("MemoryViewDock"));
        dock->setWidget(m_memoryView);
        addDockWidget(Qt::RightDockWidgetArea, dock);
    }

    // Setup docking for RegisterView

    {
        QDockWidget* dock = new QDockWidget(QStringLiteral("RegisterView"), this);
        dock->setAllowedAreas(Qt::AllDockWidgetAreas);
        dock->setObjectName(QStringLiteral("RegisterViewDock"));
        dock->setWidget(m_registerView);
        addDockWidget(Qt::BottomDockWidgetArea, dock);
    }

    m_statusbar->showMessage(tr("Ready."));

    setStatusBar(m_statusbar);

    startDummyBackend();

    initActions();
    readSettings();

    initRecentFileActions();
}

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

MainWindow::~MainWindow()
{
    delete m_recentExecutables;

    closeCurrentBackend();
}

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

void MainWindow::initActions()
{
    connect(m_ui.actionStart, &QAction::triggered, this, &MainWindow::startDebug);
    connect(m_ui.actionReloadCurrentFile, &QAction::triggered, this, &MainWindow::reloadCurrentFile);
    connect(m_ui.actionStep_Over, &QAction::triggered, this, &MainWindow::stepOver);
    connect(m_ui.actionStep_In, &QAction::triggered, this, &MainWindow::stepIn);
    connect(m_ui.actionAmiga_UAE, &QAction::triggered, this, &MainWindow::amigaUAEConfig);
    connect(m_ui.actionDebugAmigaExe, &QAction::triggered, this, &MainWindow::debugAmigaExe);
    connect(m_ui.actionToggleBreakpoint, &QAction::triggered, this, &MainWindow::toggleBreakpoint);
    connect(m_ui.actionOpen, &QAction::triggered, this, &MainWindow::openSourceFile);
    connect(m_ui.actionBreak, &QAction::triggered, this, &MainWindow::breakDebug);
    connect(m_ui.actionStop, &QAction::triggered, this, &MainWindow::stop);
    connect(m_ui.actionToggleSourceAsm, &QAction::triggered, m_codeViews, &CodeViews::toggleSourceAsm);
    connect(m_ui.actionMemoryView, &QAction::triggered, this, &MainWindow::newMemoryView);
    connect(m_ui.actionRegisterView, &QAction::triggered, this, &MainWindow::newRegisterView);
}

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

void MainWindow::initRecentFileActions()
{
    QMenu* menu = m_ui.menuRecentExecutables;

    //QAction* recentFiles = m_ui.menuFile->actionRecentExecutables;

    for (int i = 0; i < RecentExecutables::MaxFiles_Count; ++i) {
        QAction* recentFileAction = new QAction(this);
        recentFileAction->setVisible(false);
        connect(recentFileAction, &QAction::triggered, this, &MainWindow::openRecentExe);
        m_recentFileActions.append(recentFileAction);
        menu->addAction(recentFileAction);
    }

    m_recentExecutables->updateActionList(m_recentFileActions);
}

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

void MainWindow::openSourceFile()
{
    QString path = QFileDialog::getOpenFileName(nullptr, QStringLiteral("Open Source File"));

    if (path.isEmpty()) {
        return;
    }

    m_codeViews->openFile(path, true);
}

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

void MainWindow::start()
{
    printf("MainWindow::start\n");

    const QVector<BreakpointModel::FileLineBreakpoint>& fileLineBreakpoints = m_breakpoints->getFileLineBreakpoints();
    const QVector<uint64_t>& addressBreakpoints = m_breakpoints->getAddressBreakpoints();

    // add the breakpoints before we start

    for (auto& bp : fileLineBreakpoints) {
        m_backendRequests->beginAddFileLineBreakpoint(bp.filename, bp.line);
    }

    for (auto& bp : addressBreakpoints) {
        m_backendRequests->beginAddAddressBreakpoint(bp);
    }

    startBackend();
}

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

void MainWindow::breakDebug()
{
    breakBackend();
}

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

void MainWindow::amigaUAEConfig()
{
    AmigaUAEConfig cfg(this);
    cfg.setModal(true);
    cfg.exec();
}

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

void MainWindow::uaeStarted()
{
    startAmigaUAEBackend();
}

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

void MainWindow::debugAmigaExe()
{
    if (!m_amigaUae->openFile()) {
        return;
    }

    m_lastAmigaExe = m_amigaUae->m_localExeToRun;

    m_recentExecutables->setFile(m_recentFileActions, m_lastAmigaExe, BackendType_AmigaUAE);

    internalStartAmigaExe();
}

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

void MainWindow::openRecentExe()
{
    QAction* action = qobject_cast<QAction*>(sender());
    if (action) {
        QString filename = action->data().toString();
        m_lastAmigaExe = filename;
        m_amigaUae->m_localExeToRun = filename;

        m_recentExecutables->putFileOnTop(m_recentFileActions, filename);

        internalStartAmigaExe();
    }
}

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

void MainWindow::startDebug()
{
    if (m_currentBackend == Amiga && !m_lastAmigaExe.isEmpty()) {
        internalStartAmigaExe();
    }
}

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

void MainWindow::reloadCurrentFile()
{
    m_codeViews->reloadCurrentFile();
}

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

void MainWindow::internalStartAmigaExe()
{
    m_amigaUae->m_localExeToRun = m_lastAmigaExe;

    if (!m_amigaUae->validateSettings()) {
        return;
    }

    m_lastAmigaExe = m_amigaUae->m_localExeToRun;

    if (!m_amigaUae->m_skipUAELaunch) {
        connect(m_amigaUae->m_uaeProcess, &QProcess::started, this, &MainWindow::uaeStarted);
        connect(m_amigaUae->m_uaeProcess, static_cast<void(QProcess::*)(int)>(&QProcess::finished), this,
                &MainWindow::processEnded);

        printf("BackendSession::destroyPluginData\n");

        m_amigaUae->launchUAE();
    } else {
        startAmigaUAEBackend();
    }
}

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

void MainWindow::processEnded(int)
{
    stopInternal();
}

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

void MainWindow::setupBackendConnections()
{
    connect(this, &MainWindow::stepInBackend, m_backend, &BackendSession::stepIn);
    connect(this, &MainWindow::stepOverBackend, m_backend, &BackendSession::stepOver);
    connect(this, &MainWindow::startBackend, m_backend, &BackendSession::start);
    connect(this, &MainWindow::breakBackend, m_backend, &BackendSession::breakDebug);
    connect(this, &MainWindow::stopBackend, m_backend, &BackendSession::breakDebug);

    connect(m_backendThread, &QThread::finished, m_backend, &BackendSession::threadFinished);
}

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

void MainWindow::startDummyBackend()
{
    BackendSession* backend = BackendSession::createBackendSession(QStringLiteral("Dummy Backend"));

    if (!backend) {
        qDebug() << "Unable to create Dummy Backend";
        return;
    }

    setupBackend(backend);
}

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

void MainWindow::closeCurrentBackend()
{
    if (m_backendThread) {
        m_backendThread->quit();
        m_backendThread->wait();
    }

    delete m_backendThread;
    delete m_backendRequests;

    // m_registerView->setBackendInterface(nullptr);
    // m_codeViews->setBackendInterface(nullptr);
    // m_memoryView->setBackendInterface(nullptr);

    m_backend = nullptr;
    m_backendThread = nullptr;
    m_backendRequests = nullptr;
}

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

void MainWindow::startAmigaUAEBackend()
{
    closeCurrentBackend();

    BackendSession* backend = BackendSession::createBackendSession(QStringLiteral("Amiga UAE Debugger"));

    if (!backend) {
        printf("Unable to create Amiga UAE Debugger backend\n");
        return;
    }

    setupBackend(backend);

    m_backendRequests->sendCustomString(m_amigaUae->m_setFileId, m_amigaUae->m_fileToRun);
    m_backendRequests->sendCustomString(m_amigaUae->m_setHddPathId, m_amigaUae->m_dh0Path);

    // TODO: This is really temporary
    QTimer::singleShot(2000, this, &MainWindow::start);

    m_currentBackend = Amiga;
}

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

void MainWindow::setupBackend(BackendSession* backend)
{
    m_backend = backend;

    m_backendThread = new QThread;

    m_backend->moveToThread(m_backendThread);
    m_backendRequests = new BackendRequests(m_backend);

    m_registerView->setBackendInterface(m_backendRequests);
    m_codeViews->setBackendInterface(m_backendRequests);
    m_memoryView->setBackendInterface(m_backendRequests);

    m_backendThread->start();

    setupBackendConnections();
}

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

void MainWindow::stopInternal()
{
    stopBackend();
    closeCurrentBackend();

    m_statusbar->showMessage(QStringLiteral("Ready."));
}

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

void MainWindow::stop()
{
    if (m_currentBackend == Amiga) {
        m_amigaUae->killProcess();
    } else {
        stopInternal();
    }
}

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

void MainWindow::stepIn()
{
    stepInBackend();
}

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

void MainWindow::stepOver()
{
    stepOverBackend();

    printf("stepOver\n");

}

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

void MainWindow::toggleBreakpoint()
{
    m_codeViews->toggleBreakpoint();
}

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

void MainWindow::newMemoryView()
{
    MemoryView* mv = new MemoryView(this);
    mv->setBackendInterface(m_backendRequests);
    QDockWidget* dock = new QDockWidget(QStringLiteral("MemoryView"), this);
    dock->setAllowedAreas(Qt::AllDockWidgetAreas);
    dock->setObjectName(QStringLiteral("MemoryViewDock"));
    dock->setWidget(mv);
    addDockWidget(Qt::TopDockWidgetArea, dock);
    dock->setFloating(true);

    m_viewHandler->addView(mv);
}

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

Q_SLOT void MainWindow::newRegisterView()
{
    RegisterView* rg = new RegisterView(this);
    rg->setBackendInterface(m_backendRequests);
    QDockWidget* dock = new QDockWidget(QStringLiteral("RegisteView"), this);
    dock->setAllowedAreas(Qt::AllDockWidgetAreas);
    dock->setObjectName(QStringLiteral("RegisteViewDock"));
    dock->setWidget(rg);
    addDockWidget(Qt::TopDockWidgetArea, dock);
    dock->setFloating(true);

    m_viewHandler->addView(rg);
}

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

void MainWindow::statusUpdate(const QString& status)
{
    m_statusbar->showMessage(status);
}

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

void MainWindow::closeEvent(QCloseEvent* event)
{
    QSettings settings(QStringLiteral("TBL"), QStringLiteral("ProDBG"));

    writeSettings();
    event->accept();
}

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

void MainWindow::writeSettings()
{
    QSettings settings(QStringLiteral("TBL"), QStringLiteral("ProDBG"));

    settings.beginGroup(QStringLiteral("MainWindow"));

    /*
    settings.beginWriteArray(QStringLiteral("views"));

    int index = 0;

    QList<QDockWidget*> docs = findChildren<QDockWidget*>(QString());
    for (auto& doc : docs) {
        View* view = qobject_cast<View*>(doc->widget());

        if (view && view->isVisible()) {
            // TODO: Write settings here
            settings.setArrayIndex(index);
            settings.setValue(QStringLiteral("type"), QString::fromUtf8(view->metaObject()->className()));
            index += 1;
        }
    }

    settings.endArray();
    */

    settings.setValue(QStringLiteral("size"), size());
    settings.setValue(QStringLiteral("pos"), pos());
    settings.setValue(QStringLiteral("geometry"), saveGeometry());
    settings.setValue(QStringLiteral("windowState"), saveState());

    settings.endGroup();
}

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

void MainWindow::readSettings()
{
    QSettings settings(QStringLiteral("TBL"), QStringLiteral("ProDBG"));

    settings.beginGroup(QStringLiteral("MainWindow"));

    /*
    int size = settings.beginReadArray(QStringLiteral("views"));

    for (int i = 0; i < size; ++i) {
        settings.setArrayIndex(i);
        QString viewType = settings.value(QStringLiteral("type")).toString();

        View* view = nullptr;

        // hack for now
        if (viewType == QStringLiteral("prodbg::MemoryView")) {
            view = new MemoryView(this);
        } else if (viewType == QStringLiteral("prodbg::RegisterView")) {
            view = new RegisterView(this);
        } else {
            Q_ASSERT(view);
        }

        QDockWidget* dock = new QDockWidget(viewType, this);
        dock->setAllowedAreas(Qt::AllDockWidgetAreas);
        dock->setObjectName(viewType + QStringLiteral("Dock"));
        dock->setWidget(view);
        addDockWidget(Qt::RightDockWidgetArea, dock);
    }

    settings.endArray();
    */

    restoreGeometry(settings.value(QStringLiteral("geometry")).toByteArray());
    restoreState(settings.value(QStringLiteral("windowState")).toByteArray());
    resize(settings.value(QStringLiteral("size"), QSize(800, 600)).toSize());
    move(settings.value(QStringLiteral("pos"), QPoint(100, 100)).toPoint());
    settings.endGroup();
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "modules/donottrack/NavigatorDoNotTrack.h"

#include "core/frame/LocalFrame.h"
#include "core/frame/Navigator.h"
#include "core/loader/FrameLoaderClient.h"

namespace WebCore {

NavigatorDoNotTrack::NavigatorDoNotTrack(LocalFrame* frame)
    : DOMWindowProperty(frame)
{
}

NavigatorDoNotTrack::~NavigatorDoNotTrack()
{
}

const char* NavigatorDoNotTrack::supplementName()
{
    return "NavigatorDoNotTrack";
}

NavigatorDoNotTrack& NavigatorDoNotTrack::from(Navigator& navigator)
{
    NavigatorDoNotTrack* supplement = static_cast<NavigatorDoNotTrack*>(WillBeHeapSupplement<Navigator>::from(navigator, supplementName()));
    if (!supplement) {
        supplement = new NavigatorDoNotTrack(navigator.frame());
        provideTo(navigator, supplementName(), adoptPtrWillBeNoop(supplement));
    }
    return *supplement;
}

String NavigatorDoNotTrack::doNotTrack(Navigator& navigator)
{
    return NavigatorDoNotTrack::from(navigator).doNotTrack();
}

String NavigatorDoNotTrack::doNotTrack()
{
    return frame() ? frame()->loader().client()->doNotTrackValue() : String();
}

} // namespace WebCore
<commit_msg>DNT should return 'null' if there is no client.<commit_after>/*
 * Copyright (C) 2013 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "modules/donottrack/NavigatorDoNotTrack.h"

#include "core/frame/LocalFrame.h"
#include "core/frame/Navigator.h"
#include "core/loader/FrameLoaderClient.h"

namespace WebCore {

NavigatorDoNotTrack::NavigatorDoNotTrack(LocalFrame* frame)
    : DOMWindowProperty(frame)
{
}

NavigatorDoNotTrack::~NavigatorDoNotTrack()
{
}

const char* NavigatorDoNotTrack::supplementName()
{
    return "NavigatorDoNotTrack";
}

NavigatorDoNotTrack& NavigatorDoNotTrack::from(Navigator& navigator)
{
    NavigatorDoNotTrack* supplement = static_cast<NavigatorDoNotTrack*>(WillBeHeapSupplement<Navigator>::from(navigator, supplementName()));
    if (!supplement) {
        supplement = new NavigatorDoNotTrack(navigator.frame());
        provideTo(navigator, supplementName(), adoptPtrWillBeNoop(supplement));
    }
    return *supplement;
}

String NavigatorDoNotTrack::doNotTrack(Navigator& navigator)
{
    return NavigatorDoNotTrack::from(navigator).doNotTrack();
}

String NavigatorDoNotTrack::doNotTrack()
{
    if (!frame() || !frame()->loader().client())
        return String();
    return frame()->loader().client()->doNotTrackValue();
}

} // namespace WebCore
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2008, Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "platform/graphics/skia/NativeImageSkia.h"

#include "platform/PlatformInstrumentation.h"
#include "platform/TraceEvent.h"
#include "platform/geometry/FloatPoint.h"
#include "platform/geometry/FloatRect.h"
#include "platform/geometry/FloatSize.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/Image.h"
#include "platform/graphics/DeferredImageDecoder.h"
#include "platform/graphics/skia/SkiaUtils.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkScalar.h"
#include "third_party/skia/include/core/SkShader.h"

#include <math.h>

namespace blink {

// This function is used to scale an image and extract a scaled fragment.
//
// ALGORITHM
//
// Because the scaled image size has to be integers, we approximate the real
// scale with the following formula (only X direction is shown):
//
// scaledImageWidth = round(scaleX * imageRect.width())
// approximateScaleX = scaledImageWidth / imageRect.width()
//
// With this method we maintain a constant scale factor among fragments in
// the scaled image. This allows fragments to stitch together to form the
// full scaled image. The downside is there will be a small difference
// between |scaleX| and |approximateScaleX|.
//
// A scaled image fragment is identified by:
//
// - Scaled image size
// - Scaled image fragment rectangle (IntRect)
//
// Scaled image size has been determined and the next step is to compute the
// rectangle for the scaled image fragment which needs to be an IntRect.
//
// scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY)
// enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect)
//
// Finally we extract the scaled image fragment using
// (scaledImageSize, enclosingScaledSrcRect).
//
SkBitmap NativeImageSkia::extractScaledImageFragment(const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect) const
{
    SkISize imageSize = SkISize::Make(bitmap().width(), bitmap().height());
    SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.width() * scaleX)),
        clampToInteger(roundf(imageSize.height() * scaleY)));

    SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height());
    SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height());

    SkMatrix scaleTransform;
    scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit);
    scaleTransform.mapRect(scaledSrcRect, srcRect);

    scaledSrcRect->intersect(scaledImageRect);
    SkIRect enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect);

    // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because
    // of float inaccuracy so clip to get inside.
    enclosingScaledSrcRect.intersect(SkIRect::MakeSize(scaledImageSize));

    // scaledSrcRect is relative to the pixel snapped fragment we're extracting.
    scaledSrcRect->offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y());

    return resizedBitmap(scaledImageSize, enclosingScaledSrcRect);
}

NativeImageSkia::NativeImageSkia()
    : m_resizeRequests(0)
{
}

NativeImageSkia::NativeImageSkia(const SkBitmap& other)
    : m_bitmap(other)
    , m_resizeRequests(0)
{
}

NativeImageSkia::~NativeImageSkia()
{
}

bool NativeImageSkia::hasResizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
{
    bool imageScaleEqual = m_cachedImageInfo.scaledImageSize == scaledImageSize;
    bool scaledImageSubsetAvailable = m_cachedImageInfo.scaledImageSubset.contains(scaledImageSubset);
    return imageScaleEqual && scaledImageSubsetAvailable && !m_resizedImage.empty();
}

SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
{
    ASSERT(!DeferredImageDecoder::isLazyDecoded(bitmap()));

    if (!hasResizedBitmap(scaledImageSize, scaledImageSubset)) {
        bool shouldCache = isDataComplete()
            && shouldCacheResampling(scaledImageSize, scaledImageSubset);

        TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResizeImage", "cached", shouldCache);
        // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
        PlatformInstrumentation::willResizeImage(shouldCache);
        SkBitmap resizedImage = skia::ImageOperations::Resize(bitmap(), skia::ImageOperations::RESIZE_LANCZOS3, scaledImageSize.width(), scaledImageSize.height(), scaledImageSubset);
        resizedImage.setImmutable();
        PlatformInstrumentation::didResizeImage();

        if (!shouldCache)
            return resizedImage;

        m_resizedImage = resizedImage;
    }

    SkBitmap resizedSubset;
    SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset);
    m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect);
    return resizedSubset;
}

void NativeImageSkia::draw(
    GraphicsContext* context,
    const SkRect& srcRect,
    const SkRect& destRect,
    CompositeOperator compositeOp,
    WebBlendMode blendMode) const
{
    TRACE_EVENT0("skia", "NativeImageSkia::draw");

    bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());

    SkPaint paint;
    context->preparePaintForDrawRectToRect(&paint, srcRect, destRect, compositeOp, blendMode, isLazyDecoded, isDataComplete());
    // We want to filter it if we decided to do interpolation above, or if
    // there is something interesting going on with the matrix (like a rotation).
    // Note: for serialization, we will want to subset the bitmap first so we
    // don't send extra pixels.
    context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint);

    if (isLazyDecoded)
        PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID());
    context->didDrawRect(destRect, paint, &bitmap());
}

static SkBitmap createBitmapWithSpace(const SkBitmap& bitmap, int spaceWidth, int spaceHeight)
{
    SkImageInfo info = bitmap.info();
    info.fWidth += spaceWidth;
    info.fHeight += spaceHeight;
    info.fAlphaType = kPremul_SkAlphaType;

    SkBitmap result;
    result.allocPixels(info);
    result.eraseColor(SK_ColorTRANSPARENT);
    bitmap.copyPixelsTo(reinterpret_cast<uint8_t*>(result.getPixels()), result.rowBytes() * result.height(), result.rowBytes());

    return result;
}

void NativeImageSkia::drawPattern(
    GraphicsContext* context,
    const FloatRect& floatSrcRect,
    const FloatSize& scale,
    const FloatPoint& phase,
    CompositeOperator compositeOp,
    const FloatRect& destRect,
    WebBlendMode blendMode,
    const IntSize& repeatSpacing) const
{
    FloatRect normSrcRect = floatSrcRect;
    normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height()));
    if (destRect.isEmpty() || normSrcRect.isEmpty())
        return; // nothing to draw

    SkMatrix totalMatrix = context->getTotalMatrix();
    AffineTransform ctm = context->getCTM();
    SkScalar ctmScaleX = ctm.xScale();
    SkScalar ctmScaleY = ctm.yScale();
    totalMatrix.preScale(scale.width(), scale.height());

    // Figure out what size the bitmap will be in the destination. The
    // destination rect is the bounds of the pattern, we need to use the
    // matrix to see how big it will be.
    SkRect destRectTarget;
    totalMatrix.mapRect(&destRectTarget, normSrcRect);

    float destBitmapWidth = SkScalarToFloat(destRectTarget.width());
    float destBitmapHeight = SkScalarToFloat(destRectTarget.height());

    bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());

    // Compute the resampling mode.
    InterpolationQuality resampling;
    if (context->isAccelerated() || context->printing())
        resampling = InterpolationLow;
    else if (isLazyDecoded)
        resampling = InterpolationHigh;
    else
        resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight, isDataComplete());
    resampling = limitInterpolationQuality(context, resampling);

    SkMatrix localMatrix;
    // We also need to translate it such that the origin of the pattern is the
    // origin of the destination rect, which is what WebKit expects. Skia uses
    // the coordinate system origin as the base for the pattern. If WebKit wants
    // a shifted image, it will shift it from there using the localMatrix.
    const float adjustedX = phase.x() + normSrcRect.x() * scale.width();
    const float adjustedY = phase.y() + normSrcRect.y() * scale.height();
    localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY));

    RefPtr<SkShader> shader;
    SkPaint::FilterLevel filterLevel = static_cast<SkPaint::FilterLevel>(resampling);

    // Bicubic filter is only applied to defer-decoded images, see
    // NativeImageSkia::draw for details.
    if (resampling == InterpolationHigh && !isLazyDecoded) {
        // Do nice resampling.
        filterLevel = SkPaint::kNone_FilterLevel;
        float scaleX = destBitmapWidth / normSrcRect.width();
        float scaleY = destBitmapHeight / normSrcRect.height();
        SkRect scaledSrcRect;

        // Since we are resizing the bitmap, we need to remove the scale
        // applied to the pixels in the bitmap shader. This means we need
        // CTM * localMatrix to have identity scale. Since we
        // can't modify CTM (or the rectangle will be drawn in the wrong
        // place), we must set localMatrix's scale to the inverse of
        // CTM scale.
        localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1);

        // The image fragment generated here is not exactly what is
        // requested. The scale factor used is approximated and image
        // fragment is slightly larger to align to integer
        // boundaries.
        SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, scaleY, &scaledSrcRect);
        if (repeatSpacing.isZero()) {
            shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        } else {
            shader = adoptRef(SkShader::CreateBitmapShader(
                createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
                SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        }
    } else {
        // Because no resizing occurred, the shader transform should be
        // set to the pattern's transform, which just includes scale.
        localMatrix.preScale(scale.width(), scale.height());

        // No need to resample before drawing.
        SkBitmap srcSubset;
        bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect));
        if (repeatSpacing.isZero()) {
            shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        } else {
            shader = adoptRef(SkShader::CreateBitmapShader(
                createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
                SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        }
    }

    SkPaint paint;
    paint.setShader(shader.get());
    paint.setXfermode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode).get());
    paint.setColorFilter(context->colorFilter());
    paint.setFilterLevel(filterLevel);

    if (isLazyDecoded)
        PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID());

    context->drawRect(destRect, paint);
}

bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
{
    // Check whether the requested dimensions match previous request.
    bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, scaledImageSubset);
    if (matchesPreviousRequest)
        ++m_resizeRequests;
    else {
        m_cachedImageInfo.set(scaledImageSize, scaledImageSubset);
        m_resizeRequests = 0;
        // Reset m_resizedImage now, because we don't distinguish
        // between the last requested resize info and m_resizedImage's
        // resize info.
        m_resizedImage.reset();
    }

    // We can not cache incomplete frames. This might be a good optimization in
    // the future, were we know how much of the frame has been decoded, so when
    // we incrementally draw more of the image, we only have to resample the
    // parts that are changed.
    if (!isDataComplete())
        return false;

    // If the destination bitmap is excessively large, we'll never allow caching.
    static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL;
    unsigned long long fullSize = static_cast<unsigned long long>(scaledImageSize.width()) * static_cast<unsigned long long>(scaledImageSize.height());
    unsigned long long fragmentSize = static_cast<unsigned long long>(scaledImageSubset.width()) * static_cast<unsigned long long>(scaledImageSubset.height());

    if (fragmentSize > kLargeBitmapSize)
        return false;

    // If the destination bitmap is small, we'll always allow caching, since
    // there is not very much penalty for computing it and it may come in handy.
    static const unsigned kSmallBitmapSize = 4096;
    if (fragmentSize <= kSmallBitmapSize)
        return true;

    // If "too many" requests have been made for this bitmap, we assume that
    // many more will be made as well, and we'll go ahead and cache it.
    static const int kManyRequestThreshold = 4;
    if (m_resizeRequests >= kManyRequestThreshold)
        return true;

    // If more than 1/4 of the resized image is requested, it's worth caching.
    return fragmentSize > fullSize / 4;
}

NativeImageSkia::ImageResourceInfo::ImageResourceInfo()
{
    scaledImageSize.setEmpty();
    scaledImageSubset.setEmpty();
}

bool NativeImageSkia::ImageResourceInfo::isEqual(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset) const
{
    return scaledImageSize == otherScaledImageSize && scaledImageSubset == otherScaledImageSubset;
}

void NativeImageSkia::ImageResourceInfo::set(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset)
{
    scaledImageSize = otherScaledImageSize;
    scaledImageSubset = otherScaledImageSubset;
}

SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherScaledImageSubset)
{
    if (!scaledImageSubset.contains(otherScaledImageSubset))
        return SkIRect::MakeEmpty();
    SkIRect subsetRect = otherScaledImageSubset;
    subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y());
    return subsetRect;
}

} // namespace blink
<commit_msg>Fix include order pre-submit nit in NativeImageSkia.cpp<commit_after>/*
 * Copyright (c) 2008, Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "platform/graphics/skia/NativeImageSkia.h"

#include "platform/PlatformInstrumentation.h"
#include "platform/TraceEvent.h"
#include "platform/geometry/FloatPoint.h"
#include "platform/geometry/FloatRect.h"
#include "platform/geometry/FloatSize.h"
#include "platform/graphics/DeferredImageDecoder.h"
#include "platform/graphics/GraphicsContext.h"
#include "platform/graphics/Image.h"
#include "platform/graphics/skia/SkiaUtils.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/core/SkScalar.h"
#include "third_party/skia/include/core/SkShader.h"

#include <math.h>

namespace blink {

// This function is used to scale an image and extract a scaled fragment.
//
// ALGORITHM
//
// Because the scaled image size has to be integers, we approximate the real
// scale with the following formula (only X direction is shown):
//
// scaledImageWidth = round(scaleX * imageRect.width())
// approximateScaleX = scaledImageWidth / imageRect.width()
//
// With this method we maintain a constant scale factor among fragments in
// the scaled image. This allows fragments to stitch together to form the
// full scaled image. The downside is there will be a small difference
// between |scaleX| and |approximateScaleX|.
//
// A scaled image fragment is identified by:
//
// - Scaled image size
// - Scaled image fragment rectangle (IntRect)
//
// Scaled image size has been determined and the next step is to compute the
// rectangle for the scaled image fragment which needs to be an IntRect.
//
// scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY)
// enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect)
//
// Finally we extract the scaled image fragment using
// (scaledImageSize, enclosingScaledSrcRect).
//
SkBitmap NativeImageSkia::extractScaledImageFragment(const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect) const
{
    SkISize imageSize = SkISize::Make(bitmap().width(), bitmap().height());
    SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.width() * scaleX)),
        clampToInteger(roundf(imageSize.height() * scaleY)));

    SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height());
    SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height());

    SkMatrix scaleTransform;
    scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit);
    scaleTransform.mapRect(scaledSrcRect, srcRect);

    scaledSrcRect->intersect(scaledImageRect);
    SkIRect enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect);

    // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because
    // of float inaccuracy so clip to get inside.
    enclosingScaledSrcRect.intersect(SkIRect::MakeSize(scaledImageSize));

    // scaledSrcRect is relative to the pixel snapped fragment we're extracting.
    scaledSrcRect->offset(-enclosingScaledSrcRect.x(), -enclosingScaledSrcRect.y());

    return resizedBitmap(scaledImageSize, enclosingScaledSrcRect);
}

NativeImageSkia::NativeImageSkia()
    : m_resizeRequests(0)
{
}

NativeImageSkia::NativeImageSkia(const SkBitmap& other)
    : m_bitmap(other)
    , m_resizeRequests(0)
{
}

NativeImageSkia::~NativeImageSkia()
{
}

bool NativeImageSkia::hasResizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
{
    bool imageScaleEqual = m_cachedImageInfo.scaledImageSize == scaledImageSize;
    bool scaledImageSubsetAvailable = m_cachedImageInfo.scaledImageSubset.contains(scaledImageSubset);
    return imageScaleEqual && scaledImageSubsetAvailable && !m_resizedImage.empty();
}

SkBitmap NativeImageSkia::resizedBitmap(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
{
    ASSERT(!DeferredImageDecoder::isLazyDecoded(bitmap()));

    if (!hasResizedBitmap(scaledImageSize, scaledImageSubset)) {
        bool shouldCache = isDataComplete()
            && shouldCacheResampling(scaledImageSize, scaledImageSubset);

        TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "ResizeImage", "cached", shouldCache);
        // FIXME(361045): remove InspectorInstrumentation calls once DevTools Timeline migrates to tracing.
        PlatformInstrumentation::willResizeImage(shouldCache);
        SkBitmap resizedImage = skia::ImageOperations::Resize(bitmap(), skia::ImageOperations::RESIZE_LANCZOS3, scaledImageSize.width(), scaledImageSize.height(), scaledImageSubset);
        resizedImage.setImmutable();
        PlatformInstrumentation::didResizeImage();

        if (!shouldCache)
            return resizedImage;

        m_resizedImage = resizedImage;
    }

    SkBitmap resizedSubset;
    SkIRect resizedSubsetRect = m_cachedImageInfo.rectInSubset(scaledImageSubset);
    m_resizedImage.extractSubset(&resizedSubset, resizedSubsetRect);
    return resizedSubset;
}

void NativeImageSkia::draw(
    GraphicsContext* context,
    const SkRect& srcRect,
    const SkRect& destRect,
    CompositeOperator compositeOp,
    WebBlendMode blendMode) const
{
    TRACE_EVENT0("skia", "NativeImageSkia::draw");

    bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());

    SkPaint paint;
    context->preparePaintForDrawRectToRect(&paint, srcRect, destRect, compositeOp, blendMode, isLazyDecoded, isDataComplete());
    // We want to filter it if we decided to do interpolation above, or if
    // there is something interesting going on with the matrix (like a rotation).
    // Note: for serialization, we will want to subset the bitmap first so we
    // don't send extra pixels.
    context->drawBitmapRect(bitmap(), &srcRect, destRect, &paint);

    if (isLazyDecoded)
        PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID());
    context->didDrawRect(destRect, paint, &bitmap());
}

static SkBitmap createBitmapWithSpace(const SkBitmap& bitmap, int spaceWidth, int spaceHeight)
{
    SkImageInfo info = bitmap.info();
    info.fWidth += spaceWidth;
    info.fHeight += spaceHeight;
    info.fAlphaType = kPremul_SkAlphaType;

    SkBitmap result;
    result.allocPixels(info);
    result.eraseColor(SK_ColorTRANSPARENT);
    bitmap.copyPixelsTo(reinterpret_cast<uint8_t*>(result.getPixels()), result.rowBytes() * result.height(), result.rowBytes());

    return result;
}

void NativeImageSkia::drawPattern(
    GraphicsContext* context,
    const FloatRect& floatSrcRect,
    const FloatSize& scale,
    const FloatPoint& phase,
    CompositeOperator compositeOp,
    const FloatRect& destRect,
    WebBlendMode blendMode,
    const IntSize& repeatSpacing) const
{
    FloatRect normSrcRect = floatSrcRect;
    normSrcRect.intersect(FloatRect(0, 0, bitmap().width(), bitmap().height()));
    if (destRect.isEmpty() || normSrcRect.isEmpty())
        return; // nothing to draw

    SkMatrix totalMatrix = context->getTotalMatrix();
    AffineTransform ctm = context->getCTM();
    SkScalar ctmScaleX = ctm.xScale();
    SkScalar ctmScaleY = ctm.yScale();
    totalMatrix.preScale(scale.width(), scale.height());

    // Figure out what size the bitmap will be in the destination. The
    // destination rect is the bounds of the pattern, we need to use the
    // matrix to see how big it will be.
    SkRect destRectTarget;
    totalMatrix.mapRect(&destRectTarget, normSrcRect);

    float destBitmapWidth = SkScalarToFloat(destRectTarget.width());
    float destBitmapHeight = SkScalarToFloat(destRectTarget.height());

    bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap());

    // Compute the resampling mode.
    InterpolationQuality resampling;
    if (context->isAccelerated() || context->printing())
        resampling = InterpolationLow;
    else if (isLazyDecoded)
        resampling = InterpolationHigh;
    else
        resampling = computeInterpolationQuality(totalMatrix, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight, isDataComplete());
    resampling = limitInterpolationQuality(context, resampling);

    SkMatrix localMatrix;
    // We also need to translate it such that the origin of the pattern is the
    // origin of the destination rect, which is what WebKit expects. Skia uses
    // the coordinate system origin as the base for the pattern. If WebKit wants
    // a shifted image, it will shift it from there using the localMatrix.
    const float adjustedX = phase.x() + normSrcRect.x() * scale.width();
    const float adjustedY = phase.y() + normSrcRect.y() * scale.height();
    localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY));

    RefPtr<SkShader> shader;
    SkPaint::FilterLevel filterLevel = static_cast<SkPaint::FilterLevel>(resampling);

    // Bicubic filter is only applied to defer-decoded images, see
    // NativeImageSkia::draw for details.
    if (resampling == InterpolationHigh && !isLazyDecoded) {
        // Do nice resampling.
        filterLevel = SkPaint::kNone_FilterLevel;
        float scaleX = destBitmapWidth / normSrcRect.width();
        float scaleY = destBitmapHeight / normSrcRect.height();
        SkRect scaledSrcRect;

        // Since we are resizing the bitmap, we need to remove the scale
        // applied to the pixels in the bitmap shader. This means we need
        // CTM * localMatrix to have identity scale. Since we
        // can't modify CTM (or the rectangle will be drawn in the wrong
        // place), we must set localMatrix's scale to the inverse of
        // CTM scale.
        localMatrix.preScale(ctmScaleX ? 1 / ctmScaleX : 1, ctmScaleY ? 1 / ctmScaleY : 1);

        // The image fragment generated here is not exactly what is
        // requested. The scale factor used is approximated and image
        // fragment is slightly larger to align to integer
        // boundaries.
        SkBitmap resampled = extractScaledImageFragment(normSrcRect, scaleX, scaleY, &scaledSrcRect);
        if (repeatSpacing.isZero()) {
            shader = adoptRef(SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        } else {
            shader = adoptRef(SkShader::CreateBitmapShader(
                createBitmapWithSpace(resampled, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
                SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        }
    } else {
        // Because no resizing occurred, the shader transform should be
        // set to the pattern's transform, which just includes scale.
        localMatrix.preScale(scale.width(), scale.height());

        // No need to resample before drawing.
        SkBitmap srcSubset;
        bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect));
        if (repeatSpacing.isZero()) {
            shader = adoptRef(SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        } else {
            shader = adoptRef(SkShader::CreateBitmapShader(
                createBitmapWithSpace(srcSubset, repeatSpacing.width() * ctmScaleX, repeatSpacing.height() * ctmScaleY),
                SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
        }
    }

    SkPaint paint;
    paint.setShader(shader.get());
    paint.setXfermode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode).get());
    paint.setColorFilter(context->colorFilter());
    paint.setFilterLevel(filterLevel);

    if (isLazyDecoded)
        PlatformInstrumentation::didDrawLazyPixelRef(bitmap().getGenerationID());

    context->drawRect(destRect, paint);
}

bool NativeImageSkia::shouldCacheResampling(const SkISize& scaledImageSize, const SkIRect& scaledImageSubset) const
{
    // Check whether the requested dimensions match previous request.
    bool matchesPreviousRequest = m_cachedImageInfo.isEqual(scaledImageSize, scaledImageSubset);
    if (matchesPreviousRequest)
        ++m_resizeRequests;
    else {
        m_cachedImageInfo.set(scaledImageSize, scaledImageSubset);
        m_resizeRequests = 0;
        // Reset m_resizedImage now, because we don't distinguish
        // between the last requested resize info and m_resizedImage's
        // resize info.
        m_resizedImage.reset();
    }

    // We can not cache incomplete frames. This might be a good optimization in
    // the future, were we know how much of the frame has been decoded, so when
    // we incrementally draw more of the image, we only have to resample the
    // parts that are changed.
    if (!isDataComplete())
        return false;

    // If the destination bitmap is excessively large, we'll never allow caching.
    static const unsigned long long kLargeBitmapSize = 4096ULL * 4096ULL;
    unsigned long long fullSize = static_cast<unsigned long long>(scaledImageSize.width()) * static_cast<unsigned long long>(scaledImageSize.height());
    unsigned long long fragmentSize = static_cast<unsigned long long>(scaledImageSubset.width()) * static_cast<unsigned long long>(scaledImageSubset.height());

    if (fragmentSize > kLargeBitmapSize)
        return false;

    // If the destination bitmap is small, we'll always allow caching, since
    // there is not very much penalty for computing it and it may come in handy.
    static const unsigned kSmallBitmapSize = 4096;
    if (fragmentSize <= kSmallBitmapSize)
        return true;

    // If "too many" requests have been made for this bitmap, we assume that
    // many more will be made as well, and we'll go ahead and cache it.
    static const int kManyRequestThreshold = 4;
    if (m_resizeRequests >= kManyRequestThreshold)
        return true;

    // If more than 1/4 of the resized image is requested, it's worth caching.
    return fragmentSize > fullSize / 4;
}

NativeImageSkia::ImageResourceInfo::ImageResourceInfo()
{
    scaledImageSize.setEmpty();
    scaledImageSubset.setEmpty();
}

bool NativeImageSkia::ImageResourceInfo::isEqual(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset) const
{
    return scaledImageSize == otherScaledImageSize && scaledImageSubset == otherScaledImageSubset;
}

void NativeImageSkia::ImageResourceInfo::set(const SkISize& otherScaledImageSize, const SkIRect& otherScaledImageSubset)
{
    scaledImageSize = otherScaledImageSize;
    scaledImageSubset = otherScaledImageSubset;
}

SkIRect NativeImageSkia::ImageResourceInfo::rectInSubset(const SkIRect& otherScaledImageSubset)
{
    if (!scaledImageSubset.contains(otherScaledImageSubset))
        return SkIRect::MakeEmpty();
    SkIRect subsetRect = otherScaledImageSubset;
    subsetRect.offset(-scaledImageSubset.x(), -scaledImageSubset.y());
    return subsetRect;
}

} // namespace blink
<|endoftext|>


<commit_before>#include "exe/signal_action.h"

#include <signal.h>
#include <sys/mman.h>

#include "common/common/assert.h"

namespace Envoy {
constexpr int SignalAction::FATAL_SIGS[];

void SignalAction::sigHandler(int sig, siginfo_t* info, void* context) {
  void* error_pc = 0;

  const ucontext_t* ucontext = reinterpret_cast<const ucontext_t*>(context);
  if (ucontext != nullptr) {
#ifdef REG_RIP
    // x86_64
    error_pc = reinterpret_cast<void*>(ucontext->uc_mcontext.gregs[REG_RIP]);
#elif defined(__APPLE__) && defined(__x86_64__)
    error_pc = reinterpret_cast<void*>(ucontext->uc_mcontext->__ss.__rip);
#elif defined(__powerpc__)
    error_pc = reinterpret_cast<void*>(ucontext->uc_mcontext.regs->nip);
#else
#warning "Please enable and test PC retrieval code for your arch in signal_action.cc"
// x86 Classic: reinterpret_cast<void*>(ucontext->uc_mcontext.gregs[REG_EIP]);
// ARM: reinterpret_cast<void*>(ucontext->uc_mcontext.arm_pc);
#endif
  }

  BackwardsTrace tracer;
  tracer.logFault(strsignal(sig), info->si_addr);
  if (error_pc != 0) {
    tracer.captureFrom(error_pc);
  } else {
    tracer.capture();
  }
  tracer.logTrace();

  signal(sig, SIG_DFL);
  raise(sig);
}

void SignalAction::installSigHandlers() {
  stack_t stack;
  stack.ss_sp = altstack_ + guard_size_; // Guard page at one end ...
  stack.ss_size = altstack_size_;        // ... guard page at the other
  stack.ss_flags = 0;

  RELEASE_ASSERT(sigaltstack(&stack, &previous_altstack_) == 0, "");

  int hidx = 0;
  for (const auto& sig : FATAL_SIGS) {
    struct sigaction saction;
    std::memset(&saction, 0, sizeof(saction));
    sigemptyset(&saction.sa_mask);
    saction.sa_flags = (SA_SIGINFO | SA_ONSTACK | SA_RESETHAND | SA_NODEFER);
    saction.sa_sigaction = sigHandler;
    auto* handler = &previous_handlers_[hidx++];
    RELEASE_ASSERT(sigaction(sig, &saction, handler) == 0, "");
  }
}

void SignalAction::removeSigHandlers() {
#if defined(__APPLE__)
  // ss_flags contains SS_DISABLE, but Darwin still checks the size, contrary to the man page
  if (previous_altstack_.ss_size < MINSIGSTKSZ) {
    previous_altstack_.ss_size = MINSIGSTKSZ;
  }
#endif
  RELEASE_ASSERT(sigaltstack(&previous_altstack_, nullptr) == 0, "");

  int hidx = 0;
  for (const auto& sig : FATAL_SIGS) {
    auto* handler = &previous_handlers_[hidx++];
    RELEASE_ASSERT(sigaction(sig, handler, nullptr) == 0, "");
  }
}

#if defined(__APPLE__) && !defined(MAP_STACK)
#define MAP_STACK (0)
#endif

void SignalAction::mapAndProtectStackMemory() {
  // Per docs MAP_STACK doesn't actually do anything today but provides a
  // library hint that might be used in the future.
  altstack_ = static_cast<char*>(mmap(nullptr, mapSizeWithGuards(), PROT_READ | PROT_WRITE,
                                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0));
  RELEASE_ASSERT(altstack_, "");
  RELEASE_ASSERT(mprotect(altstack_, guard_size_, PROT_NONE) == 0, "");
  RELEASE_ASSERT(mprotect(altstack_ + guard_size_ + altstack_size_, guard_size_, PROT_NONE) == 0,
                 "");
}

void SignalAction::unmapStackMemory() { munmap(altstack_, mapSizeWithGuards()); }

void SignalAction::doGoodAccessForTest() {
  volatile char* altaltstack = altstack_;
  for (size_t i = 0; i < altstack_size_; ++i) {
    *(altaltstack + guard_size_ + i) = 42;
  }
  for (size_t i = 0; i < altstack_size_; ++i) {
    ASSERT(*(altaltstack + guard_size_ + i) == 42);
  }
}

void SignalAction::tryEvilAccessForTest(bool end) {
  volatile char* altaltstack = altstack_;
  if (end) {
    // One byte past the valid region
    // http://oeis.org/A001969
    *(altaltstack + guard_size_ + altstack_size_) = 43;
  } else {
    // One byte before the valid region
    *(altaltstack + guard_size_ - 1) = 43;
  }
}
} // namespace Envoy
<commit_msg>Fix bug: add support to error_pc for Arm (#5158)<commit_after>#include "exe/signal_action.h"

#include <signal.h>
#include <sys/mman.h>

#include "common/common/assert.h"

namespace Envoy {
constexpr int SignalAction::FATAL_SIGS[];

void SignalAction::sigHandler(int sig, siginfo_t* info, void* context) {
  void* error_pc = 0;

  const ucontext_t* ucontext = reinterpret_cast<const ucontext_t*>(context);
  if (ucontext != nullptr) {
#ifdef REG_RIP
    // x86_64
    error_pc = reinterpret_cast<void*>(ucontext->uc_mcontext.gregs[REG_RIP]);
#elif defined(__APPLE__) && defined(__x86_64__)
    error_pc = reinterpret_cast<void*>(ucontext->uc_mcontext->__ss.__rip);
#elif defined(__powerpc__)
    error_pc = reinterpret_cast<void*>(ucontext->uc_mcontext.regs->nip);
#elif defined(__aarch64__)
    error_pc = reinterpret_cast<void*>(ucontext->uc_mcontext.pc);
#elif defined(__arm__)
    error_pc = reinterpret_cast<void*>(ucontext->uc_mcontext.arm_pc);
#else
#warning "Please enable and test PC retrieval code for your arch in signal_action.cc"
// x86 Classic: reinterpret_cast<void*>(ucontext->uc_mcontext.gregs[REG_EIP]);
// ARM: reinterpret_cast<void*>(ucontext->uc_mcontext.arm_pc);
#endif
  }

  BackwardsTrace tracer;
  tracer.logFault(strsignal(sig), info->si_addr);
  if (error_pc != 0) {
    tracer.captureFrom(error_pc);
  } else {
    tracer.capture();
  }
  tracer.logTrace();

  signal(sig, SIG_DFL);
  raise(sig);
}

void SignalAction::installSigHandlers() {
  stack_t stack;
  stack.ss_sp = altstack_ + guard_size_; // Guard page at one end ...
  stack.ss_size = altstack_size_;        // ... guard page at the other
  stack.ss_flags = 0;

  RELEASE_ASSERT(sigaltstack(&stack, &previous_altstack_) == 0, "");

  int hidx = 0;
  for (const auto& sig : FATAL_SIGS) {
    struct sigaction saction;
    std::memset(&saction, 0, sizeof(saction));
    sigemptyset(&saction.sa_mask);
    saction.sa_flags = (SA_SIGINFO | SA_ONSTACK | SA_RESETHAND | SA_NODEFER);
    saction.sa_sigaction = sigHandler;
    auto* handler = &previous_handlers_[hidx++];
    RELEASE_ASSERT(sigaction(sig, &saction, handler) == 0, "");
  }
}

void SignalAction::removeSigHandlers() {
#if defined(__APPLE__)
  // ss_flags contains SS_DISABLE, but Darwin still checks the size, contrary to the man page
  if (previous_altstack_.ss_size < MINSIGSTKSZ) {
    previous_altstack_.ss_size = MINSIGSTKSZ;
  }
#endif
  RELEASE_ASSERT(sigaltstack(&previous_altstack_, nullptr) == 0, "");

  int hidx = 0;
  for (const auto& sig : FATAL_SIGS) {
    auto* handler = &previous_handlers_[hidx++];
    RELEASE_ASSERT(sigaction(sig, handler, nullptr) == 0, "");
  }
}

#if defined(__APPLE__) && !defined(MAP_STACK)
#define MAP_STACK (0)
#endif

void SignalAction::mapAndProtectStackMemory() {
  // Per docs MAP_STACK doesn't actually do anything today but provides a
  // library hint that might be used in the future.
  altstack_ = static_cast<char*>(mmap(nullptr, mapSizeWithGuards(), PROT_READ | PROT_WRITE,
                                      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0));
  RELEASE_ASSERT(altstack_, "");
  RELEASE_ASSERT(mprotect(altstack_, guard_size_, PROT_NONE) == 0, "");
  RELEASE_ASSERT(mprotect(altstack_ + guard_size_ + altstack_size_, guard_size_, PROT_NONE) == 0,
                 "");
}

void SignalAction::unmapStackMemory() { munmap(altstack_, mapSizeWithGuards()); }

void SignalAction::doGoodAccessForTest() {
  volatile char* altaltstack = altstack_;
  for (size_t i = 0; i < altstack_size_; ++i) {
    *(altaltstack + guard_size_ + i) = 42;
  }
  for (size_t i = 0; i < altstack_size_; ++i) {
    ASSERT(*(altaltstack + guard_size_ + i) == 42);
  }
}

void SignalAction::tryEvilAccessForTest(bool end) {
  volatile char* altaltstack = altstack_;
  if (end) {
    // One byte past the valid region
    // http://oeis.org/A001969
    *(altaltstack + guard_size_ + altstack_size_) = 43;
  } else {
    // One byte before the valid region
    *(altaltstack + guard_size_ - 1) = 43;
  }
}
} // namespace Envoy
<|endoftext|>