Convert BMP to PNG
Keep every pixel of a Windows bitmap and stop paying three bytes each for them.
How it works
A bitmap is a header followed by the pixel array, and very little else. The fourteen-byte BITMAPFILEHEADER records the signature and where the pixels begin. The BITMAPINFOHEADER behind it records width, height, plane count, bit depth, and a compression field that on any file in ordinary circulation reads BI_RGB, meaning none at all. At 24 bits per pixel every pixel costs three bytes, written blue then green then red. The sample used to check this page is 640×426 with a 124-byte V5 header: 817,920 bytes of pixels under 138 bytes of description, 818,058 bytes in total.
That total is arithmetic performed on the dimensions. It never consults the picture. Overwriting every pixel of that same file with one flat grey, leaving the header untouched, gives a second bitmap of exactly 818,058 bytes. Put through the encoder on this page, the photograph came back as 484,922 bytes and the flat grey as 128 bytes, a ratio of 6,391 to one against a source the format had charged the same price for. A format that cannot spend less on a picture holding less information is not compressing anything.
What a PNG does with the same pixels
PNG reaches its size in two stages, both specified in RFC 2083. The first is filtering. Each scanline gains one leading byte naming a method: None, Sub, Up, Average, or Paeth, after Alan Paeth's predictor. Every byte of that row is then rewritten as the difference between itself and the corresponding byte to its left, the byte in the row above, or a prediction drawn from both. The arithmetic is exact and reversible. The second stage is DEFLATE, specified in RFC 1951, which finds repeated strings with LZ77 matching and then codes the result with Huffman trees.
Filtering is the stage that turns a bitmap's weakness into an advantage. Subtract a run of identical pixels from itself and every byte becomes zero, and a long run of zero bytes is the input LZ77 handles best of all. The 128-byte result is nothing more than that: 272,640 identical pixels reduced to a filter choice per row and a handful of back-references. A photograph gives the filter nothing so easy, because neighbouring pixels differ by small amounts rather than by nothing, so the same encoder recovered 41% of the file and no more.
Nothing is lost in either direction
Both formats store their pixels exactly, so this conversion carries no generation cost and can be repeated without accumulating damage. Checked in this session on the sample file: the PNG was decoded again and compared channel by channel against the pixels the bitmap held, across 1,090,560 samples, and none of them differed. A 24-bit bitmap has no alpha channel, and the optimiser acts on that rather than padding the file out. Every output measured here declared colour type 2 in its IHDR chunk, truecolour with no alpha, at all three effort settings.
Padding, and the rows stored upside down
Two details catch anyone reading a bitmap by hand. Rows are padded out to a multiple of four bytes. At 640 pixels and 24 bits a row is 1,920 bytes and needs no padding at all; at 641 pixels it occupies 1,923 bytes, is padded to 1,924, and the file then carries one dead byte per row, 426 of them at the height of the sample. Reading successive rows at width times three, rather than at the padded stride, shears the picture diagonally by a growing offset. That shear is the usual first symptom of a hand-rolled reader.
The other detail is row order. A positive height in the header means the rows are stored from the bottom of the picture upwards, so the first row of pixel data is the bottom edge. A negative height means the opposite, top-down, and the absolute value is the real height. Most bitmaps in circulation are bottom-up, being the older convention, and a reader that ignores the sign of that field renders half its inputs vertically mirrored. The browser decoder behind this page handles both orders.
Questions
Does the bitmap leave this machine?
It does not, and the check takes about ten seconds. Open developer tools, select the Network tab, then drop a file. What appears is a short list of same-origin GET requests for this page's own JavaScript and for the WebAssembly PNG encoder, and then nothing further. No entry carries your pixels. Decoding and encoding both happen inside a Web Worker on your own processor, and the code on this page has no address to send an image to.
How much smaller will my own file be?
Entirely dependent on content, which is why the result line reports the ratio it actually achieved. Two figures measured here bracket the range: a photographic bitmap of 818,058 bytes came out at 484,922, while the same file filled with a single colour came out at 128. Screenshots, diagrams, charts and line art sit near the good end because they contain long runs of one colour. Photographs and scans sit near the other end, since sensor noise is close to incompressible. The only input that could come out larger is pure random data, where DEFLATE falls back on stored blocks of at most 65,535 bytes carrying five bytes of overhead each, about 0.008%.
What does the optimisation setting actually change?
How long oxipng spends trying filter and encoding combinations before keeping the smallest valid file. Measured on the 818 KB sample, the three settings wrote 518,863, 484,922 and 479,992 bytes, taking a median of 47 ms, 225 ms and 1.63 s across five runs on one desktop. The search stops paying above the middle of the range: levels 4 and 6 both returned 479,992 bytes, and level 3 came in 39 bytes under either of them. No setting alters a pixel, so the choice is time against a few percent of the output size.
Can this open a .dib file, or one saved from the clipboard?
A .dib on disk is normally a complete bitmap that happens to carry the older extension, opening with the same BM signature, and this page reads it. A packed DIB is different: the Windows clipboard format CF_DIB omits the fourteen-byte file header entirely, so the data begins at the BITMAPINFOHEADER and no signature exists to find. Files in that shape are recognised and refused with an explanation rather than reported as corrupt. Prepending a fourteen-byte BITMAPFILEHEADER makes one readable by every bitmap decoder there is.