Hacker News new | ask | show | jobs
by Jasper_ 1723 days ago
For PNG, it can support what's known as "filtering" [0], where it's based on the difference between the pixel and the one to the left/top of it. So if you have the values { 1, 2, 3, 4, 5, 6 }, then the differences will be { 1, 1, 1, 1, 1, 1 }, which is very compressible to DEFLATE (analogy in English, "6 1's" is shorter than "1 1 1 1 1 1").

JPEG uses a very different technology; it breaks the image into 8x8 blocks, and tries to fit the resulting 64 pixels to a gradient (yes, I know I'm simplifying). So pixels that tend to be "smooth" and "gradient-like" will compress much better than random noise.

[0] https://www.w3.org/TR/PNG/#9Filters

2 comments

This is the best layman's explanation of JPEG compression that I've ever seen.

I feel it's very much spot on, and true to the math within.

This is why I like things like svg for pure gradient graphics. Uncompressed plaintext can be used to render all RGB colors with a similar filesize as png.

I'll see if I can come up with a clever way do it without having to resort to tricks that move the goalposts (such as having repeated colors).

Okay I think I got pretty close. The png export doesn't have 16.7 million colors, so there must be some error in my logic.

1370 bytes as a human readable plaintext file.

711 bytes as a 7-zip.

658 bytes as a usable compressed svgz file.

159936 bytes as a png.

  <?xml version="1.0" encoding="UTF-8"?>
  <svg width="256" height="65536" version="1.1" viewBox="0 0 256 65536" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
   <defs>
    <linearGradient id="a" x2="256" y1="32768" y2="32768" gradientUnits="userSpaceOnUse">
     <stop stop-color="#f00" offset="0"/>
     <stop stop-color="#ff0" offset=".166666667"/>
     <stop stop-color="#0f0" offset=".333333333"/>
     <stop stop-color="#0ff" offset=".5"/>
     <stop stop-color="#00f" offset=".666666667"/>
     <stop stop-color="#f0f" offset=".833333333"/>
     <stop stop-color="#f00" offset="1"/>
    </linearGradient>
    <linearGradient id="b" x1="128" x2="128" y1="0" y2="65536" gradientUnits="userSpaceOnUse">
     <stop offset="0"/>
     <stop stop-color="#808080" stop-opacity="0" offset=".5"/>
     <stop stop-color="#fff" offset="1"/>
    </linearGradient>
   </defs>
   <metadata>
    <rdf:RDF>
     <cc:Work rdf:about="">
      <dc:format>image/svg+xml</dc:format>
      <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
     </cc:Work>
    </rdf:RDF>
   </metadata>
   <rect width="256" height="65536" fill="url(#a)" stroke-width="24" style="mix-blend-mode:normal"/>
   <rect width="256" height="65536" fill="url(#b)" stroke-width="24" style="mix-blend-mode:normal"/>
  </svg>
Okay okay okay. I got up to 10.7 million unique colors in a 67 MP raster. I expect at least about 30% overlap because the vertical gradient wastes 25% of space and the rightmost nodes of the horizontal gradient are mostly replicated in other places. As for why it doesn't have full coverage and why there are so many repeated colors: idk yet and have to stop for today.

  <svg width="4096" height="16384" version="1.1" viewBox="0 0 4096 16384" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
   <defs>
    <linearGradient id="a" x2="4096" y1="8192" y2="8192" gradientUnits="userSpaceOnUse">
     <stop stop-color="#f00" offset="0"/>
     <stop stop-color="#ff0" offset=".125"/>
     <stop stop-color="#0f0" offset=".25"/>
     <stop stop-color="#0ff" offset=".375"/>
     <stop stop-color="#00f" offset=".5"/>
     <stop stop-color="#f0f" offset=".625"/>
     <stop stop-color="#f00" offset=".75"/>
     <stop stop-color="#000" offset=".875"/>
     <stop stop-color="#fff" offset="1"/>
    </linearGradient>
    <linearGradient id="b" x1="2048" x2="2048" y2="16384" gradientUnits="userSpaceOnUse" xlink:href="#a">
     <stop stop-color="#808080" offset="0"/>
     <stop stop-color="#808080" stop-opacity="0" offset=".25"/>
     <stop stop-color="#fff" offset=".5"/>
     <stop stop-color="#fff" stop-opacity="0" offset=".75"/>
     <stop offset="1"/>
    </linearGradient>
   </defs>
   <rect width="4096" height="16384" fill="url(#a)" stroke-width="24" style="mix-blend-mode:normal"/>
   <rect width="4096" height="16384" fill="url(#b)" stroke-width="24" style="mix-blend-mode:normal"/>
  </svg>