#pragma once #include "foundation/result.h" #include "paint/blend.h" #include #include #include #include #include namespace pp::document { constexpr std::uint32_t max_canvas_dimension = 131072; constexpr std::uint32_t max_layer_count = 1024; constexpr std::uint32_t max_frame_count = 100000; constexpr std::uint32_t min_frame_duration_ms = 1; constexpr std::size_t min_document_history_entries = 2; constexpr std::size_t max_document_history_entries = 10000; constexpr std::size_t max_layer_name_length = 128; constexpr std::uint32_t cube_face_count = 6; constexpr std::uint32_t rgba8_components = 4; constexpr std::uint64_t max_face_pixel_payload_bytes = 1024ULL * 1024ULL * 1024ULL; struct DocumentConfig { std::uint32_t width = 0; std::uint32_t height = 0; std::uint32_t layer_count = 1; }; struct LayerFacePixels { std::uint32_t face_index = 0; std::uint32_t x = 0; std::uint32_t y = 0; std::uint32_t width = 0; std::uint32_t height = 0; std::vector rgba8; }; struct AnimationFrame { std::uint32_t duration_ms = 100; std::vector face_pixels; }; struct Layer { std::string name; bool visible = true; bool alpha_locked = false; float opacity = 1.0F; pp::paint::BlendMode blend_mode = pp::paint::BlendMode::normal; std::vector frames; }; struct DocumentLayerConfig { std::string_view name; bool visible = true; bool alpha_locked = false; float opacity = 1.0F; pp::paint::BlendMode blend_mode = pp::paint::BlendMode::normal; std::span frames; }; struct DocumentSnapshotConfig { std::uint32_t width = 0; std::uint32_t height = 0; std::span layers; std::span frames; }; class CanvasDocument { public: [[nodiscard]] static pp::foundation::Result create(DocumentConfig config); [[nodiscard]] static pp::foundation::Result create_from_snapshot(DocumentSnapshotConfig config); [[nodiscard]] std::uint32_t width() const noexcept; [[nodiscard]] std::uint32_t height() const noexcept; [[nodiscard]] std::size_t active_layer_index() const noexcept; [[nodiscard]] std::size_t active_frame_index() const noexcept; [[nodiscard]] std::uint64_t animation_duration_ms() const noexcept; [[nodiscard]] pp::foundation::Result layer_animation_duration_ms(std::size_t index) const noexcept; [[nodiscard]] std::size_t face_pixel_payload_count() const noexcept; [[nodiscard]] std::span layers() const noexcept; [[nodiscard]] std::span frames() const noexcept; [[nodiscard]] pp::foundation::Result add_layer(std::string_view name); [[nodiscard]] pp::foundation::Status remove_layer(std::size_t index); [[nodiscard]] pp::foundation::Status move_layer(std::size_t from, std::size_t to); [[nodiscard]] pp::foundation::Status set_active_layer(std::size_t index) noexcept; [[nodiscard]] pp::foundation::Status rename_layer(std::size_t index, std::string_view name); [[nodiscard]] pp::foundation::Status set_layer_visible(std::size_t index, bool visible) noexcept; [[nodiscard]] pp::foundation::Status set_layer_alpha_locked(std::size_t index, bool alpha_locked) noexcept; [[nodiscard]] pp::foundation::Status set_layer_opacity(std::size_t index, float opacity) noexcept; [[nodiscard]] pp::foundation::Status set_layer_blend_mode(std::size_t index, pp::paint::BlendMode blend_mode) noexcept; [[nodiscard]] pp::foundation::Result add_frame(std::uint32_t duration_ms); [[nodiscard]] pp::foundation::Result duplicate_frame(std::size_t index); [[nodiscard]] pp::foundation::Status remove_frame(std::size_t index); [[nodiscard]] pp::foundation::Status move_frame(std::size_t from, std::size_t to); [[nodiscard]] pp::foundation::Status set_frame_duration(std::size_t index, std::uint32_t duration_ms) noexcept; [[nodiscard]] pp::foundation::Status set_active_frame(std::size_t index) noexcept; [[nodiscard]] pp::foundation::Status set_layer_frame_face_pixels( std::size_t layer_index, std::size_t frame_index, LayerFacePixels pixels); private: std::uint32_t width_ = 0; std::uint32_t height_ = 0; std::size_t active_layer_index_ = 0; std::size_t active_frame_index_ = 0; std::vector layers_; std::vector frames_; }; class DocumentHistory { public: [[nodiscard]] static pp::foundation::Result create( CanvasDocument initial_document, std::size_t max_entries = 256); [[nodiscard]] const CanvasDocument& current() const noexcept; [[nodiscard]] std::size_t size() const noexcept; [[nodiscard]] std::size_t current_index() const noexcept; [[nodiscard]] bool can_undo() const noexcept; [[nodiscard]] bool can_redo() const noexcept; [[nodiscard]] pp::foundation::Status apply(CanvasDocument next_document); [[nodiscard]] pp::foundation::Status undo() noexcept; [[nodiscard]] pp::foundation::Status redo() noexcept; private: std::size_t max_entries_ = 0; std::size_t current_index_ = 0; std::vector entries_; }; }