Canvas color can now be saved in file, and sent over network

This commit is contained in:
Yousef Khadadeh 2025-08-01 20:07:47 +03:00
parent b6b1b895a2
commit 05f19ad1f2
8 changed files with 55 additions and 23 deletions

View file

@ -12,13 +12,13 @@ namespace GUIStuff {
template <typename T> class ColorPicker : public Element {
public:
void update(UpdateInputData& io, T* newData, bool newSelectAlpha, const std::function<void()>& 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 <typename T> class ColorPicker : public Element {
Vector2f newSv = cwise_vec_clamp<Vector2f>((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 <typename T> 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 <typename T> 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 <typename T> 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<Vector3f>(hsv);
(*data)[0] = a.x();
(*data)[1] = a.y();
@ -223,7 +220,6 @@ vec4 main(vec2 fragcoord) {
SCollision::AABB<float> bb;
SelectionHelper selection;
T* data = nullptr;
T oldData;
Vector3f savedHsv; // We save the HSV so that conversion doesnt ruin the UI
bool selectAlpha;

View file

@ -108,6 +108,8 @@ class MainProgram {
bool takingScreenshot = false;
bool transparentBackground = false;
Vector3f defaultCanvasBackgroundColor = DEFAULT_CANVAS_BACKGROUND_COLOR;
size_t worldIndex = 0;
std::vector<std::shared_ptr<World>> worlds;

View file

@ -42,6 +42,7 @@ void ImageResourceDisplay::update(World& w) {
bool ImageResourceDisplay::load(const std::string& fileName, const std::string& fileData) {
sk_sp<SkData> newData = SkData::MakeWithoutCopy(fileData.c_str(), fileData.size());
std::unique_ptr<SkCodec> 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);

View file

@ -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<Vector3f>(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<NetServer::ClientData> 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<NetServer::ClientData> client, cereal::PortableBinaryInputArchive& message) {
});
netServer->add_disconnect_callback([&](std::shared_ptr<NetServer::ClientData> client) {

View file

@ -12,6 +12,7 @@ class ServerData {
std::unordered_map<ServerClientID, std::shared_ptr<DrawComponent>> idToComponentMap;
std::unordered_map<ServerClientID, ResourceData> resources;
std::unordered_map<std::string, Bookmark> bookmarks;
Vector3f canvasBackColor;
void save(cereal::PortableBinaryOutputArchive& a) const;
void write_to_file(cereal::PortableBinaryOutputArchive& a) const;
void load(cereal::PortableBinaryInputArchive& a);

View file

@ -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<Vector3f>(newBackColor);
#ifndef __EMSCRIPTEN__
gui.checkbox_field("native file pick", "Use Native File Picker", &useNativeFilePicker);
#endif

View file

@ -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<Vector3f>(canvasTheme.backColor)) {
canvasTheme.backColor = SkColor4f{newBackColor.x(), newBackColor.y(), newBackColor.z(), 1.0f};
Vector3f newHSV = rgb_to_hsv<Vector3f>(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<float>().eval());
a(convert_vec3<Vector3f>(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);

View file

@ -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<ServerPortionID, ClientData> 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: