From 506eebab1433922ce9ce8a9cdff39bc463eee657 Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Sun, 20 Sep 2026 22:53:41 +0800 Subject: [PATCH 1/4] fix: preserve reference image dimensions in server requests --- CMakeLists.txt | 7 +- examples/server/routes_openai.cpp | 2 +- tests/CMakeLists.txt | 16 +++++ tests/image_decode.cpp | 105 ++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/image_decode.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 134719ca4..48fad5201 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ endif() # # general -#option(SD_BUILD_TESTS "sd: build tests" ${SD_STANDALONE}) +option(SD_BUILD_TESTS "sd: build tests" OFF) option(SD_BUILD_EXAMPLES "sd: build examples" ${SD_STANDALONE}) option(SD_WEBP "sd: enable WebP image I/O support" ${SD_WEBP_DEFAULT}) option(SD_USE_SYSTEM_WEBP "sd: link against system libwebp" OFF) @@ -352,6 +352,11 @@ if (SD_BUILD_EXAMPLES) add_subdirectory(examples) endif() +if (SD_BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif() + # diff --git a/examples/server/routes_openai.cpp b/examples/server/routes_openai.cpp index 0f122922c..e15247b2a 100644 --- a/examples/server/routes_openai.cpp +++ b/examples/server/routes_openai.cpp @@ -164,7 +164,7 @@ static bool build_openai_edit_request(const httplib::Request& req, reinterpret_cast(bytes.data()), static_cast(bytes.size()), img_w, img_h, - width, height, 3); + 0, 0, 3); if (raw_pixels == nullptr) { continue; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..67ff8ab29 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +add_executable(sd-test-image-decode + image_decode.cpp + ../examples/common/common.cpp + ../examples/common/log.cpp + ../examples/common/media_io.cpp +) + +target_include_directories(sd-test-image-decode PRIVATE + "${PROJECT_SOURCE_DIR}/examples" + "${PROJECT_SOURCE_DIR}/src" +) + +target_link_libraries(sd-test-image-decode PRIVATE stable-diffusion zip ${CMAKE_THREAD_LIBS_INIT}) +target_compile_features(sd-test-image-decode PUBLIC c_std_11 cxx_std_17) + +add_test(NAME sd-test-image-decode COMMAND sd-test-image-decode) diff --git a/tests/image_decode.cpp b/tests/image_decode.cpp new file mode 100644 index 000000000..7f243d7d4 --- /dev/null +++ b/tests/image_decode.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include + +#include "common/common.h" +#include "common/media_io.h" + +static std::string encode_base64(const std::vector& bytes) { + static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + std::string result; + result.reserve((bytes.size() + 2) / 3 * 4); + + for (size_t i = 0; i < bytes.size(); i += 3) { + const uint32_t value = (static_cast(bytes[i]) << 16) | + (i + 1 < bytes.size() ? static_cast(bytes[i + 1]) << 8 : 0) | + (i + 2 < bytes.size() ? static_cast(bytes[i + 2]) : 0); + result.push_back(alphabet[(value >> 18) & 0x3f]); + result.push_back(alphabet[(value >> 12) & 0x3f]); + result.push_back(i + 1 < bytes.size() ? alphabet[(value >> 6) & 0x3f] : '='); + result.push_back(i + 2 < bytes.size() ? alphabet[value & 0x3f] : '='); + } + + return result; +} + +static bool expect_size(const char* name, const sd_image_t& image, uint32_t width, uint32_t height) { + if (image.width == width && image.height == height) { + return true; + } + + std::cerr << name << " has size " << image.width << "x" << image.height << ", expected " << width << "x" + << height << '\n'; + return false; +} + +int main() { + const uint8_t pixels[] = { + 0, + 32, + 64, + 16, + 48, + 80, + 32, + 64, + 96, + 48, + 80, + 112, + 64, + 96, + 128, + 80, + 112, + 144, + 96, + 128, + 160, + 112, + 144, + 176, + }; + const std::vector encoded = encode_image_to_vector(EncodedImageFormat::PNG, pixels, 4, 2, 3); + if (encoded.empty()) { + std::cerr << "failed to encode test image\n"; + return 1; + } + + const std::string image = "data:image/png;base64," + encode_base64(encoded); + + int decoded_width = 0; + int decoded_height = 0; + uint8_t* decoded = load_image_from_memory(reinterpret_cast(encoded.data()), + static_cast(encoded.size()), + decoded_width, + decoded_height, + 0, + 0, + 3); + if (decoded == nullptr || decoded_width != 4 || decoded_height != 2) { + std::cerr << "decode without expected dimensions did not preserve the source size\n"; + free(decoded); + return 1; + } + free(decoded); + + SDGenerationParams params; + const std::string json = "{\"width\":8,\"height\":8,\"init_image\":\"" + image + + "\",\"mask_image\":\"" + image + "\",\"ref_images\":[\"" + image + + "\"]}"; + if (!params.from_json_str(json)) { + std::cerr << "failed to parse image generation parameters\n"; + return 1; + } + + if (!expect_size("init_image", params.init_image.get(), 8, 8) || + !expect_size("mask_image", params.mask_image.get(), 8, 8) || + params.ref_images.size() != 1 || !expect_size("ref_images[0]", params.ref_images[0].get(), 4, 2)) { + return 1; + } + + return 0; +} From 14c58258d2ebd99438d9c96fc4134610159c57c7 Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Mon, 21 Sep 2026 07:55:08 +0800 Subject: [PATCH 2/4] chore: remove test files from PR --- CMakeLists.txt | 10 +--- tests/CMakeLists.txt | 16 ------- tests/image_decode.cpp | 105 ----------------------------------------- 3 files changed, 1 insertion(+), 130 deletions(-) delete mode 100644 tests/CMakeLists.txt delete mode 100644 tests/image_decode.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 48fad5201..e50fc32e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ endif() # # general -option(SD_BUILD_TESTS "sd: build tests" OFF) +#option(SD_BUILD_TESTS "sd: build tests" ${SD_STANDALONE}) option(SD_BUILD_EXAMPLES "sd: build examples" ${SD_STANDALONE}) option(SD_WEBP "sd: enable WebP image I/O support" ${SD_WEBP_DEFAULT}) option(SD_USE_SYSTEM_WEBP "sd: link against system libwebp" OFF) @@ -351,14 +351,6 @@ target_compile_features(${SD_LIB} PUBLIC c_std_11 cxx_std_17) if (SD_BUILD_EXAMPLES) add_subdirectory(examples) endif() - -if (SD_BUILD_TESTS) - enable_testing() - add_subdirectory(tests) -endif() - - - # # install # diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index 67ff8ab29..000000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -add_executable(sd-test-image-decode - image_decode.cpp - ../examples/common/common.cpp - ../examples/common/log.cpp - ../examples/common/media_io.cpp -) - -target_include_directories(sd-test-image-decode PRIVATE - "${PROJECT_SOURCE_DIR}/examples" - "${PROJECT_SOURCE_DIR}/src" -) - -target_link_libraries(sd-test-image-decode PRIVATE stable-diffusion zip ${CMAKE_THREAD_LIBS_INIT}) -target_compile_features(sd-test-image-decode PUBLIC c_std_11 cxx_std_17) - -add_test(NAME sd-test-image-decode COMMAND sd-test-image-decode) diff --git a/tests/image_decode.cpp b/tests/image_decode.cpp deleted file mode 100644 index 7f243d7d4..000000000 --- a/tests/image_decode.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include -#include -#include -#include -#include - -#include "common/common.h" -#include "common/media_io.h" - -static std::string encode_base64(const std::vector& bytes) { - static constexpr char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - std::string result; - result.reserve((bytes.size() + 2) / 3 * 4); - - for (size_t i = 0; i < bytes.size(); i += 3) { - const uint32_t value = (static_cast(bytes[i]) << 16) | - (i + 1 < bytes.size() ? static_cast(bytes[i + 1]) << 8 : 0) | - (i + 2 < bytes.size() ? static_cast(bytes[i + 2]) : 0); - result.push_back(alphabet[(value >> 18) & 0x3f]); - result.push_back(alphabet[(value >> 12) & 0x3f]); - result.push_back(i + 1 < bytes.size() ? alphabet[(value >> 6) & 0x3f] : '='); - result.push_back(i + 2 < bytes.size() ? alphabet[value & 0x3f] : '='); - } - - return result; -} - -static bool expect_size(const char* name, const sd_image_t& image, uint32_t width, uint32_t height) { - if (image.width == width && image.height == height) { - return true; - } - - std::cerr << name << " has size " << image.width << "x" << image.height << ", expected " << width << "x" - << height << '\n'; - return false; -} - -int main() { - const uint8_t pixels[] = { - 0, - 32, - 64, - 16, - 48, - 80, - 32, - 64, - 96, - 48, - 80, - 112, - 64, - 96, - 128, - 80, - 112, - 144, - 96, - 128, - 160, - 112, - 144, - 176, - }; - const std::vector encoded = encode_image_to_vector(EncodedImageFormat::PNG, pixels, 4, 2, 3); - if (encoded.empty()) { - std::cerr << "failed to encode test image\n"; - return 1; - } - - const std::string image = "data:image/png;base64," + encode_base64(encoded); - - int decoded_width = 0; - int decoded_height = 0; - uint8_t* decoded = load_image_from_memory(reinterpret_cast(encoded.data()), - static_cast(encoded.size()), - decoded_width, - decoded_height, - 0, - 0, - 3); - if (decoded == nullptr || decoded_width != 4 || decoded_height != 2) { - std::cerr << "decode without expected dimensions did not preserve the source size\n"; - free(decoded); - return 1; - } - free(decoded); - - SDGenerationParams params; - const std::string json = "{\"width\":8,\"height\":8,\"init_image\":\"" + image + - "\",\"mask_image\":\"" + image + "\",\"ref_images\":[\"" + image + - "\"]}"; - if (!params.from_json_str(json)) { - std::cerr << "failed to parse image generation parameters\n"; - return 1; - } - - if (!expect_size("init_image", params.init_image.get(), 8, 8) || - !expect_size("mask_image", params.mask_image.get(), 8, 8) || - params.ref_images.size() != 1 || !expect_size("ref_images[0]", params.ref_images[0].get(), 4, 2)) { - return 1; - } - - return 0; -} From 508fc3e0c298894c3a1d5b3410522e62e28284e5 Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Mon, 21 Sep 2026 08:05:47 +0800 Subject: [PATCH 3/4] chore: preserve base CMake layout --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e50fc32e3..134719ca4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -351,6 +351,9 @@ target_compile_features(${SD_LIB} PUBLIC c_std_11 cxx_std_17) if (SD_BUILD_EXAMPLES) add_subdirectory(examples) endif() + + + # # install # From 1aabbdc54bda46270caae8af8fd0c20be15ed5da Mon Sep 17 00:00:00 2001 From: mikemikimike <13286568797@163.com> Date: Tue, 22 Sep 2026 01:47:25 +0800 Subject: [PATCH 4/4] fix: preserve OpenAI init image preprocessing Signed-off-by: mikemikimike <13286568797@163.com> --- examples/server/routes_openai.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/examples/server/routes_openai.cpp b/examples/server/routes_openai.cpp index e15247b2a..ccde2baef 100644 --- a/examples/server/routes_openai.cpp +++ b/examples/server/routes_openai.cpp @@ -169,13 +169,31 @@ static bool build_openai_edit_request(const httplib::Request& req, continue; } + const bool is_first_ref_image = request.gen_params.ref_images.empty(); SDImageOwner image_owner({(uint32_t)img_w, (uint32_t)img_h, 3, raw_pixels}); request.gen_params.set_width_and_height_if_unset(image_owner.get().width, image_owner.get().height); - request.gen_params.ref_images.push_back(std::move(image_owner)); - } - if (!request.gen_params.ref_images.empty()) { - request.gen_params.init_image = request.gen_params.ref_images.front(); + if (is_first_ref_image) { + int init_w = 0; + int init_h = 0; + if (request.gen_params.width_and_height_are_set()) { + init_w = request.gen_params.width; + init_h = request.gen_params.height; + } + + int init_img_w = 0; + int init_img_h = 0; + uint8_t* init_pixels = load_image_from_memory( + reinterpret_cast(bytes.data()), + static_cast(bytes.size()), + init_img_w, init_img_h, + init_w, init_h, 3); + if (init_pixels != nullptr) { + request.gen_params.init_image.reset({(uint32_t)init_img_w, (uint32_t)init_img_h, 3, init_pixels}); + } + } + + request.gen_params.ref_images.push_back(std::move(image_owner)); } if (!mask_bytes.empty()) {