PNG saving is ~30% slower than with gd on large images (png_shift_set transform)
I noticed that saving a 7680x2160 screenshot via gdk-pixbuf (for instance from gnome-shell) takes much longer than saving the same image via libgd+libpng, by about 30% (~2300ms vs ~1700ms on an old Xeon 2.26 GHz).
Profiling lead me to a large chunk of time spent in libpng's png_do_write_transformations which subsumes several inner loops. The relevant bits of gdk-pixbuf's io-png.c are these calls which enable the packing and shift transforms:
sig_bit.red = bpc;
sig_bit.green = bpc;
sig_bit.blue = bpc;
sig_bit.alpha = bpc;
png_set_sBIT (png_ptr, info_ptr, &sig_bit);
png_write_info (png_ptr, info_ptr);
png_set_shift (png_ptr, &sig_bit);
png_set_packing (png_ptr);
Comparing the source of libpng showed that pixel repacking is skipped for 8bpp -> 8bpp, but shift is not -- so it seems to be spending a lot of time reading and writing bytes one at a time, shifting them by 0. :)
This may be a logic bug in libpng, but it should be possible to simply skip the png_set_shift if the significant bits are all 8, disabling the filter and removing the bottleneck.
Note this code fragment is unchanged since PNG saving landed in the year 2000! :)
I compared against gd+libpng performance using this PHP script:
<?php
$in = $_SERVER['argv'][1];
$out = 'out.png';
$image = imagecreatefrompng($in);
imagesavealpha($image, true); // required or gd will decimate alpha, compressing faster
$start = microtime( true );
imagepng($image, $out);
$delta = microtime(true) - $start;
print "$delta seconds\n";
and this sample image: https://raw.githubusercontent.com/brion/mtpng/master/samples/dual-4k-sample.png