Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow getting image data from the system clipboard #2949

Closed
radzo73 opened this issue Jul 5, 2021 · 3 comments · Fixed by godotengine/godot#63826
Closed

Allow getting image data from the system clipboard #2949

radzo73 opened this issue Jul 5, 2021 · 3 comments · Fixed by godotengine/godot#63826
Milestone

Comments

@radzo73
Copy link

radzo73 commented Jul 5, 2021

Describe the project you are working on

My project's user inputs are images. The planned ways to accept said images are:
1: Dragging an image into the application,
2: Clicking the image well to open a FileDialog, and
3: Pasting from the Clipboard

Describe the problem or limitation you are having in your project

Currently, you can only grab Strings from the Clipboard. Image data, files, or really anything else? Nope!

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Updating Clipboard support to include additional formats (image data, sounds, videos, etc.) would help people a lot.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

var clipboardImage = OS.get_clipboard()
if clipboardImage is Image:
    #do image things
else:
    #hey wait this isn't an image

If this enhancement will not be used often, can it be worked around with a few lines of script?

Currently, no, because you can only grab Strings from the Clipboard.

Is there a reason why this should be core and not an add-on in the asset library?

In my honest opinion, features that use/require system data should be core. Something as simple and useful as, for example, OS.get_screen_size(), shouldn't be separate from the rest of the OS functions

@Calinou Calinou changed the title Better Clipboard Support Allow getting binary files from the system clipboard Jul 5, 2021
@Calinou
Copy link
Member

Calinou commented Jul 5, 2021

This is likely something that can already be implemented manually. If you copy a file in the OS file manager and then use OS.get_clipboard() in Godot, you get a path of the form:

file:///home/hugo/Documents/Git/godotengine/godot/scene/resources/default_theme/dropdown.png

I tested this on Linux, but it likely behaves the same on other operating systems. (If it doesn't, we should probably harmonize the behavior across platforms instead.)

When you have multiple files copied in the system clipboard, you will have one file:// on each line. (Use String's split() method to handle this in a for loop.)

You can then parse this string and load the image file as a resource based on its file extension. For example, you could use the File class and the Image class' load_png_from_buffer() method to achieve this.

To determine whether the clipboard contains a file or text, check if the string starts with file:// using if OS.get_clipboard().begins_with("file://").

@radzo73
Copy link
Author

radzo73 commented Jul 6, 2021

The problem with that method is that it doesn't work if I copy image data from, say, GIMP (or even from Google Images), then it only returns an empty String, and I'm back to square one.

@rainlizard
Copy link

rainlizard commented Jul 6, 2021

When I made this tool: FastRecolour (a pretty useless tool honestly) I was looking into how to copy image data to & from the clipboard and couldn't figure it out. I tried using C# in the Godot mono-build but I think the data was corrupted or something when pasting (not skilled enough with C# to know if it deserved a bug report). I ended up successfully making an external executable file (copypaste.exe) included alongside my Godot game and would use when handling clipboard image data.

here's the C# source code for my copypaste.exe executable
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Runtime.InteropServices;
public class copypaste {
    const int SW_SHOW = 5;
    const int SW_SHOWMINIMIZED = 2;
    const int SW_SHOWMINNOACTIVE = 7;
    const int SW_HIDE = 0;
    
    [DllImport("user32.dll")]
    static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    [DllImport("kernel32.dll")]
    static extern IntPtr GetConsoleWindow();
    
    [STAThread]
    //argument 0 = copy, 1 = paste.
    //returns 0 = failed, 1 = success.
    static int Main(String[] argument) {
        
        // Hide window
        IntPtr myWindow = GetConsoleWindow();
        ShowWindow(myWindow, SW_HIDE);
        
        if (argument.Length > 0) { //Prevents crashing when you pass no argument.
            if (argument[0] == "copy") {
                //Copy
                Image img = Image.FromFile("clipboard_copy.png");
                Clipboard.SetImage(img);
                return 1;
            } else {
                //Paste
                System.Drawing.Image clipboard_image = GetClipboardImage();
                if (clipboard_image is Image) {
                    //print("Saved clipboard_paste.png");
                    clipboard_image.Save("clipboard_paste.png", ImageFormat.Png);
                    return 1;
                }
                return 0;
            }
        }
        return 0;
	}
    
    static System.Drawing.Image GetClipboardImage() { // Get a PNG from the clipboard if possible. Otherwise try to get a bitmap.
        
        // Try to paste PNG data.
        if (Clipboard.ContainsData("PNG")) { //Saves alpha channel from Paint.NET, and possibly GIMP.
            object png_object = Clipboard.GetData("PNG");
            if (png_object is MemoryStream) {
                MemoryStream png_stream = png_object as MemoryStream;
                return System.Drawing.Image.FromStream(png_stream);
            }
        }
        
        // Try to paste bitmap data.
        if (Clipboard.ContainsImage()) {
            return Clipboard.GetImage();
        }
        
        return null;
    }
    
    /*static void print(object custom) {
        Console.WriteLine(custom);
    }*/
}
It creates a clipboard_paste.png file which you can then read from GDScript, or reads a clipboard_copy.png file that was created by GDScript.

This is a very very clunky method however. All I can do is toss my vote in for a GDScript method of properly handling clipboard image data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment