Why copy-pasting an image from a browser inflates its size under linux
Like others, I’ve noticed that sometimes images copied from Firefox into a chat application like Element, Discord or Whatsapp inflates the images size significantly. An image that was diplayed as ~300KB in the browser suddenly reported as ~2MB by the chat app.
Since I share images fairly often I’ve resorted to just screenshotting them as to not send around unnecessarily big images. But that obviously is cumbersome too.
So, why does copy-pasting an image inflate its size by orders of magnitude?
Observation
You can try this yourself:
- Open this image right-click it and select “Copy Image” and also save it via “Save as…”
- Go to a chat application and paste it in there
- Observe the resulting images difference in filesize
The actual size of the above image if simply downloaded is 4.4MB. However, for me, the pasted version comes in at 11MB, more than 2x the original size. It’s the same image, from the same source, yet these are clearly different files. What’s going on here?
Compression
Maybe you already noticed when comparing the image files: we’re dealing with image conversion here. The example image linked above is a JPG, while the pasted image comes out as a PNG. That explains the size inflation:
JPG uses lossy compression achieving far lower filesizes, while PNG uses lossless compression. The latter actually saves data on each individual pixel. Thus converting a JPG to a PNG fills in this data that the JPG could lose during its compression, increasing its filesize significantly. Again, you can verify this yourself using GIMP: Import the above example file and export it as a PNG.
Clipboard Wierdness
But why does copy-pasting an image convert it to a differeny filetype?
For this, we gotta look deeper into how the Linux clipboard works.
While going through man xclip looking for options related to image compression, I stumbled upon the following paragraph:
-t, -target
Specify a particular data format using the given target atom. With -o the special target atom name "TARGETS" can be used to get a list of valid target atoms for this selection. For more information about target atoms refer to ICCCM section 2.6.2
Here we see a reference to the ICCCM or Inter-Client Communication Conventions Manual, a standards document describing conventions for inter-window communication under X.org. Chapter 2 of this document describes the process of sharing data between windows through so-called selections, of which the clipboard is one.
Further, the chapter describes the above mentioned TARGETS. When requesting the data in a selection, the requesting client can specify a format that they’d like to receive the data in. This is done by passing the appropriate TARGET atom along with the request. The client owning the selection content, so the application that one copied the data out of, advertises in which formats the data can be requested.
Assuming you still have the image from before in your clipboard you can run xclip -t TARGETS or wl-paste -l to see the supported TARGETS for the example image. The output should look something like this using Firefox:
text/html
text/_moz_htmlinfo
text/_moz_htmlcontext
image/png
image/jpeg
image/bmp
image/x-bmp
image/x-MS-bmp
image/x-icon
image/x-ico
image/x-win-bitmap
image/vnd.microsoft.icon
application/ico
image/ico
image/icon
text/ico
image/tiff
SAVE_TARGETS
We see firefox offering image/png as a TARGET, despite the example image being a JPG.
Seeing as we end up with a PNG after pasting the image, the applications tested must be requesting the clipboard contents as image/png by default.
Thus forcing the source application to convert the image, causing the inflation in size.
Firefox offers images as a image/jpeg, but chromium only offers image/png, same with electron based applications.
It seems that transferring images as a PNG when passed through the clipboard has become the standard.
In fact, Element won’t even paste an image if it’s not advertised as a image/png TARGET.
Solution
Much to my dismay, understanding the mechanism now tells me that there seems to be no simple solution to my issue, since the applications I paste into determine what format to request the image in.
So the only way to do this is to convert the image locally before putting it back into the clipboard in order not to paste huge images all the time. For this I’ve written a quick and dirty PoC shell script for use under wayland:
#!/usr/bin/env bash
TMPFILE="/tmp/tmpimage.jpg"
TARGET_COUNT=$(wl-paste -l | grep -E 'text/html|image/png' -c)
if [ $TARGET_COUNT == 2 ]; then
URL=$(wl-paste -t text/html | grep -E 'https?://.*' -o | cut --delimiter=" " -f 1 | sed -e 's/\(.*\)".*/\1/')
curl -o $TMPFILE $URL
wl-copy -t text/uri-list file://$TMPFILE
echo Copied File
fi
wl-paste -w ./clip.sh
While this works, it’s really not ideal as it just downloads the file and puts that local file into the clipboard. Which is slow and inefficient.
I’d wanna verify if it’d be more usable to get the converted image from the selection, convert it to a JPG for the smaller filesize via something like ImageMagick and then place that back in the clipboard. Maybe a clipboard manager can help here. If I do, I’ll amend the blogpost.
Until then, I hope you learned something and thank you for reading!
Quick note on Wayland: It does not adhere to the ICCCM, but implements the same mechanism for the clipboard, where applications may request the data in certain formats, as defined here. Thus also running into this issue.