diff --git a/CMakeLists.txt b/CMakeLists.txt index b2a8aeb..fc80df3 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,7 @@ set(sources "src/main.cpp" "src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp" "src/DrawingProgram/Layers/DrawingProgramLayerFolder.cpp" "src/DrawingProgram/Layers/DrawingProgramLayer.cpp" + "src/DrawingProgram/Layers/SerializedBlendMode.cpp" "src/CanvasComponents/CanvasComponentContainer.cpp" "src/CanvasComponents/CanvasComponentAllocator.cpp" "src/CanvasComponents/BrushStrokeCanvasComponent.cpp" diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp index 023acbe..e0222d6 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.cpp @@ -3,6 +3,7 @@ #include #include "DrawingProgramLayer.hpp" #include "../../World.hpp" +#include "SerializedBlendMode.hpp" using namespace NetworkingObjects; @@ -45,7 +46,10 @@ DrawingProgramLayer& DrawingProgramLayerListItem::get_layer() const { void DrawingProgramLayerListItem::draw(SkCanvas* canvas, const DrawData& drawData) const { if(displayData->visible) { - canvas->saveLayerAlphaf(nullptr, displayData->alpha); + SkPaint layerPaint; + layerPaint.setAlphaf(displayData->alpha); + layerPaint.setBlendMode(serialized_blend_mode_to_sk_blend_mode(displayData->blendMode)); + canvas->saveLayer(nullptr, &layerPaint); if(folderData) folderData->draw(canvas, drawData); else @@ -87,6 +91,17 @@ bool DrawingProgramLayerListItem::get_visible() const { return displayData->visible; } +void DrawingProgramLayerListItem::set_blend_mode(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, SerializedBlendMode newBlendMode) const { + if(displayData && displayData->blendMode != newBlendMode) { + displayData->blendMode = newBlendMode; + delayedNetObjMan.send_update_to_all(displayData, false); + } +} + +SerializedBlendMode DrawingProgramLayerListItem::get_blend_mode() const { + return displayData->blendMode; +} + void DrawingProgramLayerListItem::register_class(World& w) { w.netObjMan.register_class({ .writeConstructorFuncClient = write_constructor_func, diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp index aeaf0db..9282711 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerListItem.hpp @@ -4,6 +4,7 @@ #include #include "DrawingProgramLayerFolder.hpp" #include "DrawingProgramLayer.hpp" +#include "SerializedBlendMode.hpp" class DrawingProgramLayerFolder; class DrawingProgramLayer; @@ -28,6 +29,9 @@ class DrawingProgramLayerListItem { void set_visible(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, bool newVisible) const; bool get_visible() const; + + void set_blend_mode(NetworkingObjects::DelayUpdateSerializedClassManager& delayedNetObjMan, SerializedBlendMode newBlendMode) const; + SerializedBlendMode get_blend_mode() const; private: static void write_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryOutputArchive& a); static void read_constructor_func(const NetworkingObjects::NetObjTemporaryPtr& o, cereal::PortableBinaryInputArchive& a, const std::shared_ptr& c); @@ -42,8 +46,9 @@ class DrawingProgramLayerListItem { struct DisplayData { float alpha = 1.0f; bool visible = true; + SerializedBlendMode blendMode = SerializedBlendMode::SRC_OVER; template void serialize(Archive& a) { - a(alpha, visible); + a(alpha, visible, blendMode); } }; NetworkingObjects::NetObjOwnerPtr nameData; diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp b/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp index 08a9ba3..6249982 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.cpp @@ -3,6 +3,8 @@ #include "../DrawingProgram.hpp" #include "../../World.hpp" #include "../../MainProgram.hpp" +#include "Helpers/ConvertVec.hpp" +#include "SerializedBlendMode.hpp" DrawingProgramLayerManagerGUI::DrawingProgramLayerManagerGUI(DrawingProgramLayerManager& drawLayerMan): layerMan(drawLayerMan) @@ -14,9 +16,10 @@ void DrawingProgramLayerManagerGUI::refresh_gui_data() { nameToEdit.clear(); nameForNew.clear(); alphaValToEdit = 0.0f; + blendModeValToEdit = 0; } -void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id) { +void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id, bool& hoveringOverDropdown) { using namespace NetworkingObjects; auto& world = layerMan.drawP.world; if(layerMan.layerTreeRoot) { @@ -122,15 +125,18 @@ void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id) { if(tempPtr) { nameToEdit = tempPtr->get_name(); alphaValToEdit = tempPtr->get_alpha(); + blendModeValToEdit = static_cast(tempPtr->get_blend_mode()); } else { nameToEdit.clear(); alphaValToEdit = 0.0f; + blendModeValToEdit = 0; } } else { nameToEdit.clear(); alphaValToEdit = 0.0f; + blendModeValToEdit = 0; } } @@ -162,6 +168,14 @@ void DrawingProgramLayerManagerGUI::setup_list_gui(const std::string& id) { tempPtr->set_name(world.delayedUpdateObjectManager, nameToEdit); gui.slider_scalar_field("input alpha slider", "Alpha", &alphaValToEdit, 0.0f, 1.0f, 2); tempPtr->set_alpha(world.delayedUpdateObjectManager, alphaValToEdit); + gui.left_to_right_line_layout([&]() { + gui.text_label("Blend Mode"); + gui.dropdown_select("input blend mode", &blendModeValToEdit, get_blend_mode_name_list(), 200.0f, [&hoveringOverDropdown]() { + if(Clay_Hovered()) + hoveringOverDropdown = true; + }); + }); + tempPtr->set_blend_mode(world.delayedUpdateObjectManager, static_cast(blendModeValToEdit)); } } diff --git a/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.hpp b/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.hpp index 0d002d8..5c8f5b8 100644 --- a/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.hpp +++ b/src/DrawingProgram/Layers/DrawingProgramLayerManagerGUI.hpp @@ -11,7 +11,7 @@ class DrawingProgramLayerManagerGUI { public: DrawingProgramLayerManagerGUI(DrawingProgramLayerManager& layerManager); void refresh_gui_data(); - void setup_list_gui(const std::string& id); + void setup_list_gui(const std::string& id, bool& hoveringOverDropdown); private: NetworkingObjects::NetObjOrderedListObjectInfoPtr try_to_create_in_proper_position(DrawingProgramLayerListItem* newItem); NetworkingObjects::NetObjOrderedListObjectInfoPtr create_in_proper_position(DrawingProgramLayerListItem* newItem); @@ -21,6 +21,7 @@ class DrawingProgramLayerManagerGUI { std::string nameToEdit; std::string nameForNew; float alphaValToEdit = 0.0f; + size_t blendModeValToEdit = 0; DrawingProgramLayerManager& layerMan; }; diff --git a/src/DrawingProgram/Layers/SerializedBlendMode.cpp b/src/DrawingProgram/Layers/SerializedBlendMode.cpp new file mode 100644 index 0000000..9fd1f2b --- /dev/null +++ b/src/DrawingProgram/Layers/SerializedBlendMode.cpp @@ -0,0 +1,15 @@ +#include "SerializedBlendMode.hpp" + +SkBlendMode serialized_blend_mode_to_sk_blend_mode(SerializedBlendMode serializedBlendMode) { + int skBlendModeStartVal = static_cast(SkBlendMode::kClear); + return static_cast(skBlendModeStartVal + static_cast(serializedBlendMode)); +} + +const std::vector& get_blend_mode_name_list() { + static std::vector blendModeList; + if(blendModeList.empty()) { + for(uint8_t i = 0; i < static_cast(SerializedBlendMode::LAST_MODE); i++) + blendModeList.emplace_back(nlohmann::json(static_cast(i))); + } + return blendModeList; +} diff --git a/src/DrawingProgram/Layers/SerializedBlendMode.hpp b/src/DrawingProgram/Layers/SerializedBlendMode.hpp new file mode 100644 index 0000000..0340762 --- /dev/null +++ b/src/DrawingProgram/Layers/SerializedBlendMode.hpp @@ -0,0 +1,72 @@ +#pragma once +#include +#include + +enum class SerializedBlendMode : uint8_t { + CLEAR = 0, + SRC, + DST, + SRC_OVER, + DST_OVER, + SRC_IN, + DST_IN, + SRC_OUT, + DST_OUT, + SRC_A_TOP, + DST_A_TOP, + XOR, + PLUS, + MODULATE, + SCREEN, + OVERLAY, + DARKEN, + LIGHTEN, + COLOR_DODGE, + COLOR_BURN, + HARD_LIGHT, + SOFT_LIGHT, + DIFFERENCE, + EXCLUSION, + MULTIPLY, + HUE, + SATURATION, + COLOR, + LUMINOSITY, + + LAST_MODE +}; + +NLOHMANN_JSON_SERIALIZE_ENUM(SerializedBlendMode, { + {SerializedBlendMode::CLEAR, "Clear"}, + {SerializedBlendMode::SRC, "Src"}, + {SerializedBlendMode::DST, "Dst"}, + {SerializedBlendMode::SRC_OVER, "Src Over"}, + {SerializedBlendMode::DST_OVER, "Dst Over"}, + {SerializedBlendMode::SRC_IN, "Src In"}, + {SerializedBlendMode::DST_IN, "Dst In"}, + {SerializedBlendMode::SRC_OUT, "Src Out"}, + {SerializedBlendMode::DST_OUT, "Dst Out"}, + {SerializedBlendMode::SRC_A_TOP, "Src A Top"}, + {SerializedBlendMode::DST_A_TOP, "Dst A Top"}, + {SerializedBlendMode::XOR, "XOR"}, + {SerializedBlendMode::PLUS, "Plus"}, + {SerializedBlendMode::MODULATE, "Modulate"}, + {SerializedBlendMode::SCREEN, "Screen"}, + {SerializedBlendMode::OVERLAY, "Overlay"}, + {SerializedBlendMode::DARKEN, "Darken"}, + {SerializedBlendMode::LIGHTEN, "Lighten"}, + {SerializedBlendMode::COLOR_DODGE, "Color Dodge"}, + {SerializedBlendMode::COLOR_BURN, "Color Burn"}, + {SerializedBlendMode::HARD_LIGHT, "Hard Light"}, + {SerializedBlendMode::SOFT_LIGHT, "Soft Light"}, + {SerializedBlendMode::DIFFERENCE, "Difference"}, + {SerializedBlendMode::EXCLUSION, "Exclusion"}, + {SerializedBlendMode::MULTIPLY, "Multiply"}, + {SerializedBlendMode::HUE, "Hue"}, + {SerializedBlendMode::SATURATION, "Saturation"}, + {SerializedBlendMode::COLOR, "Color"}, + {SerializedBlendMode::LUMINOSITY, "Luminosity"} +}) + +SkBlendMode serialized_blend_mode_to_sk_blend_mode(SerializedBlendMode serializedBlendMode); +const std::vector& get_blend_mode_name_list(); diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 39e87c4..5d258cf 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -837,8 +837,9 @@ void Toolbar::layer_menu(bool justOpened) { }) { gui.obstructing_window(); gui.text_label_centered("Layers"); - main.world->drawProg.layerMan.listGUI.setup_list_gui("layer menu list"); - if(io->mouse.leftClick && !Clay_Hovered() && !justOpened) { + bool hoveringOverDropdown = false; + main.world->drawProg.layerMan.listGUI.setup_list_gui("layer menu list", hoveringOverDropdown); + if(io->mouse.leftClick && !Clay_Hovered() && !justOpened && !hoveringOverDropdown) { layerMenuPopupOpen = false; main.world->drawProg.layerMan.listGUI.refresh_gui_data(); }