From afaf43823c9f8c5c8e8a3432dc6eeffe4db1e90f Mon Sep 17 00:00:00 2001 From: Yousef Khadadeh Date: Mon, 12 Jan 2026 11:27:41 +0300 Subject: [PATCH] Working on better support for Windows clipboard --- .gitmodules | 3 ++ CMakeLists.txt | 6 ++++ data/third_party_licenses/clip | 20 +++++++++++ deps/clip | 1 + src/InputManager.cpp | 64 +++++++++++++++++++++++++++++++--- 5 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 data/third_party_licenses/clip create mode 160000 deps/clip diff --git a/.gitmodules b/.gitmodules index b6b91a4..b5d6155 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "deps/datachannel-wasm"] path = deps/datachannel-wasm url = https://github.com/paullouisageneau/datachannel-wasm.git +[submodule "deps/clip"] + path = deps/clip + url = https://github.com/dacap/clip.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 575b1c5..63858b2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -314,6 +314,12 @@ target_link_libraries(infinipaint zstd::libzstd_static) if((NOT EMSCRIPTEN) AND WIN32) target_link_libraries(infinipaint dwrite) + # Clip library for retrieving images from windows clipboard + set(CLIP_EXAMPLES OFF CACHE BOOL "Compile clip examples") + set(CLIP_TESTS OFF CACHE BOOL "Compile clip tests") + set(CLIP_ENABLE_IMAGE ON CACHE BOOL "Compile with image clipboard support") + add_subdirectory(deps/clip) + target_link_libraries(infinipaint clip) endif() if(EMSCRIPTEN) diff --git a/data/third_party_licenses/clip b/data/third_party_licenses/clip new file mode 100644 index 0000000..a365f94 --- /dev/null +++ b/data/third_party_licenses/clip @@ -0,0 +1,20 @@ +Copyright (c) 2015-2025 David Capello + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/clip b/deps/clip new file mode 160000 index 0000000..4e878e1 --- /dev/null +++ b/deps/clip @@ -0,0 +1 @@ +Subproject commit 4e878e1392dc379a55e5b63de2c822e8f2b6aa98 diff --git a/src/InputManager.cpp b/src/InputManager.cpp index a05e6a4..adfdd04 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -5,6 +5,14 @@ #include +#include + +#ifdef _WIN32_ + #include + #include + #include "../deps/clip/clip.h" +#endif + #ifdef __EMSCRIPTEN__ #include @@ -148,17 +156,65 @@ void InputManager::get_clipboard_image_data_SDL(const std::function(clipboardData), clipboardDataSize)); - SDL_free(clipboardData); + if(std::string(mimeTypes[i]) == "image/bmp") { +#ifdef _WIN32_ + if(!clip::has(clip::image_format())) { + Logger::get().log("INFO", "[InputManager::get_clipboard_image_data_SDL] Clipboard doesn't contain an image"); + return; + } + + clip::image img; + if(!clip::get_image(img)) { + Logger::get().log("INFO", "[InputManager::get_clipboard_image_data_SDL] Error getting image from clipboard"); + return; + } + + clip::image_spec spec = img.spec(); + + if(spec.bits_per_pixel != 8) { + Logger::get().log("INFO", "[InputManager::get_clipboard_image_data_SDL] Clipboard doesn't use 8 bits per pixel"); + return; + } + + char* rawImageData = img.data(); + std::vector rgbaImageData(spec.width * spec.height * 4); + for(size_t y = 0; y < spec.height; y++) { + for(size_t x = 0; x < spec.width; x++) { + size_t rawDataIndex = x + y * spec.bytes_per_row; + size_t rgbaDataIndex = x + y * spec.height; + unsigned long rawDataAtIndex = *reinterpret_cast(rawImageData + rawDataIndex); + rgbaImageData[rgbaDataIndex + 0] = (rawDataAtIndex & spec.red_mask) >> spec.red_shift; + rgbaImageData[rgbaDataIndex + 1] = (rawDataAtIndex & spec.green_mask) >> spec.green_shift; + rgbaImageData[rgbaDataIndex + 2] = (rawDataAtIndex & spec.blue_mask) >> spec.blue_mask; + if(spec.alpha_mask == 0) + rgbaImageData[rgbaDataIndex + 3] = 255; + else + rgbaImageData[rgbaDataIndex + 3] = (rawDataAtIndex & spec.alpha_mask) >> spec.alpha_mask; + } + } + + SkDynamicMemoryWStream out; + SkPngEncoder::Encode(&out, SkPixmap(SkImageInfo::Make(spec.width, spec.height, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType), rgbaImageData.data(), rgbaImageData.size()), {}); + out.flush(); + std::vector result = out.detachAsVector(); + callback(std::string_view(reinterpret_cast(result.data()), result.size())); +#endif + } + else { + size_t clipboardDataSize; + void* clipboardData = SDL_GetClipboardData(mimeTypes[i], &clipboardDataSize); + callback(std::string_view(static_cast(clipboardData), clipboardDataSize)); + SDL_free(clipboardData); + } return; } }