golang-design / clipboard

đź“‹ cross-platform clipboard package that supports accessing text and image in Go (macOS/Linux/Windows/Android/iOS)
https://golang.design/x/clipboard
MIT License
579 stars 64 forks source link

PNG data overwritten on Windows #36

Closed abejfehr closed 1 year ago

abejfehr commented 1 year ago

I'd like to be able to hit "Print Screen" on my keyboard and paste the images into a Steam chat, but as it stands that doesn't work because the screenshots created by Windows are too big in terms of filesize.

I wrote a simple program using this library that uses pngquant to optimize the image data in the clipboard and then replace the clipboard with it, but it doesn't work.

Here's the important code:

imageChannel := clipboard.Watch(context.Background(), clipboard.FmtImage)

copiedImage := <-imageChannel
fmt.Println("detected that an image was copied, compressing image...")

compressedImage, err := pngquant.CompressBytes(copiedImage, "1")

fmt.Println("original image size in bytes:", len(copiedImage))
fmt.Println("compressed image size in bytes:", len(compressedImage))

if err != nil {
    panic(err)
}

fmt.Println("reading clipboard...")
fmt.Println(len(clipboard.Read(clipboard.FmtImage)))

fmt.Println("replacing clipboard...")
clipboard.Write(clipboard.FmtImage, compressedImage)

fmt.Println("reading clipboard...")
fmt.Println(len(clipboard.Read(clipboard.FmtImage)))

and here's the output I see:

detected that an image was copied, compressing image...
original image size in bytes: 1947271
compressed image size in bytes: 1696311
reading clipboard...
1947271
replacing clipboard...
reading clipboard...
3881712

As you can see, the compression does work, but when I write that to the clipboard it's replaced with something much larger, often around 2x the original size(!)

It'd be great if the original bytes could be written to the clipboard without being re-encoded (on Windows)

changkun commented 1 year ago

Is pngquant compressed data still in PNG format? The clipboard.Write and clipboard.Read both requires the data need to be PNG encoded.

abejfehr commented 1 year ago

I think so, I added this line:

fmt.Println("header data:", compressedImage[0:8])

and the output was:

header data: [137 80 78 71 13 10 26 10]

which seems to imply it's png data according to this site: http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html

I think I would've also received this error if that was not the case.

changkun commented 1 year ago

It'd be great if the original bytes could be written to the clipboard without being re-encoded (on Windows)

I am not sure if this is possible. Windows works differently than macOS, and it requires input bytes must be PNG. This package does not do anything further but just put the bytes into OS required region and let OS do stuff.

abejfehr commented 1 year ago

...it requires input bytes must be PNG.

I can very much assure you that the input bytes are in PNG, that's not the issue.

It actually looks like Windows does not expect a PNG by the way, so it turns out that's not a requirement for Windows and must just be an arbitrary choice for this library (well maybe not arbitrary, but for a different reason than Windows).

The format that this library uses to store the image in the clipboard is called DIB V5 according to the code here, which is described by Microsoft here.

Which, it turns out, is a bitmap, so it's never going to be my compressed PNG data.

image

^ I also used Free Clipboard Viewer to confirm that hitting "Print Screen" does store the data in Bitmap, so this is reasonable.

I think I'm going to have to make my screenshots small enough to be pasteable by resizing them instead of compressing them because this format isn't compressable.

I'm gonna mark this one as closed, since it's not an issue with the library.