<commit_before>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>

#define DLL_PARALLEL
#define DLL_SVM_SUPPORT

#include "dll/conv_rbm.hpp"
#include "dll/conv_dbn.hpp"
#include "dll/test.hpp"
#include "dll/cpp_utils/algorithm.hpp"
#include "dll/cpp_utils/data.hpp"

#include "icdar/icdar_reader.hpp"

constexpr const std::size_t context = 5;
constexpr const std::size_t window = context * 2 + 1;

template<typename Label>
bool is_text(const Label& label, std::size_t x, std::size_t y){
    for(auto& rectangle : label.rectangles){
        if(x >= rectangle.left && x <= rectangle.right && y >= rectangle.top && y <= rectangle.bottom){
            return true;
        }
    }

    return false;
}

int main(){
    auto dataset = icdar::read_2013_dataset(
        "/home/wichtounet/datasets/icdar_2013_natural/train",
        "/home/wichtounet/datasets/icdar_2013_natural/test", 5, 1);

    if(dataset.training_labels.empty() || dataset.training_images.empty()){
        std::cout << "Problem while reading the dataset" << std::endl;

        return -1;
    }

    std::cout << "Dataset" << std::endl;
    std::cout << dataset.training_images.size() << " training images" << std::endl;
    std::cout << dataset.test_images.size() << " test images" << std::endl;

    std::vector<std::vector<float>> windows;
    std::vector<std::size_t> labels;

    for(std::size_t image_id = 0; image_id < dataset.training_images.size(); ++image_id){
        auto& image = dataset.training_images[image_id];

        windows.reserve(windows.size() + image.width * image.height);

        for(std::size_t i = context; i < image.width - context; ++i){
            for(std::size_t j = context; j < image.height - context; ++j){

                windows.emplace_back(window * window);

                labels.push_back(is_text(dataset.training_labels[image_id], i, j) ? 1 : 0);

                for(std::size_t a = i - context; a < i - context + window; ++a){
                    for(std::size_t b = j - context; b < j - context + window; ++b){
                        auto w_i = (a - (i - context));
                        auto w_j = (b - (j - context));
                        windows.back().at(w_i * window + w_j) = image.pixels.at(a * image.height + b).r;
                    }
                }
            }
        }
    }

    std::cout << window << std::endl;

    windows.resize(1000);
    labels.resize(1000);

    cpp::normalize_each(windows);

    std::cout << "Extraction" << std::endl;
    std::cout << dataset.training_images.size() << " images" << std::endl;
    std::cout << window << "x" << window << " windows" << std::endl;
    std::cout << windows.size() << " windows and labels extracted" << std::endl;

    typedef dll::conv_dbn_desc<
        dll::dbn_layers<
            dll::conv_rbm_desc<window, 1, 5, 40
                , dll::momentum
                , dll::batch_size<50>
                , dll::weight_decay<dll::decay_type::L2>
                , dll::visible<dll::unit_type::GAUSSIAN>
                , dll::sparsity<dll::sparsity_method::LEE>
            >::rbm_t
            >>::dbn_t dbn_t;

    auto dbn = std::make_unique<dbn_t>();

    std::cout << "DBN is " << sizeof(dbn_t) << " bytes long" << std::endl;
    std::cout << "DBN input is " << dbn->input_size() << std::endl;
    std::cout << "DBN output is " << dbn->output_size() << std::endl;

    dbn->layer<0>().pbias = 0.05;
    dbn->layer<0>().pbias_lambda = 100;

    //TODO What about randomization ?

    dbn->pretrain(windows, 50);
    dbn->svm_train(windows, labels);

    double error = dll::test_set(dbn, windows, labels, dll::svm_predictor());
    std::cout << "Pixel error:" << error << std::endl;

    return 0;
}
<commit_msg>Updates<commit_after>//=======================================================================
// Copyright (c) 2014 Baptiste Wicht
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>

#define DLL_PARALLEL
#define DLL_SVM_SUPPORT

#include "dll/conv_rbm.hpp"
#include "dll/conv_dbn.hpp"
#include "dll/test.hpp"
#include "dll/cpp_utils/algorithm.hpp"
#include "dll/cpp_utils/data.hpp"

#include "icdar/icdar_reader.hpp"

constexpr const std::size_t context = 5;
constexpr const std::size_t window = context * 2 + 1;

template<typename Label>
bool is_text(const Label& label, std::size_t x, std::size_t y){
    for(auto& rectangle : label.rectangles){
        if(x >= rectangle.left && x <= rectangle.right && y >= rectangle.top && y <= rectangle.bottom){
            return true;
        }
    }

    return false;
}

template<typename Images, typename Labels>
void extract(std::vector<std::vector<float>>& windows, std::vector<std::size_t>& labels, Images& d_images, Labels& d_labels){
    for(std::size_t image_id = 0; image_id < d_images.size(); ++image_id){
        auto& image = d_images[image_id];

        windows.reserve(windows.size() + image.width * image.height);

        for(std::size_t i = context; i < image.width - context; ++i){
            for(std::size_t j = context; j < image.height - context; ++j){

                windows.emplace_back(window * window);

                labels.push_back(is_text(d_labels[image_id], i, j) ? 1 : 0);

                for(std::size_t a = i - context; a < i - context + window; ++a){
                    for(std::size_t b = j - context; b < j - context + window; ++b){
                        auto w_i = (a - (i - context));
                        auto w_j = (b - (j - context));
                        windows.back().at(w_i * window + w_j) = image.pixels.at(a * image.height + b).r;
                    }
                }
            }
        }
    }
}

int main(){
    auto dataset = icdar::read_2013_dataset(
        "/home/wichtounet/datasets/icdar_2013_natural/train",
        "/home/wichtounet/datasets/icdar_2013_natural/test", 5, 1);

    if(dataset.training_labels.empty() || dataset.training_images.empty()){
        std::cout << "Problem while reading the dataset" << std::endl;

        return -1;
    }

    std::cout << window << "x" << window << " window dimension" << std::endl;

    std::cout << "Dataset" << std::endl;
    std::cout << dataset.training_images.size() << " training images" << std::endl;
    std::cout << dataset.test_images.size() << " test images" << std::endl;

    std::vector<std::vector<float>> training_windows;
    std::vector<std::size_t> training_labels;

    std::vector<std::vector<float>> test_windows;
    std::vector<std::size_t> test_labels;

    extract(training_windows, training_labels, dataset.training_images, dataset.training_labels);

    training_windows.resize(5000);
    training_labels.resize(5000);

    extract(test_windows, test_labels, dataset.test_images, dataset.test_labels);

    test_windows.resize(5000);
    test_labels.resize(5000);

    cpp::normalize_each(training_windows);
    cpp::normalize_each(test_windows);

    std::cout << "Extraction" << std::endl;
    std::cout << dataset.training_images.size() << " training images" << std::endl;
    std::cout << training_windows.size() << " training windows and labels extracted" << std::endl;

    std::cout << dataset.test_images.size() << " test images" << std::endl;
    std::cout << test_windows.size() << " test windows and labels extracted" << std::endl;

    typedef dll::conv_dbn_desc<
        dll::dbn_layers<
            dll::conv_rbm_desc<window, 1, 5, 40
                , dll::momentum
                , dll::batch_size<50>
                , dll::weight_decay<dll::decay_type::L2>
                , dll::visible<dll::unit_type::GAUSSIAN>
                , dll::sparsity<dll::sparsity_method::LEE>
            >::rbm_t
            >>::dbn_t dbn_t;

    auto dbn = std::make_unique<dbn_t>();

    std::cout << "DBN is " << sizeof(dbn_t) << " bytes long" << std::endl;
    std::cout << "DBN input is " << dbn->input_size() << std::endl;
    std::cout << "DBN output is " << dbn->output_size() << std::endl;

    dbn->layer<0>().pbias = 0.05;
    dbn->layer<0>().pbias_lambda = 100;

    //TODO What about randomization ?

    dbn->pretrain(training_windows, 50);
    dbn->svm_train(training_windows, training_labels);

    double training_error = dll::test_set(dbn, training_windows, training_labels, dll::svm_predictor());
    std::cout << "Pixel error (training):" << training_error << std::endl;

    double test_error = dll::test_set(dbn, test_windows, test_labels, dll::svm_predictor());
    std::cout << "Pixel error (test):" << test_error << std::endl;

    return 0;
}
<|endoftext|>


<commit_before>/*
    Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz>

    This file is part of Magnum.

    Magnum is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License version 3
    only, as published by the Free Software Foundation.

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

#include <iostream>
#include <chrono>
#include <memory>

#include "PluginManager/PluginManager.h"
#include "Scene.h"
#include "Camera.h"
#include "Trade/AbstractImporter.h"
#include "Trade/MeshData.h"
#include "MeshTools/Tipsify.h"
#include "MeshTools/Interleave.h"
#include "MeshTools/CompressIndices.h"
#include "Shaders/PhongShader.h"

#include "AbstractExample.h"
#include "ViewedObject.h"
#include "configure.h"

using namespace std;
using namespace Corrade::PluginManager;
using namespace Corrade::Utility;
using namespace Magnum;
using namespace Magnum::Shaders;
using namespace Magnum::Examples;

namespace Magnum { namespace Examples {

class ViewerExample: public AbstractExample {
    public:
        ViewerExample(int& argc, char** argv): AbstractExample(argc, argv, "Magnum Viewer"), wireframe(false), fps(false) {
            if(argc != 2) {
                cout << "Usage: " << argv[0] << " file.dae" << endl;
                exit(0);
            }

            /* Instance ColladaImporter plugin */
            PluginManager<Trade::AbstractImporter> manager(PLUGIN_IMPORTER_DIR);
            if(manager.load("ColladaImporter") != AbstractPluginManager::LoadOk) {
                Error() << "Could not load ColladaImporter plugin";
                exit(1);
            }
            unique_ptr<Trade::AbstractImporter> colladaImporter(manager.instance("ColladaImporter"));
            if(!colladaImporter) {
                Error() << "Could not instance ColladaImporter plugin";
                exit(2);
            }
            if(!(colladaImporter->features() & Trade::AbstractImporter::OpenFile)) {
                Error() << "ColladaImporter cannot open files";
                exit(3);
            }

            scene.setFeature(Scene::DepthTest, true);

            /* Every scene needs a camera */
            camera = new Camera(&scene);
            camera->setPerspective(deg(35.0f), 0.001f, 100);
            camera->translate(Vector3::zAxis(5));

            /* Load file */
            if(!colladaImporter->open(argv[1]))
                exit(4);

            if(colladaImporter->meshCount() == 0)
                exit(5);

            Trade::MeshData* data = colladaImporter->mesh(0);
            if(!data || !data->indices() || !data->vertexArrayCount() || !data->normalArrayCount())
                exit(6);

            /* Optimize vertices */
            Debug() << "Optimizing mesh vertices using Tipsify algorithm (cache size 24)...";
            MeshTools::tipsify(*data->indices(), data->vertices(0)->size(), 24);

            /* Interleave mesh data */
            Buffer* buffer = mesh.addBuffer(true);
            mesh.bindAttribute<PhongShader::Vertex>(buffer);
            mesh.bindAttribute<PhongShader::Normal>(buffer);
            MeshTools::interleave(&mesh, buffer, Buffer::Usage::StaticDraw, *data->vertices(0), *data->normals(0));

            /* Compress indices */
            MeshTools::compressIndices(&mesh, Buffer::Usage::StaticDraw, *data->indices());

            /* Get material or create default one */
            Trade::PhongMaterialData* material = static_cast<Trade::PhongMaterialData*>(colladaImporter->material(0));
            if(!material) material = new Trade::PhongMaterialData({0.0f, 0.0f, 0.0f}, {0.9f, 0.9f, 0.9f}, {1.0f, 1.0f, 1.0f}, 50.0f);

            o = new ViewedObject(&mesh, static_cast<Trade::PhongMaterialData*>(material), &shader, &scene);

            colladaImporter->close();
            delete colladaImporter.release();
        }

    protected:
        inline void viewportEvent(const Math::Vector2<GLsizei>& size) {
            camera->setViewport(size);
        }

        void drawEvent() {
            if(fps) {
                chrono::high_resolution_clock::time_point now = chrono::high_resolution_clock::now();
                double duration = chrono::duration<double>(now-before).count();
                if(duration > 3.5) {
                    cout << frames << " frames in " << duration << " sec: "
                        << frames/duration << " FPS         \r";
                    cout.flush();
                    totalfps += frames/duration;
                    before = now;
                    frames = 0;
                    ++totalmeasurecount;
                }
            }
            camera->draw();
            swapBuffers();

            if(fps) {
                ++frames;
                redraw();
            }
        }

        void keyEvent(Key key, const Math::Vector2<int>& position) {
            switch(key) {
                case Key::Up:
                    o->rotate(deg(10.0f), Vector3::xAxis(-1));
                    break;
                case Key::Down:
                    o->rotate(deg(10.0f), Vector3::xAxis(1));
                    break;
                case Key::Left:
                    o->rotate(deg(10.0f), Vector3::yAxis(-1), false);
                    break;
                case Key::Right:
                    o->rotate(deg(10.0f), Vector3::yAxis(1), false);
                    break;
                case Key::PageUp:
                    camera->translate(Vector3::zAxis(-0.5));
                    break;
                case Key::PageDown:
                    camera->translate(Vector3::zAxis(0.5));
                    break;
                case Key::Home:
                    glPolygonMode(GL_FRONT_AND_BACK, wireframe ? GL_FILL : GL_LINE);
                    wireframe = !wireframe;
                    break;
                case Key::End:
                    if(fps) cout << "Average FPS on " << camera->viewport().x()
                        << 'x' << camera->viewport().y() << " from "
                        << totalmeasurecount << " measures: "
                        << totalfps/totalmeasurecount << "          " << endl;
                    else before = chrono::high_resolution_clock::now();

                    fps = !fps;
                    frames = totalmeasurecount = 0;
                    totalfps = 0;
                    break;
            }

            redraw();
        }

        void mouseEvent(MouseButton button, MouseState state, const Math::Vector2<int>& position) {
            switch(button) {
                case MouseButton::Left:
                    if(state == MouseState::Down) previousPosition = positionOnSphere(position);
                    else previousPosition = Vector3();
                    break;
                case MouseButton::WheelUp:
                case MouseButton::WheelDown: {
                    if(state == MouseState::Up) return;

                    /* Distance between origin and near camera clipping plane */
                    GLfloat distance = camera->transformation().at(3).z()-0-camera->near();

                    /* Move 15% of the distance back or forward */
                    if(button == MouseButton::WheelUp)
                        distance *= 1 - 1/0.85f;
                    else
                        distance *= 1 - 0.85f;
                    camera->translate(Vector3::zAxis(distance));

                    redraw();
                    break;
                }
                default: ;
            }
        }

        void mouseMoveEvent(const Math::Vector2<int>& position) {
            Vector3 currentPosition = positionOnSphere(position);

            Vector3 axis = Vector3::cross(previousPosition, currentPosition);

            if(previousPosition.length() < 0.001f || axis.length() < 0.001f) return;

            GLfloat angle = acos(previousPosition*currentPosition);
            o->rotate(angle, axis);

            previousPosition = currentPosition;

            redraw();
        }

    private:
        Vector3 positionOnSphere(const Math::Vector2<int>& _position) const {
            Math::Vector2<GLsizei> viewport = camera->viewport();
            Vector2 position(_position.x()*2.0f/viewport.x() - 1.0f,
                             _position.y()*2.0f/viewport.y() - 1.0f);

            GLfloat length = position.length();
            Vector3 result(length > 1.0f ? position : Vector3(position, 1.0f - length));
            result.setY(-result.y());
            return result.normalized();
        }

        Scene scene;
        Camera* camera;
        PhongShader shader;
        IndexedMesh mesh;
        Object* o;
        chrono::high_resolution_clock::time_point before;
        bool wireframe, fps;
        size_t frames;
        double totalfps;
        size_t totalmeasurecount;
        Vector3 previousPosition;
};

}}

MAGNUM_EXAMPLE_MAIN(Magnum::Examples::ViewerExample)
<commit_msg>Moved function definition out of the class body.<commit_after>/*
    Copyright © 2010, 2011, 2012 Vladimír Vondruš <mosra@centrum.cz>

    This file is part of Magnum.

    Magnum is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License version 3
    only, as published by the Free Software Foundation.

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

#include <iostream>
#include <chrono>
#include <memory>

#include "PluginManager/PluginManager.h"
#include "Scene.h"
#include "Camera.h"
#include "Trade/AbstractImporter.h"
#include "Trade/MeshData.h"
#include "MeshTools/Tipsify.h"
#include "MeshTools/Interleave.h"
#include "MeshTools/CompressIndices.h"
#include "Shaders/PhongShader.h"

#include "AbstractExample.h"
#include "ViewedObject.h"
#include "configure.h"

using namespace std;
using namespace Corrade::PluginManager;
using namespace Corrade::Utility;
using namespace Magnum;
using namespace Magnum::Shaders;
using namespace Magnum::Examples;

namespace Magnum { namespace Examples {

class ViewerExample: public AbstractExample {
    public:
        ViewerExample(int& argc, char** argv);

    protected:
        inline void viewportEvent(const Math::Vector2<GLsizei>& size) {
            camera->setViewport(size);
        }

        void drawEvent() {
            if(fps) {
                chrono::high_resolution_clock::time_point now = chrono::high_resolution_clock::now();
                double duration = chrono::duration<double>(now-before).count();
                if(duration > 3.5) {
                    cout << frames << " frames in " << duration << " sec: "
                        << frames/duration << " FPS         \r";
                    cout.flush();
                    totalfps += frames/duration;
                    before = now;
                    frames = 0;
                    ++totalmeasurecount;
                }
            }
            camera->draw();
            swapBuffers();

            if(fps) {
                ++frames;
                redraw();
            }
        }

        void keyEvent(Key key, const Math::Vector2<int>& position) {
            switch(key) {
                case Key::Up:
                    o->rotate(deg(10.0f), Vector3::xAxis(-1));
                    break;
                case Key::Down:
                    o->rotate(deg(10.0f), Vector3::xAxis(1));
                    break;
                case Key::Left:
                    o->rotate(deg(10.0f), Vector3::yAxis(-1), false);
                    break;
                case Key::Right:
                    o->rotate(deg(10.0f), Vector3::yAxis(1), false);
                    break;
                case Key::PageUp:
                    camera->translate(Vector3::zAxis(-0.5));
                    break;
                case Key::PageDown:
                    camera->translate(Vector3::zAxis(0.5));
                    break;
                case Key::Home:
                    glPolygonMode(GL_FRONT_AND_BACK, wireframe ? GL_FILL : GL_LINE);
                    wireframe = !wireframe;
                    break;
                case Key::End:
                    if(fps) cout << "Average FPS on " << camera->viewport().x()
                        << 'x' << camera->viewport().y() << " from "
                        << totalmeasurecount << " measures: "
                        << totalfps/totalmeasurecount << "          " << endl;
                    else before = chrono::high_resolution_clock::now();

                    fps = !fps;
                    frames = totalmeasurecount = 0;
                    totalfps = 0;
                    break;
            }

            redraw();
        }

        void mouseEvent(MouseButton button, MouseState state, const Math::Vector2<int>& position) {
            switch(button) {
                case MouseButton::Left:
                    if(state == MouseState::Down) previousPosition = positionOnSphere(position);
                    else previousPosition = Vector3();
                    break;
                case MouseButton::WheelUp:
                case MouseButton::WheelDown: {
                    if(state == MouseState::Up) return;

                    /* Distance between origin and near camera clipping plane */
                    GLfloat distance = camera->transformation().at(3).z()-0-camera->near();

                    /* Move 15% of the distance back or forward */
                    if(button == MouseButton::WheelUp)
                        distance *= 1 - 1/0.85f;
                    else
                        distance *= 1 - 0.85f;
                    camera->translate(Vector3::zAxis(distance));

                    redraw();
                    break;
                }
                default: ;
            }
        }

        void mouseMoveEvent(const Math::Vector2<int>& position) {
            Vector3 currentPosition = positionOnSphere(position);

            Vector3 axis = Vector3::cross(previousPosition, currentPosition);

            if(previousPosition.length() < 0.001f || axis.length() < 0.001f) return;

            GLfloat angle = acos(previousPosition*currentPosition);
            o->rotate(angle, axis);

            previousPosition = currentPosition;

            redraw();
        }

    private:
        Vector3 positionOnSphere(const Math::Vector2<int>& _position) const {
            Math::Vector2<GLsizei> viewport = camera->viewport();
            Vector2 position(_position.x()*2.0f/viewport.x() - 1.0f,
                             _position.y()*2.0f/viewport.y() - 1.0f);

            GLfloat length = position.length();
            Vector3 result(length > 1.0f ? position : Vector3(position, 1.0f - length));
            result.setY(-result.y());
            return result.normalized();
        }

        Scene scene;
        Camera* camera;
        PhongShader shader;
        IndexedMesh mesh;
        Object* o;
        chrono::high_resolution_clock::time_point before;
        bool wireframe, fps;
        size_t frames;
        double totalfps;
        size_t totalmeasurecount;
        Vector3 previousPosition;
};

ViewerExample::ViewerExample(int& argc, char** argv): AbstractExample(argc, argv, "Magnum Viewer"), wireframe(false), fps(false) {
    if(argc != 2) {
        cout << "Usage: " << argv[0] << " file.dae" << endl;
        exit(0);
    }

    /* Instance ColladaImporter plugin */
    PluginManager<Trade::AbstractImporter> manager(PLUGIN_IMPORTER_DIR);
    if(manager.load("ColladaImporter") != AbstractPluginManager::LoadOk) {
        Error() << "Could not load ColladaImporter plugin";
        exit(1);
    }
    unique_ptr<Trade::AbstractImporter> colladaImporter(manager.instance("ColladaImporter"));
    if(!colladaImporter) {
        Error() << "Could not instance ColladaImporter plugin";
        exit(2);
    }
    if(!(colladaImporter->features() & Trade::AbstractImporter::OpenFile)) {
        Error() << "ColladaImporter cannot open files";
        exit(3);
    }

    scene.setFeature(Scene::DepthTest, true);

    /* Every scene needs a camera */
    camera = new Camera(&scene);
    camera->setPerspective(deg(35.0f), 0.001f, 100);
    camera->translate(Vector3::zAxis(5));

    /* Load file */
    if(!colladaImporter->open(argv[1]))
        exit(4);

    if(colladaImporter->meshCount() == 0)
        exit(5);

    Trade::MeshData* data = colladaImporter->mesh(0);
    if(!data || !data->indices() || !data->vertexArrayCount() || !data->normalArrayCount())
        exit(6);

    /* Optimize vertices */
    Debug() << "Optimizing mesh vertices using Tipsify algorithm (cache size 24)...";
    MeshTools::tipsify(*data->indices(), data->vertices(0)->size(), 24);

    /* Interleave mesh data */
    Buffer* buffer = mesh.addBuffer(true);
    mesh.bindAttribute<PhongShader::Vertex>(buffer);
    mesh.bindAttribute<PhongShader::Normal>(buffer);
    MeshTools::interleave(&mesh, buffer, Buffer::Usage::StaticDraw, *data->vertices(0), *data->normals(0));

    /* Compress indices */
    MeshTools::compressIndices(&mesh, Buffer::Usage::StaticDraw, *data->indices());

    /* Get material or create default one */
    Trade::PhongMaterialData* material = static_cast<Trade::PhongMaterialData*>(colladaImporter->material(0));
    if(!material) material = new Trade::PhongMaterialData({0.0f, 0.0f, 0.0f}, {0.9f, 0.9f, 0.9f}, {1.0f, 1.0f, 1.0f}, 50.0f);

    o = new ViewedObject(&mesh, static_cast<Trade::PhongMaterialData*>(material), &shader, &scene);

    colladaImporter->close();
    delete colladaImporter.release();
}

}}

MAGNUM_EXAMPLE_MAIN(Magnum::Examples::ViewerExample)
<|endoftext|>


<commit_before>#pragma once
//=====================================================================//
/*!	@file
	@brief	ルネサス RX 選択
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2016, 2017 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include "common/byte_order.h"
#include "common/vect.h"
#include "common/delay.hpp"

#if defined(SIG_RX621)
#include "RX600/port.hpp"
#include "RX600/cmt.hpp"
#include "RX621/system.hpp"
#include "RX621/sci.hpp"
#include "RX621/icu.hpp"

#elif defined(SIG_RX24T)
#include "RX24T/peripheral.hpp"
#include "RX600/port.hpp"
#include "RX24T/system.hpp"
#include "RX24T/dtc.hpp"
#include "RX24T/icu.hpp"
#include "RX24T/mtu3.hpp"
#include "RX24T/poe3.hpp"
#include "RX24T/tmr.hpp"
#include "RX600/cmt.hpp"
#include "RX24T/sci.hpp"
#include "RX24T/riic.hpp"
#include "RX24T/rspi.hpp"
#include "RX24T/crc.hpp"
#include "RX24T/s12ad.hpp"
#include "RX24T/adc_io.hpp"
#include "RX24T/da.hpp"
#include "RX24T/cmpc.hpp"
#include "RX24T/doc.hpp"
#include "RX24T/port_map.hpp"
#include "RX24T/power_cfg.hpp"
#include "RX24T/icu_mgr.hpp"
#include "RX24T/flash.hpp"
#include "RX24T/flash_io.hpp"

#elif defined(SIG_RX63T)
#include "RX63T/peripheral.hpp"
#include "RX600/port.hpp"
#include "RX600/cmt.hpp"
#include "RX63T/system.hpp"
#include "RX63T/sci.hpp"
#include "RX63T/icu.hpp"
#include "RX63T/port_map.hpp"
#include "RX63T/power_cfg.hpp"
#include "RX63T/icu_mgr.hpp"

#elif defined(SIG_RX64M)
#include "RX64M/peripheral.hpp"
#include "RX600/port.hpp"
#include "RX600/cmt.hpp"
#include "RX64M/system.hpp"
#include "RX64M/system_io.hpp"
#include "RX64M/bus.hpp"
#include "RX64M/mpc.hpp"
#include "RX64M/icu.hpp"
#include "RX64M/sci.hpp"
#include "RX64M/riic.hpp"
#include "RX64M/rspi.hpp"
#include "RX64M/port_map.hpp"
#include "RX64M/power_cfg.hpp"
#include "RX64M/icu_mgr.hpp"
#include "RX64M/sdhi.hpp"
#include "RX64M/s12adc.hpp"
#include "RX64M/r12da.hpp"
#include "RX64M/adc_io.hpp"
#include "RX64M/sdram.hpp"
#include "RX64M/etherc.hpp"
#include "RX64M/edmac.hpp"
#include "RX64M/rtc.hpp"
#include "RX64M/rtc_io.hpp"
#include "RX64M/flash.hpp"
#include "RX64M/flash_io.hpp"
#include "RX64M/ether_io.hpp"

#else
#  error "Requires SIG_XXX to be defined"
#endif
<commit_msg>add GPT<commit_after>#pragma once
//=====================================================================//
/*!	@file
	@brief	ルネサス RX 選択
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2016, 2017 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include "common/byte_order.h"
#include "common/vect.h"
#include "common/delay.hpp"

#if defined(SIG_RX621)
#include "RX600/port.hpp"
#include "RX600/cmt.hpp"
#include "RX621/system.hpp"
#include "RX621/sci.hpp"
#include "RX621/icu.hpp"

#elif defined(SIG_RX24T)
#include "RX24T/peripheral.hpp"
#include "RX600/port.hpp"
#include "RX24T/system.hpp"
#include "RX24T/dtc.hpp"
#include "RX24T/icu.hpp"
#include "RX24T/mtu3.hpp"
#include "RX24T/poe3.hpp"
#include "RX24T/gpt.hpp"
#include "RX24T/tmr.hpp"
#include "RX600/cmt.hpp"
#include "RX24T/sci.hpp"
#include "RX24T/riic.hpp"
#include "RX24T/rspi.hpp"
#include "RX24T/crc.hpp"
#include "RX24T/s12ad.hpp"
#include "RX24T/adc_io.hpp"
#include "RX24T/da.hpp"
#include "RX24T/cmpc.hpp"
#include "RX24T/doc.hpp"
#include "RX24T/port_map.hpp"
#include "RX24T/power_cfg.hpp"
#include "RX24T/icu_mgr.hpp"
#include "RX24T/flash.hpp"
#include "RX24T/flash_io.hpp"

#elif defined(SIG_RX63T)
#include "RX63T/peripheral.hpp"
#include "RX600/port.hpp"
#include "RX600/cmt.hpp"
#include "RX63T/system.hpp"
#include "RX63T/sci.hpp"
#include "RX63T/icu.hpp"
#include "RX63T/port_map.hpp"
#include "RX63T/power_cfg.hpp"
#include "RX63T/icu_mgr.hpp"

#elif defined(SIG_RX64M)
#include "RX64M/peripheral.hpp"
#include "RX600/port.hpp"
#include "RX600/cmt.hpp"
#include "RX64M/system.hpp"
#include "RX64M/system_io.hpp"
#include "RX64M/bus.hpp"
#include "RX64M/mpc.hpp"
#include "RX64M/icu.hpp"
#include "RX64M/sci.hpp"
#include "RX64M/riic.hpp"
#include "RX64M/rspi.hpp"
#include "RX64M/port_map.hpp"
#include "RX64M/power_cfg.hpp"
#include "RX64M/icu_mgr.hpp"
#include "RX64M/sdhi.hpp"
#include "RX64M/s12adc.hpp"
#include "RX64M/r12da.hpp"
#include "RX64M/adc_io.hpp"
#include "RX64M/sdram.hpp"
#include "RX64M/etherc.hpp"
#include "RX64M/edmac.hpp"
#include "RX64M/rtc.hpp"
#include "RX64M/rtc_io.hpp"
#include "RX64M/flash.hpp"
#include "RX64M/flash_io.hpp"
#include "RX64M/ether_io.hpp"

#else
#  error "Requires SIG_XXX to be defined"
#endif
<|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 "meditortoolbar.h"
#include "mtopleveloverlay.h"

#include <MButton>
#include <MLayout>
#include <MLinearLayoutPolicy>
#include <MSceneManager>
#include <MWidget>
#include <QAction>

#include <QtAlgorithms>
#include <QApplication>
#include <QGraphicsLinearLayout>

MEditorToolbar::MEditorToolbar(const MWidget &followWidget)
    : overlay(new MTopLevelOverlay(followWidget.sceneManager())),
      followWidget(followWidget),
      buttonLayoutPolicy(new MLinearLayoutPolicy(new MLayout(this),
                                                 Qt::Horizontal)),
      arrow(new MEditorToolbarArrow(this)),
      buttonUpdateQueued(false),
      temporarilyDisappeared(false)
{
    setFlag(QGraphicsItem::ItemHasNoContents, true);
    overlay->hide();
    hide();
    followWidget.scene()->addItem(overlay);
    setParentItem(overlay);

    // Set z value for arrow higher than default since
    // it needs to float on top of buttons.
    arrow->setZValue(1.0f);

    // The policy notifies the widgets of their relative position inside the layout,
    // this causes the buttons to be rendered with different backgrounds for each position
    buttonLayoutPolicy->setNotifyWidgetsOfLayoutPositionEnabled(true);

    // Don't add extra margins or spacings for buttons.
    buttonLayoutPolicy->setContentsMargins(0.0f, 0.0f, 0.0f, 0.0f);
    buttonLayoutPolicy->setSpacing(0.0f);

    QObject::connect(sceneManager(), SIGNAL(orientationChanged(M::Orientation)),
                     this, SLOT(updateGeometry()));

    updateArrow(MEditorToolbarArrow::ArrowDown);
}

MEditorToolbar::~MEditorToolbar()
{
    hideEditorItem();
    // Before destroying parent, detach so it doesn't try to destroy us.
    setParentItem(0);
    if (overlay) {
        overlay->deleteLater();
    }
    qDeleteAll(buttons);
}

void MEditorToolbar::setPosition(const QPointF &pos,
                                 MEditorToolbarArrow::ArrowDirection direction)
{
    setPos(followWidget.mapToItem(overlay, pos));
    updateArrow(direction);
}

void MEditorToolbar::appear()
{
    overlay->show();
    updateEditorItemVisibility();
    temporarilyDisappeared = false;
}

void MEditorToolbar::disappear()
{
    hideEditorItem();
    overlay->hide();
    temporarilyDisappeared = false;
}

void MEditorToolbar::disappearTemporarily()
{
    if (isAppeared()) {
        disappear();
        temporarilyDisappeared = true;
    }
}

void MEditorToolbar::removeTemporaryDisappearance()
{
    if (temporarilyDisappeared) {
        appear();
    }
}

bool MEditorToolbar::isAppeared() const
{
    return overlay->isVisible();
}

void MEditorToolbar::updateArrow(MEditorToolbarArrow::ArrowDirection direction)
{
    // Clear local transforms.
    setTransform(QTransform());

    // Style mode is different with regarding to top and bottom margins.
    if (direction == MEditorToolbarArrow::ArrowUp) {
        style().setModeEditorUnderCursor();
    } else {
        style().setModeDefault();
    }
    MStylableWidget::applyStyle();

    const QRectF contentsRectangle = contentsRect();

    // Update horizontally, make sure widget is inside screen.
    qreal center = contentsRectangle.center().x();
    QRectF mappedSceneRect = mapRectFromScene(
        QRectF(QPointF(), sceneManager()->visibleSceneSize(M::Landscape)));
    mappedSceneRect.translate(center, 0.0f);

    qreal offscreenLeft = qMax<qreal>(0.0f, mappedSceneRect.left());
    qreal offscreenRight = qMax<qreal>(0.0f, (effectiveSizeHint(Qt::PreferredSize).width()
                                              - mappedSceneRect.right()));
    // Screen rectangle in overlay coordinate system, just like we are
    const QRectF screenRectInOverlay(
        overlay->mapRectFromScene(QRectF(QPointF(), sceneManager()->visibleSceneSize(M::Landscape))));
    qreal x;

    if (size().width() < screenRectInOverlay.width()) {
        // The widget won't be off the screen from both ends at the same time.
        // Width is restricted to screen width.
        x = center - arrow->size().width() / 2.0f
            - offscreenLeft + offscreenRight;
        x = qBound<qreal>(contentsRectangle.left(),
                          x,
                          contentsRectangle.right() - arrow->size().width());
    } else {
        x = geometry().center().x() - screenRectInOverlay.center().x() - arrow->size().width() / 2.0f;
    }

    // Update vertically. Arrow graphics are designed to be aligned to either
    // top or bottom of buttons, completely overlapping them.
    arrow->setDirection(direction);

    switch (arrow->direction()) {
    case MEditorToolbarArrow::ArrowUp:
        arrow->setPos(x, contentsRectangle.top());
        break;
    case MEditorToolbarArrow::ArrowDown:
        arrow->setPos(x, contentsRectangle.bottom() - arrow->size().height());
        break;
    }

    // Arrow has changed position, update widget origin.
    updateWidgetOrigin();
}

void MEditorToolbar::updateWidgetOrigin()
{
    // We include margin to arrow tip position.
    QPointF arrowTip(arrow->size().width() / 2.0f, 0);
    // We need to round to an integer coordinate to avoid graphics glitches; if
    // widgetOrigin.x() is for example 75.5, in portrait mode with German language with
    // Cut, Copy & Paste buttons visible the one pixel thick button separator lines cannot
    // be seen.
    const QPoint widgetOrigin(QPointF(mapFromItem(arrow, arrowTip).x(),
                                      arrow->direction() == MEditorToolbarArrow::ArrowUp
                                      ? 0.0f : size().height()).toPoint());

    setTransform(QTransform::fromTranslate(-widgetOrigin.x(),
                                           -widgetOrigin.y()));
}

bool MEditorToolbar::event(QEvent *event)
{
    switch (event->type()) {
    case QEvent::ActionAdded:
    {
        QActionEvent *actionEvent(static_cast<QActionEvent *>(event));
        Q_ASSERT(actionEvent);
        Q_ASSERT(!actionEvent->before()); // we support appending only
        QAction *action(qobject_cast<QAction *>(actionEvent->action()));
        Q_ASSERT(action);

        buttons << new MButton(action->text());
        QObject::connect(buttons.back(), SIGNAL(clicked(bool)), action, SLOT(trigger()));
        buttons.back()->setStyleName(action->objectName());
        if (action->isVisible()) {
            visibilityUpdated();
        }
        break;
    }
    case QEvent::ActionRemoved:
    {
        QActionEvent *actionEvent(static_cast<QActionEvent *>(event));
        const int actionIndex = actions().indexOf(actionEvent->action());
        if (actionIndex >= 0) {
            // Actions list is in sync with buttons list so we can
            // use the same index to delete the corresponding button.
            Q_ASSERT(actionIndex < buttons.count());
            delete buttons.at(actionIndex);
            buttons.removeAt(actionIndex);
        }

        if (actionEvent->action()->isVisible()) {
            // Action was visible before removal, need to update widget.
            visibilityUpdated();
        }
        break;
    }
    case QEvent::ActionChanged:
    {
        QActionEvent *actionEvent(static_cast<QActionEvent *>(event));

        // Name of action might have been changed.
        const int actionIndex = actions().indexOf(actionEvent->action());
        Q_ASSERT(actionIndex >= 0 && actionIndex < buttons.count());
        MButton *button(buttons.at(actionIndex));
        if (button->text() != actionEvent->action()->text()) {
            button->setText(actionEvent->action()->text());
        }

        // Update visibility of buttons to match visibility of actions.
        visibilityUpdated();
        break;
    }
    default:
        return MStylableWidget::event(event);
        break;
    }

    event->accept();
    return true;
}

void MEditorToolbar::mousePressEvent(QGraphicsSceneMouseEvent *)
{
    // Stop mouse event propagation.
}

void MEditorToolbar::updateAvailableButtons()
{
    buttonUpdateQueued = false;

    while (buttonLayoutPolicy->count() > 0) {
        buttonLayoutPolicy->removeAt(buttonLayoutPolicy->count() - 1);
    }

    QList<QAction *> actionList(actions());
    Q_ASSERT(actionList.count() == buttons.count());

    for (int i = 0; i < buttons.count(); ++i) {
        MButton *button = buttons.at(i);

        if (actionList.at(i)->isCheckable()) {
            button->setCheckable(true);
            button->setChecked(actionList.at(i)->isChecked());
        }

        if (actionList.at(i)->isVisible()) {
            buttonLayoutPolicy->addItem(button);
            button->show();
        } else {
            button->hide();
        }
    }

    // Resize manually since this widget is not managed by layout.
    resize(preferredSize());

    // Hide if there is no buttons.
    updateEditorItemVisibility();
}

void MEditorToolbar::visibilityUpdated()
{
    if (!buttonUpdateQueued) {
        buttonUpdateQueued = true;
        QMetaObject::invokeMethod(this, "updateAvailableButtons", Qt::QueuedConnection);
    }
}

void MEditorToolbar::updateGeometry()
{
    MStylableWidget::updateGeometry();
}

QSizeF MEditorToolbar::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    QSizeF hint;
    switch (which) {
    case Qt::MinimumSize:
    case Qt::PreferredSize: {
            qreal width = 0;
            qreal height = 0;
            for (int i = 0; i < buttonLayoutPolicy->count(); ++i) {
                QSizeF buttonHint = buttonLayoutPolicy->itemAt(i)->effectiveSizeHint(Qt::PreferredSize);
                width += buttonHint.width();
                height = qMax<qreal>(height, buttonHint.height());
            }
            width += style()->marginLeft() + style()->marginRight();
            height += style()->marginTop() + style()->marginBottom();
            hint.setWidth(width);
            hint.setHeight(height);
        }
        break;
    case Qt::MaximumSize:
        hint = QSizeF(sceneManager() ? sceneManager()->visibleSceneSize().width() : QWIDGETSIZE_MAX,
                      QWIDGETSIZE_MAX);
        if (constraint.width() > 0.0f) {
            hint.setWidth(constraint.width());
        } else {
            hint.setWidth(QWIDGETSIZE_MAX);
        }
        if (constraint.height() > 0.0f) {
            hint.setHeight(constraint.height());
        } else {
            hint.setHeight(QWIDGETSIZE_MAX);
        }
        break;
    default:
        break;
    }

    return hint;
}

void MEditorToolbar::resizeEvent(QGraphicsSceneResizeEvent *)
{
    emit sizeChanged();
}

void MEditorToolbar::updateEditorItemVisibility()
{
    // Visibility of editor item is determined solely by existence of buttons.
    if (buttonLayoutPolicy->count() > 0) {
        showEditorItem();
    } else {
        hideEditorItem();
    }
}

void MEditorToolbar::showEditorItem()
{
    // Set focus proxy so that input widget doesn't lose focus.
    setFocusPolicy(Qt::ClickFocus);
    setFocusProxy(&const_cast<MWidget &>(followWidget));
    show();
}

void MEditorToolbar::hideEditorItem()
{
    setFocusProxy(0);
    setFocusPolicy(Qt::NoFocus);
    hide();
}
<commit_msg>Fixes: NB#266547, Rich Text bubble cut when placed in empty composer<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 "meditortoolbar.h"
#include "mtopleveloverlay.h"

#include <MButton>
#include <MLayout>
#include <MLinearLayoutPolicy>
#include <MSceneManager>
#include <MWidget>
#include <QAction>

#include <QtAlgorithms>
#include <QApplication>
#include <QGraphicsLinearLayout>

MEditorToolbar::MEditorToolbar(const MWidget &followWidget)
    : overlay(new MTopLevelOverlay(followWidget.sceneManager())),
      followWidget(followWidget),
      buttonLayoutPolicy(new MLinearLayoutPolicy(new MLayout(this),
                                                 Qt::Horizontal)),
      arrow(new MEditorToolbarArrow(this)),
      buttonUpdateQueued(false),
      temporarilyDisappeared(false)
{
    setFlag(QGraphicsItem::ItemHasNoContents, true);
    overlay->hide();
    hide();
    followWidget.scene()->addItem(overlay);
    setParentItem(overlay);

    // Set z value for arrow higher than default since
    // it needs to float on top of buttons.
    arrow->setZValue(1.0f);

    // The policy notifies the widgets of their relative position inside the layout,
    // this causes the buttons to be rendered with different backgrounds for each position
    buttonLayoutPolicy->setNotifyWidgetsOfLayoutPositionEnabled(true);

    // Don't add extra margins or spacings for buttons.
    buttonLayoutPolicy->setContentsMargins(0.0f, 0.0f, 0.0f, 0.0f);
    buttonLayoutPolicy->setSpacing(0.0f);

    QObject::connect(sceneManager(), SIGNAL(orientationChanged(M::Orientation)),
                     this, SLOT(updateGeometry()));

    updateArrow(MEditorToolbarArrow::ArrowDown);
}

MEditorToolbar::~MEditorToolbar()
{
    hideEditorItem();
    // Before destroying parent, detach so it doesn't try to destroy us.
    setParentItem(0);
    if (overlay) {
        overlay->deleteLater();
    }
    qDeleteAll(buttons);
}

void MEditorToolbar::setPosition(const QPointF &pos,
                                 MEditorToolbarArrow::ArrowDirection direction)
{
    setPos(followWidget.mapToItem(overlay, pos));
    updateArrow(direction);
}

void MEditorToolbar::appear()
{
    overlay->show();
    updateEditorItemVisibility();
    temporarilyDisappeared = false;
}

void MEditorToolbar::disappear()
{
    hideEditorItem();
    overlay->hide();
    temporarilyDisappeared = false;
}

void MEditorToolbar::disappearTemporarily()
{
    if (isAppeared()) {
        disappear();
        temporarilyDisappeared = true;
    }
}

void MEditorToolbar::removeTemporaryDisappearance()
{
    if (temporarilyDisappeared) {
        appear();
    }
}

bool MEditorToolbar::isAppeared() const
{
    return overlay->isVisible();
}

void MEditorToolbar::updateArrow(MEditorToolbarArrow::ArrowDirection direction)
{
    // Clear local transforms.
    setTransform(QTransform());

    // Style mode is different with regarding to top and bottom margins.
    if (direction == MEditorToolbarArrow::ArrowUp) {
        style().setModeEditorUnderCursor();
    } else {
        style().setModeDefault();
    }
    MStylableWidget::applyStyle();

    const QRectF contentsRectangle = contentsRect();

    // Update horizontally, make sure widget is inside screen.
    qreal center = contentsRectangle.center().x();
    QRectF mappedSceneRect = mapRectFromScene(
        QRectF(QPointF(), sceneManager()->visibleSceneSize(M::Landscape)));
    mappedSceneRect.translate(center, 0.0f);

    qreal offscreenLeft = qMax<qreal>(0.0f, mappedSceneRect.left());
    qreal offscreenRight = qMax<qreal>(0.0f, (effectiveSizeHint(Qt::PreferredSize).width()
                                              - mappedSceneRect.right()));
    // Screen rectangle in overlay coordinate system, just like we are
    const QRectF screenRectInOverlay(
        overlay->mapRectFromScene(QRectF(QPointF(), sceneManager()->visibleSceneSize(M::Landscape))));
    qreal x;

    if (size().width() < screenRectInOverlay.width()) {
        // The widget won't be off the screen from both ends at the same time.
        // Width is restricted to screen width.
        x = center - arrow->size().width() / 2.0f
            - offscreenLeft + offscreenRight;
        x = qBound<qreal>(contentsRectangle.left(),
                          x,
                          contentsRectangle.right() - arrow->size().width());
    } else {
        x = geometry().center().x() - screenRectInOverlay.center().x() - arrow->size().width() / 2.0f;
    }

    // Update vertically. Arrow graphics are designed to be aligned to either
    // top or bottom of buttons, completely overlapping them.
    arrow->setDirection(direction);

    switch (arrow->direction()) {
    case MEditorToolbarArrow::ArrowUp:
        arrow->setPos(x, contentsRectangle.top());
        break;
    case MEditorToolbarArrow::ArrowDown:
        arrow->setPos(x, contentsRectangle.bottom() - arrow->size().height());
        break;
    }

    // Arrow has changed position, update widget origin.
    updateWidgetOrigin();
}

void MEditorToolbar::updateWidgetOrigin()
{
    // We include margin to arrow tip position.
    QPointF arrowTip(arrow->size().width() / 2.0f, 0);
    arrowTip = mapFromItem(arrow, arrowTip);

    qreal translateX = arrowTip.x();

    const QRectF screenRectInOverlay(
        overlay->mapRectFromScene(QRectF(QPointF(), sceneManager()->visibleSceneSize(M::Landscape))));

    // Avoid editor toolbar clipping when possible
    if (size().width() < screenRectInOverlay.width()) {
        if (pos().x() < (screenRectInOverlay.width() - size().width())) {
            // Don't allow editor toolbar to go over the left edge of the screen
            translateX = qMin(translateX, pos().x());
        } else {
            // Don't allow editor toolbar to go over the right edge of the screen
            translateX = qMax(translateX, size().width() + pos().x() - screenRectInOverlay.width());
        }
    }

    // We need to round to an integer coordinate to avoid graphics glitches; if
    // widgetOrigin.x() is for example 75.5, in portrait mode with German language with
    // Cut, Copy & Paste buttons visible the one pixel thick button separator lines cannot
    // be seen.
    const QPoint widgetOrigin(QPointF(translateX,
                                      arrow->direction() == MEditorToolbarArrow::ArrowUp
                                      ? 0.0f : size().height()).toPoint());

    setTransform(QTransform::fromTranslate(-widgetOrigin.x(),
                                           -widgetOrigin.y()));
}

bool MEditorToolbar::event(QEvent *event)
{
    switch (event->type()) {
    case QEvent::ActionAdded:
    {
        QActionEvent *actionEvent(static_cast<QActionEvent *>(event));
        Q_ASSERT(actionEvent);
        Q_ASSERT(!actionEvent->before()); // we support appending only
        QAction *action(qobject_cast<QAction *>(actionEvent->action()));
        Q_ASSERT(action);

        buttons << new MButton(action->text());
        QObject::connect(buttons.back(), SIGNAL(clicked(bool)), action, SLOT(trigger()));
        buttons.back()->setStyleName(action->objectName());
        if (action->isVisible()) {
            visibilityUpdated();
        }
        break;
    }
    case QEvent::ActionRemoved:
    {
        QActionEvent *actionEvent(static_cast<QActionEvent *>(event));
        const int actionIndex = actions().indexOf(actionEvent->action());
        if (actionIndex >= 0) {
            // Actions list is in sync with buttons list so we can
            // use the same index to delete the corresponding button.
            Q_ASSERT(actionIndex < buttons.count());
            delete buttons.at(actionIndex);
            buttons.removeAt(actionIndex);
        }

        if (actionEvent->action()->isVisible()) {
            // Action was visible before removal, need to update widget.
            visibilityUpdated();
        }
        break;
    }
    case QEvent::ActionChanged:
    {
        QActionEvent *actionEvent(static_cast<QActionEvent *>(event));

        // Name of action might have been changed.
        const int actionIndex = actions().indexOf(actionEvent->action());
        Q_ASSERT(actionIndex >= 0 && actionIndex < buttons.count());
        MButton *button(buttons.at(actionIndex));
        if (button->text() != actionEvent->action()->text()) {
            button->setText(actionEvent->action()->text());
        }

        // Update visibility of buttons to match visibility of actions.
        visibilityUpdated();
        break;
    }
    default:
        return MStylableWidget::event(event);
        break;
    }

    event->accept();
    return true;
}

void MEditorToolbar::mousePressEvent(QGraphicsSceneMouseEvent *)
{
    // Stop mouse event propagation.
}

void MEditorToolbar::updateAvailableButtons()
{
    buttonUpdateQueued = false;

    while (buttonLayoutPolicy->count() > 0) {
        buttonLayoutPolicy->removeAt(buttonLayoutPolicy->count() - 1);
    }

    QList<QAction *> actionList(actions());
    Q_ASSERT(actionList.count() == buttons.count());

    for (int i = 0; i < buttons.count(); ++i) {
        MButton *button = buttons.at(i);

        if (actionList.at(i)->isCheckable()) {
            button->setCheckable(true);
            button->setChecked(actionList.at(i)->isChecked());
        }

        if (actionList.at(i)->isVisible()) {
            buttonLayoutPolicy->addItem(button);
            button->show();
        } else {
            button->hide();
        }
    }

    // Resize manually since this widget is not managed by layout.
    resize(preferredSize());

    // Hide if there is no buttons.
    updateEditorItemVisibility();
}

void MEditorToolbar::visibilityUpdated()
{
    if (!buttonUpdateQueued) {
        buttonUpdateQueued = true;
        QMetaObject::invokeMethod(this, "updateAvailableButtons", Qt::QueuedConnection);
    }
}

void MEditorToolbar::updateGeometry()
{
    MStylableWidget::updateGeometry();
}

QSizeF MEditorToolbar::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
    QSizeF hint;
    switch (which) {
    case Qt::MinimumSize:
    case Qt::PreferredSize: {
            qreal width = 0;
            qreal height = 0;
            for (int i = 0; i < buttonLayoutPolicy->count(); ++i) {
                QSizeF buttonHint = buttonLayoutPolicy->itemAt(i)->effectiveSizeHint(Qt::PreferredSize);
                width += buttonHint.width();
                height = qMax<qreal>(height, buttonHint.height());
            }
            width += style()->marginLeft() + style()->marginRight();
            height += style()->marginTop() + style()->marginBottom();
            hint.setWidth(width);
            hint.setHeight(height);
        }
        break;
    case Qt::MaximumSize:
        hint = QSizeF(sceneManager() ? sceneManager()->visibleSceneSize().width() : QWIDGETSIZE_MAX,
                      QWIDGETSIZE_MAX);
        if (constraint.width() > 0.0f) {
            hint.setWidth(constraint.width());
        } else {
            hint.setWidth(QWIDGETSIZE_MAX);
        }
        if (constraint.height() > 0.0f) {
            hint.setHeight(constraint.height());
        } else {
            hint.setHeight(QWIDGETSIZE_MAX);
        }
        break;
    default:
        break;
    }

    return hint;
}

void MEditorToolbar::resizeEvent(QGraphicsSceneResizeEvent *)
{
    emit sizeChanged();
}

void MEditorToolbar::updateEditorItemVisibility()
{
    // Visibility of editor item is determined solely by existence of buttons.
    if (buttonLayoutPolicy->count() > 0) {
        showEditorItem();
    } else {
        hideEditorItem();
    }
}

void MEditorToolbar::showEditorItem()
{
    // Set focus proxy so that input widget doesn't lose focus.
    setFocusPolicy(Qt::ClickFocus);
    setFocusProxy(&const_cast<MWidget &>(followWidget));
    show();
}

void MEditorToolbar::hideEditorItem()
{
    setFocusProxy(0);
    setFocusPolicy(Qt::NoFocus);
    hide();
}
<|endoftext|>


<commit_before>#include "vlc_video_source.h"
#include <cstdio>
#include <cstdlib>
#include <chrono>
#include <thread>

namespace gg
{

VideoSourceVLC::VideoSourceVLC(const std::string path)
    : _vlc_inst(nullptr)
    , _vlc_mp(nullptr)
    , _video_buffer(nullptr)
    , _data_length(0)
    , _cols(0)
    , _rows(0)
    , _sub(nullptr)
    , _path(path)
{
    this->init_vlc();
    this->run_vlc();
}

VideoSourceVLC::~VideoSourceVLC()
{
    stop_vlc();
    release_vlc();
}

bool VideoSourceVLC::get_frame_dimensions(int & width, int & height)
{
    ///\todo mutex

    if(this->_cols == 0 || this->_rows == 0)
        return false;

    width = this->_cols;
    height = this->_rows;
    return true;
}

bool VideoSourceVLC::get_frame(VideoFrame_BGRA & frame)
{
    throw VideoSourceError("VLC video source supports only I420 colour space");

    // TODO

    ///\todo mutex

    if (_cols == 0 || _rows == 0 || _video_buffer == nullptr)
        return false;

    // allocate and fill the image
    if (_cols * _rows * 4 == this->_data_length)
        frame = VideoFrame_BGRA(this->_video_buffer, this->_cols, this->_rows);
    else
        throw VideoSourceError("VLC video source does not support padded images");

    return true;
}

bool VideoSourceVLC::get_frame(VideoFrame_I420 & frame)
{
    if (_data_length > 0)
    {
        // TODO manage data?
        frame = VideoFrame_I420(_video_buffer, _data_length, _cols, _rows, false);
        return true;
    }
    else
        return false;

    // TODO #86
}

double VideoSourceVLC::get_frame_rate()
{
    //return libvlc_media_player_get_rate( m_mp );
    return libvlc_media_player_get_fps(_vlc_mp);
    //throw std::runtime_error("get_frame_rate to be implemented");
    //return 0.0;
}

void VideoSourceVLC::set_sub_frame(int x, int y, int width, int height)
{
    // TODO mutex?

    if (x >= _full.x and x + width <= _full.x + _full.width and
        y >= _full.y and y + height <= _full.y + _full.height)
    {
        stop_vlc();
        release_vlc();
        if (_sub == nullptr) _sub = new FrameBox;
        _sub->x = x;
        _sub->y = y;
        _sub->width = width;
        _sub->height = height;
        init_vlc();
        run_vlc();
    }
}

void VideoSourceVLC::get_full_frame()
{
    // TODO mutex?
    stop_vlc();
    release_vlc();
    if (_sub)
    {
        delete _sub;
        _sub = nullptr;
    }
    init_vlc();
    run_vlc();
}

void VideoSourceVLC::init_vlc()
{
    // VLC pointers
    libvlc_media_t * vlc_media = nullptr;

    // VLC options
    char smem_options[512];

    sprintf(smem_options, "#");
    if (_sub != nullptr)
    {
        unsigned int croptop = _sub->y,
                     cropbottom = _full.height - (_sub->y + _sub->height),
                     cropleft = _sub->x,
                     cropright = _full.width - (_sub->x + _sub->width);
        sprintf(smem_options,
                "%stranscode{vcodec=I420,vfilter=croppadd{",
                smem_options);
        sprintf(smem_options,
                "%scroptop=%u,cropbottom=%u,cropleft=%u,cropright=%u}}:",
                smem_options,
                croptop, cropbottom, cropleft, cropright);
    }
    sprintf(smem_options,
            "%ssmem{video-data=%lld,video-prerender-callback=%lld,video-postrender-callback=%lld}",
            smem_options,
            (long long int)(intptr_t)(void*) this,
            (long long int)(intptr_t)(void*) &VideoSourceVLC::prepareRender,
            (long long int)(intptr_t)(void*) &VideoSourceVLC::handleStream );

    const char * const vlc_args[] = {
        "-I", "dummy", // Don't use any interface
        "--ignore-config", // Don't use VLC's config
        "--extraintf=logger", // Log anything
        // TODO - what about the options below?
        //"--verbose=2", // Be much more verbose then normal for debugging purpose
        //"--clock-jitter=0",
        //"--file-caching=150",
        "--no-audio",
        "--sout", smem_options // Stream to memory
    };

    // We launch VLC
    _vlc_inst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
    if (_vlc_inst == nullptr)
        throw VideoSourceError("Could not create VLC engine");

    // If path contains a colon (:), it will be treated as a
    // URL. Else, it will be considered as a local path.
    if (_path.find(":") == std::string::npos)
        vlc_media = libvlc_media_new_path(_vlc_inst, _path.c_str());
    else
        vlc_media = libvlc_media_new_location(_vlc_inst, _path.c_str());
    if (vlc_media == nullptr)
        throw VideoSourceError(std::string("Could not open ").append(_path));

    libvlc_media_add_option(vlc_media, ":noaudio");
    libvlc_media_add_option(vlc_media, ":no-video-title-show");

    // Create a media player playing environement
    _vlc_mp = libvlc_media_player_new_from_media(vlc_media);
    if (_vlc_mp == nullptr)
        throw VideoSourceError("Could create VLC media player");

    // No need to keep the media now
    libvlc_media_release( vlc_media );
}

void VideoSourceVLC::run_vlc()
{
    // play the media_player
    if (libvlc_media_player_play(_vlc_mp) != 0)
        throw VideoSourceError("Could not start VLC media player");

    // empirically determined value that allows for initialisation
    // to succeed before any API functions are called on this object
    std::this_thread::sleep_for(std::chrono::milliseconds(350));
    unsigned int width, height;
    if (libvlc_video_get_size(_vlc_mp, 0, &width, &height) != 0)
        throw VideoSourceError("Could not get video dimensions");

    _full.x = 0;
    _full.y = 0;
    _full.width = width;
    _full.height = height;
}

void VideoSourceVLC::stop_vlc()
{
    if (libvlc_media_player_is_playing(_vlc_mp) == 1)
        // stop playing
        libvlc_media_player_stop(_vlc_mp);
}

void VideoSourceVLC::release_vlc()
{
    // free media player
    if (_vlc_mp)
        libvlc_media_player_release(_vlc_mp);

    // free engine
    if (_vlc_inst)
        libvlc_release(_vlc_inst);

    // free buffer
    if (_video_buffer != nullptr)
        delete[] _video_buffer;
}

void VideoSourceVLC::prepareRender(VideoSourceVLC * p_video_data,
                                   uint8_t ** pp_pixel_buffer,
                                   size_t size)
{
    ///\todo create mutex guard

    if (size != p_video_data->_data_length)
    {
        unsigned int width,height;
        if (libvlc_video_get_size(p_video_data->_vlc_mp, 0, &width, &height) != 0)
            return;

        // TODO deallocate previous data?
        p_video_data->_video_buffer = new uint8_t[size];
        p_video_data->_data_length = size;
        p_video_data->_cols = width;
        p_video_data->_rows = height;
    }

    *pp_pixel_buffer = p_video_data->_video_buffer;
}

void VideoSourceVLC::handleStream(VideoSourceVLC * p_video_data,
                                  uint8_t * p_pixel_buffer,
                                  size_t cols,
                                  size_t rows,
                                  size_t colour_depth,
                                  size_t size)
{
    // TODO: explain how data should be handled (see #86)
    // TODO: Unlock the mutex
}

std::string VideoSourceVLC::encode_psz_geometry(int x, int y, int width, int height)
{
    std::string psz_geometry;
    psz_geometry.append(std::to_string(width)).append("x")
                .append(std::to_string(height))
                .append("+").append(std::to_string(x))
                .append("+").append(std::to_string(y));
    return psz_geometry;
}

}
<commit_msg>Issue #83: using realloc instead of new when size of new data larger than existing allocation, also width and height assigned on their own to prevent potential issue where e.g. two combinations of different sizes lead to same data length<commit_after>#include "vlc_video_source.h"
#include <cstdio>
#include <cstdlib>
#include <chrono>
#include <thread>

namespace gg
{

VideoSourceVLC::VideoSourceVLC(const std::string path)
    : _vlc_inst(nullptr)
    , _vlc_mp(nullptr)
    , _video_buffer(nullptr)
    , _data_length(0)
    , _cols(0)
    , _rows(0)
    , _sub(nullptr)
    , _path(path)
{
    this->init_vlc();
    this->run_vlc();
}

VideoSourceVLC::~VideoSourceVLC()
{
    stop_vlc();
    release_vlc();
}

bool VideoSourceVLC::get_frame_dimensions(int & width, int & height)
{
    ///\todo mutex

    if(this->_cols == 0 || this->_rows == 0)
        return false;

    width = this->_cols;
    height = this->_rows;
    return true;
}

bool VideoSourceVLC::get_frame(VideoFrame_BGRA & frame)
{
    throw VideoSourceError("VLC video source supports only I420 colour space");

    // TODO

    ///\todo mutex

    if (_cols == 0 || _rows == 0 || _video_buffer == nullptr)
        return false;

    // allocate and fill the image
    if (_cols * _rows * 4 == this->_data_length)
        frame = VideoFrame_BGRA(this->_video_buffer, this->_cols, this->_rows);
    else
        throw VideoSourceError("VLC video source does not support padded images");

    return true;
}

bool VideoSourceVLC::get_frame(VideoFrame_I420 & frame)
{
    if (_data_length > 0)
    {
        // TODO manage data?
        frame = VideoFrame_I420(_video_buffer, _data_length, _cols, _rows, false);
        return true;
    }
    else
        return false;

    // TODO #86
}

double VideoSourceVLC::get_frame_rate()
{
    //return libvlc_media_player_get_rate( m_mp );
    return libvlc_media_player_get_fps(_vlc_mp);
    //throw std::runtime_error("get_frame_rate to be implemented");
    //return 0.0;
}

void VideoSourceVLC::set_sub_frame(int x, int y, int width, int height)
{
    // TODO mutex?

    if (x >= _full.x and x + width <= _full.x + _full.width and
        y >= _full.y and y + height <= _full.y + _full.height)
    {
        stop_vlc();
        release_vlc();
        if (_sub == nullptr) _sub = new FrameBox;
        _sub->x = x;
        _sub->y = y;
        _sub->width = width;
        _sub->height = height;
        init_vlc();
        run_vlc();
    }
}

void VideoSourceVLC::get_full_frame()
{
    // TODO mutex?
    stop_vlc();
    release_vlc();
    if (_sub)
    {
        delete _sub;
        _sub = nullptr;
    }
    init_vlc();
    run_vlc();
}

void VideoSourceVLC::init_vlc()
{
    // VLC pointers
    libvlc_media_t * vlc_media = nullptr;

    // VLC options
    char smem_options[512];

    sprintf(smem_options, "#");
    if (_sub != nullptr)
    {
        unsigned int croptop = _sub->y,
                     cropbottom = _full.height - (_sub->y + _sub->height),
                     cropleft = _sub->x,
                     cropright = _full.width - (_sub->x + _sub->width);
        sprintf(smem_options,
                "%stranscode{vcodec=I420,vfilter=croppadd{",
                smem_options);
        sprintf(smem_options,
                "%scroptop=%u,cropbottom=%u,cropleft=%u,cropright=%u}}:",
                smem_options,
                croptop, cropbottom, cropleft, cropright);
    }
    sprintf(smem_options,
            "%ssmem{video-data=%lld,video-prerender-callback=%lld,video-postrender-callback=%lld}",
            smem_options,
            (long long int)(intptr_t)(void*) this,
            (long long int)(intptr_t)(void*) &VideoSourceVLC::prepareRender,
            (long long int)(intptr_t)(void*) &VideoSourceVLC::handleStream );

    const char * const vlc_args[] = {
        "-I", "dummy", // Don't use any interface
        "--ignore-config", // Don't use VLC's config
        "--extraintf=logger", // Log anything
        // TODO - what about the options below?
        //"--verbose=2", // Be much more verbose then normal for debugging purpose
        //"--clock-jitter=0",
        //"--file-caching=150",
        "--no-audio",
        "--sout", smem_options // Stream to memory
    };

    // We launch VLC
    _vlc_inst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
    if (_vlc_inst == nullptr)
        throw VideoSourceError("Could not create VLC engine");

    // If path contains a colon (:), it will be treated as a
    // URL. Else, it will be considered as a local path.
    if (_path.find(":") == std::string::npos)
        vlc_media = libvlc_media_new_path(_vlc_inst, _path.c_str());
    else
        vlc_media = libvlc_media_new_location(_vlc_inst, _path.c_str());
    if (vlc_media == nullptr)
        throw VideoSourceError(std::string("Could not open ").append(_path));

    libvlc_media_add_option(vlc_media, ":noaudio");
    libvlc_media_add_option(vlc_media, ":no-video-title-show");

    // Create a media player playing environement
    _vlc_mp = libvlc_media_player_new_from_media(vlc_media);
    if (_vlc_mp == nullptr)
        throw VideoSourceError("Could create VLC media player");

    // No need to keep the media now
    libvlc_media_release( vlc_media );
}

void VideoSourceVLC::run_vlc()
{
    // play the media_player
    if (libvlc_media_player_play(_vlc_mp) != 0)
        throw VideoSourceError("Could not start VLC media player");

    // empirically determined value that allows for initialisation
    // to succeed before any API functions are called on this object
    std::this_thread::sleep_for(std::chrono::milliseconds(350));
    unsigned int width, height;
    if (libvlc_video_get_size(_vlc_mp, 0, &width, &height) != 0)
        throw VideoSourceError("Could not get video dimensions");

    _full.x = 0;
    _full.y = 0;
    _full.width = width;
    _full.height = height;
}

void VideoSourceVLC::stop_vlc()
{
    if (libvlc_media_player_is_playing(_vlc_mp) == 1)
        // stop playing
        libvlc_media_player_stop(_vlc_mp);
}

void VideoSourceVLC::release_vlc()
{
    // free media player
    if (_vlc_mp)
        libvlc_media_player_release(_vlc_mp);

    // free engine
    if (_vlc_inst)
        libvlc_release(_vlc_inst);

    // free buffer
    if (_video_buffer != nullptr)
        delete[] _video_buffer;
}

void VideoSourceVLC::prepareRender(VideoSourceVLC * p_video_data,
                                   uint8_t ** pp_pixel_buffer,
                                   size_t size)
{
    ///\todo create mutex guard

    unsigned int width, height;
    if (libvlc_video_get_size(p_video_data->_vlc_mp, 0, &width, &height) != 0)
        return;
    p_video_data->_cols = width;
    p_video_data->_rows = height;

    if (size > p_video_data->_data_length)
        p_video_data->_video_buffer = realloc(p_video_data->_video_buffer, size * sizeof(uint8_t));
    p_video_data->_data_length = size;

    *pp_pixel_buffer = p_video_data->_video_buffer;
}

void VideoSourceVLC::handleStream(VideoSourceVLC * p_video_data,
                                  uint8_t * p_pixel_buffer,
                                  size_t cols,
                                  size_t rows,
                                  size_t colour_depth,
                                  size_t size)
{
    // TODO: explain how data should be handled (see #86)
    // TODO: Unlock the mutex
}

std::string VideoSourceVLC::encode_psz_geometry(int x, int y, int width, int height)
{
    std::string psz_geometry;
    psz_geometry.append(std::to_string(width)).append("x")
                .append(std::to_string(height))
                .append("+").append(std::to_string(x))
                .append("+").append(std::to_string(y));
    return psz_geometry;
}

}
<|endoftext|>


<commit_before><commit_msg>Adding code with dynamic programming for color trees with all comments<commit_after><|endoftext|>


<commit_before>// Copyright (c) 2017-2019 The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
// 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.
//
// Author: Mark Young <marky@lunarg.com>
//

#pragma once

#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>

#include <openxr/openxr.h>

// Use internal versions of flags similar to XR_EXT_debug_utils so that
// we're not tightly coupled to that extension.  This way, if the extension
// changes or gets replaced, we can be flexible in the loader.
#define XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT 0x00000001
#define XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT 0x00000010
#define XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT 0x00000100
#define XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT 0x00001000
typedef XrFlags64 XrLoaderLogMessageSeverityFlagBits;
typedef XrFlags64 XrLoaderLogMessageSeverityFlags;

#define XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT 0x00000001
#define XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT 0x00000002
#define XR_LOADER_LOG_MESSAGE_TYPE_PERFORMANCE_BIT 0x00000004
typedef XrFlags64 XrLoaderLogMessageTypeFlagBits;
typedef XrFlags64 XrLoaderLogMessageTypeFlags;

//! Turns a uint64_t into a string formatted as hex.
//!
//! The core of the HandleToString implementation is in here.
std::string Uint64ToHexString(uint64_t val);

template <typename T>
static inline std::string HandleToString(T handle) {
    return Uint64ToHexString(reinterpret_cast<uint64_t>(handle));
}

struct XrLoaderLogObjectInfo {
    //! Type-erased handle value
    uint64_t handle;

    //! Kind of object this handle refers to
    XrObjectType type;

    //! To be assigned by the application - not part of this object's identity
    std::string name;

    /// Un-erase the type of the handle and get it properly typed again.
    ///
    /// Note: Does not check the type before doing it!
    template <typename HandleType>
    HandleType& GetTypedHandle() {
        return reinterpret_cast<HandleType&>(handle);
    }

    //! @overload
    template <typename HandleType>
    HandleType const& GetTypedHandle() const {
        return reinterpret_cast<HandleType&>(handle);
    }

    XrLoaderLogObjectInfo() = default;

    template <typename T>
    XrLoaderLogObjectInfo(T h, XrObjectType t) : handle(reinterpret_cast<uint64_t>(h)), type(t) {}

    std::string ToString() const;
};

//! True if the two object infos have the same handle value and handle type
static inline bool Equivalent(XrLoaderLogObjectInfo const& a, XrLoaderLogObjectInfo const& b) {
    return a.handle == b.handle && a.type == b.type;
}
//! @overload
static inline bool Equivalent(XrDebugUtilsObjectNameInfoEXT const& a, XrLoaderLogObjectInfo const& b) {
    return a.objectHandle == b.handle && a.objectType == b.type;
}
//! @overload
static inline bool Equivalent(XrLoaderLogObjectInfo const& a, XrDebugUtilsObjectNameInfoEXT const& b) { return Equivalent(b, a); }

struct XrLoaderLogMessengerCallbackData {
    const char* message_id;
    const char* command_name;
    const char* message;
    uint8_t object_count;
    XrLoaderLogObjectInfo* objects;
    uint8_t session_labels_count;
    XrDebugUtilsLabelEXT* session_labels;
};

enum XrLoaderLogType {
    XR_LOADER_LOG_UNKNOWN = 0,
    XR_LOADER_LOG_STDERR,
    XR_LOADER_LOG_STDOUT,
    XR_LOADER_LOG_DEBUG_UTILS,
};

class LoaderLogRecorder {
   public:
    LoaderLogRecorder(XrLoaderLogType type, void* user_data, XrLoaderLogMessageSeverityFlags message_severities,
                      XrLoaderLogMessageTypeFlags message_types) {
        _active = false;
        _user_data = user_data;
        _type = type;
        _unique_id = 0;
        _message_severities = message_severities;
        _message_types = message_types;
    }
    virtual ~LoaderLogRecorder() {}

    XrLoaderLogType Type() { return _type; }
    uint64_t UniqueId() { return _unique_id; }
    XrLoaderLogMessageSeverityFlags MessageSeverities() { return _message_severities; }
    XrLoaderLogMessageTypeFlags MessageTypes() { return _message_types; }

    virtual void Start() { _active = true; }
    bool IsPaused() { return _active; }
    virtual void Pause() { _active = false; }
    virtual void Resume() { _active = true; }
    virtual void Stop() { _active = false; }

    virtual bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
                            const XrLoaderLogMessengerCallbackData* callback_data) = 0;

   protected:
    bool _active;
    XrLoaderLogType _type;
    uint64_t _unique_id;
    void* _user_data;
    XrLoaderLogMessageSeverityFlags _message_severities;
    XrLoaderLogMessageTypeFlags _message_types;
};

// Standard Error logger, always on for now
class StdErrLoaderLogRecorder : public LoaderLogRecorder {
   public:
    StdErrLoaderLogRecorder(void* user_data);
    ~StdErrLoaderLogRecorder() {}

    virtual bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
                            const XrLoaderLogMessengerCallbackData* callback_data);
};

// Standard Output logger used with XR_LOADER_DEBUG
class StdOutLoaderLogRecorder : public LoaderLogRecorder {
   public:
    StdOutLoaderLogRecorder(void* user_data, XrLoaderLogMessageSeverityFlags flags);
    ~StdOutLoaderLogRecorder() {}

    virtual bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
                            const XrLoaderLogMessengerCallbackData* callback_data);
};

// Debug Utils logger used with XR_EXT_debug_utils
class DebugUtilsLogRecorder : public LoaderLogRecorder {
   public:
    DebugUtilsLogRecorder(const XrDebugUtilsMessengerCreateInfoEXT* create_info, XrDebugUtilsMessengerEXT debug_messenger);
    ~DebugUtilsLogRecorder() {}

    virtual bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
                            const XrLoaderLogMessengerCallbackData* callback_data);

    // Extension-specific logging functions
    bool LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity, XrDebugUtilsMessageTypeFlagsEXT message_type,
                              const XrDebugUtilsMessengerCallbackDataEXT* callback_data);

   private:
    PFN_xrDebugUtilsMessengerCallbackEXT _user_callback;
};

// TODO: Add other Derived classes:
//  - FileLoaderLogRecorder     - During/after xrCreateInstance
//  - PipeLoaderLogRecorder?    - During/after xrCreateInstance

class ObjectInfoCollection {
   public:
    //! Called from LoaderXrTermSetDebugUtilsObjectNameEXT - an empty name means remove
    void AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name);

    //! Find the stored object info, if any, matching handle and type.
    //! Return nullptr if not found.
    XrLoaderLogObjectInfo const* LookUpStoredObjectInfo(XrLoaderLogObjectInfo const& info) const;
    //! Find the stored object info, if any, matching handle and type.
    //! Return nullptr if not found.
    XrLoaderLogObjectInfo* LookUpStoredObjectInfo(XrLoaderLogObjectInfo const& info);

    //! Find the stored object info, if any.
    //! Return nullptr if not found.
    XrLoaderLogObjectInfo const* LookUpStoredObjectInfo(uint64_t handle, XrObjectType type) const {
        return LookUpStoredObjectInfo({handle, type});
    }

    //! Find the object name, if any, and update debug utils info accordingly.
    //! Return true if found and updated.
    bool LookUpObjectName(XrDebugUtilsObjectNameInfoEXT& info) const;

    //! Find the object name, if any, and update logging info accordingly.
    //! Return true if found and updated.
    bool LookUpObjectName(XrLoaderLogObjectInfo& info) const;

    //! Is the collection empty?
    bool Empty() const { return _object_info.empty(); }

   private:
    // Object names that have been set for given objects
    std::vector<XrLoaderLogObjectInfo> _object_info;
};
class LoaderLogger {
   public:
    static LoaderLogger& GetInstance() {
        std::call_once(LoaderLogger::_once_flag, []() { _instance.reset(new LoaderLogger); });
        return *(_instance.get());
    }

    void AddLogRecorder(std::unique_ptr<LoaderLogRecorder>& recorder);
    void RemoveLogRecorder(uint64_t unique_id);

    //! Called from LoaderXrTermSetDebugUtilsObjectNameEXT - an empty name means remove
    void AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name);
    void BeginLabelRegion(XrSession session, const XrDebugUtilsLabelEXT* label_info);
    void EndLabelRegion(XrSession session);
    void InsertLabel(XrSession session, const XrDebugUtilsLabelEXT* label_info);
    void DeleteSessionLabels(XrSession session);

    bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
                    const std::string& message_id, const std::string& command_name, const std::string& message,
                    const std::vector<XrLoaderLogObjectInfo>& objects = {});
    static bool LogErrorMessage(const std::string& command_name, const std::string& message,
                                const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
                                        "OpenXR-Loader", command_name, message, objects);
    }
    static bool LogWarningMessage(const std::string& command_name, const std::string& message,
                                  const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
                                        "OpenXR-Loader", command_name, message, objects);
    }
    static bool LogInfoMessage(const std::string& command_name, const std::string& message,
                               const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
                                        "OpenXR-Loader", command_name, message, objects);
    }
    static bool LogVerboseMessage(const std::string& command_name, const std::string& message,
                                  const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
                                        "OpenXR-Loader", command_name, message, objects);
    }
    static bool LogValidationErrorMessage(const std::string& vuid, const std::string& command_name, const std::string& message,
                                          const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT, XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT,
                                        vuid, command_name, message, objects);
    }
    static bool LogValidationWarningMessage(const std::string& vuid, const std::string& command_name, const std::string& message,
                                            const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT, XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT,
                                        vuid, command_name, message, objects);
    }

    // Extension-specific logging functions
    bool LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity, XrDebugUtilsMessageTypeFlagsEXT message_type,
                              const XrDebugUtilsMessengerCallbackDataEXT* callback_data);

   private:
    struct InternalSessionLabel {
        XrDebugUtilsLabelEXT debug_utils_label;
        std::string label_name;
        bool is_individual_label;
    };

    LoaderLogger();
    LoaderLogger(const LoaderLogger&) = delete;
    LoaderLogger& operator=(const LoaderLogger&) = delete;

    /// Retrieve labels for the given session, if any, and push them in reverse order on the vector.
    void LookUpSessionLabels(XrSession session, std::vector<XrDebugUtilsLabelEXT>& labels) const;

    void RemoveIndividualLabel(std::vector<InternalSessionLabel*>* label_vec);

    static std::unique_ptr<LoaderLogger> _instance;
    static std::once_flag _once_flag;

    // List of available recorder objects
    std::vector<std::unique_ptr<LoaderLogRecorder>> _recorders;

    ObjectInfoCollection _object_names;
    // Session labels
    std::unordered_map<XrSession, std::vector<InternalSessionLabel*>*> _session_labels;
};

// Utility functions for converting to/from XR_EXT_debug_utils values
XrLoaderLogMessageSeverityFlags DebugUtilsSeveritiesToLoaderLogMessageSeverities(
    XrDebugUtilsMessageSeverityFlagsEXT utils_severities);
XrDebugUtilsMessageSeverityFlagsEXT LoaderLogMessageSeveritiesToDebugUtilsMessageSeverities(
    XrLoaderLogMessageSeverityFlags log_severities);
XrLoaderLogMessageTypeFlagBits DebugUtilsMessageTypesToLoaderLogMessageTypes(XrDebugUtilsMessageTypeFlagsEXT utils_types);
XrDebugUtilsMessageTypeFlagsEXT LoaderLogMessageTypesToDebugUtilsMessageTypes(XrLoaderLogMessageTypeFlagBits log_types);
<commit_msg>loader: Add another XrLoaderLogObjectInfo constructor from integers<commit_after>// Copyright (c) 2017-2019 The Khronos Group Inc.
// Copyright (c) 2017-2019 Valve Corporation
// Copyright (c) 2017-2019 LunarG, Inc.
//
// 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.
//
// Author: Mark Young <marky@lunarg.com>
//

#pragma once

#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>

#include <openxr/openxr.h>

// Use internal versions of flags similar to XR_EXT_debug_utils so that
// we're not tightly coupled to that extension.  This way, if the extension
// changes or gets replaced, we can be flexible in the loader.
#define XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT 0x00000001
#define XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT 0x00000010
#define XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT 0x00000100
#define XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT 0x00001000
typedef XrFlags64 XrLoaderLogMessageSeverityFlagBits;
typedef XrFlags64 XrLoaderLogMessageSeverityFlags;

#define XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT 0x00000001
#define XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT 0x00000002
#define XR_LOADER_LOG_MESSAGE_TYPE_PERFORMANCE_BIT 0x00000004
typedef XrFlags64 XrLoaderLogMessageTypeFlagBits;
typedef XrFlags64 XrLoaderLogMessageTypeFlags;

//! Turns a uint64_t into a string formatted as hex.
//!
//! The core of the HandleToString implementation is in here.
std::string Uint64ToHexString(uint64_t val);

template <typename T>
static inline std::string HandleToString(T handle) {
    return Uint64ToHexString(reinterpret_cast<uint64_t>(handle));
}

struct XrLoaderLogObjectInfo {
    //! Type-erased handle value
    uint64_t handle;

    //! Kind of object this handle refers to
    XrObjectType type;

    //! To be assigned by the application - not part of this object's identity
    std::string name;

    /// Un-erase the type of the handle and get it properly typed again.
    ///
    /// Note: Does not check the type before doing it!
    template <typename HandleType>
    HandleType& GetTypedHandle() {
        return reinterpret_cast<HandleType&>(handle);
    }

    //! @overload
    template <typename HandleType>
    HandleType const& GetTypedHandle() const {
        return reinterpret_cast<HandleType&>(handle);
    }

    XrLoaderLogObjectInfo() = default;

    //! Create from a typed handle and object type
    template <typename T>
    XrLoaderLogObjectInfo(T h, XrObjectType t) : handle(reinterpret_cast<uint64_t>(h)), type(t) {}

    //! Create from an untyped handle value (integer) and object type
    XrLoaderLogObjectInfo(uint64_t h, XrObjectType t) : handle(h), type(t) {}

    std::string ToString() const;
};

//! True if the two object infos have the same handle value and handle type
static inline bool Equivalent(XrLoaderLogObjectInfo const& a, XrLoaderLogObjectInfo const& b) {
    return a.handle == b.handle && a.type == b.type;
}
//! @overload
static inline bool Equivalent(XrDebugUtilsObjectNameInfoEXT const& a, XrLoaderLogObjectInfo const& b) {
    return a.objectHandle == b.handle && a.objectType == b.type;
}
//! @overload
static inline bool Equivalent(XrLoaderLogObjectInfo const& a, XrDebugUtilsObjectNameInfoEXT const& b) { return Equivalent(b, a); }

struct XrLoaderLogMessengerCallbackData {
    const char* message_id;
    const char* command_name;
    const char* message;
    uint8_t object_count;
    XrLoaderLogObjectInfo* objects;
    uint8_t session_labels_count;
    XrDebugUtilsLabelEXT* session_labels;
};

enum XrLoaderLogType {
    XR_LOADER_LOG_UNKNOWN = 0,
    XR_LOADER_LOG_STDERR,
    XR_LOADER_LOG_STDOUT,
    XR_LOADER_LOG_DEBUG_UTILS,
};

class LoaderLogRecorder {
   public:
    LoaderLogRecorder(XrLoaderLogType type, void* user_data, XrLoaderLogMessageSeverityFlags message_severities,
                      XrLoaderLogMessageTypeFlags message_types) {
        _active = false;
        _user_data = user_data;
        _type = type;
        _unique_id = 0;
        _message_severities = message_severities;
        _message_types = message_types;
    }
    virtual ~LoaderLogRecorder() {}

    XrLoaderLogType Type() { return _type; }
    uint64_t UniqueId() { return _unique_id; }
    XrLoaderLogMessageSeverityFlags MessageSeverities() { return _message_severities; }
    XrLoaderLogMessageTypeFlags MessageTypes() { return _message_types; }

    virtual void Start() { _active = true; }
    bool IsPaused() { return _active; }
    virtual void Pause() { _active = false; }
    virtual void Resume() { _active = true; }
    virtual void Stop() { _active = false; }

    virtual bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
                            const XrLoaderLogMessengerCallbackData* callback_data) = 0;

   protected:
    bool _active;
    XrLoaderLogType _type;
    uint64_t _unique_id;
    void* _user_data;
    XrLoaderLogMessageSeverityFlags _message_severities;
    XrLoaderLogMessageTypeFlags _message_types;
};

// Standard Error logger, always on for now
class StdErrLoaderLogRecorder : public LoaderLogRecorder {
   public:
    StdErrLoaderLogRecorder(void* user_data);
    ~StdErrLoaderLogRecorder() {}

    virtual bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
                            const XrLoaderLogMessengerCallbackData* callback_data);
};

// Standard Output logger used with XR_LOADER_DEBUG
class StdOutLoaderLogRecorder : public LoaderLogRecorder {
   public:
    StdOutLoaderLogRecorder(void* user_data, XrLoaderLogMessageSeverityFlags flags);
    ~StdOutLoaderLogRecorder() {}

    virtual bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
                            const XrLoaderLogMessengerCallbackData* callback_data);
};

// Debug Utils logger used with XR_EXT_debug_utils
class DebugUtilsLogRecorder : public LoaderLogRecorder {
   public:
    DebugUtilsLogRecorder(const XrDebugUtilsMessengerCreateInfoEXT* create_info, XrDebugUtilsMessengerEXT debug_messenger);
    ~DebugUtilsLogRecorder() {}

    virtual bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
                            const XrLoaderLogMessengerCallbackData* callback_data);

    // Extension-specific logging functions
    bool LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity, XrDebugUtilsMessageTypeFlagsEXT message_type,
                              const XrDebugUtilsMessengerCallbackDataEXT* callback_data);

   private:
    PFN_xrDebugUtilsMessengerCallbackEXT _user_callback;
};

// TODO: Add other Derived classes:
//  - FileLoaderLogRecorder     - During/after xrCreateInstance
//  - PipeLoaderLogRecorder?    - During/after xrCreateInstance

class ObjectInfoCollection {
   public:
    //! Called from LoaderXrTermSetDebugUtilsObjectNameEXT - an empty name means remove
    void AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name);

    //! Find the stored object info, if any, matching handle and type.
    //! Return nullptr if not found.
    XrLoaderLogObjectInfo const* LookUpStoredObjectInfo(XrLoaderLogObjectInfo const& info) const;
    //! Find the stored object info, if any, matching handle and type.
    //! Return nullptr if not found.
    XrLoaderLogObjectInfo* LookUpStoredObjectInfo(XrLoaderLogObjectInfo const& info);

    //! Find the stored object info, if any.
    //! Return nullptr if not found.
    XrLoaderLogObjectInfo const* LookUpStoredObjectInfo(uint64_t handle, XrObjectType type) const {
        return LookUpStoredObjectInfo({handle, type});
    }

    //! Find the object name, if any, and update debug utils info accordingly.
    //! Return true if found and updated.
    bool LookUpObjectName(XrDebugUtilsObjectNameInfoEXT& info) const;

    //! Find the object name, if any, and update logging info accordingly.
    //! Return true if found and updated.
    bool LookUpObjectName(XrLoaderLogObjectInfo& info) const;

    //! Is the collection empty?
    bool Empty() const { return _object_info.empty(); }

   private:
    // Object names that have been set for given objects
    std::vector<XrLoaderLogObjectInfo> _object_info;
};
class LoaderLogger {
   public:
    static LoaderLogger& GetInstance() {
        std::call_once(LoaderLogger::_once_flag, []() { _instance.reset(new LoaderLogger); });
        return *(_instance.get());
    }

    void AddLogRecorder(std::unique_ptr<LoaderLogRecorder>& recorder);
    void RemoveLogRecorder(uint64_t unique_id);

    //! Called from LoaderXrTermSetDebugUtilsObjectNameEXT - an empty name means remove
    void AddObjectName(uint64_t object_handle, XrObjectType object_type, const std::string& object_name);
    void BeginLabelRegion(XrSession session, const XrDebugUtilsLabelEXT* label_info);
    void EndLabelRegion(XrSession session);
    void InsertLabel(XrSession session, const XrDebugUtilsLabelEXT* label_info);
    void DeleteSessionLabels(XrSession session);

    bool LogMessage(XrLoaderLogMessageSeverityFlagBits message_severity, XrLoaderLogMessageTypeFlags message_type,
                    const std::string& message_id, const std::string& command_name, const std::string& message,
                    const std::vector<XrLoaderLogObjectInfo>& objects = {});
    static bool LogErrorMessage(const std::string& command_name, const std::string& message,
                                const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
                                        "OpenXR-Loader", command_name, message, objects);
    }
    static bool LogWarningMessage(const std::string& command_name, const std::string& message,
                                  const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
                                        "OpenXR-Loader", command_name, message, objects);
    }
    static bool LogInfoMessage(const std::string& command_name, const std::string& message,
                               const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_INFO_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
                                        "OpenXR-Loader", command_name, message, objects);
    }
    static bool LogVerboseMessage(const std::string& command_name, const std::string& message,
                                  const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_VERBOSE_BIT, XR_LOADER_LOG_MESSAGE_TYPE_GENERAL_BIT,
                                        "OpenXR-Loader", command_name, message, objects);
    }
    static bool LogValidationErrorMessage(const std::string& vuid, const std::string& command_name, const std::string& message,
                                          const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_ERROR_BIT, XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT,
                                        vuid, command_name, message, objects);
    }
    static bool LogValidationWarningMessage(const std::string& vuid, const std::string& command_name, const std::string& message,
                                            const std::vector<XrLoaderLogObjectInfo>& objects = {}) {
        return GetInstance().LogMessage(XR_LOADER_LOG_MESSAGE_SEVERITY_WARNING_BIT, XR_LOADER_LOG_MESSAGE_TYPE_SPECIFICATION_BIT,
                                        vuid, command_name, message, objects);
    }

    // Extension-specific logging functions
    bool LogDebugUtilsMessage(XrDebugUtilsMessageSeverityFlagsEXT message_severity, XrDebugUtilsMessageTypeFlagsEXT message_type,
                              const XrDebugUtilsMessengerCallbackDataEXT* callback_data);

   private:
    struct InternalSessionLabel {
        XrDebugUtilsLabelEXT debug_utils_label;
        std::string label_name;
        bool is_individual_label;
    };

    LoaderLogger();
    LoaderLogger(const LoaderLogger&) = delete;
    LoaderLogger& operator=(const LoaderLogger&) = delete;

    /// Retrieve labels for the given session, if any, and push them in reverse order on the vector.
    void LookUpSessionLabels(XrSession session, std::vector<XrDebugUtilsLabelEXT>& labels) const;

    void RemoveIndividualLabel(std::vector<InternalSessionLabel*>* label_vec);

    static std::unique_ptr<LoaderLogger> _instance;
    static std::once_flag _once_flag;

    // List of available recorder objects
    std::vector<std::unique_ptr<LoaderLogRecorder>> _recorders;

    ObjectInfoCollection _object_names;
    // Session labels
    std::unordered_map<XrSession, std::vector<InternalSessionLabel*>*> _session_labels;
};

// Utility functions for converting to/from XR_EXT_debug_utils values
XrLoaderLogMessageSeverityFlags DebugUtilsSeveritiesToLoaderLogMessageSeverities(
    XrDebugUtilsMessageSeverityFlagsEXT utils_severities);
XrDebugUtilsMessageSeverityFlagsEXT LoaderLogMessageSeveritiesToDebugUtilsMessageSeverities(
    XrLoaderLogMessageSeverityFlags log_severities);
XrLoaderLogMessageTypeFlagBits DebugUtilsMessageTypesToLoaderLogMessageTypes(XrDebugUtilsMessageTypeFlagsEXT utils_types);
XrDebugUtilsMessageTypeFlagsEXT LoaderLogMessageTypesToDebugUtilsMessageTypes(XrLoaderLogMessageTypeFlagBits log_types);
<|endoftext|>