Program Listing for File image.hpp

Return to documentation for file (/home/runner/work/Legion-Engine/Legion-Engine/legion/engine/core/data/image.hpp)

#pragma once
#include <core/types/primitives.hpp>
#include <core/containers/sparse_map.hpp>
#include <core/math/color.hpp>
#include <core/async/rw_spinlock.hpp>
#include <core/filesystem/view.hpp>
#include <mutex>

namespace legion::core
{
    enum struct channel_format : uint
    {
        depth_stencil = 0,
        eight_bit = 1,
        sixteen_bit = 2,
        float_hdr = 4
    };

    enum struct image_components : int
    {
        grey = 1,
        grey_alpha = 2,
        rgb = 3,
        rgba = 4,
        depth = 5,
        stencil = 6,
        depth_stencil = 7
    };

    struct image final
    {
        friend class ImageCache;

        std::string name;
        math::ivec2 size;
        channel_format format;
        image_components components;
        size_type dataSize;
        byte* data;

        template<typename T>
        T* get_raw_data();

        void apply_raw(bool lazyApply = true);

        const std::vector<math::color>& read_colors();

        size_type data_size();

        bool operator==(const image& other)
        {
            return m_id == other.m_id;
        }

        image() = default;

        image(const image& other) : name(other.name), size(other.size), format(other.format), components(other.components), m_id(other.m_id), dataSize(other.dataSize), data(other.data)
        {
            if (m_id)
            {
                std::lock_guard guard(m_refsLock);
                m_refs[m_id]++;
            }
        }

        image(image&& other)
            : name(std::move(other.name)),
            size(std::move(other.size)),
            format(std::move(other.format)),
            components(std::move(other.components)),
            m_id(std::move(other.m_id)),
            dataSize(std::move(other.dataSize)),
            data(other.data)
        {
            if (m_id)
            {
                std::lock_guard guard(m_refsLock);
                m_refs[m_id]++;
            }
        }

        image& operator=(const image& other)
        {

            {
                std::lock_guard guard(m_refsLock);

                if (m_id)
                {
                    m_refs[m_id]--;
                    if (m_refs[m_id] == 0)
                    {
                        delete[] data;
                        data = nullptr;
                        m_refs.erase(m_id);
                    }
                }

                m_id = other.m_id;

                m_refs[m_id]++;
            }

            name = other.name;
            size = other.size;
            format = other.format;
            components = other.components;
            dataSize = other.dataSize;
            data = other.data;
            return *this;
        }

        image& operator=(image&& other)
        {
            {
                std::lock_guard guard(m_refsLock);

                if (m_id)
                {
                    m_refs[m_id]--;
                    if (m_refs[m_id] == 0)
                    {
                        delete[] data;
                        data = nullptr;
                        m_refs.erase(m_id);
                    }
                }

                m_id = other.m_id;

                m_refs[m_id]++;
            }

            name = std::move(other.name);
            size = std::move(other.size);
            format = std::move(other.format);
            components = std::move(other.components);
            dataSize = std::move(other.dataSize);
            data = other.data;
            return *this;
        }

        ~image()
        {
            if (m_id)
            {
                std::lock_guard guard(m_refsLock);

                m_refs[m_id]--;
                if (m_refs[m_id] == 0)
                {
                    delete[] data;
                    data = nullptr;
                    m_refs.erase(m_id);
                }
            }
        }

    private:

        static std::unordered_map<id_type, uint> m_refs;
        static std::mutex m_refsLock;

        id_type m_id;
    };

    template<typename T>
    T* image::get_raw_data()
    {
        return nullptr;
    }

    template<>
    inline void* image::get_raw_data<void>()
    {
        return reinterpret_cast<void*>(data);
    }

    template<>
    inline byte* image::get_raw_data<byte>()
    {
        if (format == channel_format::eight_bit)
            return data;
        return nullptr;
    }

    template<>
    inline uint16* image::get_raw_data<uint16>()
    {
        if (format == channel_format::sixteen_bit)
            return reinterpret_cast<uint16*>(data);
        return nullptr;
    }

    template<>
    inline float* image::get_raw_data<float>()
    {
        if (format == channel_format::float_hdr)
            return reinterpret_cast<float*>(data);
        return nullptr;
    }

    struct image_handle
    {
        id_type id;

        math::ivec2 size();

        const std::vector<math::color>& read_colors();

        std::pair<async::rw_spinlock&, image&> get_raw_image();

        void destroy();

        bool operator==(const image_handle& other) { return id == other.id; }
        operator id_type() { return id; }
    };

    constexpr image_handle invalid_image_handle{ invalid_id };

    struct image_import_settings
    {
        channel_format fileFormat;
        image_components components;
        bool flipVertical;
    };

    constexpr image_import_settings default_image_settings{ channel_format::eight_bit, image_components::rgba, true };

    class ImageCache
    {
        friend class renderer;
        friend struct image;
        friend struct image_handle;
    private:
        static const std::vector<math::color> m_nullColors;
        static async::rw_spinlock m_nullLock;

        static std::unordered_map<id_type, std::unique_ptr<std::pair<async::rw_spinlock, image>>> m_images;
        static async::rw_spinlock m_imagesLock;
        static std::unordered_map<id_type, std::unique_ptr<std::vector<math::color>>> m_colors;
        static async::rw_spinlock m_colorsLock;

        static const std::vector<math::color>& process_raw(id_type id);

        static const std::vector<math::color>& read_colors(id_type id);
        static std::pair<async::rw_spinlock&, image&> get_raw_image(id_type id);

    public:
        static image_handle create_image(const std::string& name, const filesystem::view& file, image_import_settings settings = default_image_settings);
        static image_handle create_image(const filesystem::view& file, image_import_settings settings = default_image_settings);
        static image_handle insert_image(image&& img);

        static image_handle get_handle(const std::string& name);

        static image_handle get_handle(id_type id);

        static void destroy_image(const std::string& name);

        static void destroy_image(id_type id);
    };
}