From 05f19ad1f271d6a3a7324d855aa91da712c9dc3a Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Fri, 1 Aug 2025 20:07:47 +0300 Subject: [PATCH] Canvas color can now be saved in file, and sent over network --- src/GUIStuff/Elements/ColorPicker.hpp | 22 ++++++-------- src/MainProgram.hpp | 2 ++ src/ResourceDisplay/ImageResourceDisplay.cpp | 1 + src/Server/MainServer.cpp | 7 +++++ src/Server/ServerData.hpp | 1 + src/Toolbar.cpp | 5 ++++ src/World.cpp | 31 +++++++++++++++----- src/World.hpp | 9 ++++-- 8 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/GUIStuff/Elements/ColorPicker.hpp b/src/GUIStuff/Elements/ColorPicker.hpp index e104f2c..3b9d5e5 100644 --- a/src/GUIStuff/Elements/ColorPicker.hpp +++ b/src/GUIStuff/Elements/ColorPicker.hpp @@ -12,13 +12,13 @@ namespace GUIStuff { template class ColorPicker : public Element { public: void update(UpdateInputData& io, T* newData, bool newSelectAlpha, const std::function& elemUpdate) { - data = newData; + T* data = newData; selectAlpha = newSelectAlpha; if(!data) return; - force_update_colorpicker(); + force_update_colorpicker(data); CLAY({ .layout = { @@ -44,13 +44,13 @@ template class ColorPicker : public Element { Vector2f newSv = cwise_vec_clamp((io.mouse.pos - bb.min) / svSelectionAreaSize, Vector2f{0.0f, 0.0f}, Vector2f{1.0f, 1.0f}); savedHsv.y() = newSv.x(); savedHsv.z() = 1.0f - newSv.y(); - set_hsv(savedHsv); + set_hsv(data, savedHsv); } if(selection.held && modifyingHue) { Vector2f huePos = get_hue_bar_pos(); Vector2f hueDim = get_hue_bar_dim(); savedHsv.x() = (1.0f - std::clamp((io.mouse.pos.y() - huePos.y()) / hueDim.y(), 0.0f, 1.0f)) * 360.0f; - set_hsv(savedHsv); + set_hsv(data, savedHsv); } if(selection.held && modifyingAlpha) { Vector2f alphaPos = get_alpha_bar_pos(); @@ -69,9 +69,6 @@ template class ColorPicker : public Element { } virtual void clay_draw(SkCanvas* canvas, UpdateInputData& io, Clay_RenderCommand* command) { - if(!data) - return; - bb = get_bb(command); canvas->save(); @@ -115,20 +112,20 @@ template class ColorPicker : public Element { SkPaint alphaBarPaint; if(selectAlpha) - alphaBarPaint.setShader(get_alpha_bar_shader({(*data)[0], (*data)[1], (*data)[2]}, alphaBarDim.x())); + alphaBarPaint.setShader(get_alpha_bar_shader({oldData[0], oldData[1], oldData[2]}, alphaBarDim.x())); else - alphaBarPaint.setColor4f(SkColor4f{(*data)[0], (*data)[1], (*data)[2], 1.0f}); + alphaBarPaint.setColor4f(SkColor4f{oldData[0], oldData[1], oldData[2], 1.0f}); canvas->drawPaint(alphaBarPaint); if(selectAlpha) { canvas->scale(alphaBarDim.x(), alphaBarDim.y()); - canvas->drawLine((*data)[3], 0.0f, (*data)[3], 1.0f, selectionLinePaint); + canvas->drawLine(oldData[3], 0.0f, oldData[3], 1.0f, selectionLinePaint); } canvas->restore(); } private: - void force_update_colorpicker() { + void force_update_colorpicker(T* data) { if(data && (*data == oldData)) return; @@ -160,7 +157,7 @@ template class ColorPicker : public Element { return {bb.width(), BAR_WIDTH}; } - void set_hsv(const Vector3f& hsv) { + void set_hsv(T* data, const Vector3f& hsv) { Vector3f a = hsv_to_rgb(hsv); (*data)[0] = a.x(); (*data)[1] = a.y(); @@ -223,7 +220,6 @@ vec4 main(vec2 fragcoord) { SCollision::AABB bb; SelectionHelper selection; - T* data = nullptr; T oldData; Vector3f savedHsv; // We save the HSV so that conversion doesnt ruin the UI bool selectAlpha; diff --git a/src/MainProgram.hpp b/src/MainProgram.hpp index b711fe1..d1df540 100644 --- a/src/MainProgram.hpp +++ b/src/MainProgram.hpp @@ -108,6 +108,8 @@ class MainProgram { bool takingScreenshot = false; bool transparentBackground = false; + Vector3f defaultCanvasBackgroundColor = DEFAULT_CANVAS_BACKGROUND_COLOR; + size_t worldIndex = 0; std::vector> worlds; diff --git a/src/ResourceDisplay/ImageResourceDisplay.cpp b/src/ResourceDisplay/ImageResourceDisplay.cpp index b524e03..751040f 100644 --- a/src/ResourceDisplay/ImageResourceDisplay.cpp +++ b/src/ResourceDisplay/ImageResourceDisplay.cpp @@ -42,6 +42,7 @@ void ImageResourceDisplay::update(World& w) { bool ImageResourceDisplay::load(const std::string& fileName, const std::string& fileData) { sk_sp newData = SkData::MakeWithoutCopy(fileData.c_str(), fileData.size()); std::unique_ptr codec = SkCodec::MakeFromData(newData, decoders); + mustUpdateDraw = true; if(codec) { auto origin = codec->getOrigin(); bool rotate = (origin == kLeftTop_SkEncodedOrigin || origin == kRightTop_SkEncodedOrigin || origin == kRightBottom_SkEncodedOrigin || origin == kLeftBottom_SkEncodedOrigin); diff --git a/src/Server/MainServer.cpp b/src/Server/MainServer.cpp index 425b0ee..62ea053 100644 --- a/src/Server/MainServer.cpp +++ b/src/Server/MainServer.cpp @@ -25,6 +25,7 @@ WorldVec cursorPos; void ServerData::save(cereal::PortableBinaryOutputArchive& a) const { + a(canvasBackColor); a((uint64_t)components.size()); for(uint64_t i = 0; i < components.size(); i++) a(components[i].second->get_type(), components[i].first, components[i].second->coords, *(components[i].second)); @@ -32,6 +33,7 @@ void ServerData::save(cereal::PortableBinaryOutputArchive& a) const { } void ServerData::load(cereal::PortableBinaryInputArchive& a) { + a(canvasBackColor); uint64_t compCount; a(compCount); for(uint64_t i = 0; i < compCount; i++) { @@ -68,6 +70,7 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID): } data.bookmarks = world.bMan.bookmark_list(); data.resources = world.rMan.resource_list(); + data.canvasBackColor = convert_vec3(world.canvasTheme.backColor); lastKeepAliveSent = std::chrono::steady_clock::now(); @@ -250,6 +253,10 @@ MainServer::MainServer(World& initWorld, const std::string& serverLocalID): data.bookmarks.erase(name); netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, CLIENT_REMOVE_BOOKMARK, name); }); + netServer->add_recv_callback(SERVER_CANVAS_COLOR, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { + message(data.canvasBackColor); + netServer->send_items_to_all_clients(RELIABLE_COMMAND_CHANNEL, data.canvasBackColor); + }); netServer->add_recv_callback(SERVER_KEEP_ALIVE, [&](std::shared_ptr client, cereal::PortableBinaryInputArchive& message) { }); netServer->add_disconnect_callback([&](std::shared_ptr client) { diff --git a/src/Server/ServerData.hpp b/src/Server/ServerData.hpp index dae96b7..371307e 100644 --- a/src/Server/ServerData.hpp +++ b/src/Server/ServerData.hpp @@ -12,6 +12,7 @@ class ServerData { std::unordered_map> idToComponentMap; std::unordered_map resources; std::unordered_map bookmarks; + Vector3f canvasBackColor; void save(cereal::PortableBinaryOutputArchive& a) const; void write_to_file(cereal::PortableBinaryOutputArchive& a) const; void load(cereal::PortableBinaryInputArchive& a); diff --git a/src/Toolbar.cpp b/src/Toolbar.cpp index 6c9a004..cfe1575 100644 --- a/src/Toolbar.cpp +++ b/src/Toolbar.cpp @@ -222,6 +222,7 @@ nlohmann::json Toolbar::get_config_json() { toRet["velocityAffectsBrushWidth"] = velocityAffectsBrushWidth; toRet["jumpTransitionEasing"] = jumpTransitionEasing; toRet["disableDrawCache"] = main.drawProgCache.disableDrawCache; + toRet["defaultCanvasBackgroundColor"] = main.defaultCanvasBackgroundColor; json tablet; tablet["pressureAffectsBrushWidth"] = tabletOptions.pressureAffectsBrushWidth; @@ -273,6 +274,7 @@ void Toolbar::set_config_json(const nlohmann::json& j) { j.at("velocityAffectsBrushWidth").get_to(velocityAffectsBrushWidth); j.at("jumpTransitionEasing").get_to(jumpTransitionEasing); j.at("disableDrawCache").get_to(main.drawProgCache.disableDrawCache); + j.at("defaultCanvasBackgroundColor").get_to(main.defaultCanvasBackgroundColor); j.at("tablet").at("pressureAffectsBrushWidth").get_to(tabletOptions.pressureAffectsBrushWidth); j.at("tablet").at("smoothingSamplingTime").get_to(tabletOptions.smoothingSamplingTime); @@ -1161,6 +1163,9 @@ void Toolbar::options_menu() { gui.input_text_field("display name input", "Display Name", &main.displayName); main.update_display_names(); gui.text_label("Note: Changes in display name dont affect canvases that are connected online"); + SkColor4f newBackColor{main.defaultCanvasBackgroundColor.x(), main.defaultCanvasBackgroundColor.y(), main.defaultCanvasBackgroundColor.z(), 1.0f}; + gui.color_picker_button_field("defaultCanvasBackgroundColor", "Default canvas background color", &newBackColor, false); + main.defaultCanvasBackgroundColor = convert_vec3(newBackColor); #ifndef __EMSCRIPTEN__ gui.checkbox_field("native file pick", "Use Native File Picker", &useNativeFilePicker); #endif diff --git a/src/World.cpp b/src/World.cpp index 3451826..7ca4afe 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -26,6 +26,7 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo): drawProg(*this), bMan(*this) { + set_canvas_background_color(main.defaultCanvasBackgroundColor); displayName = main.displayName; netSource = worldInfo.netSource; @@ -74,6 +75,9 @@ void World::init_client_callbacks() { std::string fileDisplayName; message(displayName, fileDisplayName, clients); set_name(fileDisplayName); + Vector3f newBackColor; + message(newBackColor); + set_canvas_background_color(newBackColor, false); drawProg.initialize_draw_data(message); message(bMan); } @@ -109,6 +113,11 @@ void World::init_client_callbacks() { message(chatMessage); add_chat_message(chatMessage, Toolbar::ChatMessage::COLOR_NORMAL); }); + con.client_add_recv_callback(CLIENT_CANVAS_COLOR, [&](cereal::PortableBinaryInputArchive& message) { + Vector3f newBackColor; + message(newBackColor); + set_canvas_background_color(newBackColor, false); + }); con.client_add_recv_callback(CLIENT_KEEP_ALIVE, [&](cereal::PortableBinaryInputArchive& message) { }); } @@ -166,16 +175,16 @@ void World::unfocus_update() { con.update(); } -void World::set_canvas_background_color(const Vector3f& newBackColor) { +void World::set_canvas_background_color(const Vector3f& newBackColor, bool sendChangeOverNetwork) { if(newBackColor != convert_vec3(canvasTheme.backColor)) { canvasTheme.backColor = SkColor4f{newBackColor.x(), newBackColor.y(), newBackColor.z(), 1.0f}; Vector3f newHSV = rgb_to_hsv(newBackColor); - if(newHSV.z() >= 0.6f) { + if(newHSV.z() >= 0.6f) canvasTheme.toolFrontColor = SkColor4f{0.0f, 0.0f, 0.0f, 1.0f}; - } - else { + else canvasTheme.toolFrontColor = SkColor4f{1.0f, 1.0f, 1.0f, 1.0f}; - } + if(sendChangeOverNetwork) + con.client_send_items_to_server(RELIABLE_COMMAND_CHANNEL, SERVER_CANVAS_COLOR, newBackColor); } } @@ -249,7 +258,7 @@ void World::save_to_file(const std::filesystem::path& fileName) { std::stringstream fWorldDataToCompress; { cereal::PortableBinaryOutputArchive a(fWorldDataToCompress); - SaveLoadFileOp op{.world = this}; + SaveLoadFileOp op{.world = this, .versionStr = SAVEFILE_HEADER}; a(op); } @@ -313,7 +322,7 @@ void World::load_from_file(const std::filesystem::path& fileName, std::string_vi ByteMemStream f((char*)uncompressedDataView.data(), uncompressedDataView.size()); cereal::PortableBinaryInputArchive a(f); - SaveLoadFileOp op{.world = this}; + SaveLoadFileOp op{.world = this, .versionStr = std::string(fileHeader)}; a(op); nextClientID = std::max(rMan.get_max_id(ownID), drawProg.get_max_id(ownID)); @@ -323,6 +332,7 @@ void World::load_from_file(const std::filesystem::path& fileName, std::string_vi void World::SaveLoadFileOp::save(cereal::PortableBinaryOutputArchive& a) const { a(world->drawData.cam.c, world->main.window.size.cast().eval()); + a(convert_vec3(world->canvasTheme.backColor)); world->drawProg.write_to_file(a); a(world->bMan); world->rMan.save_strip_unused_resources(a, world->drawProg.get_used_resources()); @@ -332,6 +342,13 @@ void World::SaveLoadFileOp::load(cereal::PortableBinaryInputArchive& a) { CoordSpaceHelper coordsToJumpTo; Vector2f windowSizeToJumpTo; a(coordsToJumpTo, windowSizeToJumpTo); + if(versionStr == SAVEFILE_HEADER) { + Vector3f canvasBackColor; + a(canvasBackColor); + world->set_canvas_background_color(canvasBackColor); + } + else + world->set_canvas_background_color(DEFAULT_CANVAS_BACKGROUND_COLOR); world->drawData.cam.smooth_move_to(*world, coordsToJumpTo, windowSizeToJumpTo, true); world->drawProg.initialize_draw_data(a); a(world->bMan); diff --git a/src/World.hpp b/src/World.hpp index 283a7ca..00cb7b2 100644 --- a/src/World.hpp +++ b/src/World.hpp @@ -10,6 +10,8 @@ class MainProgram; +#define DEFAULT_CANVAS_BACKGROUND_COLOR Vector3f{0.12f, 0.12f, 0.12f} + class World { public: static constexpr std::string FILE_EXTENSION = "infpnt"; @@ -33,6 +35,7 @@ class World { struct SaveLoadFileOp { World* world; + std::string versionStr; void save(cereal::PortableBinaryOutputArchive& a) const; void load(cereal::PortableBinaryInputArchive& a); }; @@ -87,11 +90,11 @@ class World { std::string name; std::unordered_map clients; - void set_canvas_background_color(const Vector3f& newBackColor); + void set_canvas_background_color(const Vector3f& newBackColor, bool sendChangeOverNetwork = true); struct CanvasTheme { - SkColor4f backColor = {0.12f, 0.12f, 0.12f, 1.0f}; - SkColor4f toolFrontColor = {1.0f, 1.0f, 1.0f, 1.0f}; + SkColor4f backColor; + SkColor4f toolFrontColor; } canvasTheme; private: