PNGCRUSH(1)

๐Ÿ’… PNGCRUSH(1) โ€” the manual page, but make it make sense

okay bestie, welcome to pngcrush(1) ๐Ÿฆ‹ this is the glow-up era of your PNG files, and i'm gonna walk you through it like we're texting at 2am. no gatekeeping, no cap.


โœจ NAME โœจ

pngcrush โ€” optimizes (or modifies) PNG (Portable Network Graphics) files. literally its whole personality ๐Ÿ’…


๐ŸŒธ SYNOPSIS ๐ŸŒธ

like, here's how you actually call it, three iconic ways:

pngcrush [ options ]  [ infile.png ]  [ outfile.png ]
pngcrush -e ext [ options ]  [ file1.png ]  [ file2.png ] ...
pngcrush -d dir [ options ]  [ file1.png ]  [ file2.png ] ...

pick your fighter ๐ŸŽ€


๐Ÿ‘‘ DESCRIPTION ๐Ÿ‘‘

so pngcrush is an optimizer for PNG (Portable Native... no wait โ€” Portable Network Graphics) files. its main purpose is to reduce the size of the file's IDAT chunk (or stream). that's the whole mission. main character energy fr ๐Ÿ”ฅ

but it's not a one-trick pony โ€” pngcrush can also be used to modify a PNG's ancillary chunks (example: transparency information or textual comments). so it optimizes and it edits. versatile queen.

ngl, some familiarity with the PNG (pronounced 'ping') format may be helpful to users of pngcrush. not required, just helpful. consider it a little lore drop ๐Ÿงš

and here's the best part: pngcrush has reasonable defaults, so running with no options may produce smaller files. you don't even have to try and it still eats. periodt ๐Ÿ’–

this document briefly describes the PNG format where necessary to understand pngcrush. for complete documentation, see:

http://www.libpng.org/pub/png/

๐Ÿซถ the PNG format, explained by your bestie

the actual pixel data of a PNG is contained in one or many IDAT chunks. that's where the real image lives, no cap.

to make an IDAT chunk, PNG encoders take the raw pixel data, filter it with one of 5 different filters (See Section pngcrush(1)) then compress it. filter โ†’ compress โ†’ chunk. that's the pipeline, iykyk โญ

pngcrush reduces the size of PNG files by choosing a different filter or compression methods from those used in the input file. so it's like "hmm, what if we did this differently" and then it just... does.

here's the vibe: pngcrush tries multiple filter/compression methods and compares the size of the resulting IDAT chunk. the filter/compression method with the smallest IDAT chunk is chosen for the output file. smallest wins. it's giving competition arc ๐Ÿ‘‘

the filter and compression levels pngcrush tries are controlled by command line options. so yes, you get to be the director of this whole situation ๐ŸŽฌ

and before anyone panics: all of the filter methods and compression levels are lossless. they will not reduce image quality. your pixels stay exactly as gorgeous as they were. no notes. ๐Ÿ’€โœจ

๐Ÿฆ‹ ancillary chunks (the side quests)

in addition to reducing the size of a PNG file, pngcrush can modify ancillary chunks. ancillary chunks are optional information including:

  • transparency (tRNS)
  • gamma (gAMMA)
  • standard RGB color space conformance (sRGB)
  • textual information (iTXt, tEXt or zTXt)

see the associated command line options for details on manipulating these chunks. there's a whole options section coming and it's lowkey stacked ๐ŸŒˆ

โœจ OPTIONS โœจ

okay bestie, this is the big one โ€” the whole menu of options. grab a snack ๐Ÿ“

  • -already size โ€” if the file's IDAT chunk is bigger than the integer size, pngcrush decides it's already been crushed and justโ€ฆ doesn't touch it. bye, no further processing ๐Ÿ’…
  • -bit_depth n โ€” forces the output bit depth to n. the bit-depth restrictions are over in Section pngcrush(1), go check those ๐Ÿ‘‘
  • -bkgd r g b โ€” sets the default background color for the image. some PNG viewers will use this background when they display it. background is given by the r, g and b integers between 0 and (2bitdepth)-1. for output color type 0 and 4 (gray, see section pngcrush(1)) the green index is used as the gray level of the background ๐ŸŽจ
  • -brute โ€” brute force era: tries 114 different filter/compression methods [11-124]. it is very time-consuming and generally not worthwhile, ngl ๐Ÿ˜ญ you can restrict it to certain filter types, compression levels, or strategies by following it with -f filter, -l level, or -z strategy. like so:
pngcrush -brute -f 0 infile.png

that one will try only methods that use filter type 0 โœจ

  • -c type โ€” sets the output image color type to type, one of [0, 2, 4, or 6] (see section pngcrush(1)). future versions of pngcrush will also allow color type 3, if there are 256 or fewer colors present in the input file. color types 4 and 6 get padded with an opaque alpha channel if the input file has no alpha info. you can use 0 or 4 to convert color to grayscale ๐Ÿ–ค use 0 or 2 to delete an unwanted alpha channel. default is to use the same color type as the input file ๐Ÿซถ
  • -d dir โ€” picks a directory for all output files. all the output files keep the same name as their respective input files ๐Ÿ“
  • -dou โ€” doubles the image's gamma. this is for fixing gamma in PhotoShop 5.0c5.02 files. it has been claimed that the PS5 bug is actually more complex than that, in some unspecified way ๐Ÿ™ˆ
  • -e ext โ€” sets a new extension ext for all output files ๐Ÿท๏ธ
  • -exit โ€” forces pngcrush to call exit() rather than return() when it's finished. it is unclear to this writer why this option exists ๐Ÿ’€
  • -f filter โ€” specifies the filter (see section pngcrush(1)) to use with the method given in the preceding -m or -brute option. valid filter types are [0-4] : use specified filter, [5] : use adaptive filtering ๐ŸŒˆ
  • -fix โ€” fixes otherwise fatal conditions such as bad CRCs. the CRC (Cyclic Redundancy Check) is present in every chunk and can alert the decoder of corrupt data ๐Ÿฉน
  • -force โ€” writes a new output file even if it's larger than the input. otherwise the input file gets copied to output if it's smaller than any generated file and no chunk additions, removals, or changes were requested ๐Ÿ’ช
  • -g gamma โ€” the value to insert in the gAMA chunk, only if the input file has no gAMA chunk. to replace an existing gAMA chunk, use the -replace_gamma option instead. some decoders will use the gAMA chunk to display the image more accurately. gamma relates the output intensity to the input samples (input_sample = light_out^GAMMA). gamma can be any integer between 0 and 2^32 and is interpreted as GAMMA*100000. for example, if you wanted a GAMMA of 1/2.2, you'd enter 45455 for gamma โญ
  • -h โ€” displays help and legal notices ๐ŸŽ€
  • -itxt b|a keyword text โ€” inserts an (uncompressed) iTXt chunk. b|a says whether the chunk should be inserted before or after the IDAT chunk, (see Section pngcrush(1)) ๐Ÿ“
  • -keep chunk_name โ€” keeps the named chunk chunk_name even if the PNG datastream becomes invalid. currently only dSIG is recognized as an acceptable value ๐Ÿซฃ
  • -l level โ€” the zlib compression level to use on the filtered IDAT chunk with the method specified by the preceding -m or -brute option. zlib compression levels are integers between 0 and 9. 0 = no compression, 1 = fastest compression, and 9 = best compression ๐Ÿ”ฅ
  • -loco โ€” makes the file more compressible by performing a lossless, reversible, color transformation. the resulting file is a MNG format file, not a PNG, and should be given the .mng file extension. heads up: the loco option has no effect on grayscale or indexed-color PNG files ๐Ÿฆ‹
  • -m method โ€” the pngcrush method [0-200] to try (0 means try all of 1-10). can be repeated, like -m 1 -m 4 -m 7. this is useful if you run out of memory when pngcrush tries methods 2, 3, 5, 6, 8, 9, or 10 โ€” those use filtering and are memory intensive ๐Ÿ˜ฎโ€๐Ÿ’จ methods 1, 4, and 7 use no filtering; methods 11 and up use specified filter, compression level, and strategy ๐Ÿ’ซ
  • -max maximum_IDAT_size โ€” sets the maximum idat size to be used when creating the compression buffer. [1 through 524288] ๐Ÿ“
  • -mng โ€” writes a new MNG, doesn't crush embedded PNGs ๐Ÿงš
  • -n โ€” no compression and no output file. this is useful with the -v option to get info, or to test decode speed ๐Ÿƒโ€โ™€๏ธ
  • -p โ€” tells pngcrush to pause and wait for the enter key whenever the screen fills up ๐Ÿ–ฅ๏ธ
  • -plte_len n โ€” truncates the PLTE. the PLTE chunk contains from 1 to 256 palette entries. be sure not to truncate it to less than the greatest index present in IDAT โš ๏ธ
  • -q โ€” quiet, the opposite of verbose ๐Ÿคซ
  • -rem chunkname โ€” the name of an ancillary chunk or optional PLTE to be removed. be careful with this one, fr fr. please don't use this feature to remove transparency, gamma, copyright, or other valuable information ๐Ÿ™ to remove several different chunks, repeat it: -rem tEXt -rem pHYs. known chunks (those in the PNG spec or extensions document) can be named with all lower-case letters, so -rem bkgd is equivalent to -rem bKGD. but note: -rem text removes all forms of text chunks; exact case is required to remove unknown chunks. and if you want to do surgery with a chain-saw ๐Ÿชš, -rem alla removes all known ancillary chunks except for tRNS, and -rem allb removes all but tRNS and gAMA ๐Ÿ’€
  • -replace_gamma gamma โ€” forces a specified gamma in the output file even if gAMA is present in the input. see the -g option for more info ๐Ÿ’œ
  • -res dpi โ€” writes a pHYs chunk with a resolution of dpi. the pHYs chunk indicates the desired pixel size ๐Ÿ“
  • -save โ€” forces writing of unknown chunks. if the input image has chunks that aren't part of the PNG specification, they should not be discarded ๐Ÿ›Ÿ
  • -srgb n โ€” sets the value of rendering intent for the sRGB chunk to n, where n is between 0 and 3. the appropriate rendering intent depends on how the image will be used:
    • 0 โ€” Perceptual: when good adaptation to the output device gamut at the expense of colorimetric accuracy is desired, example: photographs ๐Ÿ“ธ
    • 1 โ€” Relative colorimetric: images requiring color appearance matching (relative to the output device white point), example: logos ๐ŸŽฏ
    • 2 โ€” Saturation: preservation of saturation at the expense of hue and lightness is preferred, example: charts and graphs ๐Ÿ“Š
    • 3 โ€” Absolute colorimetric: images requiring preservation of absolute colorimetry, example: proofs (previews of images destined for a different output device) ๐Ÿ–จ๏ธ
  • -text [b|a] keyword text โ€” inserts a tEXt chunk. [b|a] says whether the chunk should be inserted before or after the IDAT chunk, (see Section pngcrush(1)) โœ๏ธ
  • -newtimestamp โ€” resets the file modification time to a newer time stamp. this is the default โฐ
  • -oldtimestamp โ€” retains the input file's modification time stamp. the default is -newtimestamp, which is the opposite of this option ๐Ÿ•ฐ๏ธ
  • -trns index red green blue gray โ€” inserts a tRNS (transparency) chunk, if no tRNS chunk is found in the file. you must give all five parameters regardless of the color type, scaled to the output bit depth. see the PNG documentation for details ๐Ÿ‘ป
  • -trns_array n trns[0] trns[1] ... trns[n-1] โ€” inserts a tRNS (transparency) chunk, if no tRNS chunk is found in the file. see the PNG documentation for details ๐Ÿซง
  • -v โ€” displays more detailed information. repeat the option (use -v -v) for even more ๐Ÿ‘€
  • -w size โ€” specifies the size, in kbytes (or bytes in the case of 512), of the sliding compression window, where size is one of [32, 16, 8, 4, 2, 1, or 512]. it's best to use the default (32) unless you run out of memory. the program will use a smaller window anyway when the uncompressed file is smaller than 16k ๐ŸชŸ
  • -z strategy โ€” specifies the zlib compression strategy [0, 1, or 2] to be used on the filtered IDAT chunk for the method of the preceding -m. the zlib strategy parameter tunes the compression algorithm and is one of:
    • 0 : default, most compression is aimed towards string matching ๐Ÿงต
    • 1 : some string matching, some Huffman coding ๐ŸŒ—
    • 2 : use only Huffman coding ๐ŸŒณ
  • -zitxt [b|a] keyword text โ€” inserts a zTXt chunk. [b|a] says whether the chunk should be inserted before or after the IDAT chunk, (see Section pngcrush(1)) ๐Ÿ—œ๏ธ
  • -ztxt [b|a] keyword text โ€” zTXt chunk to insert (see -text) ๐Ÿ“ฆ

๐ŸŽจ COLOR TYPES ๐ŸŽจ

the PNG specification provides for five color types, and the color type is literally what determines how the IDAT chunk will be interpreted by the decoder. choosing a color type that's appropriate for the color information in an image can in principle reduce the size โœจ however, certain combinations of -c and -bit_depth require color counting, which is currently disabled โ€” and in such cases no reduction will take place, sorry bestie ๐Ÿ˜ญ

here are the PNG color types with their supported bit depths (note that pngcrush does not support changing a file to color type 3 from another color type):

  • 0 : grayscale without alpha channel (1,2,4,8,16) ๐Ÿ–ค
  • 2 : true color without alpha channel (8,16) ๐ŸŒˆ
  • 3 : indexed color (1,2,4,8) ๐ŸŽจ
  • 4 : grayscale with alpha channel (8,16) ๐ŸŒซ๏ธ
  • 6 : true color with alpha channel (8,16) ๐Ÿ’Ž

an alpha channel represents transparency on a per-pixel basis. an alpha value of zero is completely transparent, and an alpha channel of 2^bitdepth-1 is completely opaque ๐Ÿซถ

๐Ÿฆ‹ FILTER TYPES ๐Ÿฆ‹

okay so bestie, plot twist: the IDAT chunk can optionally get filtered before it gets compressed. and like, these filters are literally the glow-up the IDAT chunk didn't know it needed โ€” they make it way more compressible without losing a single byte of data, so you end up with a smaller PNG file. ๐Ÿ’…

important vibes check tho: these filters get applied to the bytes of the IDAT chunk, not the pixels. do not come for me. if you want the full lore, go read the PNG specification, but here's the quick rundown:

  • 0 : no filter. just vibes, no changes. ๐Ÿซถ
  • 1 : 'sub' transmits the difference between each byte and the value of the corresponding byte of the prior pixel. ngl it's basically "what changed since the last one?"
  • 2 : 'up' transmits the difference between each byte and the value of the corresponding byte of the pixel above this pixel. looking up for once, iconic. โฌ†๏ธ
  • 3 : 'average' transmits the difference between each byte and the average of the bytes described in filters 1 and 2. it's giving middle child energy, lowkey.
  • 4 : 'paeth' computes a simple linear function of the corresponding byte in three neighboring pixels (paeth_predictor = left + above - upper left), then transmits the difference between the byte in question and the neighboring byte closest to the value of paeth_predictor. yes it's a whole math moment, yes it slays. โœจ

๐ŸŽ€ TEXT CHUNKS ๐ŸŽ€

so you can slide textual information about an image into your PNG using the tEXt, iTXt and zTXt chunks. all text chunks are built the same way: a keyword followed by a string. periodt.

the PNG specification defines these keywords (but bestie, you may invent keywords for other purposes, go off ๐Ÿ‘‘):

  • Title: Short title or caption for image
  • Author: Name of image's creator
  • Description: Longer description of image
  • Copyright: Copyright notice
  • Creation Time: Time of original image creation
  • Software: Software used to create the image
  • Disclaimer: Legal disclaimer
  • Warning: Warning of nature of content
  • Source: Device used to create the image
  • Comment: Miscellaneous comment.

character set tea โ˜•:

  • A tEXt chunk stores text in the ISO/IEC 8859-1 (Latin-1) character set.
  • zTXt chunks also use the Latin-1 character set, but the text is compressed. this can be useful for large text chunks โ€” big text, small file, we love to see it. ๐Ÿ“
  • iTXt chunks consist of text in the UTF-8 of the Unicode character set. unicode girlies rise. ๐ŸŒˆ
โš ๏ธ the limits, bestie, respect the limits: keyword must be at least 1 character and less than 80 characters. text must be less than 2048 characters when using pngcrush. and for now? you can only add ten tEXt, iTXt, or zTXt chunks per pngcrush run. ten. that's the whole budget. ๐Ÿ’€

๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

png(5), libpng(3), zlib(3).


๐Ÿ’– AUTHOR ๐Ÿ’–

this manual page was written by David Whedon <dwhedon@gordian.com> for the Debian GNU/Linux system (but may be used by others โ€” yes, sharing is caring ๐Ÿซถ). much of the information was gleaned from "PNG (Portable Network Graphics) Specification, Version 1.2" โ€” credit where credit is due, king. ๐Ÿ‘‘


okay that's the whole page bestie, we made it to the end together ๐Ÿงšโœจ this was PNGCRUSH(1), dated Mon Apr 30, 2001 โ€” yes she's an elder icon but she still ate, ngl. mwah, go crush some PNGs responsibly ๐Ÿ’…๐Ÿ’œ

๐Ÿ“ฆ where this page comes from