From 00784402ea9fb24dfd9f528ac47e587f8778a9a3 Mon Sep 17 00:00:00 2001 From: Liyulingyue <852433440@qq.com> Date: Sun, 21 Dec 2025 20:40:04 +0800 Subject: [PATCH 1/5] feat: add HTML interface and update server logic for image generation --- examples/server/CMakeLists.txt | 18 +- examples/server/main.cpp | 39 +++-- examples/server/public/index.html | 269 ++++++++++++++++++++++++++++++ scripts/xxd.cmake | 6 + 4 files changed, 317 insertions(+), 15 deletions(-) create mode 100644 examples/server/public/index.html create mode 100644 scripts/xxd.cmake diff --git a/examples/server/CMakeLists.txt b/examples/server/CMakeLists.txt index d19126080..d6bcb0faf 100644 --- a/examples/server/CMakeLists.txt +++ b/examples/server/CMakeLists.txt @@ -1,6 +1,22 @@ set(TARGET sd-server) -add_executable(${TARGET} main.cpp) +set(PUBLIC_ASSETS + index.html +) + +foreach(asset ${PUBLIC_ASSETS}) + set(input "${CMAKE_CURRENT_SOURCE_DIR}/public/${asset}") + set(output "${CMAKE_CURRENT_BINARY_DIR}/${asset}.hpp") + list(APPEND TARGET_SRCS ${output}) + add_custom_command( + DEPENDS "${input}" + OUTPUT "${output}" + COMMAND "${CMAKE_COMMAND}" "-DINPUT=${input}" "-DOUTPUT=${output}" -P "${PROJECT_SOURCE_DIR}/scripts/xxd.cmake" + ) +endforeach() + +add_executable(${TARGET} main.cpp ${TARGET_SRCS}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) install(TARGETS ${TARGET} RUNTIME) target_link_libraries(${TARGET} PRIVATE stable-diffusion ${CMAKE_THREAD_LIBS_INIT}) target_compile_features(${TARGET} PUBLIC c_std_11 cxx_std_17) \ No newline at end of file diff --git a/examples/server/main.cpp b/examples/server/main.cpp index 39359fbbe..b07266e1f 100644 --- a/examples/server/main.cpp +++ b/examples/server/main.cpp @@ -13,6 +13,8 @@ #include "common/common.hpp" +#include "index.html.hpp" + namespace fs = std::filesystem; // ----------------------- helpers ----------------------- @@ -312,7 +314,7 @@ int main(int argc, const char** argv) { // health svr.Get("/", [&](const httplib::Request&, httplib::Response& res) { - res.set_content(R"({"ok":true,"service":"sd-cpp-http"})", "application/json"); + res.set_content(reinterpret_cast(index), index_len, "text/html"); }); // models endpoint (minimal) @@ -332,14 +334,16 @@ int main(int argc, const char** argv) { return; } - json j = json::parse(req.body); - std::string prompt = j.value("prompt", ""); - int n = std::max(1, j.value("n", 1)); - std::string size = j.value("size", ""); - std::string output_format = j.value("output_format", "png"); - int output_compression = j.value("output_compression", 100); - int width = 512; - int height = 512; + json j = json::parse(req.body); + std::string prompt = j.value("prompt", ""); + int n = std::max(1, j.value("n", 1)); + std::string size = j.value("size", ""); + std::string response_format = j.value("response_format", "b64_json"); + std::string output_format = j.value("output_format", "png"); + int output_compression = j.value("output_compression", 100); + int width = j.value("width", 512); + int height = j.value("height", 512); + int sample_steps = j.value("steps", j.value("sample_steps", 20)); if (!size.empty()) { auto pos = size.find('x'); if (pos != std::string::npos) { @@ -359,6 +363,12 @@ int main(int argc, const char** argv) { std::string sd_cpp_extra_args_str = extract_and_remove_sd_cpp_extra_args(prompt); + if (response_format != "b64_json") { + res.status = 400; + res.set_content(R"({"error":"invalid response_format, only b64_json is supported"})", "application/json"); + return; + } + if (output_format != "png" && output_format != "jpeg") { res.status = 400; res.set_content(R"({"error":"invalid output_format, must be one of [png, jpeg]"})", "application/json"); @@ -380,11 +390,12 @@ int main(int argc, const char** argv) { out["data"] = json::array(); out["output_format"] = output_format; - SDGenerationParams gen_params = default_gen_params; - gen_params.prompt = prompt; - gen_params.width = width; - gen_params.height = height; - gen_params.batch_count = n; + SDGenerationParams gen_params = default_gen_params; + gen_params.prompt = prompt; + gen_params.width = width; + gen_params.height = height; + gen_params.batch_count = n; + gen_params.sample_params.sample_steps = sample_steps; if (!sd_cpp_extra_args_str.empty() && !gen_params.from_json_str(sd_cpp_extra_args_str)) { res.status = 400; diff --git a/examples/server/public/index.html b/examples/server/public/index.html new file mode 100644 index 000000000..d91c10dfd --- /dev/null +++ b/examples/server/public/index.html @@ -0,0 +1,269 @@ + + + + + + Stable Diffusion Chat + + + +
+ +
+
+
+ + +
+
+
+ + + + + \ No newline at end of file diff --git a/scripts/xxd.cmake b/scripts/xxd.cmake new file mode 100644 index 000000000..d36f0f44f --- /dev/null +++ b/scripts/xxd.cmake @@ -0,0 +1,6 @@ +file(READ "${INPUT}" hex_data HEX) +string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," hex_data "${hex_data}") +string(REGEX REPLACE ",$" "" hex_data "${hex_data}") +get_filename_component(name "${INPUT}" NAME_WE) +string(MAKE_C_IDENTIFIER "${name}" name) +file(WRITE "${OUTPUT}" "unsigned char ${name}[] = {${hex_data}};\nunsigned int ${name}_len = sizeof(${name});\n") \ No newline at end of file From 56b011a1861e2f15181b329ec3648aaf8076e97e Mon Sep 17 00:00:00 2001 From: Liyulingyue <852433440@qq.com> Date: Sun, 21 Dec 2025 22:58:25 +0800 Subject: [PATCH 2/5] feat: implement HTML interface support and update server logic for serving HTML files --- examples/server/CMakeLists.txt | 17 +---------- examples/server/README.md | 12 ++++++++ .../{public/index.html => demo_txt2img.html} | 0 examples/server/main.cpp | 28 ++++++++++++++++--- scripts/xxd.cmake | 6 ---- 5 files changed, 37 insertions(+), 26 deletions(-) rename examples/server/{public/index.html => demo_txt2img.html} (100%) delete mode 100644 scripts/xxd.cmake diff --git a/examples/server/CMakeLists.txt b/examples/server/CMakeLists.txt index d6bcb0faf..56f6355db 100644 --- a/examples/server/CMakeLists.txt +++ b/examples/server/CMakeLists.txt @@ -1,21 +1,6 @@ set(TARGET sd-server) -set(PUBLIC_ASSETS - index.html -) - -foreach(asset ${PUBLIC_ASSETS}) - set(input "${CMAKE_CURRENT_SOURCE_DIR}/public/${asset}") - set(output "${CMAKE_CURRENT_BINARY_DIR}/${asset}.hpp") - list(APPEND TARGET_SRCS ${output}) - add_custom_command( - DEPENDS "${input}" - OUTPUT "${output}" - COMMAND "${CMAKE_COMMAND}" "-DINPUT=${input}" "-DOUTPUT=${output}" -P "${PROJECT_SOURCE_DIR}/scripts/xxd.cmake" - ) -endforeach() - -add_executable(${TARGET} main.cpp ${TARGET_SRCS}) +add_executable(${TARGET} main.cpp) include_directories(${CMAKE_CURRENT_BINARY_DIR}) install(TARGETS ${TARGET} RUNTIME) target_link_libraries(${TARGET} PRIVATE stable-diffusion ${CMAKE_THREAD_LIBS_INIT}) diff --git a/examples/server/README.md b/examples/server/README.md index ae1049680..e559b3a4c 100644 --- a/examples/server/README.md +++ b/examples/server/README.md @@ -6,6 +6,7 @@ usage: ./bin/sd-server [options] Svr Options: -l, --listen-ip server listen ip (default: 127.0.0.1) --listen-port server listen port (default: 1234) + --serve-html-path path to HTML file to serve at root (optional) -v, --verbose print extra info --color colors the logging tags according to level -h, --help show this help message and exit @@ -121,4 +122,15 @@ Default Generation Options: --high-noise-skip-layers (high noise) layers to skip for SLG steps (default: [7,8,9]) -r, --ref-image reference image for Flux Kontext models (can be used multiple times) --easycache enable EasyCache for DiT models with optional "threshold,start_percent,end_percent" (default: 0.2,0.15,0.95) + +## HTML Interface + +To enable a web-based interface, use the `--serve-html-path` option to specify the path to an HTML file. A demo HTML file `demo.html` is provided in this directory for testing purposes. + +Example: +``` +./bin/sd-server --serve-html-path demo.html [other options] +``` + +Then open `http://127.0.0.1:1234` in your browser to access the interface. ``` diff --git a/examples/server/public/index.html b/examples/server/demo_txt2img.html similarity index 100% rename from examples/server/public/index.html rename to examples/server/demo_txt2img.html diff --git a/examples/server/main.cpp b/examples/server/main.cpp index b07266e1f..bdba0aa09 100644 --- a/examples/server/main.cpp +++ b/examples/server/main.cpp @@ -13,8 +13,6 @@ #include "common/common.hpp" -#include "index.html.hpp" - namespace fs = std::filesystem; // ----------------------- helpers ----------------------- @@ -106,6 +104,7 @@ std::string iso_timestamp_now() { struct SDSvrParams { std::string listen_ip = "127.0.0.1"; int listen_port = 1234; + std::string serve_html_path; bool normal_exit = false; bool verbose = false; bool color = false; @@ -117,7 +116,11 @@ struct SDSvrParams { {"-l", "--listen-ip", "server listen ip (default: 127.0.0.1)", - &listen_ip}}; + &listen_ip}, + {"", + "--serve-html-path", + "path to HTML file to serve at root (optional)", + &serve_html_path}}; options.int_options = { {"", @@ -161,6 +164,11 @@ struct SDSvrParams { LOG_ERROR("error: listen_port should be in the range [0, 65535]"); return false; } + + if (!serve_html_path.empty() && !fs::exists(serve_html_path)) { + LOG_ERROR("error: serve_html_path file does not exist: %s", serve_html_path.c_str()); + return false; + } return true; } @@ -169,6 +177,7 @@ struct SDSvrParams { oss << "SDSvrParams {\n" << " listen_ip: " << listen_ip << ",\n" << " listen_port: \"" << listen_port << "\",\n" + << " serve_html_path: \"" << serve_html_path << "\",\n" << "}"; return oss.str(); } @@ -314,7 +323,18 @@ int main(int argc, const char** argv) { // health svr.Get("/", [&](const httplib::Request&, httplib::Response& res) { - res.set_content(reinterpret_cast(index), index_len, "text/html"); + if (!svr_params.serve_html_path.empty()) { + std::ifstream file(svr_params.serve_html_path); + if (file) { + std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + res.set_content(content, "text/html"); + } else { + res.status = 500; + res.set_content("Error: Unable to read HTML file", "text/plain"); + } + } else { + res.set_content("Stable Diffusion Server is running", "text/plain"); + } }); // models endpoint (minimal) diff --git a/scripts/xxd.cmake b/scripts/xxd.cmake deleted file mode 100644 index d36f0f44f..000000000 --- a/scripts/xxd.cmake +++ /dev/null @@ -1,6 +0,0 @@ -file(READ "${INPUT}" hex_data HEX) -string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," hex_data "${hex_data}") -string(REGEX REPLACE ",$" "" hex_data "${hex_data}") -get_filename_component(name "${INPUT}" NAME_WE) -string(MAKE_C_IDENTIFIER "${name}" name) -file(WRITE "${OUTPUT}" "unsigned char ${name}[] = {${hex_data}};\nunsigned int ${name}_len = sizeof(${name});\n") \ No newline at end of file From 033ff71d20141d6d93c2ece0c04c7df0d8881879 Mon Sep 17 00:00:00 2001 From: Liyulingyue <852433440@qq.com> Date: Sun, 21 Dec 2025 23:01:31 +0800 Subject: [PATCH 3/5] feat: remove unused variables and simplify parameter handling in main function --- examples/server/CMakeLists.txt | 1 - examples/server/main.cpp | 35 +++++++++++++--------------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/examples/server/CMakeLists.txt b/examples/server/CMakeLists.txt index 56f6355db..d19126080 100644 --- a/examples/server/CMakeLists.txt +++ b/examples/server/CMakeLists.txt @@ -1,7 +1,6 @@ set(TARGET sd-server) add_executable(${TARGET} main.cpp) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) install(TARGETS ${TARGET} RUNTIME) target_link_libraries(${TARGET} PRIVATE stable-diffusion ${CMAKE_THREAD_LIBS_INIT}) target_compile_features(${TARGET} PUBLIC c_std_11 cxx_std_17) \ No newline at end of file diff --git a/examples/server/main.cpp b/examples/server/main.cpp index bdba0aa09..a139e663b 100644 --- a/examples/server/main.cpp +++ b/examples/server/main.cpp @@ -354,16 +354,14 @@ int main(int argc, const char** argv) { return; } - json j = json::parse(req.body); - std::string prompt = j.value("prompt", ""); - int n = std::max(1, j.value("n", 1)); - std::string size = j.value("size", ""); - std::string response_format = j.value("response_format", "b64_json"); - std::string output_format = j.value("output_format", "png"); - int output_compression = j.value("output_compression", 100); - int width = j.value("width", 512); - int height = j.value("height", 512); - int sample_steps = j.value("steps", j.value("sample_steps", 20)); + json j = json::parse(req.body); + std::string prompt = j.value("prompt", ""); + int n = std::max(1, j.value("n", 1)); + std::string size = j.value("size", ""); + std::string output_format = j.value("output_format", "png"); + int output_compression = j.value("output_compression", 100); + int width = 512; + int height = 512; if (!size.empty()) { auto pos = size.find('x'); if (pos != std::string::npos) { @@ -383,12 +381,6 @@ int main(int argc, const char** argv) { std::string sd_cpp_extra_args_str = extract_and_remove_sd_cpp_extra_args(prompt); - if (response_format != "b64_json") { - res.status = 400; - res.set_content(R"({"error":"invalid response_format, only b64_json is supported"})", "application/json"); - return; - } - if (output_format != "png" && output_format != "jpeg") { res.status = 400; res.set_content(R"({"error":"invalid output_format, must be one of [png, jpeg]"})", "application/json"); @@ -410,12 +402,11 @@ int main(int argc, const char** argv) { out["data"] = json::array(); out["output_format"] = output_format; - SDGenerationParams gen_params = default_gen_params; - gen_params.prompt = prompt; - gen_params.width = width; - gen_params.height = height; - gen_params.batch_count = n; - gen_params.sample_params.sample_steps = sample_steps; + SDGenerationParams gen_params = default_gen_params; + gen_params.prompt = prompt; + gen_params.width = width; + gen_params.height = height; + gen_params.batch_count = n; if (!sd_cpp_extra_args_str.empty() && !gen_params.from_json_str(sd_cpp_extra_args_str)) { res.status = 400; From 328ade31a885257583887fbd7fe2e68a010d8827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=98=A5=E4=B9=94?= <83450930+Liyulingyue@users.noreply.github.com> Date: Tue, 23 Dec 2025 05:25:59 +0800 Subject: [PATCH 4/5] Delete examples/server/demo_txt2img.html --- examples/server/demo_txt2img.html | 269 ------------------------------ 1 file changed, 269 deletions(-) delete mode 100644 examples/server/demo_txt2img.html diff --git a/examples/server/demo_txt2img.html b/examples/server/demo_txt2img.html deleted file mode 100644 index d91c10dfd..000000000 --- a/examples/server/demo_txt2img.html +++ /dev/null @@ -1,269 +0,0 @@ - - - - - - Stable Diffusion Chat - - - -
- -
-
-
- - -
-
-
- - - - - \ No newline at end of file From 87f431469c89f2dd1fa800f015f849aebaef3114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=98=A5=E4=B9=94?= <83450930+Liyulingyue@users.noreply.github.com> Date: Tue, 23 Dec 2025 05:30:05 +0800 Subject: [PATCH 5/5] Update README.md --- examples/server/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/server/README.md b/examples/server/README.md index 453ef8cb0..591411944 100644 --- a/examples/server/README.md +++ b/examples/server/README.md @@ -135,5 +135,4 @@ Default Generation Options: --cache-preset cache-dit preset: 'slow'/'s', 'medium'/'m', 'fast'/'f', 'ultra'/'u' --scm-mask SCM steps mask for cache-dit: comma-separated 0/1 (e.g., "1,1,1,0,0,1,0,0,1,0") - 1=compute, 0=can cache --scm-policy SCM policy: 'dynamic' (default) or 'static' - --serve-html-path specify a path of an HTML file to enable a web-based interface ```