PNG Parser Differential
da.vidbuchanan.co.uk
da.vidbuchanan.co.uk
> on the first retina iPads, decoding PNGs was a huge portion of the total launch times for some apps, while one of the two cores sat completely idle.
Could they distribute decoding PNG files to a thread pool, instead of making multithreaded PNG files? Or would this fail for single large PNG files?
The "trick" used by Apple, and a few other encoders, is to flush the zlib state every so often (ZLIB_FULL_FLUSH), which means that subsequent data is both byte alligned, and does not make any backreferences to before the sync.
If you know where these sync points are (Apple encodes this information in their non-standard "iDOT" chunk) then you can start decompressing from that point, in an isolated thread.
There are ongoing discussions as to how this metadata could be standardised: https://github.com/w3c/PNG-spec/issues/54
Of course, this only scales to 2 CPUs. Beyond that you will need to do some sort of splitting of the input to achieve wins (since no matter how much you optimize the filtering, you're still bottlenecked on DEFLATE, which is inherently serial).
iDOT seems like overengineering to me: Why not break up the PNG and tile them in an SVG? This should be just as fast, about the same size, and you'd never have run the risk of inventing your own format and implementing it badly[1]. Users would probably be happier with all their SVGs being faster than having specially-crafted PNGs accelerated (if given the choice)
[1]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-1811
An SVG is a list of compositing instructions for some (graphical) artefact. These instructions are given so that their dependencies (the things that must be composed before other things) are stated directly.
If you have familiar with Adobe's Photoshop, you might imagine a SVG as a set of nested layers: An SVG "renderer" will simply compose these layers together in order to have some pixels to display.
Now, to give a clear example of what I'm referring to, I am going to show you a simplified SVG that composes four png files together in tiles:
<svg width="100" height="100">
<image x="0" y="0" width="50" height="50" href="data:image/png,xxx" />
<image x="50" y="0" width="50" height="50" href="data:image/png,xxx" />
<image x="0" y="50" width="50" height="50" href="data:image/png,xxx" />
<image x="50" y="50" width="50" height="50" href="data:image/png,xxx" />
</svg>
That data:image/png,xxx stanza is the SVG-encoding of a PNG (a slight simplification: the format actually belongs to a number of different standards, that the SVG specification leverages).That is to say, I'm not exactly suggesting converting a PNG to an SVG: I am also suggesting breaking apart the (large!) PNG into several component PNG files (tiles) so that they can be decoded independently. Note carefully my example, how none of the tiles overlap. A decoder can (trivially) determine these instructions are independent, and so process them independently.
Being able to decode parts of the resulting image independently is what the iDOT metadata makes possible: It is essentially a different encoding of the x/y/width/height information in the above, the difference is that SVG already existed.
for each path {
decode(path)
}
and the OS can't parallelize that, because it learns about a new file only after decoding the previous one.Between this and project zero's analysis (https://news.ycombinator.com/item?id=29568625) of NSO using compression encoding to create a virtual machine for calculating exploit offsets, while unrelated except at a very high level of abstraction, it reminds me conceptually of cryptographic hash collisions, where over a large enough search space or field / domain of complexity there are many equivalent encodings or homonyms/isomorphisms.
The issue with the NSO exploit was they found that the compression encoding for a font was Turing complete, and then wrote a virtual architecture in it, and then ran programs on it that did the calculations necessary for their exploit.
This png encoding issue is different, but if you abstract it upwards to find a general principle it may be the effect of, it's like there is fast rule where if if you know the size or definition of the field of possibilities, and then have a definition of a given string in it, the function that describes or defines that string will also yield all strings whose evaluation is the same. It's like Kolmolgorov complexity, but where instead of finding the smallest progam to compute something, it's: given the number of instructions to define programs over a field of inputs of a given size, there are N programs beneath length L that are equivalent.
Sort of a showerthought, but it's interesting to think that our ideas of encodings and general isomorphisms may be instances of the same concept linked by a sort of "imaginary" function.
# thumbnail
qlmanage -c public.image -g /System/Library/QuickLook/Image.qlgenerator -t /path/to/a.png
# preview
qlmanage -c public.image -g /System/Library/QuickLook/Image.qlgenerator -p /path/to/a.pngOne person reacts one way on the image and another reacts the other way.
It can definitely be used for fingerprinting, though, especially if Apple ever fixes their PNG decoder.
[0] https://www.howtogeek.com/149223/why-do-chrome-and-internet-...