Search Unity

Get Image from Clipboard

Discussion in 'Editor & General Support' started by GamerXP, Jan 3, 2019.

  1. GamerXP

    GamerXP

    Joined:
    Mar 22, 2014
    Posts:
    78
    I'm currently writing some tools for editor and I need to be able to load image from clipboard, usually from some graphics editors. It's more of C# problem probably, but I didn't find any useful info on it there.

    I tried using System.Windows.Forms.dll + System.Drawing.dll, copied from Editors's files, to load image from clipboard and convert it to Texture2D. Problem is that it loads images wrong:

    1. If I copy from browser, with print screen on crop tool - I get image with byte BGRA byte order, but first 3 pixels are messed up and 3 columns from right side are moved to left side.

    2. If I copy from Photoshop, Gimp or Paint - it loses transparency and color order becomes BGR-BGR-BGR and 0s for every pixel at the end of line. Problem is that all bitmap's settings are totally same as in case 1 and there is absolutely no way to say if byte order is messed up or not.

    After 3 days of googling I found a lot of example for reading image from buffer. Most of them had image from clipboard returned as MemoryStream, but I didn't find any way to do the same - it always returns already processed Bitmap.

    Anyone know the way to load clipboard image properly?
     
  2. Shinho

    Shinho

    Joined:
    Mar 21, 2013
    Posts:
    6
    I have the same problem, and I have tried multiple solution with no success.
     
  3. Shinho

    Shinho

    Joined:
    Mar 21, 2013
    Posts:
    6
    I came up with this ClipboardTexture class, it's serving my needs, but still not a very elegant and full proof solution. You will have problems with png file with transparent pixels.
    var clipboard = new ClipboardTexture();
    var texture = clipboard.GetClipboardTexture();
     

    Attached Files:

  4. GamerXP

    GamerXP

    Joined:
    Mar 22, 2014
    Posts:
    78
    What are "ImageUtils"?
    Code (CSharp):
    1. var bm32bData = ImageUtils.GetImageData(image, out stride);
     
  5. Shinho

    Shinho

    Joined:
    Mar 21, 2013
    Posts:
    6
    Just change ImageUtils.GetImageData to GetImageByteData. Seems I did not save before sending the file. Unforunatelly the 2019 version is breaking it.
     
  6. GamerXP

    GamerXP

    Joined:
    Mar 22, 2014
    Posts:
    78
    Checked it. It works only for pasting from PS. Coping image, for example, from browser will return messed up picture. Well, pretty much the problem I wrote in OP. As workaround, I wrote loader script and can load in 3 different modes. It tried to decide by itself which one to use, but it works pretty bad since it don't have enough info. So I made this "Load mode" as field in inspector for user to try different ones. Still don't know a good solution that will work for any type of image.
     
  7. Shinho

    Shinho

    Joined:
    Mar 21, 2013
    Posts:
    6
    Do you get it to work in 2019, it's breaking as soon as you try to check the clipboard, I'm thinking to create a c# app that communicates with unity to do the jobs, it's over killing but seems the only way to handle it.
     
  8. GamerXP

    GamerXP

    Joined:
    Mar 22, 2014
    Posts:
    78
    Didn't try using 2019 yet. Did you try to copy DLL's from 2019? Maybe versions are different
     
  9. Shinho

    Shinho

    Joined:
    Mar 21, 2013
    Posts:
    6
    I have resolved this problem using an external small application that just saves the clipboard to a given path. then a class in Unity checks for the completion of that app to load the file from that location, sending the the texture to the other listeners. If you are interested in this solution I can post it here.
     
  10. msfredb7

    msfredb7

    Joined:
    Nov 1, 2012
    Posts:
    163
    For those having the same issue in 2022+, here's how I bypassed the issue. Disclaimer: It's far from elegant and probably still has formats it doesn't cover (e.g I haven't tested photoshop, only web image copy, MS Paint copy and screenshots).

    Inspired from:
    https://github.com/NullTale/UnityClipboardImage

    1. Download this DLL: https://github.com/NullTale/UnityClipboardImage/raw/main/UnityClipboard.dll
    2. Put it in your assets.
    3. Add this script in your assets
    Code (CSharp):
    1. using System;
    2. using System.Runtime.InteropServices;
    3. using UnityEngine;
    4. using UnityEngine.Experimental.Rendering;
    5.  
    6. public static class ClipboardImage
    7. {
    8.     [DllImport("UnityClipboard")] private static extern bool Open();
    9.     [DllImport("UnityClipboard")] private static extern int Width();
    10.     [DllImport("UnityClipboard")] private static extern int Height();
    11.     [DllImport("UnityClipboard")] private static extern int BitsPerPixel();
    12.     [DllImport("UnityClipboard")] private static extern IntPtr Read();
    13.  
    14.     public static Texture2D Copy()
    15.     {
    16.         try
    17.         {
    18.             // no image in clipboard
    19.             if (Open() == false)
    20.                 return null;
    21.  
    22.             var width = Width();
    23.             var height = Height();
    24.             var bitsPerPixel = BitsPerPixel();
    25.             var bytesPerPixel = bitsPerPixel / 8;
    26.             var ptr = Read();
    27.             var tex = new Texture2D(width, height, GraphicsFormat.R8G8B8A8_SRGB, 0, TextureCreationFlags.None);
    28.             tex.wrapMode = TextureWrapMode.Clamp;
    29.  
    30.             var bytes = new byte[width * height * bytesPerPixel];
    31.          
    32.             if (bytesPerPixel == 3)
    33.             {
    34.                 // When we have 3 bytes per pixel, it indicates an image formatted differently.
    35.                 // The format is BGR, and it additionally skips some bytes at the end of each row.
    36.                 // The number of skipped bytes is a function of the width.
    37.                 int skippedBytesPerRow = (width % 4);
    38.                 if (skippedBytesPerRow == 0)
    39.                     skippedBytesPerRow = 4;
    40.  
    41.                 int p = 0;
    42.                 bool skipBytes = false;
    43.                 int bytesPerRow = width * bytesPerPixel;
    44.                 for (int b = 0; b < bytes.Length; b++)
    45.                 {
    46.                     if (skipBytes)
    47.                     {
    48.                         p += skippedBytesPerRow;
    49.                     }
    50.                     bytes[b] = Marshal.ReadByte(ptr, p++);
    51.                     skipBytes = (b + 1) % bytesPerRow == 0;
    52.                 }
    53.             }
    54.             else
    55.             {
    56.                 Marshal.Copy(ptr, bytes, 0, width * height * bytesPerPixel);
    57.             }
    58.  
    59.             Color32[] colors = new Color32[width * height];
    60.  
    61.             int c = 0;
    62.             for (int y = height - 1; y >= 0; y--)
    63.             {
    64.                 for (var x = 0; x < width; x++)
    65.                 {
    66.                     var pos = (y * width + x) * bytesPerPixel;
    67.  
    68.                     // read in bgra format
    69.                     var b = bytes[pos];
    70.                     var g = bytes[pos + 1];
    71.                     var r = bytes[pos + 2];
    72.                     var a = bytesPerPixel == 4 ? bytes[pos + 3] : (byte)255;
    73.  
    74.                     colors[c] = new Color32(r, g, b, a);
    75.                     c++;
    76.                 }
    77.             }
    78.  
    79.             tex.SetPixels32(colors);
    80.             tex.Apply();
    81.             return tex;
    82.         }
    83.         catch (Exception e)
    84.         {
    85.             Debug.LogError($"Can't past image from clipboard {e}");
    86.         }
    87.  
    88.         return null;
    89.     }
    90. }
    91.  
    4. Call ClipboardImage.Copy() to get the clipboard image as Texture2D. It'll return null if there are no copied images, or there's an error.
    The ugly part is the code in the "if (bytesPerPixel == 3)" section. For me, it gets the job done for now.
     
  11. Carpet_Head

    Carpet_Head

    Joined:
    Nov 27, 2014
    Posts:
    258

    is there any chance you could share the c++ source code for this dll?
     
  12. msfredb7

    msfredb7

    Joined:
    Nov 1, 2012
    Posts:
    163
    I haven't looked into it. The DLL part entirely comes from NullTale (the git repo I shared). Sorry about that. You might have a better chance contacting the repo owner.
     
  13. Carpet_Head

    Carpet_Head

    Joined:
    Nov 27, 2014
    Posts:
    258
    I'll give that a try, thanks