From a669d1313bfcbc6f81341f34b47dfe592770ef89 Mon Sep 17 00:00:00 2001 From: Omar Date: Tue, 8 Oct 2019 23:16:04 +0200 Subject: [PATCH] add javascript binding and file picker for web --- data/layout.xml | 2 +- src/app.cpp | 12 +- src/app_events.cpp | 11 +- src/node_panel_brush.cpp | 10 +- webgl/CMakeLists.txt | 242 +++++++++++++++++++-------------------- webgl/src/main.cpp | 58 +++++++++- webgl/src/mylib.js | 28 +++++ 7 files changed, 228 insertions(+), 135 deletions(-) create mode 100644 webgl/src/mylib.js diff --git a/data/layout.xml b/data/layout.xml index 75025cc..4e92493 100644 --- a/data/layout.xml +++ b/data/layout.xml @@ -96,7 +96,7 @@ - + diff --git a/src/app.cpp b/src/app.cpp index a19e2e8..d5e93b4 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -26,6 +26,8 @@ void destroy_window(); #elif __LINUX__ std::string linux_home_path(); int mkpath(const std::string& dir, mode_t mode = DEFFILEMODE); +#elif __WEB__ +#include #endif App* App::I = nullptr; // singleton @@ -52,7 +54,7 @@ void App::create() void App::open_document(std::string path) { - std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)"); + std::regex r(R"((.*)[\\/]?([^\\/]+)\.(\w+)$)"); std::smatch m; if (!std::regex_search(path, m, r)) return; @@ -236,6 +238,14 @@ void App::initLog() mkpath(data_path + "/patterns/thumbs"); mkpath(data_path + "/settings"); mkpath(data_path + "/frames"); +#elif __WEB__ + data_path = "/"; + mkdir("/brushes", 0777); + mkdir("/brushes/thumbs", 0777); + mkdir("/patterns", 0777); + mkdir("/patterns/thumbs", 0777); + mkdir("/settings", 0777); + mkdir("/frames", 0777); #endif // TODO: save this path somewhere in the settings, don't overwrite every start diff --git a/src/app_events.cpp b/src/app_events.cpp index c5c57ac..96f92ac 100644 --- a/src/app_events.cpp +++ b/src/app_events.cpp @@ -15,8 +15,10 @@ void win32_show_cursor(bool visible); bool win32_clipboard_set_text(const std::string & s); std::string win32_clipboard_get_text(); #elif __APPLE__ -#else +#elif __LINUX__ #include +#elif __WEB__ +void webgl_pick_file(std::function callback); #endif @@ -149,6 +151,11 @@ void App::pick_image(std::function callback) std::string path = win32_open_file("Image Files (*.jpg, *.png)\0*.jpg;*.png"); if (!path.empty()) callback(path); +#elif __LINUX__ + if (auto p = tinyfd_openFileDialog("Open File", "", 0, nullptr, nullptr, false)) + callback(p); +#elif __WEB__ + webgl_pick_file(callback); #endif } @@ -196,6 +203,8 @@ void App::pick_file(std::vector types, std::functionon_click = [this](Node*) { App::I->pick_file({ "JPG", "PNG" }, [this](std::string path) { std::string name, base, ext; - std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)"); + std::regex r(R"((.*)[\\/]?([^\\/]+)\.(\w+)$)"); std::smatch m; if (!std::regex_search(path, m, r)) return; @@ -546,7 +546,7 @@ void NodePanelBrushPreset::init() }); }; m_btn_download = find("download"); - m_btn_download->on_click = [] (Node*) { + m_btn_download->on_click = [this] (Node*) { App::I->dialog_preset_download(); }; m_notification = find("notification"); @@ -701,7 +701,7 @@ bool NodePanelBrushPreset::export_ppbr(const std::string& path_in, const PPBRInf path += ".ppbr"; LOG("export ppbr to: %s", path.c_str()); - std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)?$)"); + std::regex r(R"((.*)[\\/]?([^\\/]+)\.(\w+)?$)"); std::smatch m; if (!std::regex_search(path, m, r)) return false; @@ -965,7 +965,7 @@ bool NodePanelBrushPreset::import_abr(const std::string& path) LOG("ABR detected"); std::string name, base, ext; - std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)"); + std::regex r(R"((.*)[\\/]?([^\\/]+)\.(\w+)$)"); std::smatch m; if (!std::regex_search(path, m, r)) return false; @@ -1035,7 +1035,7 @@ bool NodePanelBrushPreset::import_abr(const std::string& path) bool NodePanelBrushPreset::import_brush(const std::string& path) { - std::regex r(R"((.*)[\\/]([^\\/]+)\.(\w+)$)"); + std::regex r(R"((.*)[\\/]?([^\\/]+)\.(\w+)$)"); std::smatch m; if (!std::regex_search(path, m, r)) return false; diff --git a/webgl/CMakeLists.txt b/webgl/CMakeLists.txt index 4e4cc27..73ca270 100644 --- a/webgl/CMakeLists.txt +++ b/webgl/CMakeLists.txt @@ -1,136 +1,130 @@ cmake_minimum_required(VERSION 3.4.1) project(panopainter) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ - -std=c++14 -O2 \ - -s USE_GLFW=3 \ - -s USE_WEBGL2=1 \ - -s FULL_ES3=1 \ - -s USE_PTHREADS=1 \ - -s ASSERTIONS=2 \ - -s TOTAL_MEMORY=64Mb \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s WASM_MEM_MAX=512MB \ -") add_executable(panopainter src/main.cpp - ../libs/yoga/yoga/log.cpp - ../libs/yoga/yoga/Utils.cpp - ../libs/yoga/yoga/YGConfig.cpp - ../libs/yoga/yoga/YGEnums.cpp - ../libs/yoga/yoga/YGLayout.cpp - ../libs/yoga/yoga/YGMarker.cpp - ../libs/yoga/yoga/YGNode.cpp - ../libs/yoga/yoga/YGNodePrint.cpp - ../libs/yoga/yoga/YGStyle.cpp - ../libs/yoga/yoga/YGValue.cpp - ../libs/yoga/yoga/Yoga.cpp - ../libs/tinyxml2/tinyxml2.cpp - ../libs/jpeg/jpgd.cpp - ../libs/jpeg/jpge.cpp - ../libs/poly2tri/poly2tri/common/shapes.cc - ../libs/poly2tri/poly2tri/sweep/advancing_front.cc - ../libs/poly2tri/poly2tri/sweep/cdt.cc - ../libs/poly2tri/poly2tri/sweep/sweep_context.cc - ../libs/poly2tri/poly2tri/sweep/sweep.cc - ../libs/fmt/src/format.cc - ../src/pch.cpp - ../src/util.cpp - ../src/rtt.cpp - ../src/bezier.cpp - ../src/asset.cpp - ../src/image.cpp - ../src/texture.cpp - ../src/font.cpp - ../src/shader.cpp - ../src/shape.cpp - ../src/app.cpp - ../src/app_cloud.cpp - ../src/app_dialogs.cpp - ../src/app_events.cpp - ../src/app_layout.cpp - ../src/app_shaders.cpp + ../libs/yoga/yoga/log.cpp + ../libs/yoga/yoga/Utils.cpp + ../libs/yoga/yoga/YGConfig.cpp + ../libs/yoga/yoga/YGEnums.cpp + ../libs/yoga/yoga/YGLayout.cpp + ../libs/yoga/yoga/YGMarker.cpp + ../libs/yoga/yoga/YGNode.cpp + ../libs/yoga/yoga/YGNodePrint.cpp + ../libs/yoga/yoga/YGStyle.cpp + ../libs/yoga/yoga/YGValue.cpp + ../libs/yoga/yoga/Yoga.cpp + ../libs/tinyxml2/tinyxml2.cpp + ../libs/jpeg/jpgd.cpp + ../libs/jpeg/jpge.cpp + ../libs/poly2tri/poly2tri/common/shapes.cc + ../libs/poly2tri/poly2tri/sweep/advancing_front.cc + ../libs/poly2tri/poly2tri/sweep/cdt.cc + ../libs/poly2tri/poly2tri/sweep/sweep_context.cc + ../libs/poly2tri/poly2tri/sweep/sweep.cc + ../libs/fmt/src/format.cc + ../src/pch.cpp + ../src/util.cpp + ../src/rtt.cpp + ../src/bezier.cpp + ../src/asset.cpp + ../src/image.cpp + ../src/texture.cpp + ../src/font.cpp + ../src/shader.cpp + ../src/shape.cpp + ../src/app.cpp + ../src/app_cloud.cpp + ../src/app_dialogs.cpp + ../src/app_events.cpp + ../src/app_layout.cpp + ../src/app_shaders.cpp ../src/app_vr.cpp - ../src/brush.cpp - ../src/canvas.cpp - ../src/canvas_layer.cpp - ../src/canvas_actions.cpp - ../src/canvas_modes.cpp - ../src/log.cpp - ../src/action.cpp - ../src/layout.cpp - ../src/version.cpp - ../src/node.cpp - ../src/node_about.cpp - ../src/node_border.cpp - ../src/node_button.cpp - ../src/node_button_custom.cpp - ../src/node_canvas.cpp - ../src/node_checkbox.cpp - ../src/node_color_quad.cpp - ../src/node_colorwheel.cpp - ../src/node_combobox.cpp - ../src/node_changelog.cpp - ../src/node_dialog_browse.cpp - ../src/node_dialog_cloud.cpp - ../src/node_dialog_open.cpp - ../src/node_dialog_picker.cpp - ../src/node_dialog_layer_rename.cpp - ../src/node_dialog_resize.cpp - ../src/node_icon.cpp - ../src/node_image.cpp - ../src/node_image_texture.cpp - ../src/node_message_box.cpp - ../src/node_panel_brush.cpp - ../src/node_panel_color.cpp - ../src/node_panel_grid.cpp + ../src/brush.cpp + ../src/canvas.cpp + ../src/canvas_layer.cpp + ../src/canvas_actions.cpp + ../src/canvas_modes.cpp + ../src/log.cpp + ../src/action.cpp + ../src/layout.cpp + ../src/version.cpp + ../src/node.cpp + ../src/node_about.cpp + ../src/node_border.cpp + ../src/node_button.cpp + ../src/node_button_custom.cpp + ../src/node_canvas.cpp + ../src/node_checkbox.cpp + ../src/node_color_quad.cpp + ../src/node_colorwheel.cpp + ../src/node_combobox.cpp + ../src/node_changelog.cpp + ../src/node_dialog_browse.cpp + ../src/node_dialog_cloud.cpp + ../src/node_dialog_open.cpp + ../src/node_dialog_picker.cpp + ../src/node_dialog_layer_rename.cpp + ../src/node_dialog_resize.cpp + ../src/node_icon.cpp + ../src/node_image.cpp + ../src/node_image_texture.cpp + ../src/node_message_box.cpp + ../src/node_panel_brush.cpp + ../src/node_panel_color.cpp + ../src/node_panel_grid.cpp ../src/node_panel_floating.cpp - ../src/node_panel_layer.cpp - ../src/node_panel_stroke.cpp + ../src/node_panel_layer.cpp + ../src/node_panel_stroke.cpp ../src/node_panel_quick.cpp - ../src/node_popup_menu.cpp - ../src/node_progress_bar.cpp - ../src/node_settings.cpp - ../src/node_slider.cpp - ../src/node_stroke_preview.cpp - ../src/node_text.cpp - ../src/node_text_input.cpp + ../src/node_popup_menu.cpp + ../src/node_progress_bar.cpp + ../src/node_settings.cpp + ../src/node_slider.cpp + ../src/node_stroke_preview.cpp + ../src/node_text.cpp + ../src/node_text_input.cpp ../src/node_tool_bucket.cpp - ../src/node_usermanual.cpp - ../src/node_viewport.cpp - ../src/node_scroll.cpp - ../src/abr.cpp - ../src/binary_stream.cpp - ../src/serializer.cpp - ../src/settings.cpp - ../src/node_input_box.cpp - ../src/node_dialog_export_ppbr.cpp + ../src/node_usermanual.cpp + ../src/node_viewport.cpp + ../src/node_scroll.cpp + ../src/abr.cpp + ../src/binary_stream.cpp + ../src/serializer.cpp + ../src/settings.cpp + ../src/node_input_box.cpp + ../src/node_dialog_export_ppbr.cpp +) +target_compile_options(panopainter PRIVATE -std=c++14 -O2) +set_target_properties(panopainter PROPERTIES + SUFFIX ".html" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/html" + LINK_FLAGS "\ + -s USE_GLFW=3\ + -s USE_WEBGL2=1\ + -s FULL_ES3=1\ + -s USE_PTHREADS=1\ + -s ASSERTIONS=2\ + -s TOTAL_MEMORY=256Mb\ + -s ALLOW_MEMORY_GROWTH=1\ + -s WASM_MEM_MAX=512MB\ + --embed-file data\ + --js-library ../src/mylib.js\ + --bind" ) -set_target_properties(panopainter PROPERTIES SUFFIX ".html") -set_target_properties(panopainter PROPERTIES LINK_FLAGS "\ - --embed-file data \ - -s USE_GLFW=3 \ - -s USE_WEBGL2=1 \ - -s FULL_ES3=1 \ - -s USE_PTHREADS=1 \ - -s ASSERTIONS=2 \ - -s TOTAL_MEMORY=64Mb \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s WASM_MEM_MAX=512MB \ -") target_include_directories(panopainter PRIVATE src - ../src - ../libs/glm - ../libs/tinyxml2 - ../libs/yoga - ../libs/stb - ../libs/jpeg - ../libs/poly2tri/poly2tri - ../libs/base64 - ../libs/sqlite3 - ../libs/nanort - ../libs/hash-library - ../libs/fmt/include - ../libs/glad/include - ../libs/tinyfiledialogs + ../src + ../libs/glm + ../libs/tinyxml2 + ../libs/yoga + ../libs/stb + ../libs/jpeg + ../libs/poly2tri/poly2tri + ../libs/base64 + ../libs/sqlite3 + ../libs/nanort + ../libs/hash-library + ../libs/fmt/include + ../libs/glad/include + ../libs/tinyfiledialogs ) diff --git a/webgl/src/main.cpp b/webgl/src/main.cpp index e484e17..f1d7dd8 100644 --- a/webgl/src/main.cpp +++ b/webgl/src/main.cpp @@ -1,16 +1,68 @@ #include #include #include +#include #include #include #include #include +using namespace emscripten; + App app; GLFWwindow* wnd; float theta = 0; glm::vec2 g_cursor_pos; +class TaskCallback +{ + std::function fn; +public: + template TaskCallback(T callback) : fn(callback) {} + void call(const std::string& tmp_file_path) + { + fn(tmp_file_path); + } + TaskCallback() + { + printf("callback created\n"); + } + ~TaskCallback() + { + printf("callback destroyed\n"); + } +}; + +void TaskCallback_call(uintptr_t tc, std::string tmp_file_path) +{ + auto x = reinterpret_cast(tc); + x->call(tmp_file_path); +} + +void TaskCallback_delete(uintptr_t tc) +{ + auto x = reinterpret_cast(tc); + delete x; +} + +EMSCRIPTEN_BINDINGS(TaskCallback_bind) { + class_("TaskCallback"); + function("TaskCallback_call", &TaskCallback_call); + function("TaskCallback_delete", &TaskCallback_delete); +} + +extern "C" { +extern void js_pick_file(TaskCallback* tc); +} + +void webgl_pick_file(std::function callback) +{ + js_pick_file(new TaskCallback([callback](std::string tmp_file_path){ + printf("callback called: %s\n", tmp_file_path.c_str()); + callback(tmp_file_path); + })); +} + void main_loop() { app.render_thread_tick(); @@ -30,7 +82,7 @@ int main() { if (glfwInit() != GL_TRUE) printf("Failed to init GLFW"); - wnd = glfwCreateWindow(800, 600, "hello", nullptr, nullptr); + wnd = glfwCreateWindow(1024, 768, "PanoPainter", nullptr, nullptr); glfwMakeContextCurrent(wnd); glfwSetCursorPosCallback(wnd, [](GLFWwindow* wnd, double x, double y){ @@ -72,8 +124,8 @@ int main() App::I = &app; app.initLog(); app.create(); - app.width = 800; - app.height = 600; + app.width = 1024; + app.height = 768; app.glfw_window = wnd; // app.render_thread_tick(); diff --git a/webgl/src/mylib.js b/webgl/src/mylib.js new file mode 100644 index 0000000..b57765b --- /dev/null +++ b/webgl/src/mylib.js @@ -0,0 +1,28 @@ +function js_pick_file(fn) { + var input = document.createElement('input'); + input.type = 'file'; + + input.onchange = function(e) { + // getting a hold of the file reference + var file = e.target.files[0]; + + // setting up the reader + var reader = new FileReader(); + reader.readAsArrayBuffer(file); + //reader.readAsText(file,'UTF-8'); + // here we tell the reader what to do when it's done reading... + reader.onload = function(readerEvent) { + console.log("reader.onload " + file.name); + var content = new Uint8Array(readerEvent.target.result); // this is the content! + console.log( content ); + FS.writeFile(file.name, content); + Module.TaskCallback_call(fn, file.name); + Module.TaskCallback_delete(fn); + } + } + + input.click(); +} +mergeInto(LibraryManager.library, { + js_pick_file: js_pick_file, +});