{blogdown}: Optimise PNG Image Size

Inspired by the informative post from Jumping Rivers about selecting the correct image file type, I decided to optimise PNG file size as part of this blog’s CI pipeline.

OptiPNG

As suggested, I used optipng. Let’s see how well this works on a large sample PNG file.

$ optipng moon.png 
** Processing: moon.png
8192x4096 pixels, 3x8 bits/pixel, RGB
Input IDAT size = 55785027 bytes
Input file size = 55810891 bytes

Trying:
  zc = 9  zm = 8  zs = 0  f = 5         IDAT size = 49789959
                               
Selecting parameters:
  zc = 9  zm = 8  zs = 0  f = 5         IDAT size = 49789959

Output IDAT size = 49789959 bytes (5995068 bytes decrease)
Output file size = 49795399 bytes (6015492 bytes = 10.78% decrease)

Not bad: a 10.78% decrease in file size from 54 MiB to 48 MiB.

I settled for the default level of optimisation (equivalent to -o2). As the package authors note, there is unlikely to be a substantial improvement in image size with more aggressive optimisation, although it will take substantially longer.

Automating

I wanted all of the “larger” files on my blog to get optimised. And I, obviously, didn’t want to do this by hand, so I added it to the CI pipeline.

optimise:
  stage: optimise
  variables:
    GIT_SUBMODULE_STRATEGY: none
  script:
  - find public/ -name "*.png" -size +50k -exec optipng {} \;
  only:
  - master

The -size +50k option filters out files which are smaller than 50 kiB. This is an arbitrary threshold, but it doesn’t seem worthwhile using resources to optimise files which are already fairly small.

Now each time the master branch is deployed, the larger PNG images are optimised.

Using a Hook

Another approach to integrating OptiPNG is to use the hook which is built into {knitr}.

knitr::knit_hooks$set(optipng = knitr::hook_optipng)
# Suppress output.
knitr::opts_chunk$set(optipng = "-quiet")

Alternative Options

Here are some alternative options for optimising your PNG images (courtesy of the generous @jdblischak and @davdittrich):

I’ve tried out TinyPNG and it’s really effective, producing impressive compression ratios. However, it’s limited to only 500 images per month on the free plan. I’m going to stick with optipng for the moment though because it’s 100% local.

Something to bear in mind is that optipng is lossless, while TinyPNG uses “smart lossy compression”. So, although TinyPNG might achieve better compression, the resulting image might be subtly changed (but at a level where it’s essentially imperceptible).