Pasting images as WEBP on Windows

An annotated screenshot showing the same screenshot being 3.1 MB in PNG format and 140 KB in WEBP format when pasted into Discord.

A tale as old as time: I'm playing a videogame with my pals and something interesting happens. I take a screenshot with Alt-PrtScr or Win-Shift-S, paste it into the chat, and then it takes a little bit because screenshots images are copied losslessly and not every platform will compress them.

Granted, I'm not the one to pay for all this storage space, but still!

After doing a "paste into Paint.NET, save as WEBP, upload that WEBP" ritual for a while and falling off a cliff in PEAK a couple times due to doing this mid-session, I decided to investigate.

Considerations

A low-tech solution would be to write an AutoHotKey script that does all of the things that I'm currently doing, but automatically. The worst thing about this is that I could probably make it work fairly reliably thanks to WinWait.

I strive to do a bit better than that so I decided to take an image from clipboard, convert it to WEBP, and put it back in the clipboard - preferably without getting into low-level clipboard manipulation because I already know this to be a bit of a headache.

ImageMagick supports working with Windows clipboard (how convenient!) and also supports format: prefixes for specifying the image format that doesn't match the output file extension, so I should be able to do this, right?

magick clipboard: WEBP:clipboard:

But perhaps not that easily, as that shows an error message like so:

magick: unable to open image 'clipboard:': Invalid argument @ error/blob.c/OpenBlob/3527.

And that's okay, I can put the WEBP into a temporary file and then put that in the clipboard:

magick clipboard: image.webp
magick image.webp clipboard:

...except that puts the pixels of the image in the clipboard, not the image file itself, and thus the pasted image is still lossless, but now contains WEBP compression artifacts. This also explains why clipboard: doesn't take a format prefix.

And that's okay, ImageMagick still does 2/3 of the work.

Putting files in clipboard using AHK is a bit of a bother, but I found an existing implementation for AHK v1 and adapted it for AHK v2.

The code

So here's the script!

ClipboardSetFiles(filePaths) {
    if filePaths.Length == 0
        return false
    
    static A_IsUnicode := true
    static TCS := 2 ; size of a TCHAR
    
    ; that name is there for a reason https://stackoverflow.com/a/2889962
    static PreferredDropEffect := DllCall("RegisterClipboardFormat", "Str", "Preferred DropEffect")
    
    totalLength := 1
    for path in filePaths
        totalLength += StrLen(path) + 1
    
    if DllCall("OpenClipboard", "Ptr", A_ScriptHwnd) && DllCall("EmptyClipboard") {
        static allocFlags := 0x42 ; GMEM_MOVEABLE (0x02) | GMEM_ZEROINIT (0x40)
        
        ; https://learn.microsoft.com/en-us/windows/win32/shell/clipboard#cf_hdrop
        pDropSize := 20 + totalLength * TCS
        hDrop := DllCall("GlobalAlloc",  "UInt", allocFlags,  "UInt", pDropSize,  "UPtr")
        pDrop := DllCall("GlobalLock",  "Ptr", hDrop)
        pDropPos := 20
        NumPut("UInt", pDropPos, pDrop, 0)
        NumPut("UInt", !!A_IsUnicode, pDrop, 16)
        for path in filePaths
            pDropPos += StrPut(path, pDrop + pDropPos, StrLen(path)) * TCS
        DllCall("GlobalUnlock",  "Ptr", hDrop)
        DllCall("SetClipboardData",  "UInt", 0x0F,  "UPtr", hDrop)
        
        ; and now the "Preferred DropEffect":
        DropEffect := 1 ; COPY
        hMem := DllCall("GlobalAlloc",  "UInt", allocFlags, "UInt",  4, "UPtr")
        pMem := DllCall("GlobalLock",  "Ptr", hMem)
        NumPut("UChar", DropEffect, pMem)
        DllCall("GlobalUnlock", "Ptr", hMem)
        DllCall("SetClipboardData", "UInt", PreferredDropEffect, "Ptr", hMem)
        
        DllCall("CloseClipboard")
        return true
    }
    return false
}
PasteImageAs(format, quality) {
    tmp := A_ScriptDir "\image." format
    exitCode := RunWait('magick clipboard: -quality ' quality ' "' tmp '"', , "Hide")
    if (exitCode != 0) {
        TrayTip("Magick exited with non-zero code: " exitCode, "Paste WEBP", 3)
        return
    }
    ClipboardSetFiles([tmp])
    Send("^v")
}
#!v::PasteImageAs("webp", 80)

To use it, you would want to:

  1. Install ImageMagick
  2. Install AutoHotKey v2
  3. Copy the above snippet to an .ahk file
    This assumes that you know how to enable file extensions in Explorer.
  4. Run the file using AutoHotKey.

With that done, an AutoHotKey icon should appear in your tray, and pressing Win-Alt-V will execute the script, converting the image in the clipboard and pasting it.

If you'd like to change the shortcut, you can do this on the last line (see Hotkeys).

If you'd rather use JPEG or want a different compression ratio, you can also do that on that same line (first and second function arguments, respectively).

I think 80% quality WEBP works well enough for most use cases.


Thanks for reading!

Related posts:

Leave a Reply

Your email address will not be published. Required fields are marked *
Note: JavaScript is currently required to post comments.

This site uses Akismet to reduce spam. Learn how your comment data is processed.