#pragma once #include "common.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace npix { // // geometry // template struct Point { T x; T y; Point() : x(0), y(0) {}; Point(T _x, T _y) : x(T(_x)), y(T(_y)) {} bool operator==(const Point &rhs) const { return (x == rhs.x && y == rhs.y); } Point operator + (const Point &rhs) { return Point(x + rhs.x, y + rhs.y); } static constexpr Point zero() { return Point(T(0), T(0)); } }; template struct Size { T width; T height; Size() : width(0), height(0) {} Size(const T _width, const T _height) : width(_width), height(_height) {} bool is_zero() const { return (width == 0 && height == 0); } bool operator==(const Size &rhs) const { return (width == rhs.width && height == rhs.height); } static constexpr Size zero() { return Size(T(0), T(0)); } }; template struct Rect { Point origin; Size size; Rect() = default; Rect(T _x, T _y, T _width, T _height) { origin = Point(T(_x), T(_y)); size = Size(_width, _height); } Rect(Point _origin, Size _size) : origin(_origin), size(_size) {} bool operator==(const Rect &rhs) const { return (size == rhs.size && origin == rhs.origin); } Rect union_rect(const Rect &rect) const { auto begin = Point(std::min(rect.min_x(), min_x()), std::min(rect.min_y(), min_y())); auto end = Point(std::max(rect.max_x(), max_x()), std::max(rect.max_y(), max_y())); return Rect( begin.x, begin.y, end.x - begin.x, end.y - begin.y); } T x() const { return origin.x; } T y() const { return origin.y; } T width() const { return size.width; } T height() const { return size.height; } T min_x() const { return origin.x; } T max_x() const { return origin.x + size.width; } T min_y() const { return origin.y; } T max_y() const { return origin.y + size.height; } static constexpr Rect zero() { return Rect( Point::zero(), Size::zero() ); } bool is_inside(Point pt) const { bool b = (pt.x >= min_x() && pt.y >= min_y() && pt.x < max_x() && pt.y < max_y()); return b; } }; // // unit_utility.cpp // class Px { int32_t data; public: int32_t to_int() const; Px() : data(0) {} Px(int32_t v) : data(v) {} Px operator * (const Px& a) const; Px operator / (const Px& a) const; Px operator % (const Px& a) const; Px operator + (const Px& a) const; Px operator - (const Px& a) const; bool operator==(const Px &rhs) const; bool operator > (const Px &rhs) const; bool operator < (const Px &rhs) const; }; class Blk { int32_t data; public: int32_t to_int() const; Px to_px() const; Blk() : data(0) {} Blk(int32_t v) : data(v) {} Blk operator * (const Blk& a) const; Blk operator / (const Blk& a) const; Blk operator % (const Blk& a) const; Blk operator + (const Blk& a) const; Blk operator - (const Blk& a) const; bool operator==(const Blk &rhs) const; bool operator > (const Blk &rhs) const; bool operator < (const Blk &rhs) const; }; class Dpi { uint32_t _value; public: Dpi() : _value(0) {} Dpi(double f) : _value(f * (1 << 15)) {} Dpi(uint32_t i) : _value(i << 15) {} Dpi(int i) : _value(i << 15) {} uint32_t to_int() { return _value >> 16; } double to_f64() { return _value / double(1 << 15); } }; // // thread.cpp // class TaskQueue { std::deque> queue; std::vector threads; std::condition_variable condition; std::mutex tasks_mutex; std::atomic running = true; public: TaskQueue(); TaskQueue(unsigned int _count); ~TaskQueue(); // -- std::string label = ""; size_t thread_count(); size_t task_count(); // -- void submit(std::function &&f); void interrupt(std::function &&f); private: void worker(); }; // - class GlobalTaskQueue { GlobalTaskQueue() = default; ~GlobalTaskQueue() = default; GlobalTaskQueue(const GlobalTaskQueue &) = delete; GlobalTaskQueue &operator=(const GlobalTaskQueue &) = delete; GlobalTaskQueue(GlobalTaskQueue &&) = delete; GlobalTaskQueue &operator=(GlobalTaskQueue &&) = delete; TaskQueue task_queue; TaskQueue _worker; static GlobalTaskQueue &instance() noexcept; public: static TaskQueue &worker() noexcept; static TaskQueue &shared() noexcept; static TaskQueue &serial() noexcept; }; // - class TaskGroup { size_t task_count = 0; std::condition_variable condition; std::mutex mutex; bool is_terminate = false; TaskQueue *task_queue; public: TaskGroup(TaskQueue &_task_queue) noexcept; void submit(std::function &&task) noexcept; void interrupt(std::function &&task) noexcept; void wait() noexcept; }; // // memory.cpp // namespace mem { size_t allocated(); void *alloc32(); void *alloc64(); void *alloc128(); void *alloc256(); void release32(void* data); void release64(void* data); void release128(void* data); void release256(void* data); } // end namespace mem // // // class ProgressValue { std::mutex mtx; size_t _count = 0; size_t _max_count = 0; bool failed = false; bool canceled = false; std::function _notify; public: ProgressValue() = default; ProgressValue(const ProgressValue &o) : _count(o._count) , _max_count(o._max_count) , _notify(o._notify) {} ProgressValue max_count(size_t v) { _max_count = v; return *this; } ProgressValue notify(std::function f) { _notify = f; return *this; } void increment() { auto l = std::lock_guard(mtx); _count ++; double r = double(_count) / double(_max_count); if (_notify) _notify(r); } void fail() { failed = true; } void cancel() { canceled = true; } bool is_canceled() { return canceled; } bool is_failed() { return failed; } }; // // pixel_block.cpp // constexpr size_t BLOCK_SIZE = (1 << 7); constexpr size_t SUB_BLK_SIZE = 16; namespace color_mode { constexpr uint8_t GRAY = 1; constexpr uint8_t ARGB = 4; } template class PixBlock { union TaggedPointer { mutable uint8_t _tag; uint16_t *_pix; // -- uint16_t *pix() const; void pix(uint16_t *p); uint8_t tag() const; void tag(uint8_t v); }; mutable TaggedPointer _data; public: static constexpr size_t DATA_LEN = BLOCK_SIZE * BLOCK_SIZE * C; PixBlock(); PixBlock(const PixBlock&) = delete; PixBlock(PixBlock &&o) noexcept : _data(o._data) { o._data.pix(nullptr); o._data.tag(0); } PixBlock &operator = (const PixBlock&) = delete; PixBlock &operator = (PixBlock &&) noexcept; ~PixBlock(); // -- void allocate(); void deallocate(); void clear_damage() const; // -- bool is_empty() const; bool is_damaged() const; void set_damaged(); bool has_data() const; // -- const uint16_t *raw_data() const; uint16_t *raw_data_mut(); const uint16_t *pix_at(uint8_t x, uint8_t y, uint8_t c) const; uint16_t *pix_mut(uint8_t x, uint8_t y, uint8_t c) const; const uint16_t *sub_blk_at(uint8_t x, uint8_t y, uint8_t c) const; uint16_t *sub_blk_mut(uint8_t x, uint8_t y, uint8_t c); }; class FlagMap { std::vector _data; size_t _w; size_t _h; public: void set_size(size_t w, size_t h); bool get(size_t x, size_t y); void set(bool v, size_t x, size_t y); size_t width(); size_t height(); }; // // image.cpp // constexpr size_t MAX_MIPMAP_LEVEL = 7; constexpr size_t GRID_SIZE = 128; constexpr size_t GRID_DATA_SIZE = sizeof(void*) * GRID_SIZE * GRID_SIZE; template class InnerGrid { PixBlock _data[GRID_SIZE][GRID_SIZE] = {}; public: InnerGrid(); InnerGrid(const InnerGrid&) = delete; InnerGrid(InnerGrid&& o) noexcept { for (size_t i = 0; i < GRID_SIZE; i++) for (size_t j = 0; j < GRID_SIZE; j++) { _data[i][j] = std::move(o._data[i][j]); } }; ~InnerGrid(); InnerGrid & operator = (const InnerGrid&) = delete; InnerGrid & operator = (InnerGrid &&o) noexcept { for (size_t i = 0; i < GRID_SIZE; i++) for (size_t j = 0; j < GRID_SIZE; j++) { _data[i][j] = std::move(o._data[i][j]); } return *this; } void alloc_at(uint32_t x, uint32_t y); void dealloc_at(uint32_t x, uint32_t y); bool is_damaged(uint32_t x, uint32_t y) const; void clear_damage(uint32_t x, uint32_t y); void compaction(); bool is_empty(); PixBlock *block_mut(uint32_t x, uint32_t y); const PixBlock *block(uint32_t x, uint32_t y) const; void update(InnerGrid &o); }; template class OuterGrid { InnerGrid *_data[GRID_SIZE][GRID_SIZE] = {}; public: static constexpr int32_t DEFAULT_OFFSET = 1024; ~OuterGrid(); OuterGrid(); OuterGrid(const OuterGrid&) = delete; OuterGrid(OuterGrid &&o) { for (size_t i = 0; i < GRID_SIZE; i++) for (size_t j = 0; j < GRID_SIZE; j++) { _data[i][j] = o._data[i][j]; o._data[i][j] = nullptr; } } OuterGrid & operator = (const OuterGrid&) = delete; OuterGrid & operator = (OuterGrid &&o) { for (size_t i = 0; i < GRID_SIZE; i++) for (size_t j = 0; j < GRID_SIZE; j++) { _data[i][j] = o._data[i][j]; o._data[i][j] = nullptr; } return *this; } // -- void clear_empty_pixel(); Rect damaged_bounds() const; // -- void alloc_at(int32_t x, int32_t y); void dealloc_at(int32_t x, int32_t y); void reserve(int32_t x, int32_t y, int32_t w, int32_t h); void compaction(); bool is_empty() const; bool is_damaged(int32_t x, int32_t y) const; void clear_damage(int32_t x, int32_t y); // -- PixBlock *block_mut(int32_t x, int32_t y); const PixBlock *block(int32_t x, int32_t y) const; // -- void update(OuterGrid &diff); }; template class Image { OuterGrid *_data[MAX_MIPMAP_LEVEL + 1] = {}; public: Image() noexcept { for (auto &d : _data) { d = (OuterGrid*)mem::alloc128(); new(d) OuterGrid(); } } ~Image() { for (auto &d: _data) { if (d) { d->~OuterGrid(); mem::release128(d); } } } Image(const Image&) = delete; Image(Image &&o) noexcept { for (size_t i = 0; i <= MAX_MIPMAP_LEVEL; ++i) { _data[i] = o._data[i]; o._data[i] = nullptr; } } Image &operator = (const Image &) = delete; Image &operator = (Image &&o) noexcept { for (size_t i = 0; i <= MAX_MIPMAP_LEVEL; ++i) { _data[i] = o._data[i]; o._data[i] = nullptr; } return *this; } // -- void clear_empty_pixel(); Rect damaged_bounds() const; // -- void reserve( int32_t x, int32_t y, int32_t w, int32_t h ); void shrink(); void alloc_block(int32_t x, int32_t y); void dealloc_block(int32_t x, int32_t y); PixBlock *block_mut(int32_t x, int32_t y); const PixBlock *block(int32_t x, int32_t y) const; const PixBlock *test_block_with_level( int32_t x, int32_t y, uint8_t level ) const; void update(Image &diff); void make_mipmap_all(); void make_mipmap_with_area(Rect area); const PixBlock *mipmap_block( int32_t x, int32_t y, uint8_t level ) const; bool is_damaged(int32_t x, int32_t y) const; void clear_damage(int32_t x, int32_t y); void update(OuterGrid &o); // -- void clear_mipmap(); }; // // state_info // struct StateInfo { // canvas size struct { uint32_t width_px; uint32_t height_px; double dpi; } canvas_info; // background struct { bool visible; struct { uint16_t r; uint16_t g; uint16_t b; } color; } background_info; // layer struct Layer { uint32_t id; std::string name; }; std::vector layer_tree; }; // // canvas_state.cpp // struct CanvasInfo { Px width_px; Px height_px; Dpi dpi; }; struct ConstructInfo { CanvasInfo info; // test std::vector> images; // - ConstructInfo() = default; ConstructInfo(const ConstructInfo&) = delete; ConstructInfo(ConstructInfo&& o) noexcept : info(o.info) , images(std::move(o.images)) {} ConstructInfo &operator = (const ConstructInfo&) = delete; ConstructInfo &operator = (ConstructInfo &&o) noexcept { images = std::move(o.images); return *this; } }; // -- struct LayerIdTree { std::vector v; }; struct LayerImageDiff { uint32_t id; Image v; // -- LayerImageDiff(uint32_t id, Image &&v) : id(id), v(std::move(v)) {} LayerImageDiff(const LayerImageDiff&) = delete; LayerImageDiff operator = (const LayerImageDiff&) = delete; LayerImageDiff(LayerImageDiff &&o) noexcept : id(o.id) , v(std::move(o.v)) {} LayerImageDiff &operator = (LayerImageDiff &&o) noexcept { if (this == &o) return *this; id = o.id; v = std::move(o.v); return *this; } }; struct LayerInfoDiff { uint32_t id; std::string name; }; struct StateDiff { std::optional layer_tree; std::vector layer_info; std::vector layer_image; // -- std::optional canvas_size; // delete after std::optional> selection; std::optional bg_layer_visible; StateDiff() = default; StateDiff(const StateDiff&) = delete; StateDiff(StateDiff &&o) noexcept { layer_tree = o.layer_tree; layer_info = std::move(o.layer_info); layer_image = std::move(o.layer_image); canvas_size = o.canvas_size; selection = std::move(o.selection); bg_layer_visible = o.bg_layer_visible; //-- layer_image = std::move(o.layer_image); layer_info = o.layer_info; } StateDiff &operator = (const StateDiff&) = delete; StateDiff &operator = (StateDiff&& o) noexcept { if (this != &o) { canvas_size = std::move(o.canvas_size); layer_info = std::move(o.layer_info); layer_image = std::move(o.layer_image); selection = std::move(o.selection); bg_layer_visible = o.bg_layer_visible; // -- layer_image = std::move(o.layer_image); layer_info = o.layer_info; } return *this; } }; class Layer { uint32_t layer_id; Image image_data; std::string layer_name; public: Layer(uint32_t id) : layer_id(id) { layer_name = "Layer " + std::to_string(id); }Layer(uint32_t id, Image &&image) : layer_id(id), image_data(std::move(image)) { layer_name = "Layer " + std::to_string(id); } Layer(const Layer&) = delete; Layer operator = (const Layer&) = delete; Layer(Layer &&o) : layer_id(o.layer_id) , image_data(std::move(o.image_data)) , layer_name(std::move(o.layer_name)) { o.layer_id = 0; } Layer &operator = (Layer &&o) { if (this == &o) return *this; layer_id = o.layer_id; image_data = std::move(o.image_data); layer_name = std::move(o.layer_name); o.layer_id = 0; return *this; } uint32_t id() const; std::string name() const; const Image &image() const; // -- void update(StateDiff &diff); }; class LayerController { std::vector layer_stack; class { uint32_t i = 0; public: uint32_t make() { i ++; return i; } } id_gen; public: LayerController() = default; LayerController(const LayerController&) = delete; LayerController operator = (const LayerController&) = delete; LayerController(LayerController &&o) : layer_stack(std::move(o.layer_stack)) , id_gen(o.id_gen) {} const std::vector &layers() const; void update(StateDiff &diff); // TEST: void _test_add_layer(Image &&img) { layer_stack.emplace_back(id_gen.make(), std::move(img)); } }; class CanvasState { LayerController layer_stack; //-- uint32_t current_selected_layer_id = 1; CanvasInfo canvas_property; Image selection_data; bool background_layer_visible = true; public: mutable std::mutex mtx; CanvasState(ConstructInfo &&info) : canvas_property(info.info) { ASSERT(info.images.size()); // test for (auto &img : info.images) { layer_stack._test_add_layer(std::move(img)); } } CanvasState(StateDiff &&diff) : canvas_property(std::move(*diff.canvas_size)) { // test for (auto &img : diff.layer_image) layer_stack._test_add_layer(std::move(img.v)); } CanvasState(const CanvasState&) = delete; CanvasState(CanvasState&& o) noexcept : layer_stack(std::move(o.layer_stack)) , canvas_property(o.canvas_property) {} CanvasState &operator = (const CanvasState&) = delete; CanvasState &operator = (CanvasState&&) { return *this; } // -- CanvasInfo canvas_info() const; const std::vector &layer() const; uint32_t selected_layer_id() const; const Layer &layer_at(uint32_t id) const; const Image &selection() const; bool bg_layer_visible() const; // -- StateDiff update(StateDiff &&); }; // // compositor.cpp // class ToolInterface; struct SubCompositorHandle { int x_count; int y_count; int width_px; int height_px; std::vector data; }; class SubCompositor { uint32_t width; uint32_t height; uint8_t level; FlagMap flags; class Data { uint32_t *empty_data = nullptr; std::vector data = std::vector(); uint32_t _w; uint32_t _h; void release_all(); public: bool bg_layer_visible = true; void set_background_color(); uint32_t bg_color_a = 0xA8'A8'A8'00; // 0xFF'FF'FF'00; uint32_t bg_color_b = 0x88'88'88'00; // 0xFF'FF'FF'00; Data() = default; Data(const Data&) = delete; Data(Data &&o) { empty_data = o.empty_data; data = std::move(o.data); _w = o._w; _h = o._h; o.empty_data = nullptr; } Data &operator = (const Data&) = delete; Data &operator = (Data &&o) { empty_data = o.empty_data; data = std::move(o.data); _w = o._w; _h = o._h; o.empty_data = nullptr; return *this; } ~Data(); uint32_t width(); uint32_t height(); void reset(uint32_t w, uint32_t h); void update_empty_data(uint32_t a, uint32_t b); // - void set_tag(uint8_t val, uint32_t x, uint32_t y); uint8_t tag(uint32_t x, uint32_t y); uint32_t *get_block(uint32_t x, uint32_t y); uint32_t *alloc_at(uint32_t x, uint32_t y); bool has_data(uint32_t x, uint32_t y); void release_at(uint32_t x, uint32_t y); } image = Data(); template std::vector> constant_composite( const CanvasState &state, ToolInterface *tool, Rect area ); public: SubCompositor() {} SubCompositor(const SubCompositor&) = delete; SubCompositor(SubCompositor &&o) : width(o.width) , height(o.height) , level(o.level) , flags(o.flags) , image(std::move(o.image)) {} SubCompositor &operator = (const SubCompositor&) = delete; SubCompositor &operator = (SubCompositor &&o) { level = o.level; width = o.width; height = o.height; flags = o.flags; image = std::move(o.image); return *this; } SubCompositor(uint8_t level, uint32_t w, uint32_t h); // - bool bg_layer_visible = true; void set_default_bg_color(); void set_bg_color(uint32_t a, uint32_t b); // - void set_update_area(const StateDiff &diff); void set_update_area(Rect rect); std::vector> composite(const CanvasState &state, ToolInterface *tool, Rect area); // - SubCompositorHandle get_handle(); // - test void test_output(std::string path); }; struct CompositorHandle { std::mutex *mtx; std::array image; CompositorHandle(std::mutex *m) : mtx(m) {} }; class Compositor { uint32_t width; uint32_t height; SubCompositor sub_image[MAX_MIPMAP_LEVEL + 1] = {}; public: std::mutex mtx; Compositor() = default; Compositor(CanvasInfo info); Compositor(Compositor &&o) : width(o.width) , height(o.height) { for (size_t i = 0; i <= MAX_MIPMAP_LEVEL; i++) sub_image[i] = std::move(o.sub_image[i]); } Compositor &operator = (const Compositor&) = delete; Compositor &operator = (Compositor &&o) { width = o.width; height = o.height; for (size_t i = 0; i <= MAX_MIPMAP_LEVEL; i++) sub_image[i] = std::move(o.sub_image[i]); return *this; } // - void set_default_bg_color(); void set_bg_color(uint32_t a, uint32_t b); // - void set_update_area(const StateDiff &); void set_update_area(Rect rect); std::vector> composite( const CanvasState &state, ToolInterface *tool, Rect area, uint8_t level); // -- std::unique_ptr handle(); // test void test_output(uint8_t level, std::string path); }; // // drawable.cpp // template class Drawable { Image data; const CanvasState &reference; const Layer &target_layer; public: Drawable(const CanvasState &ref) : reference(ref) , target_layer(ref.layer_at(ref.selected_layer_id())) {} Drawable(const Drawable&&) = delete; Drawable &operator = (const Drawable&&) = delete; // - Size canvas_size() const; std::unique_lock lock_canvas(); const PixBlock *ref_at(int16_t x, int16_t y) const; PixBlock &at_mut(int16_t x, int16_t y); const PixBlock *at(int16_t x, int16_t y); bool has_block(int16_t x, int16_t y); void reserve(int16_t x, int16_t y, uint16_t w, uint16_t h); StateDiff make_state(); void default_composite( uint32_t *block, int16_t x, int16_t y, uint8_t level ); }; // // tool // enum class ToolType { EMPTY, PENCIL, ERASER, }; struct ColorOption { bool transparent = false; uint16_t r = 0; uint16_t g = 0; uint16_t b = 0; }; struct BrushOption { double width = 100.0; double min_width = 0.0; double dencity = 1.0; double min_dencity = 1.0; }; struct PencilOption { ColorOption color = ColorOption(); BrushOption brush = BrushOption(); }; struct EraserOption { BrushOption brush = BrushOption(); }; struct EmptyOption {}; struct ToolOption { ToolType type = ToolType::EMPTY; union U { constexpr U() : empty(EmptyOption()) {} EmptyOption empty; PencilOption pencil; EraserOption eraser; } tool; }; // StroleEvent struct StrokeEvent { Point pt; double press; }; // // pencil.cpp // template class Pencil { Drawable drawable; Compositor *compositor; // -- std::optional last_event; void draw_line(StrokeEvent a, StrokeEvent b); Rect dirty_rect = Rect::zero(); public: PencilOption param; Pencil(PencilOption param, const CanvasState &state, Compositor &compositor) : drawable(Drawable(state)) , compositor(&compositor) , param(param) {} // commit StateDiff commit(); // strokes void begin_stroke(StrokeEvent event); void move_stroke(StrokeEvent event); void end_stroke(StrokeEvent event); // composite Rect updated_rect(); void clear_updated_rect(); bool has_image(int16_t x, int16_t y, uint8_t level); void composite( uint32_t *block, int16_t x, int16_t y, uint8_t level ); }; template class Eraser { Drawable drawable; Compositor *compositor; // -- std::optional last_event; void draw_line(StrokeEvent a, StrokeEvent b); Rect dirty_rect = Rect::zero(); public: PencilOption param; Eraser(PencilOption param, const CanvasState &state, Compositor &compositor) : drawable(Drawable(state)) , compositor(&compositor) , param(param) {} // commit StateDiff commit(); // strokes void begin_stroke(StrokeEvent event); void move_stroke(StrokeEvent event); void end_stroke(StrokeEvent event); // composite Rect updated_rect(); void clear_updated_rect(); bool has_image(int16_t x, int16_t y, uint8_t level); void composite( uint32_t *block, int16_t x, int16_t y, uint8_t level ); }; // // selection // template class SelectTool { Drawable drawable; Compositor *compositor; public: SelectTool(const CanvasState& state, Compositor &compositor) : drawable(Drawable(state)) , compositor(&compositor) {} }; // // tool_interface.cpp // class ToolInterface { ToolType _type; void *impl; public: ToolInterface() : _type(ToolType::EMPTY), impl(nullptr) {} ~ToolInterface() { unset_tool(); } ToolType type(); void unset_tool(); void set_tool(ToolOption o, const CanvasState &s, Compositor &c); StateDiff commit(); void begin_stroke(StrokeEvent event); void move_stroke(StrokeEvent event); void end_stroke(StrokeEvent event); // -- void clear_updated_rect(); Rect updated_rect(); bool has_image(int16_t x, int16_t y, uint8_t level); void composite( uint32_t *block, int16_t x, int16_t y, uint8_t level ); }; // // undo_buffer.cpp // class UndoBuffer { std::deque undo_buffer; std::deque redo_buffer; public: void commit(StateDiff &&diff); bool can_undo() const; bool can_redo() const; void undo(CanvasState &state, Compositor &compositor); void redo(CanvasState &state, Compositor &compositor); }; // // canvas_controller.cpp // class CanvasInterfaceDelegate; class CanvasController { CanvasState current_buffer; Compositor composited_image; UndoBuffer undo_buffer; std::unique_ptr edit_queue; std::unique_ptr composite_queue; std::optional> rect; ToolInterface tool; // -- StateInfo make_state_info(); public: CanvasInterfaceDelegate *delegate; CanvasController(ConstructInfo data) : current_buffer(CanvasState(std::move(data))) , composited_image(Compositor(data.info)) , edit_queue(std::make_unique(1)) , composite_queue(std::make_unique(1)) { edit_queue->label = "edit_queue"; composite_queue->label = "composite_queue"; } CanvasController(const CanvasController&) = delete; CanvasController(CanvasController&& o) noexcept : current_buffer(std::move(o.current_buffer)) , composited_image(std::move(o.composited_image)) , edit_queue(std::move(o.edit_queue)) , composite_queue(std::move(o.composite_queue)) , tool(std::move(o.tool)) {} CanvasController &operator = (CanvasController &&o) { ASSERT(this != &o); current_buffer = std::move(o.current_buffer); composited_image = std::move(o.composited_image); edit_queue = std::move(o.edit_queue); composite_queue = std::move(o.composite_queue); tool = std::move(o.tool); return *this; } // - bool save_to_path(std::string path, std::function callback); // - void composite( Rect area, uint8_t level, std::function>&&)> callback ); std::unique_ptr composite_handle(); // - void set_background_color(uint32_t a, uint32_t b); void set_default_background_color(); // - void edit_roundtrip(); void compositor_roundtrip(); // --- StateInfo info(); // undo bool can_undo(); bool can_redo(); void undo(); void redo(); // tool -- void edit(ToolOption opt); void commit(); void cancel(); // stroke void begin_stroke(StrokeEvent, std::chrono::system_clock::time_point); void move_stroke(StrokeEvent, std::chrono::system_clock::time_point); void end_stroke(StrokeEvent, std::chrono::system_clock::time_point); // layer void visible_layer(uint32_t layer_id); void hidden_layer(uint32_t layer_id); // - test void test_output_compositor(uint8_t level, std::string path); }; // // canvas_interface.cpp // class CanvasInterfaceDelegate { public: virtual ~CanvasInterfaceDelegate() {}; virtual void open_progress(double) = 0; virtual void did_open(StateInfo) = 0; virtual void open_failed() = 0; virtual void make_failed() = 0; virtual void save_progress(double) = 0; virtual void did_save() = 0; virtual void save_failed() = 0; virtual void did_update(size_t) = 0; virtual void did_composite(std::vector>&&) = 0; virtual void did_commit(StateInfo) = 0; virtual void did_cancel() = 0; virtual void did_undo(StateInfo) = 0; virtual void did_redo(StateInfo) = 0; }; class CanvasInterface { CanvasInterfaceDelegate *delegate = nullptr; std::optional canvas = std::nullopt; public: CanvasInterface() = default; CanvasInterface(const CanvasInterface&) = delete; CanvasInterface(CanvasInterface &&o) = delete; ~CanvasInterface(); CanvasInterface &operator = (const CanvasInterface&) = delete; CanvasInterface &operator = (CanvasInterface&o) = delete; //- void set_delegate(CanvasInterfaceDelegate *); //- // file void create(CanvasInfo info); void open_with_path(std::string path); void open_with_data(const void* data, size_t len); void save_to_path(std::string path); // view void composite(Rect area, uint8_t level); void set_background_color(uint32_t a, uint32_t b); void set_default_background_color(); std::unique_ptr compositor_handle(); // - void edit_roundtrip(); void composite_roundtrip(); // undo bool can_undo(); bool can_redo(); void undo(); void redo(); // tool void edit(ToolOption opt); void commit(); void cancel(); // stroke void begin_stroke(StrokeEvent event); void move_stroke(StrokeEvent event); void end_stroke(StrokeEvent event); // layer void visible_layer(uint32_t layer_id); void hidden_layer(uint32_t layer_id); // - test void test_output_compositor(std::string path, uint8_t level); }; // // buffer.cpp // class InputBuffer { const void *_data = nullptr; size_t _size; InputBuffer(std::string path [[maybe_unused]], void* data, size_t size) : _data(data), _size(size) {} public: InputBuffer(const InputBuffer&) = delete; InputBuffer(InputBuffer &&o) noexcept : _data(o._data), _size(o._size) { o._data = nullptr; o._size = 0; } InputBuffer &operator = (const InputBuffer&) = delete; InputBuffer &operator = (InputBuffer&& o) noexcept; ~InputBuffer(); InputBuffer(const void *data, size_t length) : _data(data) , _size(length) {} const std::byte *data() const; size_t size() const; // --- bool resize(size_t) { return false; } template bool set_value_le(T, size_t&) const { static_assert(std::is_scalar::value, "not scalar type"); return false; } template std::optional get_value_le(size_t offset) const; template bool get_value_le(T &value, size_t &offset) const; std::optional get_bytes(size_t offset, size_t len); }; // // file_io.cpp // class InputFile { std::string _path; void *_data = nullptr; size_t _size; InputFile(std::string path, void* data, size_t size) : _path(path), _data(data), _size(size) {} public: InputFile(const InputFile&) = delete; InputFile(InputFile &&o) noexcept : _path(std::move(o._path)), _data(o._data), _size(o._size) { o._data = nullptr; o._size = 0; } InputFile &operator = (const InputFile&) = delete; InputFile &operator = (InputFile&& o) noexcept; ~InputFile(); static std::optional open(std::string path); const std::byte *data() const; size_t size() const; // --- bool resize(size_t) { return false; } template bool set_value_le(T, size_t &) const { static_assert(std::is_scalar::value, "not scalar type"); return false; } template std::optional get_value_le(size_t offset) const; template bool get_value_le(T &value, size_t &offset) const; std::optional get_bytes(size_t offset, size_t len); }; class OutputFile { std::string _path; void *_data = nullptr; size_t _size; OutputFile(std::string path, void *data, size_t size) : _path(path), _data(data), _size(size) {} public: OutputFile(const OutputFile&) = delete; OutputFile(OutputFile &&o) noexcept : _path(std::move(o._path)), _data(o._data), _size(o._size) { o._data = nullptr; o._size = 0; } OutputFile &operator = (const OutputFile&) = delete; OutputFile &operator = (OutputFile&& o) noexcept; ~OutputFile(); static std::optional create(std::string path, size_t size); std::byte *data(); size_t size() const; bool resize(size_t new_size); // -- template std::optional get_value_le(size_t offset) const; template bool get_value_le(T &value, size_t &offset) const; std::optional get_bytes(size_t offset, size_t len); template bool set_value_le(T value, size_t &offset) const; }; // // bmp.cpp // namespace bmp { size_t write_bounds(uint32_t x, uint32_t h); constexpr uint64_t BMP_HEADER_SIZE = 14; constexpr uint64_t BMP_V4_INFOHEADER_SIZE = 56; struct FileHeader { uint8_t signature[2] = { 'B', 'M' }; uint32_t file_size; uint16_t reserved[2] = {}; uint32_t pixel_data_offs = BMP_HEADER_SIZE + BMP_V4_INFOHEADER_SIZE; }; struct DibHeaderV4 { int32_t dib_hdr_size = BMP_V4_INFOHEADER_SIZE; int32_t image_width = 1; int32_t image_height = 1; int16_t planes = 1; int16_t bit_par_pixel = 32; int32_t compression = 0; int32_t image_data_size = 0; int32_t y_pixel_par_meter = 0; int32_t x_pixel_par_meter = 0; int32_t colors_color_table = 0; int32_t important_color_count = 0; uint32_t red_mask = 0x00'ff'00'00; uint32_t green_mask = 0x00'00'ff'00; uint32_t blue_mask = 0x00'00'00'ff; uint32_t alpha_mask = 0xff'00'00'00; }; template class Bmp { FileHeader file_hdr; DibHeaderV4 dib_hdr; Data &data; Bmp(Data &data) : data(data) {} public: static std::optional create(CanvasInfo info, Data &data); static std::optional> open(Data &data); uint32_t height() const ; uint32_t width() const; uint32_t pixel_par_meter() const; size_t line_data_bytes() const; bool has_alpha() const; size_t pixel_par_byte() const; void *line_at(uint32_t y); const void *line_at(uint32_t y) const; }; // -- template bool bmp_to_image( const Bmp &src, Image &dst, ProgressValue &progress); template bool image_to_bmp( const Image &src, Bmp &dst, ProgressValue &progress); } // end namespace bmp } // end namespace npix