From a2e17f346595775f22a7c689fcf256228713ca9b Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 10 Oct 2025 17:31:15 -0700
Subject: [PATCH] Added IMG_GetClipboardImage() and showclipboard example
program
---
CMakeLists.txt | 1 +
examples/showclipboard.c | 160 +++++++++++++++++++++++++++++++++
include/SDL3_image/SDL_image.h | 12 +++
src/IMG.c | 26 ++++++
src/SDL_image.sym | 1 +
5 files changed, 200 insertions(+)
create mode 100644 examples/showclipboard.c
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ff74b40a..69db9866 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1283,6 +1283,7 @@ if(SDLIMAGE_SAMPLES)
endfunction()
add_sdl_image_example_executable(showanim examples/showanim.c)
add_sdl_image_example_executable(showimage examples/showimage.c)
+ add_sdl_image_example_executable(showclipboard examples/showclipboard.c)
endif()
if(SDLIMAGE_TESTS)
diff --git a/examples/showclipboard.c b/examples/showclipboard.c
new file mode 100644
index 00000000..e2686a4d
--- /dev/null
+++ b/examples/showclipboard.c
@@ -0,0 +1,160 @@
+/*
+ showclipboard: A test application for the SDL image loading library.
+ Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include <SDL3/SDL.h>
+#include <SDL3/SDL_main.h>
+#include <SDL3_image/SDL_image.h>
+
+
+static SDL_Texture *load_clipboard(SDL_Window *window, SDL_Renderer *renderer)
+{
+ SDL_Texture *texture = NULL;
+ SDL_Surface *surface = IMG_GetClipboardImage();
+ if (surface) {
+ char *text = SDL_GetClipboardText();
+ if (text && *text) {
+ SDL_SetWindowTitle(window, text);
+ } else {
+ SDL_SetWindowTitle(window, "Copy an image and click here");
+ }
+ SDL_free(text);
+
+ texture = SDL_CreateTextureFromSurface(renderer, surface);
+
+ SDL_SetWindowTitle(window, SDL_GetClipboardText());
+ SDL_SetWindowSize(window, surface->w, surface->h);
+ SDL_SetRenderLogicalPresentation(renderer, surface->w, surface->h, SDL_LOGICAL_PRESENTATION_LETTERBOX);
+
+ SDL_DestroySurface(surface);
+ }
+ return texture;
+}
+
+/* Draw a Gimpish background pattern to show transparency in the image */
+static void draw_background(SDL_Renderer *renderer)
+{
+ const SDL_Color col[2] = {
+ { 0x66, 0x66, 0x66, 0xff },
+ { 0x99, 0x99, 0x99, 0xff }
+ };
+ const int dx = 8, dy = 8;
+ SDL_FRect rect;
+ int i, x, y, w, h;
+
+ SDL_GetCurrentRenderOutputSize(renderer, &w, &h);
+
+ rect.w = (float)dx;
+ rect.h = (float)dy;
+ for (y = 0; y < h; y += dy) {
+ for (x = 0; x < w; x += dx) {
+ /* use an 8x8 checkerboard pattern */
+ i = (((x ^ y) >> 3) & 1);
+ SDL_SetRenderDrawColor(renderer, col[i].r, col[i].g, col[i].b, col[i].a);
+
+ rect.x = (float)x;
+ rect.y = (float)y;
+ SDL_RenderFillRect(renderer, &rect);
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ SDL_Window *window = NULL;
+ SDL_Renderer *renderer = NULL;
+ SDL_Texture *texture = NULL;
+ Uint32 flags = 0;
+ int i;
+ bool done = false;
+ SDL_Event event;
+ int result = 0;
+
+ (void)argc;
+
+ /* Check command line usage */
+ for ( i=1; argv[i]; ++i ) {
+ if (SDL_strcmp(argv[i], "-fullscreen") == 0) {
+ SDL_HideCursor();
+ flags |= SDL_WINDOW_FULLSCREEN;
+ } else {
+ SDL_Log("Usage: %s [-fullscreen]\n", argv[0]);
+ result = 1;
+ goto done;
+ }
+ }
+
+ if (!SDL_Init(SDL_INIT_VIDEO)) {
+ SDL_Log("SDL_Init(SDL_INIT_VIDEO) failed: %s\n", SDL_GetError());
+ result = 2;
+ goto done;
+ }
+
+ if (!SDL_CreateWindowAndRenderer("", 640, 480, flags, &window, &renderer)) {
+ SDL_Log("SDL_CreateWindowAndRenderer() failed: %s\n", SDL_GetError());
+ result = 2;
+ goto done;
+ }
+
+ texture = load_clipboard(window, renderer);
+
+ while ( !done ) {
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_EVENT_CLIPBOARD_UPDATE:
+ texture = load_clipboard(window, renderer);
+ break;
+ case SDL_EVENT_KEY_UP:
+ switch (event.key.key) {
+ case SDLK_ESCAPE:
+ case SDLK_Q:
+ done = 1;
+ break;
+ }
+ break;
+ case SDL_EVENT_QUIT:
+ done = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Draw a background pattern in case the image has transparency */
+ draw_background(renderer);
+
+ /* Display the image */
+ if (texture) {
+ SDL_RenderTexture(renderer, texture, NULL, NULL);
+ }
+ SDL_RenderPresent(renderer);
+
+ SDL_Delay(100);
+ }
+
+ if (texture) {
+ SDL_DestroyTexture(texture);
+ }
+
+ /* We're done! */
+done:
+ SDL_Quit();
+ return result;
+}
diff --git a/include/SDL3_image/SDL_image.h b/include/SDL3_image/SDL_image.h
index 4f2a61ec..0254b2b9 100644
--- a/include/SDL3_image/SDL_image.h
+++ b/include/SDL3_image/SDL_image.h
@@ -363,6 +363,18 @@ extern SDL_DECLSPEC SDL_Texture * SDLCALL IMG_LoadTexture_IO(SDL_Renderer *rende
*/
extern SDL_DECLSPEC SDL_Texture * SDLCALL IMG_LoadTextureTyped_IO(SDL_Renderer *renderer, SDL_IOStream *src, bool closeio, const char *type);
+/**
+ * Get the image currently in the clipboard.
+ *
+ * When done with the returned surface, the app should dispose of it with a
+ * call to SDL_DestroySurface().
+ *
+ * \returns a new SDL surface, or NULL if no supported image is available.
+ *
+ * \since This function is available since SDL_image 3.4.0.
+ */
+extern SDL_DECLSPEC SDL_Surface * SDLCALL IMG_GetClipboardImage(void);
+
/**
* Detect AVIF image data on a readable/seekable SDL_IOStream.
*
diff --git a/src/IMG.c b/src/IMG.c
index 0aec229c..8572fcd9 100644
--- a/src/IMG.c
+++ b/src/IMG.c
@@ -411,6 +411,32 @@ bool IMG_SaveTyped_IO(SDL_Surface *surface, SDL_IOStream *dst, bool closeio, con
return result;
}
+SDL_Surface *IMG_GetClipboardImage(void)
+{
+ SDL_Surface *surface = NULL;
+
+ char **mime_types = SDL_GetClipboardMimeTypes(NULL);
+ if (mime_types) {
+ for (int i = 0; !surface && mime_types[i]; ++i) {
+ if (SDL_strncmp(mime_types[i], "image/", 6) == 0) {
+ size_t size = 0;
+ void *data = SDL_GetClipboardData(mime_types[i], &size);
+ if (data) {
+ SDL_IOStream *src = SDL_IOFromConstMem(data, size);
+ if (src) {
+ surface = IMG_Load_IO(src, true);
+ }
+ SDL_free(data);
+ }
+ }
+ }
+ }
+ if (!surface) {
+ SDL_SetError("No clipboard image available");
+ }
+ return surface;
+}
+
Uint64 IMG_TimebaseDuration(Uint64 pts, Uint64 duration, Uint64 src_numerator, Uint64 src_denominator, Uint64 dst_numerator, Uint64 dst_denominator)
{
Uint64 a = ( ( ( ( pts + duration ) * 2 ) + 1 ) * src_numerator * dst_denominator ) / ( 2 * src_denominator * dst_numerator );
diff --git a/src/SDL_image.sym b/src/SDL_image.sym
index eb570463..96234f4e 100644
--- a/src/SDL_image.sym
+++ b/src/SDL_image.sym
@@ -84,5 +84,6 @@ SDL3_image_0.0.0 {
IMG_CloseAnimationDecoder;
IMG_GetAnimationDecoderProperties;
IMG_GetAnimationDecoderStatus;
+ IMG_GetClipboardImage;
local: *;
};